diff --git a/.clang-format b/.clang-format index c433c9809ef..2d6d4493d07 100644 --- a/.clang-format +++ b/.clang-format @@ -113,6 +113,6 @@ SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: c++11 +Standard: c++17 TabWidth: 4 UseTab: Never diff --git a/CMakeLists.txt b/CMakeLists.txt index 55bf2bfca85..acab23d9641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,6 @@ qtc_link_with_qt() option(WITH_TESTS "Build Tests" ${ENV_QTC_WITH_TESTS}) add_feature_info("Build tests" ${WITH_TESTS} "") -option(WITH_QMLDESIGNER "Build QmlDesigner" ${ENV_QTC_WITH_QMLDESIGNER}) -add_feature_info("Build QmlDesigner and related code" ${WITH_QMLDESIGNER} "") option(WITH_DEBUG_CMAKE "Enabled CMake project debugging functionality" OFF) option(SHOW_BUILD_DATE "Show build date in about dialog" OFF) option(WITH_SANITIZE "Build with sanitizer enabled" OFF) @@ -73,6 +71,14 @@ find_package(Qt6 COMPONENTS Concurrent Core Gui Network PrintSupport Qml Sql Widgets Xml Core5Compat ${QT_TEST_COMPONENT} REQUIRED ) + +if (Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3) + option(WITH_QMLDESIGNER "Build QmlDesigner" ${ENV_QTC_WITH_QMLDESIGNER}) +else() + option(WITH_QMLDESIGNER "Build QmlDesigner" OFF) +endif() +add_feature_info("Build QmlDesigner and related code (only if Qt is 6.4.3 or newer)" ${WITH_QMLDESIGNER} "") + # hack for Qbs which still supports Qt5 and Qt6 if (TARGET Qt6::Core5CompatPrivate) if (CMAKE_VERSION VERSION_LESS 3.18) diff --git a/README.md b/README.md index 4bb9109cab0..7d6102f94f2 100644 --- a/README.md +++ b/README.md @@ -423,11 +423,11 @@ we thank the authors who made this possible: committee draft. It is compatible with C++11, but will use newer language features if they are available. - https://github.com/tcbrindle/span + https://github.com/martinmoene/span-lite QtCreator/src/libs/3rdparty/span - Copyright Tristan Brindle, 2018 + Copyright 2018-2021 Martin Moene Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) diff --git a/cmake/QtCreatorAPI.cmake b/cmake/QtCreatorAPI.cmake index 7c50189be83..5ae0e25bdc3 100644 --- a/cmake/QtCreatorAPI.cmake +++ b/cmake/QtCreatorAPI.cmake @@ -125,7 +125,7 @@ endfunction() function(add_qtc_library name) cmake_parse_arguments(_arg "STATIC;OBJECT;SHARED;SKIP_TRANSLATION;ALLOW_ASCII_CASTS;FEATURE_INFO;SKIP_PCH;EXCLUDE_FROM_INSTALL" "DESTINATION;COMPONENT;SOURCES_PREFIX;BUILD_DEFAULT" - "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES" ${ARGN} + "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;SYSTEM_INCLUDES;PUBLIC_INCLUDES;PUBLIC_SYSTEM_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES" ${ARGN} ) get_default_defines(default_defines_copy ${_arg_ALLOW_ASCII_CASTS}) @@ -198,7 +198,9 @@ function(add_qtc_library name) SOURCES_PREFIX ${_arg_SOURCES_PREFIX} SOURCES ${_arg_SOURCES} INCLUDES ${_arg_INCLUDES} + SYSTEM_INCLUDES ${_arg_SYTEM_INCLUDES} PUBLIC_INCLUDES ${_arg_PUBLIC_INCLUDES} + PUBLIC_SYSTEM_INCLUDES ${_arg_PUBLIC_SYSTEM_INCLUDES} DEFINES ${default_defines_copy} ${_arg_DEFINES} ${TEST_DEFINES} PUBLIC_DEFINES ${_arg_PUBLIC_DEFINES} DEPENDS ${_arg_DEPENDS} ${IMPLICIT_DEPENDS} @@ -322,7 +324,7 @@ function(add_qtc_plugin target_name) cmake_parse_arguments(_arg "SKIP_INSTALL;INTERNAL_ONLY;SKIP_TRANSLATION;EXPORT;SKIP_PCH" "VERSION;COMPAT_VERSION;PLUGIN_PATH;PLUGIN_NAME;OUTPUT_NAME;BUILD_DEFAULT;PLUGIN_CLASS" - "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PLUGIN_DEPENDS;PLUGIN_RECOMMENDS;PLUGIN_TEST_DEPENDS;PROPERTIES" + "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;SYSTEM_INCLUDES;PUBLIC_INCLUDES;PUBLIC_SYSTEM_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PLUGIN_DEPENDS;PLUGIN_RECOMMENDS;PLUGIN_TEST_DEPENDS;PROPERTIES" ${ARGN} ) @@ -465,7 +467,9 @@ function(add_qtc_plugin target_name) extend_qtc_target(${target_name} INCLUDES ${_arg_INCLUDES} + SYSTEM_INCLUDES ${_arg_SYSTEM_INCLUDES} PUBLIC_INCLUDES ${_arg_PUBLIC_INCLUDES} + PUBLIC_SYSTEM_INCLUDES ${_arg_PUBLIC_SYSTEM_INCLUDES} DEFINES ${DEFAULT_DEFINES} ${_arg_DEFINES} ${TEST_DEFINES} PUBLIC_DEFINES ${_arg_PUBLIC_DEFINES} DEPENDS ${_arg_DEPENDS} ${_DEP_PLUGINS} ${IMPLICIT_DEPENDS} diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index c91f5779836..95fb83207d4 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -244,13 +244,13 @@ function(update_resource_files_list sources) endforeach() endfunction() -function(set_public_includes target includes) +function(set_public_includes target includes system) foreach(inc_dir IN LISTS includes) if (NOT IS_ABSOLUTE ${inc_dir}) set(inc_dir "${CMAKE_CURRENT_SOURCE_DIR}/${inc_dir}") endif() file(RELATIVE_PATH include_dir_relative_path ${PROJECT_SOURCE_DIR} ${inc_dir}) - target_include_directories(${target} PUBLIC + target_include_directories(${target} ${system} PUBLIC $ $ ) @@ -463,7 +463,7 @@ function(extend_qtc_target target_name) cmake_parse_arguments(_arg "" "SOURCES_PREFIX;SOURCES_PREFIX_FROM_TARGET;FEATURE_INFO" - "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES;SOURCES_PROPERTIES" + "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;SYSTEM_INCLUDES;PUBLIC_INCLUDES;PUBLIC_SYSTEM_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES;SOURCES_PROPERTIES" ${ARGN} ) @@ -504,8 +504,10 @@ function(extend_qtc_target target_name) PUBLIC ${_arg_PUBLIC_DEFINES} ) target_include_directories(${target_name} PRIVATE ${_arg_INCLUDES}) + target_include_directories(${target_name} SYSTEM PRIVATE ${_arg_SYSTEM_INCLUDES}) - set_public_includes(${target_name} "${_arg_PUBLIC_INCLUDES}") + set_public_includes(${target_name} "${_arg_PUBLIC_INCLUDES}" "") + set_public_includes(${target_name} "${_arg_PUBLIC_SYSTEM_INCLUDES}" "SYSTEM") if (_arg_SOURCES_PREFIX) foreach(source IN LISTS _arg_SOURCES) diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 1024ddc0278..f806b1dff18 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -35,6 +35,8 @@ macro.QMLD = "Qt Quick Designer" macro.QQV = "Qt QML Viewer" macro.QSDK = "Qt" macro.QUL = "Qt Quick Ultralite" +macro.QOI = "Qt Online Installer" +macro.QMT = "Qt Maintenance Tool" macro.qtcversion = $QTC_VERSION macro.param = "\\e" macro.raisedaster.HTML = "*" diff --git a/doc/qtcreator/src/conan/creator-projects-conan.qdoc b/doc/qtcreator/src/conan/creator-projects-conan.qdoc index f081c850d22..2309f0453b5 100644 --- a/doc/qtcreator/src/conan/creator-projects-conan.qdoc +++ b/doc/qtcreator/src/conan/creator-projects-conan.qdoc @@ -31,7 +31,7 @@ sources. Because the client has a local cache for package storage, you can work offline, as long as no new packages are needed from remote servers. - To use Conan, install it by using the Qt installer or the tools that + To use Conan, install it by using \QOI or the tools that your operating system has. For example, on Windows, you can use the \c {choco install conan} or \c {pip install conan} command. diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc index cbf6925ed60..564f93675f2 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc @@ -169,7 +169,7 @@ \section2 Debugging Tools for Windows To use the CDB debugger, install the \e {Debugging Tools for Windows} when - you install \QC either by using the Qt Online Installer (in \uicontrol Qt + you install \QC either by using \QOI (in \uicontrol Qt > \uicontrol Tools > \uicontrol {\QC}) or by using the stand-alone \QC installation packages. diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index 67a5ba9bbbb..25172cf94d3 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -102,21 +102,13 @@ \e{Windows} - Check whether \QC has been compiled with OpenGL/Desktop, or ANGLE as - a backend. The official binaries are always built with ANGLE (a library that - maps OpenGL ES API to DirectX). + By default Qt Quick uses Direct3D 11. If you have problems, try updating + your graphics drivers or update your DirectX version. Run \c dxdiag.exe to + check whether \e{Direct3D Acceleration} is indeed enabled. - \list - - \li ANGLE backend: This requires a Windows version newer than Windows XP. - If you have problems, try updating your graphics drivers or update - your DirectX version. Run \c dxdiag.exe to check whether - \e{Direct3D Acceleration} is indeed enabled. - - \li OpenGL backend: Make sure your graphics driver supports OpenGL 2.1 or - newer. Try to update your graphics driver. - - \endlist + You can also try setting the \c QSG_RHI_BACKEND environment variable to + \c vulkan or \c opengl. See \l {Qt for Windows - Graphics Acceleration} + for details. \e{Unix} diff --git a/doc/qtcreator/src/howto/creator-telemetry.qdoc b/doc/qtcreator/src/howto/creator-telemetry.qdoc index de7c887583e..128d358910b 100644 --- a/doc/qtcreator/src/howto/creator-telemetry.qdoc +++ b/doc/qtcreator/src/howto/creator-telemetry.qdoc @@ -83,7 +83,7 @@ The data is transmitted to the backend storage using an encrypted connection. The storage is located in the same Heroku backend as the - Qt installer backend. Physically, data is stored in the Amazon cloud. + \QOI backend. Physically, data is stored in the Amazon cloud. \section1 Specify telemetry settings diff --git a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc index e60f48a5e70..1d6ec78e30e 100644 --- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc @@ -22,10 +22,10 @@ \endlist You must install the tool chain for building applications for the targeted - embedded platform on the development PC and use the Qt Maintenance Tool to + embedded platform on the development PC and use \QMT to install Qt libraries that are built for the platform. You can then add a \l{glossary-buildandrun-kit}{kit} with the tool chain and the Qt version - for the device's architecture. When possible, the Maintenance Tool creates + for the device's architecture. When possible, \QMT creates suitable kits for you. You can connect embedded devices to the development PC to run, debug, and diff --git a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc index 012a5d5e2eb..f2ad72b6402 100644 --- a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc +++ b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc @@ -54,7 +54,7 @@ \section2 MCU Plugin To be able to develop applications for MCUs, you need the MCU plugin. - This plugin is enabled automatically by the Qt Installer when you + This plugin is enabled automatically by \QOI when you install \QMCU. \section2 Specifying MCU Settings diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index a5e2feb5efb..0e5b2a60ab2 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -376,7 +376,7 @@ the C++20 committee draft. It is compatible with C++11, but will use newer language features if they are available. - Copyright Tristan Brindle, 2018 + Copyright 2018-2021 Martin Moene Distributed under the \l {http://boost.org/LICENSE_1_0.txt} {Boost Software License, Version 1.0}. @@ -384,7 +384,7 @@ The source code can be found here: \list - \li \l{https://github.com/tcbrindle/span} + \li \l{https://github.com/martinmoene/span-lite} \li QtCreator/src/libs/3rdparty/span \li \l{https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/src/libs/3rdparty/span} \endlist @@ -394,180 +394,6 @@ Roberto Raggi \br QtCreator/src/libs/3rdparty/cplusplus\br\br - \li \b{ANGLE Library (Windows)} - - Used on Windows to implement OpenGL ES on top of DirectX. - - The source code of ANGLE is part of the Qt libraries. For more - information about the licenses used in Qt GUI, see - \l{https://doc.qt.io/qt-5.11/licenses-used-in-qt.html#qt-gui}{Qt GUI}. - - \l{https://spdx.org/licenses/BSD-3-Clause.html} - {BSD 3-clause "New" or "Revised" License} - - \badcode - Copyright (C) 2002-2013 The ANGLE Project Authors. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their contributors may be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - \endcode - - \li \b{ANGLE Array Bounds Clamper for WebKit (Windows)} - - Implements clamping of array indexing expressions during shader - translation. - - Used on Windows to implement OpenGL ES on top of DirectX. Configure with - \c {-no-opengl}, or \c {-opengl desktop} to exclude. - - The sources can be found in - \c qtbase/src/3rdparty/angle/src/third_party/compiler. - - BSD 2-clause "Simplified" License. - - \badcode - Copyright (C) 2012 Apple Inc. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, INC. OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - \endcode - - \li \b{ANGLE: Murmurhash (Windows)} - - Used on Windows to implement OpenGL ES on top of DirectX. Configure - with \c {-no-opengl}, or \c {-opengl desktop} to exclude. - - The sources can be found in - \c qtbase/src/3rdparty/angle/src/third_party/murmurhash. - - \badcode - MurmurHash3 was written by Austin Appleby, and is placed in the public - domain. The author hereby disclaims copyright to this source code. - \endcode - - Public Domain. - - \li \b{ANGLE: Systeminfo (Windows)} - - Used on Windows to implement OpenGL ES on top of DirectX. Configure - with \c {-no-opengl}, or \c {-opengl desktop} to exclude. - - The sources can be found in - \c qtbase/src/3rdparty/angle/src/third_party/systeminfo. - - BSD 2-clause "Simplified" License. - - \badcode - Copyright (C) 2009 Apple Inc. All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - \endcode - - \li \b{ANGLE: trace_event (Windows)} - - Used on Windows to implement OpenGL ES on top of DirectX. Configure - with \c {-no-opengl}, or \c {-opengl desktop} to exclude. - - The sources can be found in - \c qtbase/src/3rdparty/angle/src/third_party/trace_event. - - BSD 3-clause "New" or "Revised" License. - - \badcode - Copyright 2013 The Chromium Authors. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - \endcode - \li \b{SQLite (version 3.8.10.2)} SQLite is a C-language library that implements a small, fast, @@ -586,14 +412,6 @@ \li \l{https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/src/libs/3rdparty/sqlite} \endlist - \li \b{three.js} - - Copyright (C) 2010-2015 three.js authors - - MIT License. - - share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication - \li \b{OpenSSL} The OpenSSL toolkit stays under a double license, i.e. both the conditions of diff --git a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc index 62140a8733a..5fb9f2886cd 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc @@ -34,13 +34,13 @@ \section1 Checking Build and Run Settings \QC is an integrated development environment (IDE) that you can use to - develop Qt applications. While you can use the Qt Installer to install \QC, + develop Qt applications. While you can use \QOI to install \QC, the stand-alone \QC installer never installs Qt or any Qt tools, such as qmake. To use \QC for Qt development, you also need to install a Qt version and a compiler. If you update the compiler version later, you can register it into \QC. - The Qt Installer attempts to auto-detect compilers and Qt versions. If it + \QOI attempts to auto-detect compilers and Qt versions. If it succeeds, the relevant kits will automatically become available in \QC. If it does not, you must add the kits yourself to tell \QC where everything is. diff --git a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc index a7ae08afbac..c6da02cd9c7 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-getting-started.qdoc @@ -46,8 +46,8 @@ \row \li \b {\l{Building and Running an Example}} - To check that the \l{https://www.qt.io/download-qt-installer} - {Qt Online Installer} created \l{glossary-buildandrun-kit} + To check that \l{https://www.qt.io/download-qt-installer} + {\QOI} created \l{glossary-buildandrun-kit} {build and run kits}, open an example application and run it. If you have not done so before, go to \l{Building and Running an Example}. diff --git a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc index fa9ef13bf9c..5b3fbeaecc8 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc @@ -19,10 +19,10 @@ \endlist You must install the tool chain for building applications for the targeted - mobile platform on the development PC and use the Qt Maintenance Tool to + mobile platform on the development PC and use \QMT to install Qt libraries that are built for the platform. You can then add a \l{glossary-buildandrun-kit}{kit} with the tool chain and the Qt version - for the device's architecture. When possible, the Maintenance Tool creates + for the device's architecture. When possible, \QMT creates suitable kits for you. You can connect mobile devices to the development PC and select the diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc index 525e4e2f04d..b156fef7ac0 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc @@ -20,7 +20,7 @@ specifies the compiler and other necessary tools for building an application for and running it on a particular platform. - \QC automatically detects the compilers that your system or the Qt Installer + \QC automatically detects the compilers that your system or \QOI registers and lists them in \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > \uicontrol Compilers. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc index 87295563945..704a38ff436 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc @@ -28,7 +28,7 @@ To remove invalid Qt versions, select \uicontrol {Clean Up}. - You can link to a Qt that the Qt Installer installed to + You can link to a Qt that \QOI installed to automatically detect the installed Qt versions. However, you cannot link to a Qt that the system installed with some other package manager, such as your Linux distribution, brew on \macos, or Chocolatey on @@ -112,7 +112,7 @@ output exist. When \QC complains about the installation of a self-built Qt version, try running \c {make install} in the build directory to actually install Qt into the configured location. If you installed Qt using the Qt - Installer, run the Qt maintenance tool to check for updates or to reinstall + Installer, run \QMT to check for updates or to reinstall the Qt version. \section1 Minimum Requirements diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc index 108b69477f2..82bad206b69 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc @@ -14,8 +14,8 @@ \title Configuring Projects - When you install Qt for a target platform, such as Android or QNX, the - \l{https://www.qt.io/download-qt-installer}{Qt Online Installer} + When you install Qt for a target platform, such as Android or QNX, + \l{https://www.qt.io/download-qt-installer}{\QOI} creates \l{glossary-buildandrun-kit}{kits} for the development targets. diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc index 3188f73d200..e952d2a1e9b 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc @@ -83,8 +83,8 @@ \note If you have not installed the Qt Virtual Keyboard module when you installed Qt, an error message will appear when you try to open - \e Main.qml for editing. You can use the \l {Installing Qt} - {Qt Maintenance Tool} to install Qt Virtual Keyboard. + \e Main.qml for editing. You can use \l {Installing Qt} + {\QMT} to install Qt Virtual Keyboard. \li Select \uicontrol Next to open the \uicontrol {Kit Selection} dialog. diff --git a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc b/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc index 158aac049cb..60b742e92da 100644 --- a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc @@ -119,6 +119,9 @@ name of the main QML file in the Qt Quick UI project. \li Select \uicontrol Build > \uicontrol Run to build and run your project. + + \note If you get error messages related to modules, perfom the steps + described in \l{Adding Qt Quick Designer Components to Qt Installations}. \endlist For example, if you copy the source files of the \e ProgressBar diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index 16550f0ee2e..1b34918ba89 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -30,6 +30,11 @@ You can use the \l{http://code.google.com/p/gerrit/}{Gerrit} code review tool for projects that use Git. + \if defined(qtdesignstudio) + \include creator-vcs-options.qdocinc vcs options + \endif + + \if defined(qtcreator) \section1 Using Git for Windows If you configure Git for use with \c {git bash}, only, and use SSH @@ -48,6 +53,16 @@ authorization works as it would with \c {git bash}. \image qtcreator-preferences-vcs-git.webp {Git preferences} + \endif + + \section1 Initializing Git Repositories + + To start controlling a project directory that is currently not under + version control, select \uicontrol Tools > \uicontrol Git > + \uicontrol {Create Repository}. \QC creates a new subdirectory named .git + that has all the necessary repository files. However, Git does not track + anything in the project yet, so you will need to create an initial commit to + start tracking the project files. \section1 Working with the Current File @@ -183,7 +198,7 @@ \section2 Cleaning Projects - To clean the working directory, select \uicontrol {Build Project} > \uicontrol {Clean}. + To clean the working directory, select \uicontrol {Clean Project}. All files that are not under version control are displayed in the \uicontrol {Clean Repository} dialog. Ignored files are deselected by default. Select the files to delete, and then select \uicontrol Delete. @@ -465,15 +480,6 @@ \li Show the commit in the diff editor. \endtable - \section1 Initializing Git Repositories - - To start controlling a project directory that is currently not under - version control, select \uicontrol Tools > \uicontrol Git > - \uicontrol {Create Repository}. \QC creates a new subdirectory named .git - that has all the necessary repository files. However, Git does not track - anything in the project yet, so you will need to create an initial commit to - start tracking the project files. - \section1 Working with Remote Repositories To work with remote repositories, select the commands in \uicontrol Tools > diff --git a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc index c05f84832e5..64f8cd0e75e 100644 --- a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc +++ b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc @@ -39,7 +39,7 @@ \section1 Setting Up the Development Environment You need to install and configure Qt for WebAssembly and the tool chain for - compiling to WebAssembly. The Qt Installer automatically adds a build and + compiling to WebAssembly. \QOI automatically adds a build and run kit to \QC. \section2 Setting Up Qt for WebAssembly @@ -47,7 +47,7 @@ To set up Qt for WebAssembly: \list 1 - \li Use the Qt maintenance tool to install Qt for WebAssembly and, on + \li Use \QMT to install Qt for WebAssembly and, on Windows, \MinGW (found in \uicontrol {Developer and Designer Tools}). \li Check out a known-good Emscripten version supported by the Qt for WebAssembly version that you installed, and install and activate diff --git a/doc/qtcreatordev/src/getting-and-building.qdoc b/doc/qtcreatordev/src/getting-and-building.qdoc index 7079e9f0558..5fe3b53f21e 100644 --- a/doc/qtcreatordev/src/getting-and-building.qdoc +++ b/doc/qtcreatordev/src/getting-and-building.qdoc @@ -44,9 +44,9 @@ When developing your plugin, point the \c {CMAKE_PREFIX_PATH} to the installation location of \QC, or the \QC app on macOS. - Get prebuilt packages either from the + Get prebuilt packages either from \l{https://download.qt.io/official_releases/online_installers/} - {Qt online installer}, or a standalone \QC installer either for a + {\QOI}, or a standalone \QC installer either for a \l{https://download.qt.io/official_releases/qtcreator/} {released \QC version} or a \l{https://download.qt.io/snapshots/qtcreator/} {development snapshot}. diff --git a/doc/qtdesignstudio/images/icons/snapping-3d-conf.png b/doc/qtdesignstudio/images/icons/snapping-3d-conf.png new file mode 100644 index 00000000000..bf57f857be1 Binary files /dev/null and b/doc/qtdesignstudio/images/icons/snapping-3d-conf.png differ diff --git a/doc/qtdesignstudio/images/icons/snapping-3d.png b/doc/qtdesignstudio/images/icons/snapping-3d.png new file mode 100644 index 00000000000..a434305e257 Binary files /dev/null and b/doc/qtdesignstudio/images/icons/snapping-3d.png differ diff --git a/doc/qtdesignstudio/images/qmldesigner-mcu-support.png b/doc/qtdesignstudio/images/qmldesigner-mcu-support.png deleted file mode 100644 index 98ee56b2a3e..00000000000 Binary files a/doc/qtdesignstudio/images/qmldesigner-mcu-support.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/qt-insight-view.png b/doc/qtdesignstudio/images/qt-insight-view.png new file mode 100644 index 00000000000..7e814f12342 Binary files /dev/null and b/doc/qtdesignstudio/images/qt-insight-view.png differ diff --git a/doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp b/doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp new file mode 100644 index 00000000000..1bd070f8213 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp differ diff --git a/doc/qtdesignstudio/images/studio-preset-for-mcus.png b/doc/qtdesignstudio/images/studio-preset-for-mcus.png new file mode 100644 index 00000000000..0177791ffd2 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-preset-for-mcus.png differ diff --git a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc index fc6ccdcf3cb..74c9eb142ce 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc @@ -107,8 +107,6 @@ > \uicontrol {My Components}, and you can use instances of them to build more components. - \include qtquick-mcu-support.qdocinc mcu qtquick components - \section1 Merging Files with Templates You can merge the current component file against an existing second diff --git a/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc b/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc deleted file mode 100644 index edd75e3a467..00000000000 --- a/doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! -//! [mcu qtquick components] - - \section1 Creating UIs for MCUs - - \l{\QMCU} enables you to use subsets of components to create UIs for - devices that are powered by microcontroller units (MCU). The subset of - supported components depends on the \QMCU version that you use for - development. In this manual, we indicate which components are supported at - the time of writing. - - To develop for MCUs, \l{Creating a Project}{create an MCU project}. Only - the components available on MCUs are displayed in \l Components. Only a - subset of properties is supported for the supported components. The - properties that are not available on MCUs are marked in the \l Properties - view with strikethrough text. - - \image qmldesigner-mcu-support.png "Components and Text properties supported for MCUs" - - For more information about the supported components and their properties, - see \l{\QMCU - All QML Types}. - -//! [mcu qtquick components] -*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc index 2a0e49cda65..9dad2fd4057 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage studio-on-mcus.html + \previouspage studio-mcu-framework.html \page studio-compatibility-with-mcu-sdks.html \nextpage studio-features-on-mcu-projects.html diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc new file mode 100644 index 00000000000..0195a6e62af --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc @@ -0,0 +1,30 @@ +/ Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-connecting-mcus-with-creator.html + \previouspage studio-developing-apps-for-mcus.html + \nextpage studio-help.html + + \title Connecting MCUs with Qt Creator + + \l {Connecting MCUs} {Connect MCU boards} to a development host to + build applications for them using the GNU Arm Embedded GCC compiler, libraries, + and other GNU tools necessary for BareMetal software development on devices + based on the Arm Cortex-M processors. Deploy the applications on MCUs to run + and debug them using Qt Creator. + + The toolchains are available for cross-compilation on Microsoft Windows, + Linux, and macOS. However, the \QMCU SDK is currently only available for + Windows and Linux. + + For more information on how to manage the complete cycle of developing \QMCU + applications using Qt tools, see: + + \list + \li \l {Infineon Traveo II quick start guide} + \li \l {NXP i.MX RT1170 quick start guide} + \li \l {Renesas EK-RA6M3G quick start guide} + \li \l {Renesas RH850-D1M1A quick start guide} + \endlist +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc new file mode 100644 index 00000000000..40995ec84a9 --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc @@ -0,0 +1,148 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage studio-features-on-mcu-projects.html + \page studio-projects-for-mcus.html + \nextpage studio-creating-uis-for-mcus.html + + \title Creating Projects for MCUs + + Use the \uicontrol {\QMCU} preset in the \QDS wizard to set up a new \QMCU + project. When you create a project with the wizard, all the necessary files + are created, you can adjust the project settings, and save custom presets. + + \image studio-preset-for-mcus.png + + Using the \uicontrol {\QMCU} preset creates an application that uses a subset + of the default components that you can deploy, run, and debug on MCU boards. + + \note For more information on the default components available for MCU + projects, see \l {Qt Design Studio Features on MCU Projects}. + + \section1 Creating an MCU Project + + To create an MCU project: + + \list 1 + \li Select \uicontrol {File} > \uicontrol {New Project}. + \li In the \uicontrol {Presets} tab, select the \uicontrol {\QMCU} preset. + \li In the \uicontrol {Details} tab: + \list + \li Select the path for the project files. You can move the project + folders later. + \li Set the screen size to match the device screen, which also enables + previewing on the desktop. You can change the screen size later in + \l {Properties}. + \endlist + \li Select \uicontrol {Create} to create the project. + \endlist + + \QDS creates the following files and folders: + + \list + \li .qmlproject project file defines that all component and image files + in the project folder belong to the project. All files are added + automatically to their respective Files node based on their + type. + \note \QMCU does not recommend using the directory property to + individually list the files in the project. + \li .qml files define the functionality and appearance of application + components. + \li \e Screen01.ui.qml defines a custom component that you can edit in + the \l {2D} view. For more information, see \l {UI Files}. + + While the custom component is a good starting point for new users, + you don't have to use it. Specifically, if you export and import + designs using \QB, your file is most likely called something + else. For more information, see \l {Exporting from Design Tools}. + + \note For MCU projects you can only import 2D assets. + \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 + \uicontrol Assets. + \li \e imports folder contains a \e {Constants.qml} file that specifies + a font loader for the Arial font and the screen resolution. The size + of the default Screen.ui.qml \l{basic-rectangle}{Rectangle} should + be set as \c {width: Constants.width} & \c {height: Constants.height} + so that it inherits the global resolution saved here. + \li \e MCUDefaultStyle folder contains the default UI images and + components available for the MCU project. + \endlist + + To use image files in the UI, select \uicontrol Assets > \inlineimage icons/plus.png + . + + \sa {Using Custom Presets} + + \section1 Adding Files to MCU Projects + + You can use wizard templates to add individual files to projects. + + The wizard templates in the \uicontrol {Qt Quick Controls} category create + stylable versions of the components in the \uicontrol {Qt Quick Controls} + module. For more information, see \l{Creating Custom Controls}. + + You can create the following types of files: + + \table + \header + \li Category + \li Wizard Template + \li Purpose + \row + \li {1,5} Qt Quick Files + \row + \li Qt Quick File + \li Generates a component with one of the following default components + or \l{Using Positioners}{positioners} as the root component: + \l {basic-item}{Item}, \l {basic-rectangle}{Rectangle}, \l {Images} + {Image}, \l {Border Image}, \l Flickable, Row, Column, Flow, or + Grid. + \row + \li Qt Quick UI File + \li Generates a UI file with one of the above components as the root + component. + \row + \li Qt Quick Views + \li Generates a List View. For more information, see + \l{List and Grid Views}. + \row + \li Qt Quick UI Form + \li Creates a UI file along with a matching QML file for + implementation purposes. + \row + \li {1,8} Qt Quick Controls + \li Custom Button + \li Creates a \l {Button}{push button} with a text label. + \row + \li Custom \CheckBox + \li Creates a \l {Check Box}{check box}. + \row + \li Custom Dial + \li Creates a \l {Slider and Dial}{dial}. + \row + \li Custom Slider + \li Creates a \l {Slider and Dial}{slider}. + \row + \li Custom \SpinBox + \li Creates a \l {Spin Box}{spin box}. + \row + \li Custom Switch + \li Creates a \l {Switch}{switch} with on and off states. + \row + \li \l Pane + \li Provides a background that matches the UI style and theme. + \row + \li SwipeView + \li Enables users to navigate pages by swiping sideways. + \row + \li QML Files + \li ListModel + \li Adds a \l{Editing List Models}{list model} to the project. + \endtable +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc new file mode 100644 index 00000000000..dbcb90269ba --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc @@ -0,0 +1,52 @@ +/ Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-creating-uis-for-mcus.html + \previouspage studio-projects-for-mcus.html + \nextpage studio-developing-apps-for-mcus.html + + \title Creating UIs for MCUs + + As a technical artist or a designer, you can use specialized UI design tools, + such as Adobe Photoshop, Sketch, Figma, Blender, or Maya, to create the + original UI design files for your MCU application. After the initial design + work, export your design from the design tools, and import your 2D and 3D UI + design assets into \QDS, which can convert them into code for developers. + For more information on managing the original assets created with + specialized UI design tools, see \l {Asset Creation with Other Tools}. + + Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe} + your MCU application, to visualize its structure. To modify the look and feel + of your UI further, utilize the preset UI components available in \QDS. + + \section1 Using MCU Components + + With \QDS, you can use subsets of components to create UIs for + devices that are powered by microcontroller units (MCU). The subset of + supported components depends on the \QMCU version that you use for + development. + + To develop for MCUs, \l{Creating Projects for MCUs}{create an MCU project}. + Only the components available on MCUs are displayed in \l Components. Also, + only a subset of properties is supported for the supported components. The + properties that are not available on MCUs are marked in the \l Properties + view with strikethrough text. + + \image studio-mcu-components-and-properties.webp "Components and Text properties supported for MCUs" + + For more information on the supported views and features, see \l{\QDS Features on MCU Projects}. + + For an example on how to create a UI that runs both on the desktop and + on MCUs, see \l {Washing Machine UI}. For step-by-step instructions on how + to use \QDS to design a UI for a specific MCU target device, see: + + \list + \li \l {Designing a UI for Infineon Traveo II} + \li \l {Designing a UI for NXP i.MX RT1170} + \li \l {Designing a UI for Renesas RA6M3G} + \li \l {Designing a UI for Renesas RH850-D1M1A} + \endlist + + \sa {Specifying Component Properties} +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc new file mode 100644 index 00000000000..187de26c31e --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc @@ -0,0 +1,28 @@ +/ Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-developing-apps-for-mcus.html + \previouspage studio-creating-uis-for-mcus.html + \nextpage studio-connecting-mcus-with-creator.html + + \title Developing Applications for MCUs + + As a GUI/application developer, use \QDS to bring your designs to life. Add + further functionality to your applications and utilize the \l {Prototyping} + {prototyping} features of \QDS to simulate and validate interactions and + their dynamic behavior. + + You can also test, preview, and fine-tune your designs to pixel-perfection + live on the desktop or on an actual MCU target device. For more information, + see \l {Validating with Target Hardware}. + + \image qds-mcu-target-deployment.png + + With \QDS, designers and developers can work together on common projects to + develop applications. As a designer you can use the views in the \e Design + mode to modify UI files (.ui.qml). As a developer you can use Qt Creator to + work on the Qt Quick (.qml) and other files that are needed to implement the + application logic and to prepare the application for production. For more + information, see \l {Implementing Applications}. +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc index 5cec8d690e4..89c1051cf90 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc @@ -4,7 +4,7 @@ /*! \previouspage studio-compatibility-with-mcu-sdks.html \page studio-features-on-mcu-projects.html - \nextpage studio-help.html + \nextpage studio-projects-for-mcus.html \title \QDS Features on MCU Projects diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc new file mode 100644 index 00000000000..0df424505f7 --- /dev/null +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc @@ -0,0 +1,27 @@ +/ Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-mcu-framework.html + \previouspage studio-on-mcus.html + \nextpage studio-compatibility-with-mcu-sdks.html + + \title \QMCU Framework + + \QMCU is a comprehensive framework that supports various hardware ecosystems + and platforms. One of the most important libraries provided by the \QMCU + framework is \QUL (QUL), a lightweight implementation of the Qt Quick + framework. \QUL provides a QML API and an efficient graphics rendering engine + that has a low memory footprint and is optimized for MCUs and other + resource-constrained devices. + + In addition to a lightweight graphics framework, \QMCU offers a toolkit that + enables you to design, develop, and deploy graphical user interfaces (GUI) + on microcontrollers (MCU). Also, it lets you run the applications either + on BareMetal or a real-time operating system (RTOS). + + \note In addition to BareMetal and RTOS, you can use the desktop kit to run + and test the application on desktop without flashing it each time. + + For more information on \QMCU, see \l {\QMCU documentation}. +*/ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc index a16df932c91..2db39fb06dd 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc @@ -4,89 +4,49 @@ /*! \previouspage creator-editor-external.html \page studio-on-mcus.html - \nextpage studio-compatibility-with-mcu-sdks.html + \nextpage studio-mcu-framework.html \title \QDS on MCUs - \QMCU is a comprehensive framework that supports various hardware ecosystems - and platforms. One of the most important libraries provided by the \QMCU - framework is \QUL (QUL), a lightweight implementation of the Qt Quick - framework. \QUL provides a QML API and an efficient graphics rendering engine - that has a low memory footprint and is optimized for MCUs and other - resource-constrained devices. - - In addition to a lightweight graphics framework, \QMCU offers a toolkit that - enables you to design, develop, and deploy graphical user interfaces (GUI) - on microcontrollers (MCU). Also, it lets you run the applications either - on BareMetal or a real-time operating system (RTOS). - - For more information on \QMCU, see \l {\QMCU documentation}. - - \section1 Designing application UIs for MCU devices with \QDS - - As a technical artist or a designer you can use specialized UI design tools, - such as Adobe Photoshop, Sketch, Figma, Blender, or Maya to create the - original UI design files for your MCU application. After the initial design - work, export your design from the design tools, and import your 2D and 3D UI - design assets into \QDS, which can convert them into code for developers. - For more information on managing the original assets created with - specialized UI design tools, see \l {Asset Creation with Other Tools}. - - Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe} - your MCU application, to visualize its structure. To modify the look and feel - of your UI further, utilize the preset UI components available in \QDS. - - For an example on how to create a UI that runs both on the desktop and - on MCUs, see \l {Washing Machine UI}. For step-by-step instructions on how - to use \QDS to design a UI for a specific MCU target device, see: + \table + \row + \li \image qds-front-gs.png + \li With \QDS, you can use subsets of components to create UIs + for devices that are powered by microcontroller units (MCU). Use + the \uicontrol {\QMCU} preset in the \QDS wizard to set up a new + \QMCU project. Using the \uicontrol {\QMCU} preset creates an + application that utilizes a subset of the default components that + you can deploy, run, and debug on MCU boards. + \endtable \list - \li \l {Designing a UI for Infineon Traveo II} - \li \l {Designing a UI for NXP i.MX RT1170} - \li \l {Designing a UI for Renesas RA6M3G} - \li \l {Designing a UI for Renesas RH850-D1M1A} - \endlist + \li \l {\QMCU Framework} - \section1 Developing applications for MCU devices with \QDS + Provides an overview of the \QMCU framework. + \li \l {\QDS Version Compatibility with \QMCU SDKs} - As a GUI/application developer, use \QDS to bring your designs to life. Add - further functionality to your applications and utilize the \l {Prototyping} - {prototyping} features of \QDS to simulate and validate interactions and - their dynamic behavior. + Lists how the \QDS versions match with particular \QMCU SDKs. + \li \l {\QDS Features on MCU Projects} - You can also test, preview, and fine-tune your designs to pixel-perfection - live on the desktop or on an actual MCU target device. For more information, - see \l {Validating with Target Hardware}. + Specifies how the \QDS features are supported for developing MCU projects. + \li \l {Creating Projects for MCUs} - \image qds-mcu-target-deployment.png + Describes how to use the \QDS wizard and \uicontrol {\QMCU} preset + to set up a new \QMCU project. + \li \l {Creating UIs for MCUs} - \QDS enables designers and developers to work together on common projects to - develop applications. Designers can use the views in the Design mode to modify - UI files (.ui.qml), whereas developers can use Qt Creator to work on the Qt - Quick (.qml) and other files that are needed to implement the application - logic and to prepare the application for production. For more information, - see \l {Implementing Applications}. + Provides an overview of how to create UIs for MCUs from a technical + artist/designer perspective. Also, offers links to detailed instructions + for designing UIs for specific MCU target devices. + \li \l {Developing applications for MCUs} - \section1 Connecting MCUs with Qt Creator - - \l {Connecting MCUs} {Connect MCU boards} to a development host to - build applications for them using the GNU Arm Embedded GCC compiler, libraries, - and other GNU tools necessary for BareMetal software development on devices - based on the Arm Cortex-M processors. Deploy the applications on MCUs to run - and debug them using Qt Creator. - - The toolchains are available for cross-compilation on Microsoft Windows, - Linux, and macOS. However, the \QMCU SDK is currently only available for - Windows and Linux. - - For more information on how to manage the complete cycle of developing \QMCU - applications using Qt tools, see: - - \list - \li \l {Infineon Traveo II quick start guide} - \li \l {NXP i.MX RT1170 quick start guide} - \li \l {Renesas EK-RA6M3G quick start guide} - \li \l {Renesas RH850-D1M1A quick start guide} - \endlist + Provides an overview of how to develop, prototype, validate, and + implement applications for MCUs from a GUI/application developer + perspective. + \li \l {Connecting MCUs with Qt Creator} + Describes how to connect MCUs with Qt Creator. Also, offers links to + detailed instructions on how to manage the complete cycle of developing + \QMCU applications using Qt tools. + \endlist */ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index 1af2351e7da..5dd5f6d8bdf 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -170,6 +170,36 @@ \c {QtQuick.Studio.Components 1.0}. You can add components from all the available modules in \QDS later. You can also import a module as an \e alias. + \row + \li \uicontrol {Properties} + \li Specify new properties or assign values to the existing properties of + the component. You can also add and modify properties in \QDS. + The following are a few examples of the properties: + \code + property int counter: 5 + property string label: "ok" + antialiasing : true + width: parent.width / 2 + \endcode + To remove a property, write a "dash" (-) followed by the "property name". + For example: + \code + - width + \endcode + will remove the property \e width from the generated code. + \row + \li \uicontrol {Snippet} + \li Specify component to be added as child under this component. + Following example adds a Connection component: + \code + Connections { + target: myItem + onVisibleChanged: console.log(original_Text.visible) + } + \endcode + \note The code must have a scope of a component(e.g. Item, MouseArea, + Connections etc.) with a valid syntax for \l {UI Files}. + \note Add respective imports for your snippet in \uicontrol {Imports}. \row \li \uicontrol Alias \li Exports the component generated from this layer as an alias in the diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc index 560c8232d27..7c62a994f8a 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc @@ -21,7 +21,7 @@ \section1 2D Assets - You can use the Qt Installer to install \QB if you have a + You can use \QOI to install \QB if you have a \QDS enterprise license. \table diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc index e2e8e8f389e..864e12c9e47 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc @@ -10,7 +10,7 @@ \QBPS is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. - You can use the Qt Installer to have the \QBPS plugin package copied to the + You can use \QOI to have the \QBPS plugin package copied to the following path in your Qt installation folder: \list diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc index e3b634a05d6..ed046b416fc 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc @@ -187,8 +187,22 @@ Components 1.0, you need the import statement \c {QtQuick.Studio.Components 1.0}. You can also import a module as an alias. - \li In the \uicontrol {Properties} field, specify properties for - the component. You can add and modify properties in \QDS. + \li In the \uicontrol {Properties} field, specify new properties or assign + values to the existing properties of the component. You can also add and modify + properties in \QDS. + The following are a few examples of the properties: + \code + property int counter: 5 + property string label: "ok" + antialiasing : true + width: parent.width / 2 + \endcode + To remove a property, write a "dash" (-) followed by the "property name". + For example: + \code + - width + \endcode + will remove the property \e width from the generated code. \li Select the \uicontrol {Clip Contents} check box to enable clipping in the type generated from the layer. The generated type will clip its own painting, as well as the painting of its children, to its diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc index b3a8ac1e970..9fbccf11714 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc @@ -10,7 +10,7 @@ \QBSK is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. - You can use the Qt Installer to have the \QBSK plugin package copied to the + You can use \QOI to have the \QBSK plugin package copied to the following path in your Qt installation folder: \list diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc index 50bfff87060..27b1e0e8b96 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc @@ -209,8 +209,22 @@ \c {QtQuick.Controls 2.3} and to use Qt Quick Studio Components 1.0, you need the import statement \c {QtQuick.Studio.Components 1.0}. You can also import a module as an alias. - \li In the \uicontrol {Properties} field, specify properties for the - component. You can add and modify properties in \QDS. + \li In the \uicontrol {Properties} field, specify new properties or assign + values to the existing properties of the component. You can also add and modify + properties in \QDS. + The following are a few examples of the properties: + \code + property int counter: 5 + property string label: "ok" + antialiasing : true + width: parent.width / 2 + \endcode + To remove a property, write a "dash" (-) followed by the "property name". + For example: + \code + - width + \endcode + will remove the property \e width from the generated code. \li Select the \uicontrol Alias check box to export the item generated from this layer as an alias in the parent component. \li Select the \uicontrol Clip check box to enable clipping in the diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc index b542732e54a..392a3c1f7a3 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc @@ -10,7 +10,7 @@ \QBXD is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. - You can use the Qt Installer to have the \QBXD plugin package copied to the + You can use \QOI to have the \QBXD plugin package copied to the following path in your Qt installation folder: \list diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index 40c72ab697a..3a10de0147d 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc @@ -114,15 +114,21 @@ you need the import statement \c {QtQuick.Studio.Components 1.0}. You can also import a module as an alias. \li In the \uicontrol {Properties} field, specify new properties or assign - value to the existing properties of the component. You can also add and modify + values to the existing properties of the component. You can also add and modify properties in \QDS. - Following are few examples of properties: + The following are a few examples of the properties: \code property int counter: 5 property string label: "ok" antialiasing : true width: parent.width / 2 \endcode + To remove a property, write a "dash" (-) followed by the "property name". + For example: + \code + - width + \endcode + will remove the property \e width from the generated code. \li In the \uicontrol {Snippet} field, specify component to be added as child under this component. Following example adds a Connection component: @@ -134,6 +140,7 @@ \endcode \note The code must have a scope of a component(e.g. Item, MouseArea, Connections etc.) with a valid syntax for \l {UI Files}. + \note Add respective imports for your snippet in \uicontrol Imports. \li Select the \uicontrol Clip check box to enable clipping in the component generated from the layer. The generated component will clip its own painting, as well as the painting of its children, to its diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc index 47800afab72..c10ee9d3304 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc @@ -24,7 +24,7 @@ \li \l {Installation} \QDS is available either as a standalone installation package or - as an option in the Qt online installer. + as an option in \QOI. \li \l {Tutorials} Follow a set of hands-on tutorials that illustrate how to use the diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 74c84952b63..be9e6b4ed55 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -3,7 +3,7 @@ /*! \page studio-help.html - \previouspage studio-features-on-mcu-projects.html + \previouspage studio-connecting-mcus-with-creator.html \nextpage creator-how-to-get-help.html \title Help diff --git a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc index 863e3cf818c..2ee66d9e15c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc @@ -8,15 +8,15 @@ \title Installation - You can install \QDS either using a stand-alone installation package or the - Qt online installer. The installers copy all the modules and tools you need + You can install \QDS either using a stand-alone installation package or \QOI. + The installers copy all the modules and tools you need to design UIs and preview them on the desktop to your computer and configure them for you. \QDS is available for Linux, \macOS, and Windows operating systems. For more information, see \l{Supported Platforms}. To begin, create a \l{Qt Account}. This account gives you access to a web portal to manage your licenses and download the standalone \QDS package or - the Qt online installer. Alternatively, you can download an evaluation + \QOI. Alternatively, you can download an evaluation package \l{https://www.qt.io/product/ui-design-tools}{here}. After the installation, you can start exploring \QDS by following @@ -31,15 +31,15 @@ would for any other software, and follow the instructions of the installer to complete it. - \section1 Using Qt Online Installer + \section1 Using \QOI - You can download the Qt online installer for your operating system + You can download \QOI for your operating system from your Qt Account. \list 1 - \li Start the Qt online installer. + \li Start \QOI. \li Select \uicontrol {Design Tools}. - \image studio-installation.png "Design Tools selected in Qt online installer" + \image studio-installation.png "Design Tools selected in Qt Online Installer" \li Select \uicontrol Next and follow the instructions of the installer to complete the installation. \endlist diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index 1aab65187ab..e43232423f1 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -51,7 +51,7 @@ \li MCU \li Creates an application that uses a subset of default components (as supported by \QMCU) that you can deploy, run, and debug - on MCU boards. + on MCU boards. For more information, see \l {Creating Projects for MCUs}. \row \li {1,3} Mobile \li Scroll diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 6b691c459c5..92e15ced030 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -33,6 +33,7 @@ \li \l{Open Documents} \li \l{Content Library} \li \l{Texture Editor} + \li \l{Qt Insight} \endlist \li \l{Managing Workspaces} \li \l{Managing Sessions} @@ -254,8 +255,13 @@ \li \l{Use external tools} \li \l{\QDS on MCUs} \list + \li \l {\QMCU Framework} \li \l {\QDS Version Compatibility with \QMCU SDKs} \li \l {\QDS Features on MCU Projects} + \li \l {Creating Projects for MCUs} + \li \l {Creating UIs for MCUs} + \li \l {Developing Applications for MCUs} + \li \l {Connecting MCUs with Qt Creator} \endlist \endlist \li \l Help diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index d86d1f55249..454a7a0845d 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -183,7 +183,7 @@ and drag the component along the axis. \li To move components on a plane, click the plane handle and drag the component on the plane. - \li To move an component freely in the 3D view, click and drag the gray + \li To move components freely in the 3D view, click and drag the gray handle at the center of the move gizmo. \endlist @@ -221,6 +221,35 @@ gray handle at the center of the component. \endlist + \section1 Snapping + + With snapping turned on, the objects in the \uicontrol 3D view snap to certain + intervals during transformation (move, rotate, scale). + + You can toggle snapping in the following ways: + + \list + \li Select \inlineimage icons/snapping-3d.png + in the \uicontrol 3D view toolbar. + \li Hold down the \key Ctrl key. + \endlist + + With snapping turned on, you can press and hold \key Shift to snap objects to one tenth of + the specified snap interval. + + \section2 Configuring Snapping + + To edit snapping settings, select \inlineimage icons/snapping-3d-conf.png + in the \uicontrol 3D view toolbar. + + In the configure dialog, you can do the following: + \list + \li Turn on and off snapping separately for the different transformations + (move, rotate, scale). + \li Set snap intervals. + \li Toggle if the position snaps to absolute or relative values. + \endlist + \section1 Aligning Views and Cameras To align a camera to the \uicontrol{3D} view: @@ -375,6 +404,16 @@ \li Toggle Edit Light On/Off \li \key U \li \l{Using Edit Light} + \row + \li \inlineimage icons/snapping-3d.png + \li Toggle Snapping During Node Drag + \li \key Shift + \key Tab + \li \l{Snapping} + \row + \li \inlineimage icons/snapping-3d-conf.png + \li Open Snap Configuration Dialog + \li + \li \l{Configuring Snapping} \row \li \inlineimage icons/align-camera-on.png \li Align Selected Cameras to View diff --git a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc new file mode 100644 index 00000000000..ddcced3afe6 --- /dev/null +++ b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-qt-insight.html + \previouspage studio-texture-editor.html + \nextpage creator-project-managing-workspaces.html + + \title Qt Insight + + In the \uicontrol {Qt Insight} view, you manage your Qt Insight. + + Qt Insight is an analytics solution that provides real user insights on the usage of Qt + applications. It shows, for example, an application’s performance, usage, and user data. + + For more information, see the + \l{https://www.qt.io/product/insight/onboarding-instructions}{Getting Started with Qt Insight} + documentation. + + \image qt-insight-view.png + + In \QDS, you can do the following with Qt Insight: + \list + \li Turn on and off tracking + \li Set send cadence + \li Manage categories + \endlist +*/ diff --git a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc index 4d644706e51..ef45d8efdf1 100644 --- a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc @@ -4,7 +4,7 @@ /*! \page studio-texture-editor.html \previouspage studio-content-library.html - \nextpage creator-project-managing-workspaces.html + \nextpage studio-qt-insight.html \title Texture Editor diff --git a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc index b5f7abf0d34..35fcf8418fb 100644 --- a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc +++ b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc @@ -3,7 +3,7 @@ /*! \page creator-project-managing-workspaces.html - \previouspage studio-texture-editor.html + \previouspage studio-qt-insight.html \nextpage creator-project-managing-sessions.html \title Managing Workspaces diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index 12f5ebf4c15..6a511c72524 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -156,7 +156,7 @@ TreeViewDelegate { mouseArea.allowTooltip = true } - onPositionChanged: tooltipBackend.reposition() + onPositionChanged: AssetsLibraryBackend.tooltipBackend.reposition() onPressed: (mouse) => { mouseArea.forceActiveFocus() diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml index 211d3449a85..cfa40209950 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml @@ -145,11 +145,11 @@ Item { Column { id: toolbarColumn anchors.fill: parent - anchors.topMargin: 6 - anchors.bottomMargin: 6 - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 12 + anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + spacing: StudioTheme.Values.toolbarColumnSpacing StudioControls.SearchBox { id: searchBox @@ -260,10 +260,13 @@ Item { AssetsView { id: assetsView - assetsRoot: root - contextMenu: contextMenu + width: parent.width height: parent.height - assetsView.y + + assetsRoot: root + contextMenu: contextMenu + focus: true } } } diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index ede7f58cafb..98b85d85c96 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -15,6 +15,11 @@ TreeView { boundsBehavior: Flickable.StopAtBounds rowSpacing: 5 + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" + property var assetsModel: AssetsLibraryBackend.assetsModel property var rootView: AssetsLibraryBackend.rootView property var tooltipBackend: AssetsLibraryBackend.tooltipBackend @@ -46,9 +51,18 @@ TreeView { return -1 } - ScrollBar.vertical: HelperWidgets.VerticalScrollBar { + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: HelperWidgets.ScrollBar { id: verticalScrollBar - scrollBarVisible: root.contentHeight > root.height + parent: root + x: root.width - verticalScrollBar.width + y: 0 + height: root.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || root.adsFocus || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded } model: assetsModel @@ -348,22 +362,21 @@ TreeView { root.currentFilePath = filePath } - Keys.enabled: true - - Keys.onUpPressed: { - if (!root.currentFilePath) + function moveSelection(amount) + { + if (!assetsModel.haveFiles || !amount) return - let index = assetsModel.indexForPath(root.currentFilePath) + let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) + : root.__modelIndex(root.firstRow) let row = root.rowAtIndex(index) let nextRow = row let nextIndex = index do { - if (nextRow <= root.firstRow) - return // don't select hidden rows - - nextRow-- + nextRow = nextRow + amount + if ((amount < 0 && nextRow < root.firstRow) || (amount > 0 && nextRow > root.lastRow)) + return nextIndex = root.__modelIndex(nextRow) } while (assetsModel.isDirectory(nextIndex)) @@ -371,26 +384,14 @@ TreeView { root.positionViewAtRow(nextRow, TableView.Contain) } + Keys.enabled: true + + Keys.onUpPressed: { + moveSelection(-1) + } + Keys.onDownPressed: { - if (!root.currentFilePath) - return - - let index = assetsModel.indexForPath(root.currentFilePath) - let row = root.rowAtIndex(index) - - let nextRow = row - let nextIndex = index - - do { - if (nextRow >= root.lastRow) - return // don't select hidden rows - - nextRow++ - nextIndex = root.__modelIndex(nextRow) - } while (assetsModel.isDirectory(nextIndex)) - - root.__selectRow(nextRow) - root.positionViewAtRow(nextRow, TableView.Contain) + moveSelection(1) } ConfirmDeleteFilesDialog { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml new file mode 100644 index 00000000000..5be2dacb8d7 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -0,0 +1,288 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import Qt.labs.platform as PlatformWidgets +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +Item { + id: root + + implicitWidth: 300 + implicitHeight: innerRect.height + 6 + + property color textColor + + signal selectItem(int itemIndex) + signal deleteItem() + + Item { + id: boundingRect + + anchors.centerIn: root + width: root.width - 24 + height: nameHolder.height + clip: true + + MouseArea { + id: itemMouse + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + propagateComposedEvents: true + hoverEnabled: true + onClicked: (event) => { + if (!collectionIsSelected) { + collectionIsSelected = true + event.accepted = true + } + } + } + + Rectangle { + id: innerRect + anchors.fill: parent + } + + Row { + width: parent.width - threeDots.width + leftPadding: 20 + + Text { + id: moveTool + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + width: moveTool.style.squareControlSize.width + height: nameHolder.height + + text: StudioTheme.Constants.dragmarks + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: moveTool.style.baseIconFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Text { + id: nameHolder + + text: collectionName + font.pixelSize: StudioTheme.Values.baseFontSize + color: textColor + leftPadding: 5 + topPadding: 8 + rightPadding: 8 + bottomPadding: 8 + elide: Text.ElideMiddle + verticalAlignment: Text.AlignVCenter + } + } + + Text { + id: threeDots + + text: "..." + font.pixelSize: StudioTheme.Values.baseFontSize + color: textColor + anchors.right: boundingRect.right + anchors.verticalCenter: parent.verticalCenter + rightPadding: 12 + topPadding: nameHolder.topPadding + bottomPadding: nameHolder.bottomPadding + verticalAlignment: Text.AlignVCenter + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + Qt.LeftButton + onClicked: (event) => { + collectionMenu.open() + event.accepted = true + } + } + } + } + + PlatformWidgets.Menu { + id: collectionMenu + + PlatformWidgets.MenuItem { + text: qsTr("Delete") + shortcut: StandardKey.Delete + onTriggered: deleteDialog.open() + } + + PlatformWidgets.MenuItem { + text: qsTr("Rename") + shortcut: StandardKey.Replace + onTriggered: renameDialog.open() + } + } + + StudioControls.Dialog { + id: deleteDialog + + title: qsTr("Deleting whole collection") + + contentItem: Column { + spacing: 2 + + Text { + text: qsTr("Are you sure that you want to delete collection \"" + collectionName + "\"?") + color: StudioTheme.Values.themeTextColor + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + id: btnDelete + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Delete") + onClicked: root.deleteItem(index) + } + + HelperWidgets.Button { + text: qsTr("Cancel") + anchors.verticalCenter: parent.verticalCenter + onClicked: deleteDialog.reject() + } + } + } + } + + StudioControls.Dialog { + id: renameDialog + + title: qsTr("Rename collection") + + onAccepted: { + if (newNameField.text !== "") + collectionName = newNameField.text + } + + onOpened: { + newNameField.text = collectionName + } + + contentItem: Column { + spacing: 2 + + Text { + text: qsTr("Previous name: " + collectionName) + color: StudioTheme.Values.themeTextColor + } + + Row { + spacing: 10 + Text { + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: newNameField + + anchors.verticalCenter: parent.verticalCenter + actionIndicator.visible: false + translationIndicator.visible: false + validator: newNameValidator + + Keys.onEnterPressed: renameDialog.accept() + Keys.onReturnPressed: renameDialog.accept() + Keys.onEscapePressed: renameDialog.reject() + + onTextChanged: { + btnRename.enabled = newNameField.text !== "" + } + } + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + spacing: 10 + + HelperWidgets.Button { + id: btnRename + + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Rename") + onClicked: renameDialog.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + anchors.verticalCenter: parent.verticalCenter + onClicked: renameDialog.reject() + } + } + } + } + + HelperWidgets.RegExpValidator { + id: newNameValidator + regExp: /^\w+$/ + } + + states: [ + State { + name: "default" + when: !collectionIsSelected && !itemMouse.containsMouse + + PropertyChanges { + target: innerRect + opacity: 0.6 + color: StudioTheme.Values.themeControlBackground + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeTextColor + } + }, + State { + name: "hovered" + when: !collectionIsSelected && itemMouse.containsMouse + + PropertyChanges { + target: innerRect + opacity: 0.8 + color: StudioTheme.Values.themeControlBackgroundHover + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeTextColor + } + }, + State { + name: "selected" + when: collectionIsSelected + + PropertyChanges { + target: innerRect + opacity: 1 + color: StudioTheme.Values.themeControlBackgroundInteraction + } + + PropertyChanges { + target: root + textColor: StudioTheme.Values.themeIconColorSelected + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml new file mode 100644 index 00000000000..30128ecadf4 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -0,0 +1,154 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuickDesignerTheme 1.0 +import HelperWidgets 2.0 as HelperWidgets +import StudioTheme 1.0 as StudioTheme +import CollectionEditorBackend + +Item { + id: root + focus: true + + property var rootView: CollectionEditorBackend.rootView + property var model: CollectionEditorBackend.model + property var singleCollectionModel: CollectionEditorBackend.singleCollectionModel + + function showWarning(title, message) { + warningDialog.title = title + warningDialog.message = message + warningDialog.open() + } + + JsonImport { + id: jsonImporter + + backendValue: root.rootView + anchors.centerIn: parent + } + + CsvImport { + id: csvImporter + + backendValue: root.rootView + anchors.centerIn: parent + } + + NewCollectionDialog { + id: newCollection + + backendValue: root.rootView + anchors.centerIn: parent + } + + Message { + id: warningDialog + + title: "" + message: "" + } + + Rectangle { + id: collectionsRect + + color: StudioTheme.Values.themeToolbarBackground + width: 300 + height: root.height + + Column { + width: parent.width + + Rectangle { + width: parent.width + height: StudioTheme.Values.height + 5 + color: StudioTheme.Values.themeToolbarBackground + + Text { + id: collectionText + + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Collections") + font.pixelSize: StudioTheme.Values.mediumIconFont + color: StudioTheme.Values.themeTextColor + leftPadding: 15 + } + + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + rightPadding: 12 + spacing: 2 + + HelperWidgets.IconButton { + icon: StudioTheme.Constants.translationImport + tooltip: qsTr("Import Json") + + onClicked: jsonImporter.open() + } + + HelperWidgets.IconButton { + icon: StudioTheme.Constants.translationImport + tooltip: qsTr("Import CSV") + + onClicked: csvImporter.open() + } + } + } + + Rectangle { // Collections + width: parent.width + color: StudioTheme.Values.themeBackgroundColorNormal + height: 330 + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + onClicked: (event) => { + root.model.deselect() + event.accepted = true + } + } + + ListView { + id: collectionListView + + width: parent.width + height: contentHeight + model: root.model + + delegate: CollectionItem { + onDeleteItem: root.model.removeRow(index) + } + + } + } + + Rectangle { + width: parent.width + height: addCollectionButton.height + color: StudioTheme.Values.themeBackgroundColorNormal + + IconTextButton { + id: addCollectionButton + + anchors.centerIn: parent + text: qsTr("Add new collection") + icon: StudioTheme.Constants.create_medium + onClicked: newCollection.open() + } + } + } + } + + SingleCollectionView { + model: root.singleCollectionModel + anchors { + left: collectionsRect.right + right: parent.right + top: parent.top + bottom: parent.bottom + } + } +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml new file mode 100644 index 00000000000..5323d6759e9 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml @@ -0,0 +1,188 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuickDesignerTheme 1.0 +import Qt.labs.platform as PlatformWidgets +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme + +StudioControls.Dialog { + id: root + + title: qsTr("Import A CSV File") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + modal: true + + required property var backendValue + + property bool fileExists: false + + onOpened: { + collectionName.text = "Collection_" + fileName.text = qsTr("New CSV File") + fileName.selectAll() + fileName.forceActiveFocus() + } + + onRejected: { + fileName.text = "" + } + + HelperWidgets.RegExpValidator { + id: fileNameValidator + regExp: /^(\w[^*>" + tooltip: QT_TR_NOOP("This is GREATER (>)") + } + ListElement { + name: "LESS" + value: "<" + tooltip: QT_TR_NOOP("This is LESS (<)") + } + ListElement { + name: "GREATER OR EQUAL" + value: ">=" + tooltip: QT_TR_NOOP("This is GREATER OR EQUAL (>=)") + } + ListElement { + name: "LESS OR EQUAL" + value: "<=" + tooltip: QT_TR_NOOP("This is LESS OR EQUAL (<=)") + } + } + + StudioControls.ToolTip { + id: toolTip + visible: mouseArea.containsMouse && toolTip.text !== "" + delay: 1000 + text: root.conditionListModel.error + } + + MouseArea { + id: mouseArea + anchors.fill: parent + cursorShape: Qt.IBeamCursor + hoverEnabled: true + + onPressed: function (event) { + // Check if empty + if (!repeater.count) { + root.placeCursor(0) + return + } + + // Map to flow item + let point = mouseArea.mapToItem(flow, Qt.point(event.x, event.y)) + + let horizontalDistance = Number.MAX_VALUE + let verticalDistance = Number.MAX_VALUE + let cursorPosition = 0 + + for (var i = 0; i < repeater.count; ++i) { + let item = repeater.itemAt(i) + + let y = item.y + (item.height / 2) + + // Vertical distance + let vDistance = Math.abs(point.y - y) + + // Horizontal distance + let hLeftDistance = Math.abs(point.x - item.x) + let hRightDistance = Math.abs(point.x - (item.x + item.width)) + + // Early return if vertical distance increases + if (vDistance > verticalDistance) + break + + if (vDistance <= verticalDistance) { + // Rest horizontal distance if vertical distance is smaller than before + if (vDistance !== verticalDistance) + horizontalDistance = Number.MAX_VALUE + + if (hLeftDistance < horizontalDistance) { + horizontalDistance = hLeftDistance + cursorPosition = i + } + + if (hRightDistance < horizontalDistance) { + horizontalDistance = hRightDistance + cursorPosition = i + 1 + } + + verticalDistance = vDistance + } + } + + root.placeCursor(cursorPosition) + } + } + + Item { + anchors.fill: parent + anchors.margins: StudioTheme.Values.flowMargin + + Text { + id: placeholder + height: 20 + topPadding: 1 + font.pixelSize: root.style.baseFontSize + color: (focusScope.activeFocus || popup.searchActive) + ? root.style.text.placeholderInteraction + : root.style.text.placeholder + visible: !repeater.count + text: qsTr("Condition") + } + } + + FocusScope { + id: focusScope + anchors.fill: parent + + onActiveFocusChanged: { + if (!focusScope.activeFocus && !popup.searchActive) + popup.close() + } + + Flow { + id: flow + + property int focusIndex: -1 + + anchors.fill: parent + anchors.margins: StudioTheme.Values.flowMargin + spacing: StudioTheme.Values.flowSpacing + + onPositioningComplete: { + if (root.textInputActive()) + root.placeCursor(newTextInput.index) + + if (!root.shadowPillVisible) + root.heightBeforeShadowPill = root.baseHeight + } + + Repeater { + id: repeater + + onItemRemoved: function(index, item) { + if (!root.textInputActive()) + return + + // Udpate the cursor position + if (index < newTextInput.index) + newTextInput.index = newTextInput.index - 1 + } + + onItemAdded: function(index, item) { + if (!root.textInputActive()) + return + + if (index >= newTextInput.index) + newTextInput.index = newTextInput.index + 1 + } + + Pill { + id: pill + + operatorModel: __operatorModel + + onRemove: function() { + // If pill has focus due to selection or keyboard navigation + if (pill.focus) + root.placeCursor(pill.index) + + Qt.callLater(root.remove, pill.index) + } + + onUpdate: function(value) { + if (value === "") + Qt.callLater(root.remove, pill.index) // Otherwise crash + else + Qt.callLater(root.update, pill.index, value) + } + + onFocusChanged: function() { + if (pill.focus) + flow.focusIndex = pill.index + } + + onSubmit: function (cursorPosition) { + let index = pill.index + // If cursor position is 0 the user moved the cursor out to left side, + // so place the cursor before the pill + if (cursorPosition !== 0) + index++ + + root.placeCursor(index) + } + } + } + } + + TextInput { + id: newTextInput + + property int index + + height: 20 + topPadding: 1 + font.pixelSize: root.style.baseFontSize + color: root.style.text.idle + visible: false + validator: RegularExpressionValidator { regularExpression: /^\S.+/ } + + onTextEdited: { + if (newTextInput.text === "") + return + + newTextInput.visible = false + + root.insert(newTextInput.index, newTextInput.text, ConditionListModel.Intermediate) + + newTextInput.clear() + + // Set focus on the newly created item + let newItem = repeater.itemAt(newTextInput.index) + newItem.forceActiveFocus() + } + + Keys.onPressed: function (event) { + if (event.key === Qt.Key_Backspace) { + if (root.textInputActive()) { + let previousIndex = newTextInput.index - 1 + if (previousIndex < 0) + return + + let item = repeater.itemAt(previousIndex) + item.setCursorEnd() + item.forceActiveFocus() + popup.close() + } + } + } + } + } + + SuggestionPopup { + id: popup + + style: StudioTheme.Values.connectionPopupControlStyle + + x: 0 + y: root.height + width: root.width + operatorModel: __operatorModel + + //onOpened: console.log("POPUP opened") + //onClosed: console.log("POPUP closed") + + onAboutToHide: { + newTextInput.visible = false + } + + onSelect: function(value) { + newTextInput.visible = true + newTextInput.forceActiveFocus() + + if (root.shadowPillVisible) { // Active shadow pill + root.remove(root.shadowPillIndex) + root.shadowPillIndex = -1 + } + + root.insert(newTextInput.index, value, ConditionListModel.Variable) + + // Clear search, reset stack view and tree model + popup.reset() + } + + onSearchActiveChanged: { + if (popup.searchActive) { + root.heightBeforeShadowPill = root.baseHeight + root.insert(newTextInput.index, "...", ConditionListModel.Shadow) + root.shadowPillIndex = newTextInput.index + } else { + if (!root.shadowPillVisible) + return + + root.remove(root.shadowPillIndex) + root.shadowPillIndex = -1 + } + } + + onEntered: function(value) { + if (!popup.searchActive) { + if (!root.shadowPillVisible) { + root.heightBeforeShadowPill = root.baseHeight + root.shadowPillIndex = newTextInput.index + root.insert(newTextInput.index, value, ConditionListModel.Shadow) + } else { + root.setValue(root.shadowPillIndex, value) + } + } else { + root.setValue(root.shadowPillIndex, value) + } + } + + onExited: function(value) { + let shadowItem = repeater.itemAt(root.shadowPillIndex) + + if (!popup.searchActive) { + if (root.shadowPillVisible && shadowItem?.value === value) { + root.remove(root.shadowPillIndex) + root.shadowPillIndex = -1 + } + } else { + // Reset to 3 dots if still the same value as the exited item + if (shadowItem?.value === value) + root.setValue(root.shadowPillIndex, "...") + } + } + } + + Keys.onPressed: function (event) { + if (event.key === Qt.Key_Left) { + if (root.textInputActive()) { + let previousIndex = newTextInput.index - 1 + if (previousIndex < 0) + return + + let item = repeater.itemAt(previousIndex) + item.setCursorEnd() + item.forceActiveFocus() + popup.close() + } else { + if (flow.focusIndex < 0) + return + + root.placeCursor(flow.focusIndex) + } + } else if (event.key === Qt.Key_Right) { + if (root.textInputActive()) { + let nextIndex = newTextInput.index + if (nextIndex >= repeater.count) + return + + let item = repeater.itemAt(nextIndex) + item.setCursorBegin() + item.forceActiveFocus() + popup.close() + } else { + root.placeCursor(flow.focusIndex + 1) + } + } + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 628a00a548a..74efe6dddfd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -4,114 +4,129 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import ConnectionsEditor -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend Rectangle { - width: 640 - height: 1080 - color: "#232222" + id: root - Rectangle { - id: rectangle - x: 10 - y: 10 - width: 620 - height: 97 - color: "#333333" + color: StudioTheme.Values.themePanelBackground + + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" + + Column { + id: column + anchors.fill: parent + spacing: 8 // TODO Rectangle { - id: rectangle1 - x: 10 - y: 10 - width: 600 - height: 34 - color: "#00ffffff" - border.width: 1 + id: toolbar + width: parent.width + height: StudioTheme.Values.doubleToolbarHeight + color: StudioTheme.Values.themeToolbarBackground - Text { - id: text1 - x: 10 - y: 10 - color: "#b5b2b2" - text: qsTr("Search") - font.pixelSize: 12 - } - } - - RowLayout { - x: 10 - y: 50 - TabCheckButton { - id: connections - text: "Connections" - checked: true - autoExclusive: true - checkable: true - } - - TabCheckButton { - id: bindings - text: "Bindings" - autoExclusive: true - checkable: true - } - - TabCheckButton { - id: properties - text: "Properties" - autoExclusive: true - checkable: true - } - } - - Text { - id: text2 - x: 577 - y: 58 - color: "#ffffff" - text: qsTr("+") - font.pixelSize: 18 - font.bold: true - MouseArea { + Column { anchors.fill: parent - onClicked: { - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate) - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type) - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type.model) + anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + spacing: StudioTheme.Values.toolbarColumnSpacing - if (connections.checked) - ConnectionsEditorEditorBackend.connectionModel.add() - else if (bindings.checked) - ConnectionsEditorEditorBackend.bindingModel.add() - else if (properties.checked) - ConnectionsEditorEditorBackend.dynamicPropertiesModel.add() + StudioControls.SearchBox { + id: searchBox + width: parent.width + style: StudioTheme.Values.searchControlStyle + + onSearchChanged: function(searchText) {} + } + + Row { + id: row + width: parent.width + height: StudioTheme.Values.toolbarHeight + spacing: 6 + + TabCheckButton { + id: connections + buttonIcon: StudioTheme.Constants.connections_medium + text: qsTr("Connections") + tooltip: qsTr("This is a tooltip.") + checked: true + autoExclusive: true + checkable: true + } + + TabCheckButton { + id: bindings + buttonIcon: StudioTheme.Constants.binding_medium + text: qsTr("Bindings") + tooltip: qsTr("This is a tooltip.") + autoExclusive: true + checkable: true + } + + TabCheckButton { + id: properties + buttonIcon: StudioTheme.Constants.properties_medium + text: qsTr("Properties") + tooltip: qsTr("This is a tooltip.") + autoExclusive: true + checkable: true + } + + Item { + id: spacer + width: row.width - connections.width - bindings.width + - properties.width - addButton.width - row.spacing * 4 + height: 1 + } + + HelperWidgets.AbstractButton { + id: addButton + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.add_medium + tooltip: qsTr("Add something.") + onClicked: { + if (connections.checked) + ConnectionsEditorEditorBackend.connectionModel.add() + else if (bindings.checked) + ConnectionsEditorEditorBackend.bindingModel.add() + else if (properties.checked) + ConnectionsEditorEditorBackend.dynamicPropertiesModel.add() + } + } } } } - } - ConnectionsListView { - visible: connections.checked - x: 17 - y: 124 - model: ConnectionsEditorEditorBackend.connectionModel - } + ConnectionsListView { + visible: connections.checked + width: parent.width + height: parent.height - toolbar.height - column.spacing + model: ConnectionsEditorEditorBackend.connectionModel + adsFocus: root.adsFocus + } - BindingsListView { - visible: bindings.checked - x: 17 - y: 124 - model: ConnectionsEditorEditorBackend.bindingModel - } + BindingsListView { + visible: bindings.checked + width: parent.width + height: parent.height - toolbar.height - column.spacing + model: ConnectionsEditorEditorBackend.bindingModel + adsFocus: root.adsFocus + } - PropertiesListView { - visible: properties.checked - x: 17 - y: 124 - model: ConnectionsEditorEditorBackend.dynamicPropertiesModel + PropertiesListView { + visible: properties.checked + width: parent.width + height: parent.height - toolbar.height - column.spacing + model: ConnectionsEditorEditorBackend.dynamicPropertiesModel + adsFocus: root.adsFocus + } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml b/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml new file mode 100644 index 00000000000..936d06daee6 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls + +ItemDelegate { + id: control + hoverEnabled: true + + contentItem: Text { + leftPadding: 8 + rightPadding: 8 + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + color: control.hovered ? "#111111" : "white" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 30 + opacity: enabled ? 1 : 0.3 + color: control.hovered ? "#4DBFFF" : "transparent" + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml new file mode 100644 index 00000000000..366860b6757 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Templates as T +import StudioTheme as StudioTheme + +T.TreeViewDelegate { + id: control + hoverEnabled: true + implicitWidth: 200 + implicitHeight: 30 + indentation: 12 + leftPadding: control.leftMargin + control.__contentIndent + + readonly property int customDepth: control.depth - 1 + + readonly property real __contentIndent: !control.isTreeNode ? 0 + : (control.customDepth * control.indentation) + + (control.indicator ? control.indicator.width + control.spacing : 0) + + indicator: Item { + x: control.leftMargin + (control.customDepth * control.indentation) + width: 30 + height: 30 + + Text { + id: icon + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.smallIconFontSize + color: control.hovered ? "#111111" : "white" // TODO colors + text: StudioTheme.Constants.sectionToggle + rotation: control.expanded ? 0 : -90 + anchors.centerIn: parent + } + } + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 30 + color: control.hovered ? "#4DBFFF" : "transparent" + } + + contentItem: Text { + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + color: control.hovered ? "#111111" : "white" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml new file mode 100644 index 00000000000..bbaefb16e4c --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -0,0 +1,163 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +FocusScope { + id: root + + required property int index + required property string value + required property int type + + function setCursorBegin() { textInput.cursorPosition = 0 } + function setCursorEnd() { textInput.cursorPosition = textInput.text.length } + + function isEditable() { return root.type === ConditionListModel.Intermediate + || root.type === ConditionListModel.Literal + || root.type === ConditionListModel.Invalid } + + function isIntermediate() { return root.type === ConditionListModel.Intermediate } + function isLiteral() { return root.type === ConditionListModel.Literal } + function isOperator() { return root.type === ConditionListModel.Operator } + function isProperty() { return root.type === ConditionListModel.Variable } + function isShadow() { return root.type === ConditionListModel.Shadow } + function isInvalid() { return root.type === ConditionListModel.Invalid } + + signal remove() + signal update(var value) + signal submit(int cursorPosition) + + readonly property int margin: StudioTheme.Values.flowPillMargin + property var operatorModel + + width: { + if (root.isEditable()) { + if (root.isInvalid()) + return textInput.width + 1 + 2 * root.margin + else + return textInput.width + 1 + } + return textItem.contentWidth + icon.width + root.margin + } + height: StudioTheme.Values.flowPillHeight + + onActiveFocusChanged: { + if (root.activeFocus && root.isEditable()) + textInput.forceActiveFocus() + } + + Keys.onPressed: function (event) { + if (root.isEditable()) + return + + if (event.key === Qt.Key_Backspace || event.key === Qt.Key_Delete) + root.remove() + } + + MouseArea { + id: rootMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: root.isEditable() ? Qt.IBeamCursor : Qt.ArrowCursor + onClicked: root.forceActiveFocus() + } + + Rectangle { + id: pill + anchors.fill: parent + color: { + if (root.isShadow()) + return StudioTheme.Values.themeInteraction + if (root.isEditable()) + return "transparent" + + return StudioTheme.Values.themePillBackground + } + border.color: root.isInvalid() ? StudioTheme.Values.themeWarning : "white" // TODO colors + border.width: { + if (root.isShadow()) + return 0 + if (root.isInvalid()) + return 1 + if (root.isEditable()) + return 0 + if (rootMouseArea.containsMouse || root.focus) + return 1 + + return 0 + } + radius: StudioTheme.Values.flowPillRadius + + Row { + id: row + anchors.left: parent.left + anchors.leftMargin: root.margin + anchors.verticalCenter: parent.verticalCenter + visible: root.isOperator() || root.isProperty() || root.isShadow() + + Text { + id: textItem + font.pixelSize: StudioTheme.Values.baseFontSize + color: root.isShadow() ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor + text: root.isOperator() ? root.operatorModel.convertValueToName(root.value) + : root.value + anchors.verticalCenter: parent.verticalCenter + } + + Item { + id: icon + width: root.isShadow() ? root.margin : StudioTheme.Values.flowPillHeight + height: StudioTheme.Values.flowPillHeight + visible: !root.isShadow() + + Text { + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.smallIconFontSize + color: StudioTheme.Values.themeIconColor + text: StudioTheme.Constants.close_small + anchors.centerIn: parent + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.remove() + } + } + } + + TextInput { + id: textInput + + x: root.isInvalid() ? root.margin : 0 + height: StudioTheme.Values.flowPillHeight + topPadding: 1 + font.pixelSize: StudioTheme.Values.baseFontSize + color: (rootMouseArea.containsMouse || textInput.activeFocus) ? StudioTheme.Values.themeIconColor + : StudioTheme.Values.themeTextColor + text: root.value + visible: root.isEditable() + enabled: root.isEditable() + + //validator: RegularExpressionValidator { regularExpression: /^\S+/ } + + onEditingFinished: { + root.update(textInput.text) // emit + root.submit(textInput.cursorPosition) // emit + } + + Keys.onPressed: function (event) { + if (event.key === Qt.Key_Backspace) { + if (textInput.text !== "") + return + + root.remove() // emit + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 3aad2785ce2..9d17218b00a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -3,70 +3,100 @@ import QtQuick import QtQuick.Controls +import HelperWidgets 2.0 as HelperWidgets +import StudioTheme 1.0 as StudioTheme +import ConnectionsEditorEditorBackend Window { id: window - width: 400 - height: 800 + + property alias titleBar: titleBarContent.children + default property alias content: mainContent.children + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + width: 320 + height: column.implicitHeight visible: true - flags: Qt.FramelessWindowHint || Qt.Dialog + flags: Qt.FramelessWindowHint | Qt.Dialog + color: StudioTheme.Values.themePopoutBackground - color: Qt.transparent - - property int bw: 5 - - function popup(item) { - print("popup " + item) - var padding = 12 - var p = item.mapToGlobal(0, 0) - dialog.x = p.x - dialog.width - padding - if (dialog.x < 0) - dialog.x = p.x + item.width + padding - dialog.y = p.y - dialog.show() - dialog.raise() + function ensureVerticalPosition() { + if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) { + window.y = (Screen.height - window.height - window.style.dialogScreenMargin) + } } - Rectangle { - id: rectangle1 - color: "#d7d7d7" - border.color: "#232323" + onHeightChanged: window.ensureVerticalPosition() + + function popup(item) { + var padding = 12 + var p = item.mapToGlobal(0, 0) + window.x = p.x - window.width - padding + if (window.x < 0) + window.x = p.x + item.width + padding + window.y = p.y + + window.ensureVerticalPosition() + + window.show() + window.raise() + } + + Column { + id: column anchors.fill: parent + Item { + id: titleBarItem + width: parent.width + height: StudioTheme.Values.titleBarHeight - - Rectangle { - id: rectangle - height: 32 - color: "#797979" - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: 0 - anchors.leftMargin: 0 - anchors.rightMargin: 0 DragHandler { - grabPermissions: TapHandler.CanTakeOverFromAnything - onActiveChanged: if (active) { window.startSystemMove(); } + id: dragHandler + + target: null + grabPermissions: PointerHandler.CanTakeOverFromAnything + onActiveChanged: { + if (dragHandler.active) + window.startSystemMove() // QTBUG-102488 + } } - Rectangle { - id: rectangle2 - x: 329 - width: 16 - height: 16 - color: "#ffffff" - radius: 4 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 6 + Row { + id: row + anchors.fill: parent + anchors.leftMargin: StudioTheme.Values.popupMargin + anchors.rightMargin: StudioTheme.Values.popupMargin + spacing: 0 - MouseArea { - id: mouseArea - anchors.fill: parent + Item { + id: titleBarContent + width: row.width - closeIndicator.width //- row.anchors.leftMargin + height: row.height + } + + HelperWidgets.IconIndicator { + id: closeIndicator + anchors.verticalCenter: parent.verticalCenter + icon: StudioTheme.Constants.colorPopupClose + pixelSize: StudioTheme.Values.myIconFontSize// * 1.4 onClicked: window.close() } } } + + Rectangle { + width: parent.width - 8 + height: 1 + anchors.horizontalCenter: parent.horizontalCenter + color: "#636363" + } + + Item { + id: mainContent + width: parent.width - 2 * StudioTheme.Values.popupMargin + height: mainContent.childrenRect.height + 2 * StudioTheme.Values.popupMargin + anchors.horizontalCenter: parent.horizontalCenter + } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml new file mode 100644 index 00000000000..6e5a117d8ce --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Text { + width: root.columnWidth + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.myFontSize + property alias tooltip: area.tooltip + HelperWidgets.ToolTipArea { + id: area + anchors.fill: parent + tooltip: qsTr("missing") + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml index cd0ae16226a..20e9568c14b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml @@ -2,12 +2,39 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import StudioTheme 1.0 as StudioTheme +import HelperWidgets as HelperWidgets PopupDialog { property alias backend: form.backend + titleBar: Row { + spacing: 30 // TODO + anchors.fill: parent + + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Owner") + font.pixelSize: StudioTheme.Values.myFontSize + anchors.verticalCenter: parent.verticalCenter + HelperWidgets.ToolTipArea { + anchors.fill: parent + tooltip: qsTr("The owner of the property") + } + } + + Text { + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.myFontSize + anchors.verticalCenter: parent.verticalCenter + text: form.backend.targetNode + } + + } + PropertiesDialogForm { id: form y: 32 + height: 180 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml index c728027f30f..26398e7acbb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -1,76 +1,73 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Controls +import StudioControls as StudioControls +import StudioTheme as StudioTheme -import StudioControls +Column { + id: root + + readonly property real horizontalSpacing: 10 + readonly property real verticalSpacing: 16 + readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 -Rectangle { - width: 400 - height: 800 - color: "#1b1b1b" property var backend - Text { - id: text1 - x: 10 - y: 25 - color: "#ffffff" - text: qsTr("Type:") - font.pixelSize: 15 - } + y: StudioTheme.Values.popupMargin + width: parent.width + spacing: root.verticalSpacing + + PopupLabel { + text: qsTr("Type") + tooltip: qsTr("The type of the property") + } + StudioControls.TopLevelComboBox { + id: type + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //width: root.width - TopLevelComboBox { - id: target - x: 95 - width: 210 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -367 model: backend.type.model ?? [] - onActivated: backend.type.activateIndex(target.currentIndex) + onActivated: backend.type.activateIndex(type.currentIndex) property int currentTypeIndex: backend.type.currentIndex ?? 0 - onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex + onCurrentTypeIndexChanged: type.currentIndex = type.currentTypeIndex } - Text { - id: text2 - x: 10 - y: 131 - color: "#ffffff" - text: qsTr("Name") - font.pixelSize: 15 + Row { + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Name") ; tooltip: qsTr("The name of the property.")} + PopupLabel { text: qsTr("Value"); tooltip: qsTr("The value of the property.") } } - TextInput { - id: name - x: 70 - y: 131 - color: "white" - width: 156 - text: backend.name.text ?? "" - onEditingFinished: { - backend.name.activateText(name.text) + Row { + spacing: root.horizontalSpacing + StudioControls.TextField { + id: name + + width: root.columnWidth + actionIndicatorVisible: false + translationIndicatorVisible: false + + text: backend.name.text ?? "" + onEditingFinished: { + backend.name.activateText(name.text) + } } - } + StudioControls.TextField { + id: value - Text { - x: 10 - y: 81 - color: "#ffffff" - text: qsTr("Value") - font.pixelSize: 15 - } + width: root.columnWidth + actionIndicatorVisible: false + translationIndicatorVisible: false - TextInput { - id: value - color: "red" - x: 70 - y: 81 - width: 156 - text: backend.value.text ?? "" - onEditingFinished: { - backend.value.activateText(value.text) + + text: backend.value.text ?? "" + onEditingFinished: { + backend.value.activateText(value.text) + } } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 9e0822dc6c3..2f4382020b3 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -3,126 +3,191 @@ import QtQuick import QtQuick.Controls -import ConnectionsEditor import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend ListView { - id: listView - width: 606 - height: 160 - interactive: false + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + property bool adsFocus: false + + clip: true + interactive: true highlightMoveDuration: 0 + highlightResizeDuration: 0 + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: HelperWidgets.ScrollBar { + id: verticalScrollBar + parent: root + x: root.width - verticalScrollBar.width + y: 0 + height: root.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || root.focus || verticalScrollBar.inUse || root.adsFocus) + && verticalScrollBar.isNeeded + } onVisibleChanged: { dialog.hide() } - property int modelCurrentIndex: listView.model.currentIndex ?? 0 + property int modelCurrentIndex: root.model.currentIndex ?? 0 + + // Something weird with currentIndex happens when items are removed added. + // listView.model.currentIndex contains the persistent index. - /* Something weird with currentIndex happens when items are removed added. - listView.model.currentIndex contains the persistent index. - */ onModelCurrentIndexChanged: { - listView.currentIndex = listView.model.currentIndex + root.currentIndex = root.model.currentIndex } onCurrentIndexChanged: { - listView.currentIndex = listView.model.currentIndex - dialog.backend.currentRow = listView.currentIndex + root.currentIndex = root.model.currentIndex } + // Number of columns + readonly property int numColumns: 4 + // Proper row width calculation + readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin + readonly property int rowSpace: root.width - (root.rowSpacing * (root.numColumns + 1)) + - root.style.squareControlSize.width + property int rowWidth: root.rowSpace / root.numColumns + property int rowRest: root.rowSpace % root.numColumns + data: [ PropertiesDialog { id: dialog visible: false - backend: listView.model.delegate + backend: root.model.delegate } ] - delegate: Item { + delegate: Rectangle { + id: itemDelegate - width: 600 - height: 18 + required property int index + + required property string target + required property string name + required property string type + required property string value + + width: ListView.view.width + height: root.style.squareControlSize.height + color: mouseArea.containsMouse ? + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover + : "transparent" MouseArea { id: mouseArea anchors.fill: parent - property int currentIndex: listView.currentIndex + property int currentIndex: root.currentIndex Connections { target: mouseArea function onClicked() { - listView.model.currentIndex = index - listView.currentIndex = index - dialog.backend.currentRow = index + root.model.currentIndex = itemDelegate.index + root.currentIndex = itemDelegate.index dialog.popup(mouseArea) } } } Row { - id: row1 - x: 0 - y: 0 - width: 600 - height: 16 - spacing: 10 + id: row + height: itemDelegate.height + spacing: root.rowSpacing + + property color textColor: itemDelegate.ListView.isCurrentItem ? root.style.text.selectedText + : root.style.icon.idle Text { - width: 120 - color: "#ffffff" - text: target ?? "" - anchors.verticalCenter: parent.verticalCenter + width: root.rowWidth + root.rowRest + height: itemDelegate.height + color: row.textColor + text: itemDelegate.target ?? "" + verticalAlignment: Text.AlignVCenter font.bold: false + elide: Text.ElideMiddle + leftPadding: root.rowSpacing } Text { - width: 120 - text: name ?? "" - color: "#ffffff" - anchors.verticalCenter: parent.verticalCenter + width: root.rowWidth + height: itemDelegate.height + color: row.textColor + text: itemDelegate.name ?? "" + verticalAlignment: Text.AlignVCenter font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 - text: type ?? "" - anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" + width: root.rowWidth + root.rowRest + height: itemDelegate.height + color: row.textColor + text: itemDelegate.type ?? "" + verticalAlignment: Text.AlignVCenter font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 - text: value ?? "" - anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" + width: root.rowWidth + root.rowRest + height: itemDelegate.height + color: row.textColor + text: itemDelegate.value ?? "" + verticalAlignment: Text.AlignVCenter font.bold: false + elide: Text.ElideMiddle } - Text { - width: 120 + Rectangle { + width: root.style.squareControlSize.width + height: root.style.squareControlSize.height - text: "-" - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignRight - font.pointSize: 14 - color: "#ffffff" - font.bold: true - MouseArea { + color: toolTipArea.containsMouse ? + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover + : "transparent" + + Text { anchors.fill: parent - onClicked: listView.model.remove(index) + + text: StudioTheme.Constants.remove_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: root.style.baseIconFontSize + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + color: row.textColor + renderType: Text.QtRendering + } + + HelperWidgets.ToolTipArea { + id: toolTipArea + tooltip: qsTr("This is a test.") + anchors.fill: parent + onClicked: root.model.remove(itemDelegate.index) } } + } } highlight: Rectangle { - color: "#2a5593" + color: root.style.interaction width: 600 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml new file mode 100644 index 00000000000..3e7e552a28f --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -0,0 +1,252 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import ConnectionsEditorEditorBackend + +Column { + id: root + + property int actionType + + property int horizontalSpacing + property int columnWidth + + property var statement + + //implicitWidth: Math.max(16, container.childrenRect.width + container.childrenRect.x) + //implicitHeight: Math.max(16, container.childrenRect.height + container.childrenRect.y) + + // Call Function + Row { + visible: root.actionType === ConnectionModelStatementDelegate.CallFunction + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Item") ; tooltip: qsTr("The target item of the function.")} + PopupLabel { text: qsTr("Method") ; tooltip: qsTr("The name of the function.")} + } + + Row { + visible: root.actionType === ConnectionModelStatementDelegate.CallFunction + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: functionId + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + + model: root.statement.function.id.model ?? 0 + + onActivated: backend.okStatement.function.id.activateIndex(functionId.currentIndex) + property int currentTypeIndex: backend.okStatement.function.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: functionId.currentIndex = functionId.currentTypeIndex + } + + StudioControls.TopLevelComboBox { + id: functionName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + model: root.statement.function.name.model ?? 0 + + onActivated: root.statement.function.name.activateIndex(functionName.currentIndex) + property int currentTypeIndex: root.statement.function.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: functionName.currentIndex = functionName.currentTypeIndex + } + } + + // Assign + Row { + visible: root.actionType === ConnectionModelStatementDelegate.Assign + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("From") ; tooltip: qsTr("The Property to assign from.")} + PopupLabel { text: qsTr("To"); tooltip: qsTr("The Property to assign to.") } + } + + Row { + visible: root.actionType === ConnectionModelStatementDelegate.Assign + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: rhsAssignmentId + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //from - rhs - id + + model: root.statement.rhsAssignment.id.model ?? 0 + + onActivated: root.statement.rhsAssignment.id.activateIndex(rhsAssignmentId.currentIndex) + property int currentTypeIndex: root.statement.rhsAssignment.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: rhsAssignmentId.currentIndex = rhsAssignmentId.currentTypeIndex + } + + StudioControls.TopLevelComboBox { + id: lhsAssignmentId + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //to lhs - id + model: root.statement.lhs.id.model ?? 0 + + onActivated: root.statement.lhs.id.activateIndex(lhsAssignmentId.currentIndex) + property int currentTypeIndex: root.statement.lhs.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsAssignmentId.currentIndex = lhsAssignmentId.currentTypeIndex + } + } + + Row { + visible: root.actionType === ConnectionModelStatementDelegate.Assign + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: rhsAssignmentName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //from - rhs - name + + model: root.statement.rhsAssignment.name.model ?? 0 + + onActivated: root.statement.rhsAssignment.name.activateIndex(rhsAssignmentName.currentIndex) + property int currentTypeIndex: root.statement.rhsAssignment.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: rhsAssignmentName.currentIndex = rhsAssignmentName.currentTypeIndex + } + + StudioControls.TopLevelComboBox { + id: lhsAssignmentName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //to lhs - name + model: root.statement.lhs.name.model ?? 0 + + onActivated: root.statement.lhs.name.activateIndex(lhsAssignmentName.currentIndex) + property int currentTypeIndex: root.statement.lhs.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsAssignmentName.currentIndex = lhsAssignmentName.currentTypeIndex + } + } + + // Change State + Row { + visible: root.actionType === ConnectionModelStatementDelegate.ChangeState + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("State Group"); tooltip: qsTr("The State Group.") } + PopupLabel { text: qsTr("State"); tooltip: qsTr("The State .") } + } + + Row { + visible: root.actionType === ConnectionModelStatementDelegate.ChangeState + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: stateGroups + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + model: root.statement.stateTargets.model ?? 0 + + onActivated: root.statement.stateTargets.activateIndex(stateGroups.currentIndex) + property int currentTypeIndex: root.statement.stateTargets.currentIndex ?? 0 + onCurrentTypeIndexChanged: stateGroups.currentIndex = stateGroups.currentTypeIndex + } + + StudioControls.TopLevelComboBox { + id: states + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + + model: root.statement.states.model ?? 0 + + onActivated: root.statement.states.activateIndex(states.currentIndex) + property int currentTypeIndex: root.statement.states.currentIndex ?? 0 + onCurrentTypeIndexChanged: states.currentIndex = states.currentTypeIndex + } + } + + // Set Property + Row { + visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Item"); tooltip: qsTr("The Item.")} + PopupLabel { text: qsTr("Property"); tooltip: qsTr("The property of the item.")} + } + + Row { + visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: lhsPropertyId + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + + model: root.statement.lhs.id.model ?? 0 + + onActivated: root.statement.lhs.id.activateIndex(lhsPropertyId.currentIndex) + property int currentTypeIndex: root.statement.lhs.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsPropertyId.currentIndex = lhsPropertyId.currentTypeIndex + + } + + StudioControls.TopLevelComboBox { + id: lhsPropertyName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + model: root.statement.lhs.name.model ?? 0 + + onActivated: root.statement.lhs.name.activateIndex(lhsPropertyName.currentIndex) + property int currentTypeIndex: root.statement.lhs.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsPropertyName.currentIndex = lhsPropertyName.currentTypeIndex + } + } + + PopupLabel { + visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + text: qsTr("Value") + } + + StudioControls.TextField { + id: setPropertyArgument + visible: root.actionType === ConnectionModelStatementDelegate.SetProperty + width: root.width + actionIndicatorVisible: false + translationIndicatorVisible: false + + text: root.statement.stringArgument.text ?? "" + onEditingFinished: { + root.statement.stringArgument.activateText(setPropertyArgument.text) + } + } + + // Print Message + PopupLabel { + visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage + text: qsTr("Message") + tooltip: qsTr("The message that is printed.") + } + + StudioControls.TextField { + id: messageString + visible: root.actionType === ConnectionModelStatementDelegate.PrintMessage + width: root.width + actionIndicatorVisible: false + translationIndicatorVisible: false + text: root.statement.stringArgument.text ?? "" + onEditingFinished: { + root.statement.stringArgument.activateText(messageString.text) + } + } + + // Custom + PopupLabel { + visible: root.actionType === ConnectionModelStatementDelegate.Custom + text: qsTr("Custom Connections can only be edited with the binding editor") + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 30 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml new file mode 100644 index 00000000000..fb9643ca14b --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -0,0 +1,303 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls as Controls +import HelperWidgets as HelperWidgets +import StudioTheme as StudioTheme +import StudioControls as StudioControls +import ConnectionsEditorEditorBackend + +Controls.Popup { + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property var listModel: ConnectionsEditorEditorBackend.connectionModel.delegate.propertyListProxyModel + property var treeModel: ConnectionsEditorEditorBackend.connectionModel.delegate.propertyTreeModel + + signal select(var value) + signal entered(var value) + signal exited(var value) + + property alias searchActive: search.activeFocus + property bool showOperators: false + property alias operatorModel: repeater.model + + function reset() { + search.clear() + stack.pop(null, Controls.StackView.Immediate) + root.listModel.reset() + } + + closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent + padding: 0 + focus: search.activeFocus + + background: Rectangle { + implicitWidth: root.width + color: root.style.background.idle + border { + color: root.style.border.idle + width: root.style.borderWidth + } + } + + contentItem: Column { + StudioControls.SearchBox { + id: search + width: parent.width + visible: !root.showOperators + + onSearchChanged: function(value) { + root.treeModel.setFilter(value) + } + } + + Controls.StackView { + id: stack + visible: !root.showOperators + width: parent.width + height: currentItem?.implicitHeight + clip: true + initialItem: mainView + } + + Component { + id: mainView + + Column { + Rectangle { + width: stack.width + height: 30 + visible: root.listModel.parentName !== "" + color: backMouseArea.containsMouse ? "#4DBFFF" : "transparent" + + MouseArea { + id: backMouseArea + anchors.fill: parent + hoverEnabled: true + + onClicked: { + stack.pop(Controls.StackView.Immediate) + root.listModel.goUp() //treeModel.pop() + } + } + + Row { + anchors.fill: parent + + Item { + width: 30 + height: 30 + + Text { + id: chevronLeft + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: root.style.baseIconFontSize + color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors + text: StudioTheme.Constants.back_medium + anchors.centerIn: parent + } + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: root.listModel.parentName + color: backMouseArea.containsMouse ? "#111111" : "white" // TODO colors + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + } + + Rectangle { + width: stack.width - 8 + height: 1 + visible: root.listModel.parentName !== "" + color: "#3C3C3C" + anchors.horizontalCenter: parent.horizontalCenter + } + + ListView { + id: listView + visible: search.empty + width: stack.width + implicitHeight: Math.min(180, childrenRect.height) + clip: true + model: root.listModel + + HoverHandler { id: listViewHoverHandler } + + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + + ScrollBar.vertical: HelperWidgets.ScrollBar { + id: listScrollBar + parent: listView + x: listView.width - listScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (listViewHoverHandler.hovered || listView.focus || listScrollBar.inUse) + && listScrollBar.isNeeded + } + + delegate: MyListViewDelegate { + id: listViewDelegate + + required property int index + + required property string propertyName + required property int childCount + required property string expression + + text: listViewDelegate.propertyName + implicitWidth: listView.width + + onClicked: { + if (!listViewDelegate.childCount) { + root.select(listViewDelegate.expression) + return + } + + stack.push(mainView, Controls.StackView.Immediate) + + ListView.view.model.goInto(listViewDelegate.index) + } + + onHoveredChanged: { + if (listViewDelegate.childCount) + return + + if (listViewDelegate.hovered) + root.entered(listViewDelegate.expression) + else + root.exited(listViewDelegate.expression) + } + } + } + + TreeView { + id: treeView + visible: !search.empty + width: stack.width + implicitHeight: Math.min(180, childrenRect.height) + clip: true + model: root.treeModel + + HoverHandler { id: treeViewHoverHandler } + + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + + ScrollBar.vertical: HelperWidgets.ScrollBar { + id: treeScrollBar + parent: treeView + x: treeView.width - treeScrollBar.width + y: 0 + height: treeView.availableHeight + orientation: Qt.Vertical + + show: (treeViewHoverHandler.hovered || treeView.focus || treeScrollBar.inUse) + && treeScrollBar.isNeeded + } + + onLayoutChanged: function() { + treeView.expand(0) + } + + rowHeightProvider: function(row) { + return (row <= 0) ? 0 : -1 + } + + delegate: MyTreeViewDelegate { + id: treeViewDelegate + + required property int index + + required property string propertyName + required property int childCount + required property string expression + + text: treeViewDelegate.propertyName + implicitWidth: treeView.width + + onClicked: { + if (!treeViewDelegate.childCount) + root.select(treeViewDelegate.expression) + else + treeView.toggleExpanded(treeViewDelegate.index) + } + + onHoveredChanged: { + if (treeViewDelegate.childCount) + return + + if (treeViewDelegate.hovered) + root.entered(treeViewDelegate.expression) + else + root.exited(treeViewDelegate.expression) + } + } + } + } + } + + Item { + visible: root.showOperators + width: stack.width + height: flow.childrenRect.height + 2 * StudioTheme.Values.flowMargin + + Flow { + id: flow + + anchors.fill: parent + anchors.margins: StudioTheme.Values.flowMargin + spacing: StudioTheme.Values.flowSpacing + + Repeater { + id: repeater + + Rectangle { + id: delegate + + required property int index + + required property string name + required property string value + required property string tooltip + + width: textItem.contentWidth + 2 * StudioTheme.Values.flowPillMargin + height: StudioTheme.Values.flowPillHeight + color: "#161616" + radius: StudioTheme.Values.flowPillRadius + border { + color: "white" + width: mouseArea.containsMouse ? 1 : 0 + } + + HelperWidgets.ToolTipArea { + id: mouseArea + hoverEnabled: true + anchors.fill: parent + tooltip: delegate.tooltip + + onClicked: root.select(delegate.value) + } + + Text { + id: textItem + font.pixelSize: StudioTheme.Values.baseFontSize + color: StudioTheme.Values.themeTextColor + text: delegate.name + anchors.centerIn: parent + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml index c49bc539157..a16e18962cb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml @@ -2,79 +2,163 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Templates +import QtQuick.Templates as T +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme -Button { +T.TabButton { 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: 4 - rightPadding: 4 + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - text: "My Button" + property alias tooltip: toolTipArea.tooltip + property alias buttonIcon: buttonIcon.text - background: buttonBackground - Rectangle { - id: buttonBackground - color: "#047eff" - implicitWidth: 100 - implicitHeight: 40 - opacity: enabled ? 1 : 0.3 - radius: 12 - border.color: "#047eff" + width: control.style.squareControlSize.width + buttonLabel.implicitWidth + + buttonLabel.leftPadding + buttonLabel.rightPadding + height: control.style.squareControlSize.height + + contentItem: Row { + spacing: 0 + + Text { + id: buttonIcon + width: control.style.squareControlSize.width + height: control.height + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: control.style.baseIconFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Text { + id: buttonLabel + height: control.height + rightPadding: 4 + font.pixelSize: control.style.baseFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + } } - contentItem: textItem - Text { - id: textItem - text: control.text + background: Rectangle { + id: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: StudioTheme.Values.smallRadius //control.style.radius + } - opacity: enabled ? 1.0 : 0.3 - color: "#ffffff" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + HelperWidgets.ToolTipArea { + id: toolTipArea + anchors.fill: parent + // Without setting the acceptedButtons property the clicked event won't + // reach the AbstractButton, it will be consumed by the ToolTipArea + acceptedButtons: Qt.NoButton } states: [ State { - name: "normal" - when: !control.down && !control.checked - + name: "default" + when: control.enabled && !control.hovered && !control.pressed && !control.checked PropertyChanges { - target: buttonBackground - visible: false - color: "#00000000" - border.color: "#047eff" + target: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle } - PropertyChanges { - target: textItem - color: "#ffffff" + target: buttonIcon + color: control.style.icon.idle + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.idle } }, State { - name: "down" - when: control.down && !control.checked + name: "hover" + when: control.enabled && control.hovered && !control.pressed && !control.checked PropertyChanges { - target: textItem - color: "#ffffff" + target: controlBackground + color: control.style.background.hover + border.color: control.style.border.hover } - PropertyChanges { - target: buttonBackground - color: "#047eff" - border.color: "#00000000" + target: buttonIcon + color: control.style.icon.hover + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.hover } }, State { - name: "down1" - when: control.checked - extend: "down" + name: "hoverCheck" + when: control.enabled && control.hovered && !control.pressed && control.checked + PropertyChanges { + target: controlBackground + color: control.style.interactionHover + border.color: control.style.interactionHover + } + PropertyChanges { + target: buttonIcon + color: control.style.text.selectedText + } + PropertyChanges { + target: buttonLabel + color: control.style.text.selectedText + } + }, + State { + name: "pressed" + when: control.enabled && control.hovered && control.pressed + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.interaction + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.interaction + } + }, + State { + name: "check" + when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "pressedButNotHovered" + when: control.enabled && !control.hovered && control.pressed + extend: "hover" + }, + State { + name: "disable" + when: !control.enabled + PropertyChanges { + target: controlBackground + color: control.style.background.disabled + border.color: control.style.border.disabled + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.disabled + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.disabled + } } ] } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml index 3abbfe88ada..1e0bcf1eb40 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml @@ -26,7 +26,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Add an instance") - enabled: root.targetAvailable + enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasActive3DScene onTriggered: ContentLibraryBackend.effectsModel.addInstance(root.targetItem) } diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index ddbfcf5a850..36a60ea3b31 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -1,186 +1,284 @@ -{ - "ContextMenuArea": { - "size": "28x28", - "Off": { - "Disabled": { "color": "DStextColorDisabled" }, - "Hovered": { "color": "DSpanelBackground" }, - "Normal": { "color": "DStextColor" }, - "Selected": { "color": "DStextSelectedTextColor" } +[ + { + "ContextMenuArea": { + "size": "28x28", + "Off": { + "Disabled": { "color": "DStextColorDisabled" }, + "Hovered": { "color": "DSpanelBackground" }, + "Normal": { "color": "DStextColor" }, + "Selected": { "color": "DStextSelectedTextColor" } + }, + "On": { + "Disabled": { "color": "DStextColorDisabled" }, + "Hovered": { "color": "DSsubPanelBackground" }, + "Normal": { "color": "DStextColor" }, + "Selected": { "color": "DStextSelectedTextColor" } + } }, - "On": { - "Disabled": { "color": "DStextColorDisabled" }, - "Hovered": { "color": "DSsubPanelBackground" }, - "Normal": { "color": "DStextColor" }, - "Selected": { "color": "DStextSelectedTextColor" } + "AddMouseAreaIcon": { + "iconName": "mouseArea_small" + }, + "AlignCameraToViewIcon": { + "iconName": "alignToCamera_small" + }, + "AlignViewToCameraIcon": { + "iconName": "alignToObject_small" + }, + "AnchorsIcon": { + "iconName": "anchors_small" + }, + "AnnotationIcon": { + "iconName": "annotations_small" + }, + "ArrangeIcon": { + "iconName": "arrange_small" + }, + "BackspaceIcon": { + "iconName": "backspace_small" + }, + "CameraIcon": { + "iconName": "camera_small" + }, + "CameraOrthographicIcon": { + "iconName": "orthCam_small" + }, + "CameraPerspectiveIcon": { + "iconName": "perspectiveCam_small" + }, + "ConnectionsIcon": { + "iconName": "connection_small" + }, + "CopyIcon": { + "iconName": "copy_small" + }, + "CreateIcon": { + "iconName": "create_small" + }, + "DeleteIcon": { + "iconName": "delete_small" + }, + "DuplicateIcon": { + "iconName": "duplicate_small" + }, + "EditComponentIcon": { + "iconName": "editComponent_small" + }, + "EditIcon": { + "iconName": "edit_small" + }, + "EnterComponentIcon": { + "iconName": "editComponent_small" + }, + "EventListIcon": { + "iconName": "events_small" + }, + "FitSelectedIcon": { + "iconName": "fitSelected_small" + }, + "GroupSelectionIcon": { + "iconName": "group_small" + }, + "ImportedModelsIcon": { + "iconName": "importedModels_small" + }, + "LayoutsIcon": { + "iconName": "layouts_small" + }, + "LightIcon": { + "Off": { + "iconName": "editLightOff_medium" + }, + "On": { + "iconName": "editLightOn_medium" + } + }, + "LightDirectionalIcon": { + "iconName": "directionalLight_small" + }, + "LightPointIcon": { + "iconName": "pointLight_small" + }, + "LightSpotIcon": { + "iconName": "spotLight_small" + }, + "MakeComponentIcon": { + "iconName": "createComponent_small" + }, + "MaterialIcon": { + "iconName": "material_medium" + }, + "MergeWithTemplateIcon": { + "iconName": "merge_small" + }, + "MinimalDownArrowIcon" : { + "iconName": "upDownSquare2" + }, + "ModelConeIcon": { + "iconName": "cone_small" + }, + "ModelCubeIcon": { + "iconName": "cube_small" + }, + "ModelCylinderIcon": { + "iconName": "cylinder_small" + }, + "ModelPlaneIcon": { + "iconName": "plane_small" + }, + "ModelSphereIcon": { + "iconName": "sphere_small" + }, + "ParentIcon": { + "iconName": "selectParent_small" + }, + "PasteIcon": { + "iconName": "paste_small" + }, + "PositionsersIcon": { + "iconName": "positioners_small" + }, + "PrimitivesIcon": { + "iconName": "cube_small" + }, + "ResetViewIcon": { + "iconName": "reload_medium" + }, + "SelecionIcon": { + "iconName": "selection_small" + }, + "ShowBoundsIcon": { + "Off": { + "iconName": "visibilityOff" + }, + "On": { + "iconName": "visibilityOn" + } + }, + "SnappingIcon": { + "iconName": "snapping_small" + }, + "SimpleCheckIcon": { + "Off": { + "iconName": "transparent" + }, + "On": { + "iconName": "tickMark_small" + } + }, + "TimelineIcon": { + "iconName": "timeline_small" + }, + "ToggleGroupIcon": { + "Off": { + "iconName": "selectOutline_medium" + }, + "On": { + "iconName": "selectFill_medium" + } + }, + "VisibilityIcon": { + "Off": { + "iconName": "visibilityOff" + }, + "On": { + "iconName": "visibilityOn" + } } }, - "AddMouseAreaIcon": { - "iconName": "mouseArea_small" - }, - "AlignCameraToViewIcon": { - "iconName": "alignToCamera_small" - }, - "AlignViewToCameraIcon": { - "iconName": "alignToObject_small" - }, - "AnchorsIcon": { - "iconName": "anchors_small" - }, - "AnnotationIcon": { - "iconName": "annotations_small" - }, - "ArrangeIcon": { - "iconName": "arrange_small" - }, - "BackspaceIcon": { - "iconName": "backspace_small" - }, - "CameraIcon": { - "iconName": "camera_small" - }, - "CameraOrthographicIcon": { - "iconName": "orthCam_small" - }, - "CameraPerspectiveIcon": { - "iconName": "perspectiveCam_small" - }, - "ConnectionsIcon": { - "iconName": "connection_small" - }, - "CopyIcon": { - "iconName": "copy_small" - }, - "CreateIcon": { - "iconName": "create_small" - }, - "DeleteIcon": { - "iconName": "delete_small" - }, - "DuplicateIcon": { - "iconName": "duplicate_small" - }, - "EditComponentIcon": { - "iconName": "editComponent_small" - }, - "EditIcon": { - "iconName": "edit_small" - }, - "EnterComponentIcon": { - "iconName": "editComponent_small" - }, - "EventListIcon": { - "iconName": "events_small" - }, - "FitSelectedIcon": { - "iconName": "fitSelected_small" - }, - "GroupSelectionIcon": { - "iconName": "group_small" - }, - "ImportedModelsIcon": { - "iconName": "importedModels_small" - }, - "LayoutsIcon": { - "iconName": "layouts_small" - }, - "LightIcon": { - "Off": { - "iconName": "editLightOff_medium" + { + "ToolbarArea": { + "size": "32x32", + "Off": { + "Disabled": { "color": "DStoolbarIcon_blocked" }, + "Hovered": { "color": "DSiconColor" }, + "Normal": { "color": "DSiconColor" }, + "Selected": { "color": "DStextSelectedTextColor" } + }, + "On": { + "Disabled": { "color": "DStoolbarIcon_blocked" }, + "Hovered": { "color": "DStextSelectedTextColor" }, + "Normal": { "color": "DStextSelectedTextColor" }, + "Selected": { "color": "DStextSelectedTextColor" } + } }, - "On": { - "iconName": "editLightOn_medium" - } - }, - "LightDirectionalIcon": { - "iconName": "directionalLight_small" - }, - "LightPointIcon": { - "iconName": "pointLight_small" - }, - "LightSpotIcon": { - "iconName": "spotLight_small" - }, - "MakeComponentIcon": { - "iconName": "createComponent_small" - }, - "MaterialIcon": { - "iconName": "material_medium" - }, - "MergeWithTemplateIcon": { - "iconName": "merge_small" - }, - "MinimalDownArrowIcon" : { - "iconName": "upDownSquare2" - }, - "ModelConeIcon": { - "iconName": "cone_small" - }, - "ModelCubeIcon": { - "iconName": "cube_small" - }, - "ModelCylinderIcon": { - "iconName": "cylinder_small" - }, - "ModelPlaneIcon": { - "iconName": "plane_small" - }, - "ModelSphereIcon": { - "iconName": "sphere_small" - }, - "ParentIcon": { - "iconName": "selectParent_small" - }, - "PasteIcon": { - "iconName": "paste_small" - }, - "PositionsersIcon": { - "iconName": "positioners_small" - }, - "PrimitivesIcon": { - "iconName": "cube_small" - }, - "ResetViewIcon": { - "iconName": "reload_medium" - }, - "SelecionIcon": { - "iconName": "selection_small" - }, - "ShowBoundsIcon": { - "Off": { - "iconName": "visibilityOff" + "AlignCameraToViewIcon": { + "iconName": "alignToCam_medium" }, - "On": { - "iconName": "visibilityOn" - } - }, - "SnappingIcon": { - "iconName": "snapping_small" - }, - "SimpleCheckIcon": { - "Off": { - "iconName": "transparent" + "AlignViewToCameraIcon": { + "iconName": "alignToView_medium" }, - "On": { - "iconName": "tickMark_small" - } - }, - "TimelineIcon": { - "iconName": "timeline_small" - }, - "ToggleGroupIcon": { - "Off": { - "iconName": "selectOutline_medium" + "CameraIcon": { + "Off": { + "iconName": "orthCam_small" + }, + "On": { + "iconName": "perspectiveCam_small" + } }, - "On": { - "iconName": "selectFill_medium" - } - }, - "VisibilityIcon": { - "Off": { - "iconName": "visibilityOff" + "EditColorIcon": { + "iconName": "colorSelection_medium" }, - "On": { - "iconName": "visibilityOn" + "EditLightIcon": { + "Off": { + "iconName": "editLightOff_medium" + }, + "On": { + "iconName": "editLightOn_medium" + } + }, + "FitToViewIcon": { + "iconName": "fitToView_medium" + }, + "LocalOrientIcon": { + "iconName": "localOrient_medium" + }, + "MoveToolIcon": { + "iconName": "move_medium" + }, + "ParticlesAnimationIcon": { + "iconName": "particleAnimation_medium" + }, + "ParticlesPlayIcon": { + "Off": { + "iconName": "playOutline_medium" + }, + "On": { + "iconName": "pause" + } + }, + "ParticlesRestartIcon": { + "iconName": "restartParticles_medium" + }, + "ResetViewIcon": { + "iconName": "reload_medium" + }, + "RotateToolIcon": { + "iconName": "roatate_medium" + }, + "ScaleToolIcon": { + "iconName": "scale_medium" + }, + "SnappingIcon": { + "iconName": "snapping_medium" + }, + "SnappingConfIcon": { + "iconName": "snapping_conf_medium" + }, + "ToggleGroupIcon": { + "Off": { + "iconName": "selectOutline_medium" + }, + "On": { + "iconName": "selectFill_medium" + } + }, + "VisibilityIcon": { + "Off": { + "iconName": "invisible_medium" + }, + "On": { + "iconName": "visible_medium" + } } } -} +] diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml new file mode 100644 index 00000000000..b246eb62daf --- /dev/null +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -0,0 +1,242 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property int toolTipDelay: 1000 + + width: 230 + height: 270 + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + Connections { + target: rootView + + // Spinboxes lose the initial binding if the value changes so we need these connections + onPosIntChanged: posIntSpin.realValue = rootView.posInt + onRotIntChanged: rotIntSpin.realValue = rootView.rotInt + onScaleIntChanged: scaleIntSpin.realValue = rootView.scaleInt + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + RowLayout { + height: 32 + Layout.topMargin: 8 + Layout.rightMargin: 8 + Layout.leftMargin: 8 + Layout.fillWidth: true + spacing: 16 + + Rectangle { + width: 40 + height: 40 + radius: 5 + Layout.fillHeight: false + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + HelperWidgets.IconIndicator { + anchors.fill: parent + icon: StudioTheme.Constants.snapping_conf_medium + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + iconColor: StudioTheme.Values.themeLinkIndicatorColorHover + enabled: false + states: [] // Disable normal state based coloring + } + } + Text { + text: qsTr("Snap Configuration") + font.pixelSize: 12 + horizontalAlignment: Text.AlignLeft + Layout.fillWidth: true + font.bold: true + color: StudioTheme.Values.themeTextColor + } + } + + GridLayout { + Layout.margins:10 + Layout.fillWidth: true + Layout.fillHeight: true + + rowSpacing: 5 + columnSpacing: 5 + rows: 5 + columns: 3 + + Text { + text: qsTr("Interval") + Layout.column: 1 + Layout.row: 0 + Layout.leftMargin: 10 + font.pixelSize: 12 + font.bold: true + color: StudioTheme.Values.themeTextColor + } + + StudioControls.CheckBox { + text: qsTr("Position") + Layout.column: 0 + Layout.row: 1 + Layout.minimumWidth: 100 + checked: rootView.posEnabled + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap position.") + ToolTip.delay: root.toolTipDelay + + onToggled: rootView.posEnabled = checked + } + + StudioControls.RealSpinBox { + id: posIntSpin + Layout.fillWidth: true + Layout.column: 1 + Layout.row: 1 + Layout.leftMargin: 10 + realFrom: 1 + realTo: 10000 + realValue: rootView.posInt + realStepSize: 1 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap interval for move gizmo.") + ToolTip.delay: root.toolTipDelay + + onRealValueChanged: rootView.posInt = realValue + } + + StudioControls.CheckBox { + text: qsTr("Rotation") + Layout.column: 0 + Layout.row: 2 + Layout.minimumWidth: 100 + checked: rootView.rotEnabled + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap rotation.") + ToolTip.delay: root.toolTipDelay + + onToggled: rootView.rotEnabled = checked + } + + StudioControls.RealSpinBox { + id: rotIntSpin + Layout.fillWidth: true + Layout.column: 1 + Layout.row: 2 + Layout.leftMargin: 10 + realFrom: 1 + realTo: 90 + realValue: rootView.rotInt + realStepSize: 1 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap interval in degrees for rotation gizmo.") + ToolTip.delay: root.toolTipDelay + + onRealValueChanged: rootView.rotInt = realValue + } + + StudioControls.CheckBox { + text: qsTr("Scale") + Layout.column: 0 + Layout.row: 3 + Layout.minimumWidth: 100 + checked: rootView.scaleEnabled + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap scale.") + ToolTip.delay: root.toolTipDelay + + onToggled: rootView.scaleEnabled = checked + } + + StudioControls.RealSpinBox { + id: scaleIntSpin + Layout.fillWidth: true + Layout.column: 1 + Layout.row: 3 + Layout.leftMargin: 10 + realFrom: 1 + realTo: 100 + realValue: rootView.scaleInt + realStepSize: 1 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap interval for scale gizmo in percentage of original scale.") + ToolTip.delay: root.toolTipDelay + + onRealValueChanged: rootView.scaleInt = realValue + } + + StudioControls.CheckBox { + text: qsTr("Absolute Position") + Layout.fillWidth: false + Layout.leftMargin: 0 + Layout.column: 0 + Layout.row: 4 + Layout.columnSpan: 3 + checked: rootView.absolute + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Toggles if the position snaps to absolute values or relative to object position.") + ToolTip.delay: root.toolTipDelay + + onToggled: rootView.absolute = checked + } + + Text { + text: qsTr("deg") + font.pixelSize: 12 + Layout.column: 2 + Layout.row: 2 + color: StudioTheme.Values.themeTextColor + } + + Text { + text: qsTr("%") + font.pixelSize: 12 + Layout.column: 2 + Layout.row: 3 + color: StudioTheme.Values.themeTextColor + } + } + + HelperWidgets.Button { + text: qsTr("Reset All") + Layout.bottomMargin: 8 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + onClicked: rootView.resetDefaults() + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml new file mode 100644 index 00000000000..9b0046f67c3 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +HelperWidgets.Section { + id: root + + // model properties + required property string nodeName + required property bool nodeEnabled + required property var nodeUniformsModel + + required property int index + + caption: root.nodeName + category: "EffectMaker" + + draggable: true + fillBackground: true + showCloseButton: true + closeButtonToolTip: qsTr("Remove") + + onCloseButtonClicked: { + EffectMakerBackend.effectMakerModel.removeNode(root.index) + } + + showEyeButton: true + eyeEnabled: root.nodeEnabled + eyeButtonToolTip: qsTr("Enable/Disable Node") + + onEyeButtonClicked: { + root.nodeEnabled = root.eyeEnabled + } + + Column { + spacing: 10 + + Repeater { + model: root.nodeUniformsModel + + EffectCompositionNodeUniform { + width: root.width + } + } + } +} + diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml new file mode 100644 index 00000000000..7c632730ccb --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Item { + id: root + + height: layout.implicitHeight + + Component.onCompleted: { + if (uniformType === "int") + valueLoader.source = "ValueInt.qml" + else if (uniformType === "vec2") + valueLoader.source = "ValueVec2.qml" + else if (uniformType === "vec3") + valueLoader.source = "ValueVec3.qml" + else if (uniformType === "vec4") + valueLoader.source = "ValueVec4.qml" + else if (uniformType === "bool") + valueLoader.source = "ValueBool.qml" + else if (uniformType === "color") + valueLoader.source = "ValueColor.qml" + else if (uniformType === "image") + valueLoader.source = "ValueImage.qml" + else if (uniformType === "define") + valueLoader.source = "ValueDefine.qml" + else + valueLoader.source = "ValueFloat.qml" + } + + RowLayout { + id: layout + + spacing: 20 + anchors.fill: parent + + Text { + text: uniformName + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignRight + Layout.maximumWidth: 140 + Layout.minimumWidth: 140 + Layout.preferredWidth: 140 + + HelperWidgets.ToolTipArea { + anchors.fill: parent + tooltip: uniformDescription + } + } + + Loader { + id: valueLoader + Layout.fillWidth: true + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml new file mode 100644 index 00000000000..456539d13cf --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -0,0 +1,152 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import EffectMakerBackend + +Item { + id: root + + property var draggedSec: null + property var secsY: [] + property int moveFromIdx: 0 + property int moveToIdx: 0 + + Column { + id: col + anchors.fill: parent + spacing: 1 + + EffectMakerTopBar { + + } + + EffectMakerPreview { + + } + + Rectangle { + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + EffectNodesComboBox { + mainRoot: root + + anchors.verticalCenter: parent.verticalCenter + } + + HelperWidgets.AbstractButton { + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.code + tooltip: qsTr("Open Shader in Code Editor") + + onClicked: {} // TODO + } + } + + HelperWidgets.ScrollView { + id: scrollView + + width: parent.width + height: parent.height - y + clip: true + + Column { + width: scrollView.width + spacing: 1 + + Repeater { + id: repeater + + width: root.width + model: EffectMakerBackend.effectMakerModel + + onCountChanged: { + HelperWidgets.Controller.setCount("EffectMaker", repeater.count) + } + + delegate: EffectCompositionNode { + width: root.width + + Behavior on y { + PropertyAnimation { + duration: 300 + easing.type: Easing.InOutQuad + } + } + + onStartDrag: (section) => { + root.draggedSec = section + root.moveFromIdx = index + + highlightBorder = true + + root.secsY = [] + for (let i = 0; i < repeater.count; ++i) + root.secsY[i] = repeater.itemAt(i).y + } + + onStopDrag: { + if (root.moveFromIdx === root.moveToIdx) + root.draggedSec.y = root.secsY[root.moveFromIdx] + else + EffectMakerBackend.effectMakerModel.moveNode(root.moveFromIdx, root.moveToIdx) + + highlightBorder = false + root.draggedSec = null + } + } + } // Repeater + + Timer { + running: root.draggedSec + interval: 50 + repeat: true + + onTriggered: { + root.moveToIdx = root.moveFromIdx + for (let i = 0; i < repeater.count; ++i) { + let currItem = repeater.itemAt(i) + if (i > root.moveFromIdx) { + if (root.draggedSec.y > currItem.y + (currItem.height - root.draggedSec.height) * .5) { + currItem.y = root.secsY[i] - root.draggedSec.height + root.moveToIdx = i + } else { + currItem.y = root.secsY[i] + } + } else if (i < root.moveFromIdx) { + if (root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) { + currItem.y = root.secsY[i] + root.draggedSec.height + root.moveToIdx = Math.min(root.moveToIdx, i) + } else { + currItem.y = root.secsY[i] + } + } + } + } + } // Timer + } // Column + } // ScrollView + } + + Text { + id: emptyText + + text: qsTr("Add an effect node to start") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + + x: scrollView.x + (scrollView.width - emptyText.width) * .5 + y: scrollView.y + scrollView.height * .5 + + visible: EffectMakerBackend.effectMakerModel.isEmpty + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml new file mode 100644 index 00000000000..10f6b516024 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -0,0 +1,127 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Column { + id: root + + width: parent.width + + Rectangle { // toolbar + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + RowLayout { + anchors.fill: parent + spacing: 5 + anchors.rightMargin: 5 + anchors.leftMargin: 5 + + Item { + Layout.fillWidth: true + } + + HelperWidgets.AbstractButton { + enabled: previewImage.scale > .4 + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.zoomOut_medium + tooltip: qsTr("Zoom out") + + onClicked: { + previewImage.scale -= .2 + } + } + + HelperWidgets.AbstractButton { + enabled: previewImage.scale < 2 + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.zoomIn_medium + tooltip: qsTr("Zoom In") + + onClicked: { + previewImage.scale += .2 + } + } + + HelperWidgets.AbstractButton { + enabled: previewImage.scale !== 1 + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.fitAll_medium + tooltip: qsTr("Zoom Fit") + + onClicked: { + previewImage.scale = 1 + } + } + + Item { + Layout.fillWidth: true + } + + Column { + Text { + text: "0.000s" + color: StudioTheme.Values.themeTextColor + font.pixelSize: 10 + } + + Text { + text: "0000000" + color: StudioTheme.Values.themeTextColor + font.pixelSize: 10 + } + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.toStartFrame_medium + tooltip: qsTr("Restart Animation") + + onClicked: {} // TODO + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.topToolbar_runProject + tooltip: qsTr("Play Animation") + + onClicked: {} // TODO + } + } + } + + Rectangle { // preview image + id: previewImageBg + + color: "#dddddd" + width: parent.width + height: 200 + clip: true + + Image { + id: previewImage + + anchors.margins: 5 + anchors.fill: parent + fillMode: Image.PreserveAspectFit + smooth: true + + source: "images/qt_logo.png" // TODO: update image + + Behavior on scale { + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml new file mode 100644 index 00000000000..eb5463f3bdf --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Rectangle { + id: root + + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + HelperWidgets.Button { + anchors.verticalCenter: parent.verticalCenter + x: 5 + + text: qsTr("Save in Library") + + onClicked: { + // TODO + } + } + + HelperWidgets.AbstractButton { + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 5 + anchors.right: parent.right + + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.help + tooltip: qsTr("How to use Effect Maker: +1. Click \"+ Add Effect\" to add effect node +2. Adjust the effect nodes properties +3. Change the order of the effects, if you like +4. See the preview +5. Save in the library, if you wish to reuse the effect later") // TODO: revise with doc engineer + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml new file mode 100644 index 00000000000..8ae703adb7e --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Rectangle { + id: root + + width: 140 + height: 32 + + color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction + : "transparent" + + signal addEffectNode(var nodeQenPath) + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + + onClicked: { + root.addEffectNode(modelData.nodeQenPath) + } + } + + Row { + spacing: 5 + + IconImage { + id: nodeIcon + + width: 32 + height: 32 + + color: StudioTheme.Values.themeTextColor + source: modelData.nodeIcon + } + + Text { + text: modelData.nodeName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.smallFontSize + anchors.verticalCenter: nodeIcon.verticalCenter + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml new file mode 100644 index 00000000000..9c52f687aac --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -0,0 +1,113 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +StudioControls.ComboBox { + id: root + + actionIndicatorVisible: false + x: 5 + width: parent.width - 50 + + model: [qsTr("+ Add Effect")] + + // hide default popup + popup.width: 0 + popup.height: 0 + + required property Item mainRoot + + Connections { + target: root.popup + + function onAboutToShow() { + var a = mainRoot.mapToGlobal(0, 0) + var b = root.mapToItem(mainRoot, 0, 0) + + effectNodesWindow.x = a.x + b.x + root.width - effectNodesWindow.width + effectNodesWindow.y = a.y + b.y + root.height - 1 + + effectNodesWindow.show() + effectNodesWindow.requestActivate() + } + + function onAboutToHide() { + effectNodesWindow.hide() + } + } + + Window { + id: effectNodesWindow + + width: row.width + 2 // 2: scrollView left and right 1px margins + height: Math.min(800, Math.min(row.height + 2, Screen.height - y - 40)) // 40: some bottom margin to cover OS bottom toolbar + flags: Qt.Popup | Qt.FramelessWindowHint + + onActiveChanged: { + if (!active && !root.hover) + root.popup.close() + } + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeInteraction + border.width: 1 + + HelperWidgets.ScrollView { + anchors.fill: parent + anchors.margins: 1 + + Row { + id: row + + onWidthChanged: { + // Needed to update on first window showing, as row.width only gets + // correct value after the window is shown, so first showing is off + + var a = mainRoot.mapToGlobal(0, 0) + var b = root.mapToItem(mainRoot, 0, 0) + + effectNodesWindow.x = a.x + b.x + root.width - row.width + } + + padding: 10 + spacing: 10 + + Repeater { + model: EffectMakerBackend.effectMakerNodesModel + + Column { + spacing: 10 + + Text { + text: categoryName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.baseFontSize + } + + Item { width: 1; height: 5 } // spacer + + Repeater { + model: categoryNodes + + EffectNode { + onAddEffectNode: (nodeQenPath) => { + EffectMakerBackend.rootView.addEffectNode(modelData.nodeQenPath) + root.popup.close() + } + } + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml new file mode 100644 index 00000000000..201f9976989 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +StudioControls.CheckBox { + actionIndicatorVisible: false + checked: uniformValue + onToggled: uniformValue = checked +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml new file mode 100644 index 00000000000..8b381ccaa1b --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml @@ -0,0 +1,23 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + id: itemPane + + width: parent.width + spacing: 5 + + HelperWidgets.ColorEditor { + backendValue: uniformBackendValue + + showExtendedFunctionButton: false + + onValueChanged: uniformValue = convertColorToString(color) + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueDefine.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueDefine.qml new file mode 100644 index 00000000000..d5a6cf139a1 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueDefine.qml @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + width: parent.width + + StudioControls.TextField { + id: textField + + width: parent.width - 20 + + actionIndicatorVisible: false + translationIndicatorVisible: false + + text: uniformValue + + onEditingFinished: uniformValue = text + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml new file mode 100644 index 00000000000..9c2d2c80a23 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: spinBox + + width: 60 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue + realTo: uniformMaxValue + realValue: uniformValue + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue = realValue + } + + StudioControls.Slider { + id: slider + + width: parent.width - 100 + visible: width > 20 + labels: false + decimals: 2 + actionIndicatorVisible: false + handleLabelVisible: false + from: uniformMinValue + to: uniformMaxValue + value: uniformValue + onMoved: { + uniformValue = value + spinBox.realValue = value // binding isn't working for this property so update it + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml new file mode 100644 index 00000000000..571fac50002 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -0,0 +1,23 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + id: itemPane + + width: parent.width + spacing: 5 + + HelperWidgets.UrlChooser { + backendValue: uniformBackendValue + + actionIndicatorVisible: false + + onAbsoluteFilePathChanged: uniformValue = absoluteFilePath + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml new file mode 100644 index 00000000000..b5db8db05e8 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + width: parent.width + spacing: 5 + + StudioControls.SpinBox { + id: spinBox + + width: 60 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + from: uniformMinValue + to: uniformMaxValue + value: uniformValue + onValueModified: uniformValue = value + } + + StudioControls.Slider { + id: slider + + width: parent.width - 100 + visible: width > 20 + labels: false + actionIndicatorVisible: false + handleLabelVisible: false + from: uniformMinValue + to: uniformMaxValue + value: uniformValue + onMoved: { + uniformValue = value + spinBox.value = value + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml new file mode 100644 index 00000000000..9207e98a621 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml @@ -0,0 +1,88 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +RowLayout { + width: parent.width + spacing: 0 + + StudioControls.RealSpinBox { + id: vX + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("X") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vY + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("Y") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + } + +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml new file mode 100644 index 00000000000..52df4c999a6 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml @@ -0,0 +1,125 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +RowLayout { + width: parent.width + spacing: 0 + + StudioControls.RealSpinBox { + id: vX + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("X") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vY + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("Y") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vZ + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.z + realTo: uniformMaxValue.z + realValue: uniformValue.z + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.z = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("Z") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + } + +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml new file mode 100644 index 00000000000..05954e5b65c --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml @@ -0,0 +1,162 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuickDesignerTheme +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +RowLayout { + width: parent.width + spacing: 0 + + StudioControls.RealSpinBox { + id: vX + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("X") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vY + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("Y") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vZ + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.z + realTo: uniformMaxValue.z + realValue: uniformValue.z + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.z = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("Z") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + Layout.maximumWidth: 20 + } + + StudioControls.RealSpinBox { + id: vW + + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 + + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.w + realTo: uniformMaxValue.w + realValue: uniformValue.w + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.w = realValue + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 + } + + Text { + text: qsTr("W") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter + } + + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 10 + } + +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png new file mode 100644 index 00000000000..5181f1b5ab4 Binary files /dev/null and b/share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png differ diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index f0031999fa5..9272d2321e4 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -241,9 +241,9 @@ Item { MouseArea { id: rootMouseArea - y: topContent.height + y: toolbar.height width: parent.width - height: parent.height - topContent.height + height: parent.height - toolbar.height acceptedButtons: Qt.RightButton @@ -515,18 +515,18 @@ Item { spacing: 5 Rectangle { - id: topContent + id: toolbar width: parent.width height: StudioTheme.Values.doubleToolbarHeight color: StudioTheme.Values.themeToolbarBackground Column { anchors.fill: parent - anchors.topMargin: 6 - anchors.bottomMargin: 6 - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 12 + anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + spacing: StudioTheme.Values.toolbarColumnSpacing StudioControls.SearchBox { id: searchBox @@ -581,31 +581,38 @@ Item { } } - Text { - text: { - if (!materialBrowserModel.hasQuick3DImport) - qsTr("To use Material Browser, first add the QtQuick3D module in the Components view.") - else if (!materialBrowserModel.hasMaterialLibrary) - qsTr("Material Browser is disabled inside a non-visual component.") - else - "" - } - - textFormat: Text.RichText - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - topPadding: 30 - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap + Item { width: root.width - visible: text !== "" + height: root.height - toolbar.height + visible: hint.text !== "" + + Text { + id: hint + width: parent.width - 40 + anchors.centerIn: parent + + text: { + if (!materialBrowserModel.hasQuick3DImport) + qsTr("To use Material Browser, first add the QtQuick3D module in the Components view.") + else if (!materialBrowserModel.hasMaterialLibrary) + qsTr("Material Browser is disabled inside a non-visual component.") + else + "" + } + + textFormat: Text.RichText + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.mediumFontSize + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } } HelperWidgets.ScrollView { id: scrollView width: root.width - height: root.height - topContent.height + height: root.height - toolbar.height clip: true visible: root.enableUiElements interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index 355f71f7591..005b4dfff1d 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml @@ -1,8 +1,7 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 +import QtQuick import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme @@ -10,6 +9,9 @@ import StudioTheme 1.0 as StudioTheme PropertyEditorPane { id: root + width: 420 + height: 420 + signal toolBarAction(int action) signal previewEnvChanged(string env) signal previewModelChanged(string model) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index 52e49074483..0187cde6951 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -1,13 +1,16 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 +import QtQuick import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 PropertyEditorPane { id: itemPane + width: 420 + height: 420 + signal toolBarAction(int action) signal previewEnvChanged(string env) signal previewModelChanged(string model) @@ -53,12 +56,12 @@ PropertyEditorPane { anchors.left: parent.left anchors.right: parent.right - visible: theSource !== "" + visible: specificsTwo.theSource !== "" sourceComponent: specificQmlComponent onTheSourceChanged: { - active = false - active = true + specificsTwo.active = false + specificsTwo.active = true } } diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml index cdc6f51900b..ef8e020a5d7 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml @@ -397,13 +397,7 @@ Item { currentIndex: BackendApi.targetQtVersionIndex font.pixelSize: DialogValues.defaultPixelSize - model: ListModel { - ListElement { name: "Qt 5.15" } - ListElement { name: "Qt 6.2" } - ListElement { name: "Qt 6.3" } - ListElement { name: "Qt 6.4" } - ListElement { name: "Qt 6.5" } - } + model: BackendApi.targetQtVersions onActivated: (index) => { BackendApi.targetQtVersionIndex = index diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes index b757c79a4e6..f6f21596899 100644 --- a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes +++ b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes @@ -27,10 +27,6 @@ Component { name: "QVector" } -Component { - name: "uint" -} - Component { name: "QGeometryView" } @@ -43,18 +39,6 @@ Component { name: "Qt3DRender::QLevelOfDetailBoundingSphere" } -Component { - name: "QImage" -} - -Component { - name: "qlonglong" -} - -Component { - name: "qulonglong" -} - Component { name: "Hits" } @@ -87,10 +71,6 @@ Component { name: "QGraphicsEffect" } -Component { - name: "QGraphicsLayout" -} - Component { name: "QPalette" } @@ -99,10 +79,6 @@ Component { name: "QRegExp" } -Component { - name: "QRegExp" -} - Component { name: "QList" } @@ -111,10 +87,6 @@ Component { name: "QList" } -Component { - name: "QList" -} - Component { name: "QList" } @@ -235,10 +207,6 @@ Component { name: "ShadowInputControl_QMLTYPE_16" } -Component { - name: "ShadowInputControl_QMLTYPE_16" -} - Component { name: "const QPointingDevice" } @@ -283,10 +251,6 @@ Component { name: "QQuickWebEngineScriptCollection" } -Component { - name: "QQuickWebEngineScriptCollection" -} - Component { name: "QOrientationReading::Orientation" } @@ -323,19 +287,35 @@ Component { name: "QOpcUaLocalizedText" } -Component { - name: "QDate" -} - Component { name: "Qt::InputMethodHints" } Component { - name: "QChar" + name: "QRgb" } Component { - name: "QVector3D" + name: "ulong" +} + +Component { + name: "QQuickTapHandler::ExclusiveSignals" +} + +Component { + name: "QColorDialogOptions::ColorDialogOptions" +} + +Component { + name: "QPolygonF" +} + +Component { + name: "QScreen" +} + +Component { + name: "QScxmlError" } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml index 943b35aeed3..60bd415a6af 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml @@ -1,9 +1,9 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import HelperWidgets 2.0 import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme @@ -116,12 +116,12 @@ PropertyEditorPane { anchors.left: parent.left anchors.right: parent.right - visible: theSource !== "" + visible: specificsTwo.theSource !== "" sourceComponent: specificQmlComponent onTheSourceChanged: { - active = false - active = true + specificsTwo.active = false + specificsTwo.active = true } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml index 59b08175576..36d39f829cf 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/QtObjectPane.qml @@ -1,8 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Object3DPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Object3DPane.qml index 04691f5b1ef..c532281d6f5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Object3DPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/Object3DPane.qml @@ -1,8 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme @@ -25,16 +25,17 @@ PropertyEditorPane { Loader { id: specificsTwo - anchors.left: parent.left - anchors.right: parent.right - visible: theSource !== "" - sourceComponent: specificQmlComponent property string theSource: specificQmlData + anchors.left: parent.left + anchors.right: parent.right + visible: specificsTwo.theSource !== "" + sourceComponent: specificQmlComponent + onTheSourceChanged: { - active = false - active = true + specificsTwo.active = false + specificsTwo.active = true } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 96745a2502b..7c5b9535473 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -34,6 +34,8 @@ SecondColumnLayout { property alias gradientThumbnail: gradientThumbnail property alias shapeGradientThumbnail: shapeGradientThumbnail + property alias showExtendedFunctionButton: hexTextField.showExtendedFunctionButton + property bool shapeGradients: false property color originalColor property bool isVector3D: false diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 7a53153c6d8..2a4b036effb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -280,7 +280,7 @@ T.Popup { property ListModel items: ListModel {} enabled: isBaseState - implicitWidth: StudioTheme.Values.colorEditorPopupCmoboBoxWidth + implicitWidth: StudioTheme.Values.colorEditorPopupComboBoxWidth width: implicitWidth actionIndicatorVisible: false textRole: "text" @@ -959,6 +959,7 @@ T.Popup { component ControlsRow: RowLayout { property alias propertyName: spinBox.propertyName + property alias gradientTypeName: spinBox.gradientTypeName property alias labelText: label.text property alias labelTooltip: label.tooltip property alias value: spinBox.value @@ -980,21 +981,21 @@ T.Popup { } } + ControlLabel { + id: label + horizontalAlignment: Text.AlignLeft + width: StudioTheme.Values.controlGap + + StudioTheme.Values.colorEditorPopupSpinBoxWidth + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + GradientPropertySpinBox { id: spinBox implicitWidth: StudioTheme.Values.controlGap + 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth width: implicitWidth } - - Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } - - ControlLabel { - id: label - horizontalAlignment: Text.AlignLeft - width: StudioTheme.Values.controlGap - + 2 * StudioTheme.Values.colorEditorPopupSpinBoxWidth - } } // Default Gradient Controls @@ -1051,31 +1052,32 @@ T.Popup { id: linearGradientControls spacing: 10 visible: cePopup.hasLinearGradient() && colorEditor.shapeGradients + readonly property string gradientTypeName: "LinearGradient" ControlsRow { - id: linearGradientX1 propertyName: "x1" + gradientTypeName: linearGradientControls.gradientTypeName labelText: "X1" labelTooltip: qsTr("Defines the start point for color interpolation.") } ControlsRow { - id: linearGradientX2 propertyName: "x2" + gradientTypeName: linearGradientControls.gradientTypeName labelText: "X2" labelTooltip: qsTr("Defines the end point for color interpolation.") } ControlsRow { - id: linearGradientY1 propertyName: "y1" + gradientTypeName: linearGradientControls.gradientTypeName labelText: "Y1" labelTooltip: qsTr("Defines the start point for color interpolation.") } ControlsRow { - id: linearGradientY2 propertyName: "y2" + gradientTypeName: linearGradientControls.gradientTypeName labelText: "Y2" labelTooltip: qsTr("Defines the end point for color interpolation.") } @@ -1086,39 +1088,46 @@ T.Popup { id: radialGradientControls spacing: 10 visible: cePopup.hasRadialGradient() + readonly property string gradientTypeName: "RadialGradient" ControlsRow { propertyName: "centerX" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "CenterX" labelTooltip: qsTr("Defines the center point.") } ControlsRow { propertyName: "centerY" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "CenterY" labelTooltip: qsTr("Defines the center point.") } ControlsRow { propertyName: "focalX" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "FocalX" labelTooltip: qsTr("Defines the focal point.") } ControlsRow { propertyName: "focalY" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "FocalY" labelTooltip: qsTr("Defines the focal point.") } ControlsRow { propertyName: "centerRadius" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "Center Radius" labelTooltip: qsTr("Defines the center radius.") } ControlsRow { propertyName: "focalRadius" + gradientTypeName: radialGradientControls.gradientTypeName labelText: "Focal Radius" labelTooltip: qsTr("Defines the focal radius. Set to 0 for simple radial gradients.") } @@ -1126,24 +1135,28 @@ T.Popup { // Conical Gradient Controls Column { - id: concialGradientControls + id: conicalGradientControls spacing: 10 visible: cePopup.hasConicalGradient() + readonly property string gradientTypeName: "ConicalGradient" ControlsRow { propertyName: "centerX" + gradientTypeName: conicalGradientControls.gradientTypeName labelText: "CenterX" labelTooltip: qsTr("Defines the center point.") } ControlsRow { propertyName: "centerY" + gradientTypeName: conicalGradientControls.gradientTypeName labelText: "CenterY" labelTooltip: qsTr("Defines the center point.") } ControlsRow { propertyName: "angle" + gradientTypeName: conicalGradientControls.gradientTypeName labelText: "Angle" labelTooltip: qsTr("Defines the start angle for the conical gradient. The value is in degrees (0-360).") } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml index 1825a5e8fff..b2c0aee1c9a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml @@ -5,13 +5,29 @@ pragma Singleton import QtQuick 2.15 QtObject { - id: values + id: root + + // counts of sections in each category, allows accessing the count from inside a section + property var counts: ({}) property Item mainScrollView property bool contextMenuOpened: false + function count(category) { + if (!root.counts.hasOwnProperty(category)) + return 0 + + return root.counts[category] + } + + function setCount(category, count) { + root.counts[category] = count + root.countChanged(category, count) + } + signal collapseAll(string category) signal expandAll(string category) signal closeContextMenu() + signal countChanged(string category, int count) } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml index 51a67d2bd15..2656c251177 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GradientPropertySpinBox.qml @@ -4,11 +4,13 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme -Item { +SecondColumnLayout { id: wrapper property string propertyName + property string gradientTypeName property alias decimals: spinBox.decimals property alias value: spinBox.realValue @@ -23,8 +25,20 @@ Item { onFocusChanged: restoreCursor() + property bool __isPercentage: false + property bool __mightHavePercents: gradientLine.model.isPercentageSupportedByProperty(wrapper.propertyName, wrapper.gradientTypeName) + function readValue() { - spinBox.realValue = gradientLine.model.readGradientProperty(wrapper.propertyName) + wrapper.__isPercentage = (gradientLine.model.readGradientPropertyUnits(wrapper.propertyName) === GradientModel.Percentage); + + if (wrapper.__isPercentage) { + unitType.currentIndex = 1; + spinBox.realValue = gradientLine.model.readGradientPropertyPercentage(wrapper.propertyName) + } + else { + unitType.currentIndex = 0; + spinBox.realValue = gradientLine.model.readGradientProperty(wrapper.propertyName) + } } StudioControls.RealSpinBox { @@ -32,21 +46,52 @@ Item { __devicePixelRatio: devicePixelRatio() - width: wrapper.width + implicitWidth: StudioTheme.Values.colorEditorPopupSpinBoxWidth * 1.5 + width: implicitWidth actionIndicatorVisible: false realFrom: -9999 realTo: 9999 - realStepSize: 1 - decimals: 0 + realStepSize: wrapper.__isPercentage ? 0.1 : 1 + decimals: wrapper.__isPercentage ? 4 : 0 Component.onCompleted: wrapper.readValue() onCompressedRealValueModified: { - gradientLine.model.setGradientProperty(wrapper.propertyName, spinBox.realValue) + if (wrapper.__isPercentage) + gradientLine.model.setGradientPropertyPercentage(wrapper.propertyName, spinBox.realValue) + else + gradientLine.model.setGradientProperty(wrapper.propertyName, spinBox.realValue) } onDragStarted: hideCursor() onDragEnded: restoreCursor() onDragging: holdCursorInPlace() } + + Spacer { + implicitWidth: StudioTheme.Values.twoControlColumnGap + } + + StudioControls.ComboBox { + id: unitType + implicitWidth: StudioTheme.Values.colorEditorPopupSpinBoxWidth + width: implicitWidth + model: ["px", "%"] //px = 0, % = 1 + actionIndicatorVisible: false + visible: wrapper.__mightHavePercents + + onActivated: { + if (!wrapper.__mightHavePercents) + return + + if (unitType.currentIndex === 0) + gradientLine.model.setGradientPropertyUnits(wrapper.propertyName, GradientModel.Pixels) + else + gradientLine.model.setGradientPropertyUnits(wrapper.propertyName, GradientModel.Percentage) + + wrapper.readValue() + } + } + + ExpandingSpacer {} } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml index 08a560baa25..4852b05a98a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml @@ -10,6 +10,8 @@ Rectangle { id: root signal clicked() + signal pressed() + signal released() property alias icon: icon.text property alias tooltip: toolTip.text @@ -20,6 +22,7 @@ Rectangle { property alias iconStyleColor: icon.styleColor property alias containsMouse: mouseArea.containsMouse + property alias drag: mouseArea.drag property bool enabled: true property bool transparentBg: false @@ -55,12 +58,22 @@ Rectangle { if (root.enabled) root.clicked() } + + onPressed: { + if (root.enabled) + root.pressed() + } + + onReleased: { + if (root.enabled) + root.released() + } } ToolTip { id: toolTip - visible: mouseArea.containsMouse + visible: mouseArea.containsMouse && text !== "" delay: 1000 } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index 17a62874c25..26151ec9165 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -1,15 +1,16 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme 1.0 -import HelperWidgets 2.0 +import HelperWidgets 2.0 as HelperWidgets import StudioTheme 1.0 as StudioTheme Rectangle { id: itemPane + width: 320 height: 400 color: Theme.qmlDesignerBackgroundColorDarkAlternate() @@ -19,8 +20,7 @@ Rectangle { default property alias content: mainColumn.children // Called from C++ to close context menu on focus out - function closeContextMenu() - { + function closeContextMenu() { Controller.closeContextMenu() } @@ -29,9 +29,9 @@ Rectangle { onClicked: forceActiveFocus() } - ScrollView { + HelperWidgets.ScrollView { id: mainScrollView - clip: true + //clip: true anchors.fill: parent interactive: !Controller.contextMenuOpened diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml new file mode 100644 index 00000000000..43456d46ddd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml @@ -0,0 +1,91 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Templates as T +import StudioTheme 1.0 as StudioTheme + +T.ScrollBar { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property bool show: false + property bool otherInUse: false + property bool isNeeded: control.size < 1.0 + property bool inUse: control.hovered || control.pressed + property int thickness: control.inUse || control.otherInUse ? 10 : 8 + + property bool scrollBarVisible: parent.childrenRect.height > parent.height + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + hoverEnabled: true + padding: 0 + minimumSize: orientation === Qt.Horizontal ? height / width : width / height + + opacity: 0.0 + + contentItem: Rectangle { + implicitWidth: control.thickness + implicitHeight: control.thickness + radius: width / 2 + color: control.inUse ? control.style.scrollBar.handleHover : control.style.scrollBar.handle + } + + background: Rectangle { + id: controlTrack + color: control.style.scrollBar.track + opacity: control.inUse || control.otherInUse ? 0.3 : 0.0 + radius: width / 2 + + Behavior on opacity { + PropertyAnimation { + duration: 100 + easing.type: Easing.InOutQuad + } + } + } + + states: [ + State { + name: "show" + when: control.show + PropertyChanges { + target: control + opacity: 1.0 + } + }, + State { + name: "hide" + when: !control.show + PropertyChanges { + target: control + opacity: 0.0 + } + } + ] + + transitions: Transition { + from: "show" + SequentialAnimation { + PauseAnimation { duration: 450 } + NumberAnimation { + target: control + duration: 200 + property: "opacity" + to: 0.0 + } + } + } + + Behavior on thickness { + PropertyAnimation { + duration: 100 + easing.type: Easing.InOutQuad + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml index 8b898a1c2d3..f33d0dd35bc 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Controls 2.15 +import QtQuick +//import QtQuick.Controls as C import StudioTheme 1.0 as StudioTheme Flickable { @@ -12,29 +12,54 @@ Flickable { property alias verticalThickness: verticalScrollBar.width readonly property bool verticalScrollBarVisible: verticalScrollBar.scrollBarVisible readonly property bool horizontalScrollBarVisible: horizontalScrollBar.scrollBarVisible - readonly property bool bothVisible: verticalScrollBarVisible && horizontalScrollBarVisible + readonly property bool bothVisible: flickable.verticalScrollBarVisible + && flickable.horizontalScrollBarVisible property real temporaryHeight: 0 - contentWidth: areaItem.childrenRect.width - contentHeight: Math.max(areaItem.childrenRect.height, flickable.temporaryHeight) - boundsBehavior: Flickable.StopAtBounds - default property alias content: areaItem.children - Item { - id: areaItem - } + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" - ScrollBar.horizontal: HorizontalScrollBar { + HoverHandler { id: hoverHandler } + + ScrollBar.horizontal: ScrollBar { id: horizontalScrollBar parent: flickable - scrollBarVisible: flickable.contentWidth > flickable.width + x: 0 + y: flickable.height - horizontalScrollBar.height + width: flickable.availableWidth - (verticalScrollBar.isNeeded ? verticalScrollBar.thickness : 0) + orientation: Qt.Horizontal + + show: (hoverHandler.hovered || flickable.focus || flickable.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && horizontalScrollBar.isNeeded + otherInUse: verticalScrollBar.inUse } - ScrollBar.vertical: VerticalScrollBar { + ScrollBar.vertical: ScrollBar { id: verticalScrollBar parent: flickable - scrollBarVisible: flickable.contentHeight > flickable.height + x: flickable.width - verticalScrollBar.width + y: 0 + height: flickable.availableHeight - (horizontalScrollBar.isNeeded ? horizontalScrollBar.thickness : 0) + orientation: Qt.Vertical + + show: (hoverHandler.hovered || flickable.focus || flickable.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && verticalScrollBar.isNeeded + otherInUse: horizontalScrollBar.inUse } + + contentWidth: areaItem.childrenRect.width + contentHeight: Math.max(areaItem.childrenRect.height, flickable.temporaryHeight) + + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + + Item { id: areaItem } + } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 1cee92847fc..5eb40e42cc5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -18,7 +18,14 @@ Item { property alias showTopSeparator: topSeparator.visible property alias showArrow: arrow.visible property alias showLeftBorder: leftBorder.visible + property alias showCloseButton: closeButton.visible + property alias closeButtonToolTip: closeButton.tooltip + property alias showEyeButton: eyeButton.visible + property alias eyeButtonToolTip: eyeButton.tooltip property alias spacing: column.spacing + property alias draggable: dragButton.visible + property alias fillBackground: sectionBackground.visible + property alias highlightBorder: sectionBorder.visible property int leftPadding: StudioTheme.Values.sectionLeftPadding property int rightPadding: 0 @@ -35,6 +42,7 @@ Item { property bool addBottomPadding: true property bool dropEnabled: false property bool highlight: false + property bool eyeEnabled: true // eye button enabled (on) property bool useDefaulContextMenu: true @@ -70,6 +78,10 @@ Item { function onCloseContextMenu() { contextMenu.close() } + function onCountChanged(cat, count) { + if (section.showEyeButton && cat === section.category) + dragButton.enabled = count > 1 + } } signal drop(var drag) @@ -79,6 +91,10 @@ Item { signal toggleExpand() signal expand() signal collapse() + signal closeButtonClicked() + signal eyeButtonClicked() + signal startDrag(var section) + signal stopDrag() DropArea { id: dropArea @@ -125,7 +141,7 @@ Item { height: 4 source: "image://icons/down-arrow" anchors.left: parent.left - anchors.leftMargin: 4 + (section.level * section.levelShift) + anchors.leftMargin: 4 + (section.level * section.levelShift) + (section.draggable ? 20 : 0) + (section.showEyeButton ? 25 : 0) anchors.verticalCenter: parent.verticalCenter } @@ -133,7 +149,7 @@ Item { id: label anchors.verticalCenter: parent.verticalCenter color: StudioTheme.Values.themeTextColor - x: 22 + (section.level * section.levelShift) + x: arrow.x + 18 font.pixelSize: StudioTheme.Values.myFontSize font.capitalization: Font.AllUppercase } @@ -157,8 +173,66 @@ Item { } } } + + IconButton { + id: closeButton + + icon: StudioTheme.Constants.closeCross + buttonSize: 22 + iconScale: containsMouse ? 1.2 : 1 + transparentBg: true + anchors.right: parent.right + anchors.rightMargin: 10 + visible: false + + onClicked: root.closeButtonClicked() + } + + IconButton { + id: dragButton + + icon: StudioTheme.Constants.dragmarks + buttonSize: 22 + iconScale: dragButton.enabled && dragButton.containsMouse ? 1.2 : 1 + transparentBg: true + + visible: false + drag.target: dragButton.enabled ? section : null + drag.axis: Drag.YAxis + + onPressed: { + section.startDrag(section) + + section.z = ++section.parent.z // put the dragged section on top + } + + onReleased: { + section.stopDrag() + } + } + + IconButton { + id: eyeButton + + anchors.left: dragButton.right + + icon: section.eyeEnabled ? StudioTheme.Constants.visible_small : StudioTheme.Constants.invisible_small + buttonSize: 22 + iconScale: eyeButton.containsMouse ? 1.2 : 1 + transparentBg: true + + visible: false + + onClicked: { + section.eyeEnabled = !section.eyeEnabled + root.eyeButtonClicked() + } + } } + Drag.active: dragButton.drag.active + Drag.source: dragButton + Rectangle { id: topSeparator height: 1 @@ -176,6 +250,23 @@ Item { implicitHeight: Math.round(column.height + header.height + topSpacer.height + bottomSpacer.height) + Rectangle { + id: sectionBackground + anchors.top: header.bottom + width: section.width + height: topSpacer.height + column.height + bottomSpacer.height + color: StudioTheme.Values.themePanelBackground + visible: false + } + + Rectangle { + id: sectionBorder + anchors.fill: parent + color: "transparent" + border.color: StudioTheme.Values.themeInteraction + border.width: 1 + visible: false + } Item { id: topSpacer height: section.addTopPadding && column.height > 0 ? section.topPadding : 0 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml index b604710e67e..d54d64007a6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ToolTipArea.qml @@ -1,9 +1,9 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import HelperWidgets 2.0 +import QtQuick +import QtQuick.Layouts +import HelperWidgets MouseArea { id: mouseArea diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index b7540227809..8b06c20e5c4 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -27,6 +27,7 @@ Row { property alias comboBox: comboBox property alias spacer: spacer + property alias actionIndicatorVisible: comboBox.actionIndicatorVisible FileResourcesModel { id: fileModel diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 9c19a45e2ef..1e475a665c2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -59,6 +59,7 @@ PropertyEditorPane 2.0 PropertyEditorPane.qml PropertyLabel 2.0 PropertyLabel.qml PaddingSection 2.0 PaddingSection.qml RoundedPanel 2.0 RoundedPanel.qml +ScrollBar 2.0 ScrollBar.qml ScrollView 2.0 ScrollView.qml SecondColumnLayout 2.0 SecondColumnLayout.qml Section 2.0 Section.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml index 16a76c2e640..07265e41a73 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml @@ -9,6 +9,8 @@ import StudioTheme 1.0 as StudioTheme T.MenuItem { id: control + property alias shortcut: itemAction.shortcut + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle property int labelSpacing: control.style.contextMenuLabelSpacing @@ -22,7 +24,9 @@ T.MenuItem { padding: 0 spacing: 0 horizontalPadding: control.style.contextMenuHorizontalPadding - action: Action {} + action: Action { + id: itemAction + } contentItem: Item { Text { @@ -39,14 +43,14 @@ T.MenuItem { id: shortcutLabel anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - text: shortcut.nativeText + text: shortcutObserver.nativeText font: control.font color: textLabel.color Shortcut { - id: shortcut - property int shortcutWorkaround: control.action.shortcut ?? 0 - sequence: shortcut.shortcutWorkaround + id: shortcutObserver + property int shortcutWorkaround: control.shortcut ?? 0 + sequence: shortcutObserver.shortcutWorkaround } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 2f219a411da..19496da346d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -56,6 +56,7 @@ T.SpinBox { property alias __devicePixelRatio: spinBoxInput.devicePixelRatio property alias pixelsPerUnit: spinBoxInput.pixelsPerUnit + property alias inputHAlignment: spinBoxInput.horizontalAlignment property alias compressedValueTimer: myTimer @@ -306,6 +307,7 @@ T.SpinBox { spinBoxInput.handleEditingFinished() } } + onDecimalsChanged: spinBoxInput.text = control.textFromValue(control.realValue, control.locale) Keys.onPressed: function(event) { if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml index 4891c969aec..d885271ecb7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml @@ -13,6 +13,8 @@ T.TextField { signal searchChanged(string searchText) + property bool empty: control.text === "" + function isEmpty() { return control.text === "" } @@ -81,7 +83,7 @@ T.TextField { */ } - onTextChanged: control.searchChanged(text) + onTextChanged: control.searchChanged(control.text) T.Label { id: searchIcon diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml index 3cd5266dce0..c3432a92731 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml @@ -28,6 +28,7 @@ T.Slider { property bool hover: false // This property is used to indicate the global hover state property bool edit: control.activeFocus + property bool handleLabelVisible: true property alias actionIndicatorVisible: actionIndicator.visible property real __actionIndicatorWidth: control.style.actionIndicatorSize.width @@ -83,6 +84,8 @@ T.Slider { anchors.horizontalCenter: parent.horizontalCenter anchors.top: sliderHandleLabelBackground.bottom + visible: control.handleLabelVisible + ShapePath { id: sliderHandleLabelPointerPath strokeColor: "transparent" @@ -112,6 +115,7 @@ T.Slider { anchors.bottom: parent.top anchors.bottomMargin: control.style.sliderMargin color: control.style.interaction + visible: control.handleLabelVisible Text { id: sliderHandleLabel diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml index a0c7244c79c..da2a7f1d5d7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml @@ -43,6 +43,7 @@ T.SpinBox { property alias __devicePixelRatio: spinBoxInput.devicePixelRatio property alias pixelsPerUnit: spinBoxInput.pixelsPerUnit + property alias inputHAlignment: spinBoxInput.horizontalAlignment property alias compressedValueTimer: myTimer @@ -274,6 +275,7 @@ T.SpinBox { if (sliderPopup.opened && !control.activeFocus) sliderPopup.close() } + onDecimalsChanged: spinBoxInput.text = control.textFromValue(control.value, control.locale) Keys.onPressed: function(event) { if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 3210559a688..61483feadfb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -93,7 +93,7 @@ T.ComboBox { control.listView.focus = true } - onAboutToHide: window.hide() + onAboutToHide: window.close() } // Close popup when application goes to background @@ -128,22 +128,28 @@ T.ComboBox { } property ListView listView: ListView { + id: listView x: 0 y: control.style.borderWidth width: control.width - height: control.listView.contentHeight - interactive: false + height: Math.min(control.style.maxComboBoxPopupHeight, control.listView.contentHeight) model: control.model Keys.onEscapePressed: comboBoxPopup.close() currentIndex: control.highlightedIndex + boundsBehavior: Flickable.StopAtBounds + + ScrollBar.vertical: ScrollBar { + id: comboBoxPopupScrollBar + visible: listView.height < listView.contentHeight + } delegate: ItemDelegate { id: itemDelegate onClicked: { + comboBoxPopup.close() control.currentIndex = index control.activated(index) - comboBoxPopup.close() } width: control.width diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupButtonStyle.qml new file mode 100644 index 00000000000..dd7f15247d0 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupButtonStyle.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick + +ControlStyle { + + baseIconFontSize: Values.bigFont + controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight) + smallIconFontSize: Values.viewBarComboIcon + + radius: Values.smallRadius + + icon: ControlStyle.IconColors { + idle: Values.themeTextColor + hover: Values.themeTextColor + interaction: Values.themeIconColor + disabled: "#636363" + } + + background: ControlStyle.BackgroundColors { + idle: Values.themePopoutButtonBackground_idle + hover: Values.themePopoutButtonBackground_hover + interaction: Values.themeInteraction + disabled: Values.themePopoutButtonBackground_disabled + } + + border: ControlStyle.BorderColors { + idle: Values.themePopoutButtonBorder_idle + hover: Values.themePopoutButtonBorder_hover + interaction: Values.themeInteraction + disabled: Values.themecontrolBackground_statusbarIdle + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml new file mode 100644 index 00000000000..9a4d1194c67 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick + +ControlStyle { + + radius: Values.smallRadius + + baseIconFontSize: Values.baseFont + controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight) + smallIconFontSize: Values.baseFont + + background: ControlStyle.BackgroundColors { + idle: Values.themePopoutControlBackground_idle + hover: Values.themePopoutControlBackground_hover + globalHover: Values.themePopoutControlBackground_globalHover + interaction: Values.themeControlBackgroundInteraction + disabled: Values.themePopoutControlBackground_disabled + } + + popup: ControlStyle.PopupColors { + background: Values.themePopoutPopupBackground + } + + icon: ControlStyle.IconColors { + idle: Values.themeTextColor + hover: Values.themeTextColor + interaction: Values.themeTextSelectedTextColor + disabled: Values.themeTextColorDisabled + } + + border: ControlStyle.BorderColors { + idle: Values.themePopoutControlBorder_idle + hover: Values.themePopoutControlBorder_hover + interaction: Values.themeInteraction + disabled: Values.themePopoutControlBorder_disabled + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml index f8e959d8254..460ff6db3f3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml @@ -71,6 +71,8 @@ QtObject { property real scrollBarActivePadding: Values.scrollBarActivePadding property real scrollBarInactivePadding: Values.scrollBarInactivePadding + property real dialogScreenMargin: Values.dialogScreenMargin + // Special colors property color interaction: Values.themeInteraction property color interactionHover: Values.themeInteractionHover @@ -148,7 +150,8 @@ QtObject { component ScrollBarColors: QtObject { property color track: Values.themeScrollBarTrack - property color handle: Values.themeScrollBarHandle + property color handle: Values.themeScrollBarHandle_idle + property color handleHover: Values.themeScrollBarHandle } property ScrollBarColors scrollBar: ScrollBarColors {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 364b80a4856..0feb5063441 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -74,273 +74,279 @@ QtObject { readonly property string closeFile_large: "\u005B" readonly property string closeLink: "\u005C" readonly property string close_small: "\u005D" - readonly property string colorPopupClose: "\u005E" - readonly property string colorSelection_medium: "\u005F" - readonly property string columnsAndRows: "\u0060" - readonly property string cone_medium: "\u0061" - readonly property string cone_small: "\u0062" - readonly property string connection_small: "\u0063" - readonly property string connections_medium: "\u0064" - readonly property string copyLink: "\u0065" - readonly property string copyStyle: "\u0066" - readonly property string copy_small: "\u0067" - readonly property string cornerA: "\u0068" - readonly property string cornerB: "\u0069" - readonly property string cornersAll: "\u006A" - readonly property string createComponent_large: "\u006B" - readonly property string createComponent_small: "\u006C" - readonly property string create_medium: "\u006D" - readonly property string create_small: "\u006E" - readonly property string cube_medium: "\u006F" - readonly property string cube_small: "\u0070" - readonly property string curveDesigner: "\u0071" - readonly property string curveDesigner_medium: "\u0072" - readonly property string curveEditor: "\u0073" - readonly property string customMaterialEditor: "\u0074" - readonly property string cylinder_medium: "\u0075" - readonly property string cylinder_small: "\u0076" - readonly property string decisionNode: "\u0077" - readonly property string deleteColumn: "\u0078" - readonly property string deleteMaterial: "\u0079" - readonly property string deleteRow: "\u007A" - readonly property string deleteTable: "\u007B" - readonly property string delete_medium: "\u007C" - readonly property string delete_small: "\u007D" - readonly property string designMode_large: "\u007E" - readonly property string detach: "\u007F" - readonly property string directionalLight_small: "\u0080" - readonly property string distributeBottom: "\u0081" - readonly property string distributeCenterHorizontal: "\u0082" - readonly property string distributeCenterVertical: "\u0083" - readonly property string distributeLeft: "\u0084" - readonly property string distributeOriginBottomRight: "\u0085" - readonly property string distributeOriginCenter: "\u0086" - readonly property string distributeOriginNone: "\u0087" - readonly property string distributeOriginTopLeft: "\u0088" - readonly property string distributeRight: "\u0089" - readonly property string distributeSpacingHorizontal: "\u008A" - readonly property string distributeSpacingVertical: "\u008B" - readonly property string distributeTop: "\u008C" - readonly property string download: "\u008D" - readonly property string downloadUnavailable: "\u008E" - readonly property string downloadUpdate: "\u008F" - readonly property string downloaded: "\u0090" - readonly property string duplicate_small: "\u0091" - readonly property string edit: "\u0092" - readonly property string editComponent_large: "\u0093" - readonly property string editComponent_small: "\u0094" - readonly property string editLightOff_medium: "\u0095" - readonly property string editLightOn_medium: "\u0096" - readonly property string edit_medium: "\u0097" - readonly property string edit_small: "\u0098" - readonly property string effects: "\u0099" - readonly property string events_small: "\u009A" - readonly property string export_medium: "\u009B" - readonly property string eyeDropper: "\u009D" - readonly property string favorite: "\u009E" - readonly property string fitAll_medium: "\u009F" - readonly property string fitSelected_small: "\u00A0" - readonly property string fitSelection_medium: "\u00A1" - readonly property string fitToView_medium: "\u00A2" - readonly property string flowAction: "\u00A3" - readonly property string flowTransition: "\u00A4" - readonly property string fontStyleBold: "\u00A5" - readonly property string fontStyleItalic: "\u00A6" - readonly property string fontStyleStrikethrough: "\u00A7" - readonly property string fontStyleUnderline: "\u00A8" - readonly property string forward_medium: "\u00A9" - readonly property string globalOrient_medium: "\u00AA" - readonly property string gradient: "\u00AB" - readonly property string gridView: "\u00AC" - readonly property string grid_medium: "\u00AE" - readonly property string group_small: "\u00AF" - readonly property string home_large: "\u00B0" - readonly property string idAliasOff: "\u00B1" - readonly property string idAliasOn: "\u00B2" - readonly property string import_medium: "\u00B3" - readonly property string imported: "\u00B4" - readonly property string importedModels_small: "\u00B5" - readonly property string infinity: "\u00B6" - readonly property string invisible_medium: "\u00B7" - readonly property string keyframe: "\u00B8" - readonly property string languageList_medium: "\u00B9" - readonly property string layouts_small: "\u00BA" - readonly property string lights_small: "\u00BB" - readonly property string linear_medium: "\u00BC" - readonly property string linkTriangle: "\u00BD" - readonly property string linked: "\u00BE" - readonly property string listView: "\u00BF" - readonly property string list_medium: "\u00C0" - readonly property string localOrient_medium: "\u00C1" - readonly property string lockOff: "\u00C2" - readonly property string lockOn: "\u00C3" - readonly property string loopPlayback_medium: "\u00C4" - readonly property string materialBrowser_medium: "\u00C5" - readonly property string materialPreviewEnvironment: "\u00C6" - readonly property string materialPreviewModel: "\u00C7" - readonly property string material_medium: "\u00C8" - readonly property string maxBar_small: "\u00C9" - readonly property string mergeCells: "\u00CA" - readonly property string merge_small: "\u00CB" - readonly property string minus: "\u00CC" - readonly property string mirror: "\u00CD" - readonly property string more_medium: "\u00CE" - readonly property string mouseArea_small: "\u00CF" - readonly property string moveDown_medium: "\u00D0" - readonly property string moveInwards_medium: "\u00D1" - readonly property string moveUp_medium: "\u00D2" - readonly property string moveUpwards_medium: "\u00D3" - readonly property string move_medium: "\u00D4" - readonly property string newMaterial: "\u00D5" - readonly property string nextFile_large: "\u00D6" - readonly property string normalBar_small: "\u00D7" - readonly property string openLink: "\u00D8" - readonly property string openMaterialBrowser: "\u00D9" - readonly property string orientation: "\u00DA" - readonly property string orthCam_medium: "\u00DB" - readonly property string orthCam_small: "\u00DC" - readonly property string paddingEdge: "\u00DD" - readonly property string paddingFrame: "\u00DE" - readonly property string particleAnimation_medium: "\u00DF" - readonly property string pasteStyle: "\u00E0" - readonly property string paste_small: "\u00E1" - readonly property string pause: "\u00E2" - readonly property string perspectiveCam_medium: "\u00E3" - readonly property string perspectiveCam_small: "\u00E4" - readonly property string pin: "\u00E5" - readonly property string plane_medium: "\u00E6" - readonly property string plane_small: "\u00E7" - readonly property string play: "\u00E8" - readonly property string playFill_medium: "\u00E9" - readonly property string playOutline_medium: "\u00EA" - readonly property string plus: "\u00EB" - readonly property string pointLight_small: "\u00EC" - readonly property string positioners_small: "\u00ED" - readonly property string previewEnv_medium: "\u00EE" - readonly property string previousFile_large: "\u00EF" - readonly property string promote: "\u00F0" - readonly property string properties_medium: "\u00F1" - readonly property string readOnly: "\u00F2" - readonly property string recordFill_medium: "\u00F3" - readonly property string recordOutline_medium: "\u00F4" - readonly property string redo: "\u00F5" - readonly property string reload_medium: "\u00F6" - readonly property string remove_medium: "\u00F7" - readonly property string remove_small: "\u00F8" - readonly property string rename_small: "\u00F9" - readonly property string replace_small: "\u00FA" - readonly property string resetView_small: "\u00FB" - readonly property string restartParticles_medium: "\u00FC" - readonly property string reverseOrder_medium: "\u00FD" - readonly property string roatate_medium: "\u00FE" - readonly property string rotationFill: "\u00FF" - readonly property string rotationOutline: "\u0100" - readonly property string runProjFill_large: "\u0101" - readonly property string runProjOutline_large: "\u0102" - readonly property string s_anchors: "\u0103" - readonly property string s_annotations: "\u0104" - readonly property string s_arrange: "\u0105" - readonly property string s_boundingBox: "\u0106" - readonly property string s_component: "\u0107" - readonly property string s_connections: "\u0108" - readonly property string s_edit: "\u0109" - readonly property string s_enterComponent: "\u010A" - readonly property string s_eventList: "\u010B" - readonly property string s_group: "\u010C" - readonly property string s_layouts: "\u010D" - readonly property string s_merging: "\u010E" - readonly property string s_mouseArea: "\u010F" - readonly property string s_positioners: "\u0110" - readonly property string s_selection: "\u0111" - readonly property string s_snapping: "\u0112" - readonly property string s_timeline: "\u0113" - readonly property string s_visibility: "\u0114" - readonly property string saveLogs_medium: "\u0115" - readonly property string scale_medium: "\u0116" - readonly property string search: "\u0117" - readonly property string search_small: "\u0118" - readonly property string sectionToggle: "\u0119" - readonly property string selectFill_medium: "\u011A" - readonly property string selectOutline_medium: "\u011B" - readonly property string selectParent_small: "\u011C" - readonly property string selection_small: "\u011D" - readonly property string settings_medium: "\u011E" - readonly property string signal_small: "\u011F" - readonly property string snapping_small: "\u0120" - readonly property string sphere_medium: "\u0121" - readonly property string sphere_small: "\u0122" - readonly property string splitColumns: "\u0123" - readonly property string splitRows: "\u0124" - readonly property string spotLight_small: "\u0125" - readonly property string stackedContainer_small: "\u0126" - readonly property string startNode: "\u0127" - readonly property string step_medium: "\u0128" - readonly property string stop_medium: "\u0129" - readonly property string testIcon: "\u012A" - readonly property string textAlignBottom: "\u012B" - readonly property string textAlignCenter: "\u012C" - readonly property string textAlignJustified: "\u012D" - readonly property string textAlignLeft: "\u012E" - readonly property string textAlignMiddle: "\u012F" - readonly property string textAlignRight: "\u0130" - readonly property string textAlignTop: "\u0131" - readonly property string textBulletList: "\u0132" - readonly property string textFullJustification: "\u0133" - readonly property string textNumberedList: "\u0134" - readonly property string textures_medium: "\u0135" - readonly property string tickIcon: "\u0136" - readonly property string tickMark_small: "\u0137" - readonly property string timeline_small: "\u0138" - readonly property string toEndFrame_medium: "\u0139" - readonly property string toNextFrame_medium: "\u013A" - readonly property string toPrevFrame_medium: "\u013B" - readonly property string toStartFrame_medium: "\u013C" - readonly property string topToolbar_annotations: "\u013D" - readonly property string topToolbar_closeFile: "\u013E" - readonly property string topToolbar_designMode: "\u013F" - readonly property string topToolbar_enterComponent: "\u0140" - readonly property string topToolbar_home: "\u0141" - readonly property string topToolbar_makeComponent: "\u0142" - readonly property string topToolbar_navFile: "\u0143" - readonly property string topToolbar_runProject: "\u0144" - readonly property string translationCreateFiles: "\u0145" - readonly property string translationCreateReport: "\u0146" - readonly property string translationExport: "\u0147" - readonly property string translationImport: "\u0148" - readonly property string translationSelectLanguages: "\u0149" - readonly property string translationTest: "\u014A" - readonly property string transparent: "\u014B" - readonly property string triState: "\u014C" - readonly property string triangleArcA: "\u014D" - readonly property string triangleArcB: "\u014E" - readonly property string triangleCornerA: "\u014F" - readonly property string triangleCornerB: "\u0150" - readonly property string unLinked: "\u0151" - readonly property string undo: "\u0152" - readonly property string unify_medium: "\u0153" - readonly property string unpin: "\u0154" - readonly property string upDownIcon: "\u0155" - readonly property string upDownSquare2: "\u0156" - readonly property string updateAvailable_medium: "\u0157" - readonly property string updateContent_medium: "\u0158" - readonly property string visibilityOff: "\u0159" - readonly property string visibilityOn: "\u015A" - readonly property string visible_medium: "\u015B" - readonly property string visible_small: "\u015C" - readonly property string wildcard: "\u015D" - readonly property string wizardsAutomotive: "\u015E" - readonly property string wizardsDesktop: "\u015F" - readonly property string wizardsGeneric: "\u0160" - readonly property string wizardsMcuEmpty: "\u0161" - readonly property string wizardsMcuGraph: "\u0162" - readonly property string wizardsMobile: "\u0163" - readonly property string wizardsUnknown: "\u0164" - readonly property string zoomAll: "\u0165" - readonly property string zoomIn: "\u0166" - readonly property string zoomIn_medium: "\u0167" - readonly property string zoomOut: "\u0168" - readonly property string zoomOut_medium: "\u0169" - readonly property string zoomSelection: "\u016A" + readonly property string code: "\u005E" + readonly property string colorPopupClose: "\u005F" + readonly property string colorSelection_medium: "\u0060" + readonly property string columnsAndRows: "\u0061" + readonly property string cone_medium: "\u0062" + readonly property string cone_small: "\u0063" + readonly property string connection_small: "\u0064" + readonly property string connections_medium: "\u0065" + readonly property string copyLink: "\u0066" + readonly property string copyStyle: "\u0067" + readonly property string copy_small: "\u0068" + readonly property string cornerA: "\u0069" + readonly property string cornerB: "\u006A" + readonly property string cornersAll: "\u006B" + readonly property string createComponent_large: "\u006C" + readonly property string createComponent_small: "\u006D" + readonly property string create_medium: "\u006E" + readonly property string create_small: "\u006F" + readonly property string cube_medium: "\u0070" + readonly property string cube_small: "\u0071" + readonly property string curveDesigner: "\u0072" + readonly property string curveDesigner_medium: "\u0073" + readonly property string curveEditor: "\u0074" + readonly property string customMaterialEditor: "\u0075" + readonly property string cylinder_medium: "\u0076" + readonly property string cylinder_small: "\u0077" + readonly property string decisionNode: "\u0078" + readonly property string deleteColumn: "\u0079" + readonly property string deleteMaterial: "\u007A" + readonly property string deleteRow: "\u007B" + readonly property string deleteTable: "\u007C" + readonly property string delete_medium: "\u007D" + readonly property string delete_small: "\u007E" + readonly property string designMode_large: "\u007F" + readonly property string detach: "\u0080" + readonly property string directionalLight_small: "\u0081" + readonly property string distributeBottom: "\u0082" + readonly property string distributeCenterHorizontal: "\u0083" + readonly property string distributeCenterVertical: "\u0084" + readonly property string distributeLeft: "\u0085" + readonly property string distributeOriginBottomRight: "\u0086" + readonly property string distributeOriginCenter: "\u0087" + readonly property string distributeOriginNone: "\u0088" + readonly property string distributeOriginTopLeft: "\u0089" + readonly property string distributeRight: "\u008A" + readonly property string distributeSpacingHorizontal: "\u008B" + readonly property string distributeSpacingVertical: "\u008C" + readonly property string distributeTop: "\u008D" + readonly property string download: "\u008E" + readonly property string downloadUnavailable: "\u008F" + readonly property string downloadUpdate: "\u0090" + readonly property string downloaded: "\u0091" + readonly property string dragmarks: "\u0092" + readonly property string duplicate_small: "\u0093" + readonly property string edit: "\u0094" + readonly property string editComponent_large: "\u0095" + readonly property string editComponent_small: "\u0096" + readonly property string editLightOff_medium: "\u0097" + readonly property string editLightOn_medium: "\u0098" + readonly property string edit_medium: "\u0099" + readonly property string edit_small: "\u009A" + readonly property string effects: "\u009B" + readonly property string events_small: "\u009D" + readonly property string export_medium: "\u009E" + readonly property string eyeDropper: "\u009F" + readonly property string favorite: "\u00A0" + readonly property string fitAll_medium: "\u00A1" + readonly property string fitSelected_small: "\u00A2" + readonly property string fitSelection_medium: "\u00A3" + readonly property string fitToView_medium: "\u00A4" + readonly property string flowAction: "\u00A5" + readonly property string flowTransition: "\u00A6" + readonly property string fontStyleBold: "\u00A7" + readonly property string fontStyleItalic: "\u00A8" + readonly property string fontStyleStrikethrough: "\u00A9" + readonly property string fontStyleUnderline: "\u00AA" + readonly property string forward_medium: "\u00AB" + readonly property string globalOrient_medium: "\u00AC" + readonly property string gradient: "\u00AE" + readonly property string gridView: "\u00AF" + readonly property string grid_medium: "\u00B0" + readonly property string group_small: "\u00B1" + readonly property string help: "\u00B2" + readonly property string home_large: "\u00B3" + readonly property string idAliasOff: "\u00B4" + readonly property string idAliasOn: "\u00B5" + readonly property string import_medium: "\u00B6" + readonly property string imported: "\u00B7" + readonly property string importedModels_small: "\u00B8" + readonly property string infinity: "\u00B9" + readonly property string invisible_medium: "\u00BA" + readonly property string invisible_small: "\u00BB" + readonly property string keyframe: "\u00BC" + readonly property string languageList_medium: "\u00BD" + readonly property string layouts_small: "\u00BE" + readonly property string lights_small: "\u00BF" + readonly property string linear_medium: "\u00C0" + readonly property string linkTriangle: "\u00C1" + readonly property string linked: "\u00C2" + readonly property string listView: "\u00C3" + readonly property string list_medium: "\u00C4" + readonly property string localOrient_medium: "\u00C5" + readonly property string lockOff: "\u00C6" + readonly property string lockOn: "\u00C7" + readonly property string loopPlayback_medium: "\u00C8" + readonly property string materialBrowser_medium: "\u00C9" + readonly property string materialPreviewEnvironment: "\u00CA" + readonly property string materialPreviewModel: "\u00CB" + readonly property string material_medium: "\u00CC" + readonly property string maxBar_small: "\u00CD" + readonly property string mergeCells: "\u00CE" + readonly property string merge_small: "\u00CF" + readonly property string minus: "\u00D0" + readonly property string mirror: "\u00D1" + readonly property string more_medium: "\u00D2" + readonly property string mouseArea_small: "\u00D3" + readonly property string moveDown_medium: "\u00D4" + readonly property string moveInwards_medium: "\u00D5" + readonly property string moveUp_medium: "\u00D6" + readonly property string moveUpwards_medium: "\u00D7" + readonly property string move_medium: "\u00D8" + readonly property string newMaterial: "\u00D9" + readonly property string nextFile_large: "\u00DA" + readonly property string normalBar_small: "\u00DB" + readonly property string openLink: "\u00DC" + readonly property string openMaterialBrowser: "\u00DD" + readonly property string orientation: "\u00DE" + readonly property string orthCam_medium: "\u00DF" + readonly property string orthCam_small: "\u00E0" + readonly property string paddingEdge: "\u00E1" + readonly property string paddingFrame: "\u00E2" + readonly property string particleAnimation_medium: "\u00E3" + readonly property string pasteStyle: "\u00E4" + readonly property string paste_small: "\u00E5" + readonly property string pause: "\u00E6" + readonly property string perspectiveCam_medium: "\u00E7" + readonly property string perspectiveCam_small: "\u00E8" + readonly property string pin: "\u00E9" + readonly property string plane_medium: "\u00EA" + readonly property string plane_small: "\u00EB" + readonly property string play: "\u00EC" + readonly property string playFill_medium: "\u00ED" + readonly property string playOutline_medium: "\u00EE" + readonly property string plus: "\u00EF" + readonly property string pointLight_small: "\u00F0" + readonly property string positioners_small: "\u00F1" + readonly property string previewEnv_medium: "\u00F2" + readonly property string previousFile_large: "\u00F3" + readonly property string promote: "\u00F4" + readonly property string properties_medium: "\u00F5" + readonly property string readOnly: "\u00F6" + readonly property string recordFill_medium: "\u00F7" + readonly property string recordOutline_medium: "\u00F8" + readonly property string redo: "\u00F9" + readonly property string reload_medium: "\u00FA" + readonly property string remove_medium: "\u00FB" + readonly property string remove_small: "\u00FC" + readonly property string rename_small: "\u00FD" + readonly property string replace_small: "\u00FE" + readonly property string resetView_small: "\u00FF" + readonly property string restartParticles_medium: "\u0100" + readonly property string reverseOrder_medium: "\u0101" + readonly property string roatate_medium: "\u0102" + readonly property string rotationFill: "\u0103" + readonly property string rotationOutline: "\u0104" + readonly property string runProjFill_large: "\u0105" + readonly property string runProjOutline_large: "\u0106" + readonly property string s_anchors: "\u0107" + readonly property string s_annotations: "\u0108" + readonly property string s_arrange: "\u0109" + readonly property string s_boundingBox: "\u010A" + readonly property string s_component: "\u010B" + readonly property string s_connections: "\u010C" + readonly property string s_edit: "\u010D" + readonly property string s_enterComponent: "\u010E" + readonly property string s_eventList: "\u010F" + readonly property string s_group: "\u0110" + readonly property string s_layouts: "\u0111" + readonly property string s_merging: "\u0112" + readonly property string s_mouseArea: "\u0113" + readonly property string s_positioners: "\u0114" + readonly property string s_selection: "\u0115" + readonly property string s_snapping: "\u0116" + readonly property string s_timeline: "\u0117" + readonly property string s_visibility: "\u0118" + readonly property string saveLogs_medium: "\u0119" + readonly property string scale_medium: "\u011A" + readonly property string search: "\u011B" + readonly property string search_small: "\u011C" + readonly property string sectionToggle: "\u011D" + readonly property string selectFill_medium: "\u011E" + readonly property string selectOutline_medium: "\u011F" + readonly property string selectParent_small: "\u0120" + readonly property string selection_small: "\u0121" + readonly property string settings_medium: "\u0122" + readonly property string signal_small: "\u0123" + readonly property string snapping_conf_medium: "\u0124" + readonly property string snapping_medium: "\u0125" + readonly property string snapping_small: "\u0126" + readonly property string sphere_medium: "\u0127" + readonly property string sphere_small: "\u0128" + readonly property string splitColumns: "\u0129" + readonly property string splitRows: "\u012A" + readonly property string spotLight_small: "\u012B" + readonly property string stackedContainer_small: "\u012C" + readonly property string startNode: "\u012D" + readonly property string step_medium: "\u012E" + readonly property string stop_medium: "\u012F" + readonly property string testIcon: "\u0130" + readonly property string textAlignBottom: "\u0131" + readonly property string textAlignCenter: "\u0132" + readonly property string textAlignJustified: "\u0133" + readonly property string textAlignLeft: "\u0134" + readonly property string textAlignMiddle: "\u0135" + readonly property string textAlignRight: "\u0136" + readonly property string textAlignTop: "\u0137" + readonly property string textBulletList: "\u0138" + readonly property string textFullJustification: "\u0139" + readonly property string textNumberedList: "\u013A" + readonly property string textures_medium: "\u013B" + readonly property string tickIcon: "\u013C" + readonly property string tickMark_small: "\u013D" + readonly property string timeline_small: "\u013E" + readonly property string toEndFrame_medium: "\u013F" + readonly property string toNextFrame_medium: "\u0140" + readonly property string toPrevFrame_medium: "\u0141" + readonly property string toStartFrame_medium: "\u0142" + readonly property string topToolbar_annotations: "\u0143" + readonly property string topToolbar_closeFile: "\u0144" + readonly property string topToolbar_designMode: "\u0145" + readonly property string topToolbar_enterComponent: "\u0146" + readonly property string topToolbar_home: "\u0147" + readonly property string topToolbar_makeComponent: "\u0148" + readonly property string topToolbar_navFile: "\u0149" + readonly property string topToolbar_runProject: "\u014A" + readonly property string translationCreateFiles: "\u014B" + readonly property string translationCreateReport: "\u014C" + readonly property string translationExport: "\u014D" + readonly property string translationImport: "\u014E" + readonly property string translationSelectLanguages: "\u014F" + readonly property string translationTest: "\u0150" + readonly property string transparent: "\u0151" + readonly property string triState: "\u0152" + readonly property string triangleArcA: "\u0153" + readonly property string triangleArcB: "\u0154" + readonly property string triangleCornerA: "\u0155" + readonly property string triangleCornerB: "\u0156" + readonly property string unLinked: "\u0157" + readonly property string undo: "\u0158" + readonly property string unify_medium: "\u0159" + readonly property string unpin: "\u015A" + readonly property string upDownIcon: "\u015B" + readonly property string upDownSquare2: "\u015C" + readonly property string updateAvailable_medium: "\u015D" + readonly property string updateContent_medium: "\u015E" + readonly property string visibilityOff: "\u015F" + readonly property string visibilityOn: "\u0160" + readonly property string visible_medium: "\u0161" + readonly property string visible_small: "\u0162" + readonly property string wildcard: "\u0163" + readonly property string wizardsAutomotive: "\u0164" + readonly property string wizardsDesktop: "\u0165" + readonly property string wizardsGeneric: "\u0166" + readonly property string wizardsMcuEmpty: "\u0167" + readonly property string wizardsMcuGraph: "\u0168" + readonly property string wizardsMobile: "\u0169" + readonly property string wizardsUnknown: "\u016A" + readonly property string zoomAll: "\u016B" + readonly property string zoomIn: "\u016C" + readonly property string zoomIn_medium: "\u016D" + readonly property string zoomOut: "\u016E" + readonly property string zoomOut_medium: "\u016F" + readonly property string zoomSelection: "\u0170" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index ed47402de7c..43badf91300 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -185,6 +185,8 @@ QtObject { property real columnFactor: values.propertyLabelWidthMin / (values.propertyLabelWidthMin + values.controlColumnWidthMin) + property real dialogScreenMargin: Math.round(160 * values.scaleFactor) + function responsiveResize(width) { var tmpWidth = width - values.sectionColumnSpacing - values.sectionLeftPadding - values.sectionLayoutRightPadding @@ -215,20 +217,34 @@ QtObject { property real hueSliderHeight: 20 property real hueSliderHandleWidth: 10 - property real colorEditorPopupCmoboBoxWidth: 110 + property real colorEditorPopupComboBoxWidth: 110 property real colorEditorPopupSpinBoxWidth: 54 + // Popup Window + property real titleBarHeight: values.height + 10 + property real popupMargin: 10 + // Toolbar property real toolbarHeight: 41 property real doubleToolbarHeight: values.toolbarHeight * 2 property real toolbarSpacing: 10 + property real toolbarColumnSpacing: 12 + property real toolbarHorizontalMargin: 10 + property real toolbarVerticalMargin: 6 // Dialog property real dialogPadding: 12 property real dialogButtonSpacing: 10 property real dialogButtonPadding: 4 + // NEW NEW NEW + readonly property int flowMargin: 7 + readonly property int flowSpacing: 7 // Odd so cursor has a center location + readonly property int flowPillMargin: 4 + readonly property int flowPillHeight: 20 + readonly property int flowPillRadius: 4 + // Theme Colors property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue @@ -268,6 +284,30 @@ QtObject { property color themeIdleGreen: Theme.color(Theme.DSidleGreen) property color themeRunningGreen: Theme.color(Theme.DSrunningGreen) + //popoutWindow (connections) + property color themePopoutBackground: Theme.color(Theme.DSpopoutBackground) + property color themePopoutControlBackground_idle: Theme.color(Theme.DSpopoutControlBackground_idle) + property color themePopoutControlBackground_hover: Theme.color(Theme.DSpopoutControlBackground_hover) + property color themePopoutControlBackground_globalHover: Theme.color(Theme.DSpopoutControlBackground_globalHover) + property color themePopoutControlBackground_interaction: Theme.color(Theme.DSpopoutControlBackground_interaction) + property color themePopoutControlBackground_disabled: Theme.color(Theme.DSpopoutControlBackground_disabled) + + property color themePopoutPopupBackground: Theme.color(Theme.DSpopoutPopupBackground) + + property color themePopoutControlBorder_idle: Theme.color(Theme.DSpopoutControlBorder_idle) + property color themePopoutControlBorder_hover: Theme.color(Theme.DSpopoutControlBorder_hover) + property color themePopoutControlBorder_interaction: Theme.color(Theme.DSpopoutControlBorder_interaction) + property color themePopoutControlBorder_disabled: Theme.color(Theme.DSpopoutControlBorder_disabled) + + property color themePopoutButtonBackground_idle: Theme.color(Theme.DSpopoutButtonBackground_idle) + property color themePopoutButtonBackground_hover: Theme.color(Theme.DSpopoutButtonBackground_hover) + property color themePopoutButtonBackground_interaction: Theme.color(Theme.DSpopoutButtonBackground_interaction) + property color themePopoutButtonBackground_disabled: Theme.color(Theme.DSpopoutButtonBackground_disabled) + + property color themePopoutButtonBorder_idle: Theme.color(Theme.DSpopoutButtonBorder_idle) + property color themePopoutButtonBorder_hover: Theme.color(Theme.DSpopoutButtonBorder_hover) + property color themePopoutButtonBorder_interaction: Theme.color(Theme.DSpopoutButtonBorder_interaction) + //END NEW COLORS QtDS 4.0 property color themePanelBackground: Theme.color(Theme.DSpanelBackground) @@ -350,6 +390,7 @@ QtObject { property color themeScrollBarTrack: Theme.color(Theme.DSscrollBarTrack) property color themeScrollBarHandle: Theme.color(Theme.DSscrollBarHandle) + property color themeScrollBarHandle_idle: Theme.color(Theme.DSscrollBarHandle_idle) property color themeSectionHeadBackground: Theme.color(Theme.DSsectionHeadBackground) @@ -400,8 +441,14 @@ QtObject { property color themeDialogBackground: values.themeThumbnailBackground property color themeDialogOutline: values.themeInteraction + // Expression Builder + property color themePillBackground: Theme.color(Theme.DSdockWidgetSplitter) + + // Control Style Mapping property ControlStyle controlStyle: DefaultStyle {} + property ControlStyle connectionPopupControlStyle: ConnectionPopupControlStyle {} + property ControlStyle connectionPopupButtonStyle: ConnectionPopupButtonStyle {} property ControlStyle toolbarStyle: ToolbarStyle {} property ControlStyle primaryToolbarStyle: PrimaryButtonStyle {} property ControlStyle toolbarButtonStyle: TopToolbarButtonStyle {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 312554382ce..75f1fb94fad 100644 Binary files a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf and b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf differ diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index 2814e7809bb..60138d86d0f 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -1,6 +1,8 @@ singleton Values 1.0 Values.qml singleton Constants 1.0 Constants.qml ControlStyle 1.0 ControlStyle.qml +ConnectionPopupControlStyle 1.0 ConnectionPopupControlStyle.qml +ConnectionPopupButtonStyle 1.0 ConnectionPopupButtonStyle.qml DefaultStyle 1.0 DefaultStyle.qml InternalConstants 1.0 InternalConstants.qml ToolbarStyle 1.0 ToolbarStyle.qml diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml index 2fedac684d1..cb20043c3c9 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-14.qml @@ -53,7 +53,10 @@ VersionData { "QtQuick.Timeline" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml index 95e10ad2e2d..5a29a3b7e62 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-17.qml @@ -55,7 +55,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml index 5e3a76277cf..8597cfd9320 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-18.qml @@ -60,7 +60,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml index 8f8cb78921f..97a75d9bdf5 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-19.qml @@ -60,7 +60,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml index 07bee0e403e..54fff0b39d7 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-20.qml @@ -60,7 +60,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml index 5f7de7a71f6..47d416ba9ee 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml @@ -60,7 +60,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml index b5c5c924b81..9af17175b49 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml @@ -60,7 +60,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml index 0c55d7db3a4..867790a6542 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-23.qml @@ -59,7 +59,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml index 1c5d00a33f3..0475203251a 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-24.qml @@ -58,7 +58,10 @@ VersionData { "QtQuickUltralite.Layers" ] - bannedImports: ["FlowView"] + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] //ComplexProperty is not a type, it's just a way to handle bigger props ComplexProperty { diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml new file mode 100644 index 00000000000..cc9235ed63c --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-26.qml @@ -0,0 +1,217 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +VersionData { + name: "Qt for MCUs 2.6" + + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.Flow", + "QtQuick.FocusScope", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers" + ] + + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["gradient", "border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsMovement", "flickDeceleration", + "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "maximumLineCount"] + } + + QtQuick.Loader { + bannedProperties: ["asynchronous", "progress", "status"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive", "icon"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", + "fillGradient", "strokeStyle"] + } + + QtQuickUltralite.Extras.ItemBuffer { + allowedProperties: ["rotation", "scale", "transformOrigin"] + } +} diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 13f26884bcf..7e8ad669327 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -24,7 +24,7 @@ ****************************************************************************/ import QtQuick -import QtQuick.Controls +import QtQuick.Controls.Basic as Basic import StatesEditor import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls @@ -567,28 +567,42 @@ Rectangle { height: root.isLandscape ? root.height - toolBar.height - (2 * root.padding) : root.scrollViewHeight clip: true - ScrollView { + Basic.ScrollView { id: scrollView + + property bool adsFocus: false + // objectName is used by the dock widget to find this particular ScrollView + // and set the ads focus on it. + objectName: "__mainSrollView" + anchors.fill: parent anchors.topMargin: root.topMargin anchors.leftMargin: root.leftMargin - ScrollBar.horizontal: StateScrollBar { + ScrollBar.horizontal: HelperWidgets.ScrollBar { id: horizontalBar parent: scrollView x: scrollView.leftPadding y: scrollView.height - height width: scrollView.availableWidth orientation: Qt.Horizontal + + show: (scrollView.hovered || scrollView.focus || scrollView.adsFocus) + && horizontalBar.isNeeded + otherInUse: verticalBar.inUse } - ScrollBar.vertical: StateScrollBar { + ScrollBar.vertical: HelperWidgets.ScrollBar { id: verticalBar parent: scrollView x: scrollView.mirrored ? 0 : scrollView.width - width y: scrollView.topPadding height: scrollView.availableHeight orientation: Qt.Vertical + + show: (scrollView.hovered || scrollView.focus || scrollView.adsFocus) + && verticalBar.isNeeded + otherInUse: horizontalBar.inUse } Flickable { diff --git a/share/qtcreator/qmldesigner/studio_templates/files/scxml/file.scxml b/share/qtcreator/qmldesigner/studio_templates/files/scxml/file.scxml new file mode 100644 index 00000000000..960867a73f4 --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/files/scxml/file.scxml @@ -0,0 +1,3 @@ + + + diff --git a/share/qtcreator/qmldesigner/studio_templates/files/scxml/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/scxml/wizard.json new file mode 100644 index 00000000000..36bbda7b71e --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/files/scxml/wizard.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "supportedProjectTypes": [ ], + "id": "F.Scxml", + "category": "O.Model", + "trDescription": "Creates a new empty state chart.", + "trDisplayName": "State Chart", + "trDisplayCategory": "Modeling", + "iconText": "scxml", + "platformIndependent": true, + "enabled": "%{JS: value('Plugins').indexOf('ScxmlEditor') >= 0}", + + "options": + [ + { "key": "TargetPath", "value": "%{JS: Util.fileName(value('Location') + '/' + value('FileName'), Util.preferredSuffix('application/scxml+xml'))}" }, + { "key": "FileName", "value": "%{Name}" } + ], + + "pages" : + [ + { + "trDisplayName": "State Chart Name and Location", + "trShortTitle": "Location", + "typeId": "Fields", + "data": + [ + { + "name": "Name", + "trDisplayName": "State chart name:", + "mandatory": true, + "type": "LineEdit" + }, + { + "name": "Location", + "trDisplayName": "Location:", + "type": "PathChooser", + "isComplete": "%{JS: value('Location') === '' || !Util.exists(value('TargetPath'))}", + "trIncompleteMessage": "\"%{JS: Util.toNativeSeparators(value('TargetPath'))}\" exists in the filesystem.", + "data": + { + "kind": "directory", + "basePath": "%{InitialPath}", + "path": "%{InitialPath}" + } + } + ] + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators" : + [ + { + "typeId": "File", + "data": + { + "source": "file.scxml", + "target": "%{TargetPath}", + "openInEditor": true + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index 0bc60900074..4809b7d410b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -238,7 +238,7 @@ "type": "ComboBox", "data": { - "index": 4, + "index": 3, "items": [ { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Button.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Button.qml index 46d69cea6e8..e8be964890e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Button.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Button.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.Button { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/CheckBox.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/CheckBox.qml index 9fe6da21f88..e86da1c485d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/CheckBox.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/CheckBox.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.CheckBox { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/DefaultStyle.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/DefaultStyle.qml index d1a753fdfef..08437b53fad 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/DefaultStyle.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/DefaultStyle.qml @@ -1,9 +1,9 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause pragma Singleton -import QtQuick 2.15 +import QtQuick QtObject { readonly property int inset: 5 diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Dial.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Dial.qml index 2ab36330c01..bcb2af25134 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Dial.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Dial.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.Dial { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/ProgressBar.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/ProgressBar.qml index 95d2bda9317..5908f115842 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/ProgressBar.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/ProgressBar.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.ProgressBar { id: root diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/RadioButton.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/RadioButton.qml index 8033332d923..f182a82421a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/RadioButton.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/RadioButton.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.RadioButton { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Slider.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Slider.qml index eaf58a3804c..db7a8bc8e97 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Slider.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Slider.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.Slider { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/SwipeView.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/SwipeView.qml index ed091e6662f..90065ac0ce5 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/SwipeView.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/SwipeView.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.SwipeView { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Switch.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Switch.qml index 7186b4a7eba..e614c746df5 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Switch.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/Switch.qml @@ -1,8 +1,8 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick 2.15 -import QtQuick.Templates 2.15 as T +import QtQuick +import QtQuick.Templates as T T.Switch { id: control diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/qmldir b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/qmldir index 6992d7ae4a3..b29eb8fbf91 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/qmldir +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/MCUDefaultStyle/qmldir @@ -1,2 +1,12 @@ module QtQuick.Controls.MCUDefaultStyle -singleton DefaultStyle 1.0 DefaultStyle.qml +import QtQuick.Controls.Basic auto +depends QtQuick auto +singleton DefaultStyle 6.0 DefaultStyle.qml +Button 6.0 Button.qml +CheckBox 6.0 CheckBox.qml +Dial 6.0 Dial.qml +ProgressBar 6.0 ProgressBar.qml +RadioButton 6.0 RadioButton.qml +Slider 6.0 Slider.qml +SwipeView 6.0 SwipeView.qml +Switch 6.0 Switch.qml diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl similarity index 100% rename from share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject rename to share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json index 8b54e315b2f..5c27a294467 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json @@ -144,7 +144,7 @@ "data": [ { - "source": "../app_mcu.qmlproject", + "source": "app_mcu.qmlproject.tpl", "target": "%{QmlProjectFileName}", "openAsProject": true }, @@ -183,7 +183,7 @@ }, { "source": "MCUDefaultStyle", - "target": "%{ProjectDirectory}/MCUDefaultStyle" + "target": "%{ProjectDirectory}/imports/MCUDefaultStyle" }, { "isBinary": true, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl index 6fcae14ea65..cc5da7be55a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl @@ -21,7 +21,7 @@ QtObject { pixelSize: Qt.application.font.pixelSize * 1.6 }) - readonly property color backgroundColor: "#c2c2c2" + readonly property color backgroundColor: "#EAEAEA" @if %{IsQt6Project} diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml index 9623232146b..72f1c464f92 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml @@ -1,8 +1,7 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 -import QtQuick.Layouts 1.15 +import QtQuick import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 import StudioTheme 1.0 as StudioTheme @@ -10,6 +9,9 @@ import StudioTheme 1.0 as StudioTheme PropertyEditorPane { id: root + width: 420 + height: 420 + signal toolBarAction(int action) // Called from C++, dummy method to avoid warnings diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml index 0aa6bd4e093..ddf452d6f0f 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml @@ -1,13 +1,16 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.15 +import QtQuick import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 PropertyEditorPane { id: itemPane + width: 420 + height: 420 + signal toolBarAction(int action) // invoked from C++ to refresh material preview image @@ -41,12 +44,12 @@ PropertyEditorPane { anchors.left: parent.left anchors.right: parent.right - visible: theSource !== "" + visible: specificsTwo.theSource !== "" sourceComponent: specificQmlComponent onTheSourceChanged: { - active = false - active = true + specificsTwo.active = false + specificsTwo.active = true } } diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index ebed433e724..1ddedd08b48 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -238,9 +238,7 @@ Rectangle { anchors.right: annotations.left anchors.rightMargin: 10 visible: !root.flyoutEnabled - model: WorkspaceModel { - id: workspaceModel - } + model: WorkspaceModel { id: workspaceModel } textRole: "displayName" valueRole: "fileName" suffix: qsTr(" Workspace") @@ -249,6 +247,7 @@ Rectangle { onCurrentWorkspaceIndexChanged: workspaces.currentIndex = workspaces.currentWorkspaceIndex onActivated: backend.setCurrentWorkspace(workspaces.currentValue) + onCountChanged: workspaces.currentIndex = workspaces.indexOfValue(backend.currentWorkspace) } ToolbarButton { @@ -401,6 +400,7 @@ Rectangle { currentIndex: workspacesFlyout.indexOfValue(backend.currentWorkspace) onCompressedActivated: backend.setCurrentWorkspace(workspacesFlyout.currentValue) + onCountChanged: workspacesFlyout.currentIndex = workspacesFlyout.indexOfValue(backend.currentWorkspace) } } } diff --git a/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk index 47b47b213b1..6dc95483470 100644 --- a/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk +++ b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk @@ -1,5 +1,5 @@ - + @@ -7,15 +7,17 @@ - + + - + + - 343 353 270 + 437 450 344 @@ -23,10 +25,12 @@ - + + + - 353 353 + 450 450 @@ -35,9 +39,9 @@ - 353 353 + 450 450 - 448 465 + 704 730 @@ -48,9 +52,9 @@ - 457 456 + 718 716 - 707 260 + 901 331 @@ -62,9 +66,18 @@ - 343 353 270 + 437 450 344 - 310 914 406 + 486 1435 637 + + + + AdnQywADAAAAAAQuAAAB5gAABdEAAAOcAAAELgAAAfkAAAXRAAADnAAAAAAAAAAACgAAAAQuAAAB+QAABdEAAAOc + + + + + 420 diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index eeac285a5fe..46fc695570a 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -61,7 +61,8 @@ highlightHover=ff74CBFC [Colors] ;DS controls theme START -;NEW FOR QtDS 4.0 +;NEW FOR QtDS 4 +;4.0 DScontrolBackground_toolbarIdle=offWhite DScontrolBackground_toolbarHover=offWhite DScontrolBackground_topToolbarHover=concreteGrey @@ -81,7 +82,32 @@ DSstateBackgroundColor_hover=concreteGrey DSstateControlBackgroundColor_globalHover=concreteGrey DSstateControlBackgroundColor_hover=smokeGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offWhite +DSpopoutControlBackground_idle=offWhite +DSpopoutControlBackground_hover=lightWhite +DSpopoutControlBackground_globalHover=lightWhite +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offWhite +DSpopoutPopupBackground=lightWhite + +DSpopoutControlBorder_idle=slateGrey +DSpopoutControlBorder_hover=concreteGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offWhite + +DSpopoutButtonBackground_idle=offWhite +DSpopoutButtonBackground_hover=lightWhite +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offWhite + +DSpopoutButtonBorder_idle=smokeGrey +DSpopoutButtonBorder_hover=shadowGrey +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offWhite + +;END NEW FOR QtDS 4 DSpanelBackground=ffeaeaea @@ -143,8 +169,9 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ffb5b4b4 -DSscrollBarHandle=ff9b9b9b +DSscrollBarTrack=smokeGrey +DSscrollBarHandle=shadowGrey +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=ffd8d8d8 diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index 5e8294efa55..1d8295145e4 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -59,7 +59,8 @@ highlightHover=ff74CBFC [Colors] ;DS controls theme -;NEW FOR QtDS 4.0 +;NEW FOR QtDS 4 +;4.0 DScontrolBackground_toolbarIdle=midnightGrey DScontrolBackground_toolbarHover=midnightGrey DScontrolBackground_topToolbarHover=ashGrey @@ -79,7 +80,32 @@ DSstateBackgroundColor_hover=ashGrey DSstateControlBackgroundColor_globalHover=ashGrey DSstateControlBackgroundColor_hover=raincloudGrey DSpanelBackground=dawnGrey -;END NEW FOR QtDS 4.0 + +;4.3 +DSpopoutBackground=offBlack +DSpopoutControlBackground_idle=offBlack +DSpopoutControlBackground_hover=dawnGrey +DSpopoutControlBackground_globalHover=dawnGrey +DSpopoutControlBackground_interaction=highlightBlue +DSpopoutControlBackground_disabled=offBlack +DSpopoutPopupBackground=nearBlack + +DSpopoutControlBorder_idle=nearBlack +DSpopoutControlBorder_hover=midnightGrey +DSpopoutControlBorder_interaction=highlightBlue +DSpopoutControlBorder_disabled=offBlack + +DSpopoutButtonBackground_idle=offBlack +DSpopoutButtonBackground_hover=dawnGrey +DSpopoutButtonBackground_interaction=highlightBlue +DSpopoutButtonBackground_disabled=offBlack + +DSpopoutButtonBorder_idle=slateGrey +DSpopoutButtonBorder_hover=lightWhite +DSpopoutButtonBorder_interaction=highlightBlue +DSpopoutButtonBorder_disabled=offBlack + +;END NEW FOR QtDS 4 ;REMAPPED DSinteraction=highlightBlue @@ -109,8 +135,9 @@ DSnavigatorTextSelected=highlightBlue QmlDesigner_BackgroundColorDarkAlternate=dawnGrey ;TODO -DSscrollBarTrack=dawnGrey -DSscrollBarHandle=offBlack +DSscrollBarTrack=smokeGrey +DSscrollBarHandle=concreteGrey +DSscrollBarHandle_idle=slateGrey DSdockWidgetTitleBar=dawnGrey DSdockWidgetSplitter=fullBlack diff --git a/src/libs/3rdparty/span/README.md b/src/libs/3rdparty/span/README.md index 3601bc497d2..dcc95ab6cea 100644 --- a/src/libs/3rdparty/span/README.md +++ b/src/libs/3rdparty/span/README.md @@ -1,118 +1,557 @@ + +# span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later -[![Standard](https://img.shields.io/badge/c%2B%2B-11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) -[![License](https://img.shields.io/badge/license-BSL-blue.svg)](http://www.boost.org/LICENSE_1_0.txt) -[![Build Status](https://travis-ci.org/tcbrindle/span.svg?branch=master)](https://travis-ci.org/tcbrindle/span) -[![Build status](https://ci.appveyor.com/api/projects/status/ow7cj56s108fs439/branch/master?svg=true)](https://ci.appveyor.com/project/tcbrindle/span/branch/master) -[![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/-vlZZR) +[![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/span-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fspan-lite.svg)](https://github.com/martinmoene/span-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/span-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/htwpnb) -`std::span` implementation for C++11 and later -============================================== +**Contents** -This repository contains a single-header implementation of C++20's `std::span`, -conforming to the C++20 committee draft. -It is compatible with C++11, but will use newer language features if they -are available. +- [Example usage](#example-usage) +- [In a nutshell](#in-a-nutshell) +- [License](#license) +- [Dependencies](#dependencies) +- [Installation and use](#installation-and-use) +- [Synopsis](#synopsis) +- [Reported to work with](#reported-to-work-with) +- [Building the tests](#building-the-tests) +- [Other implementations of span](#other-implementations-of-span) +- [Notes and references](#notes-and-references) +- [Appendix](#appendix) -It differs from the implementation in the [Microsoft GSL](https://github.com/Microsoft/GSL/) -in that it is single-header and does not depend on any other GSL facilities. It -also works with C++11, while the GSL version requires C++14. - -Usage ------ - -The recommended way to use the implementation simply copy the file `span.hpp` -from `include/tcb/` into your own sources and `#include` it like -any other header. By default, it lives in namespace `tcb`, but this can be -customised by setting the macro `TCB_SPAN_NAMESPACE_NAME` to an appropriate string -before `#include`-ing the header -- or simply edit the source code. - -The rest of the repository contains testing machinery, and is not required for -use. - -Compatibility -------------- - -This implementation requires a conforming C++11 (or later) compiler, and is tested as far -back as GCC 5, Clang 3.5 and MSVC 2015 Update 3. Older compilers may work, but this is not guaranteed. - -Documentation -------------- - -Documentation for `std::span` is available [on cppreference](https://en.cppreference.com/w/cpp/container/span). - -Implementation Notes --------------------- - -### Bounds Checking ### - -This implementation of `span` includes optional bounds checking, which is handled -either by throwing an exception or by calling `std::terminate()`. - -The default behaviour with C++14 and later is to check the macro `NDEBUG`: -if this is set, bounds checking is disabled. Otherwise, `std::terminate()` will -be called if there is a precondition violation (i.e. the same behaviour as -`assert()`). If you wish to terminate on errors even if `NDEBUG` is set, define -the symbol `TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION` before `#include`-ing the -header. - -Alternatively, if you want to throw on a contract violation, define -`TCB_SPAN_THROW_ON_CONTRACT_VIOLATION`. This will throw an exception of an -implementation-defined type (deriving from `std::logic_error`), allowing -cleanup to happen. Note that defining this symbol will cause the checks to be -run even if `NDEBUG` is set. - -Lastly, if you wish to disable contract checking even in debug builds, -`#define TCB_SPAN_NO_CONTRACT_CHECKING`. - -Under C++11, due to the restrictions on `constexpr` functions, contract checking -is disabled by default even if `NDEBUG` is not set. You can change this by -defining either of the above symbols, but this will result in most of `span`'s -interface becoming non-`constexpr`. - -### `constexpr` ### - -This implementation is fully `constexpr` under C++17 and later. Under earlier -versions, it is "as `constexpr` as possible". - -Note that even in C++17, it is generally not possible to declare a `span` -as non-default constructed `constexpr` variable, for the same reason that you -cannot form a `constexpr` pointer to a value: it involves taking the address of -a compile-time variable in a way that would be visible at run-time. -You can however use a `span` freely in a `constexpr` function. For example: +## Example usage ```cpp -// Okay, even in C++11 -constexpr std::ptrdiff_t get_span_size(span span) +#include "nonstd/span.hpp" +#include +#include +#include + +std::ptrdiff_t size( nonstd::span spn ) { - return span.size(); + return spn.size(); } -constexpr int arr[] = {1, 2, 3}; -constexpr auto size = get_span_size(arr); // Okay -constexpr span span{arr}; // ERROR -- not a constant expression -constexpr const int* p = arr; // ERROR -- same +int main() +{ + int arr[] = { 1, }; + + std::cout << + "C-array:" << size( arr ) << + " array:" << size( std::array { 1, 2, } ) << + " vector:" << size( std::vector{ 1, 2, 3, } ); +} ``` -Constructor deduction guides are provided if the compiler supports them. For -older compilers, a set of `make_span()` functions are provided as an extension -which use the same logic, for example: +### Compile and run - ```cpp - constexpr int c_array[] = {1, 2, 3}; - std::array std_array{1, 2, 3}; - const std::vector vec{1, 2, 3}; +```bash +prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe +C-array:1 array:2 vector:3 +``` - auto s1 = make_span(c_array); // returns span - auto s2 = make_span(std_array); // returns span - auto s3 = make_span(vec); // returns span - ``` +## In a nutshell -Alternatives ------------- +**span lite** is a single-file header-only library to provide a bounds-safe view for sequences of objects. The library provides a [C++20-like span](http://en.cppreference.com/w/cpp/container/span) for use with C++98 and later. If available, `std::span` is used, unless [configured otherwise](#configuration). *span-lite* can detect the presence of [*byte-lite*](https://github.com/martinmoene/byte-lite) and if present, it provides `as_bytes()` and `as_writable_bytes()` also for C++14 and earlier. -* [Microsoft/GSL](https://github.com/Microsoft/GSL): The original `span` reference - implementation from which `std::span` was born. - -* [martinmoene/span_lite](https://github.com/martinmoene/span-lite): An - alternative implementation which offers C++98 compatibility. +**Features and properties of span lite** are ease of installation (single header), freedom of dependencies other than the standard library. To compensate for the class template argument deduction that is missing from pre-C++17 compilers, `nonstd::span` can provide `make_span` functions. See [configuration](#configuration). +## License + +*span lite* is distributed under the [Boost Software License](https://github.com/martinmoene/span-lite/blob/master/LICENSE.txt). + +## Dependencies + +*span lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). + +## Installation and use + +*span lite* is a single-file header-only library. Put `span.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. + +## Synopsis + +**Contents** +[Documentation of `std::span`](#documentation-of-stdspan) +[Later additions](#later-additions) +[Non-standard extensions](#non-standard-extensions) +[Configuration](#configuration) + +## Documentation of `std::span` + +Depending on the compiler and C++-standard used, `nonstd::span` behaves less or more like `std::span`. To get an idea of the capabilities of `nonstd::span` with your configuration, look at the output of the [tests](test/span.t.cpp), issuing `span-main.t --pass @`. For `std::span`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/container/span). + +## Later additions + +### `back()` and `front()` + +*span lite* can provide `back()` and `front()` member functions for element access. See the table below and section [configuration](#configuration). + +## Non-standard extensions + +### Construct from std::initializer_list (p2447) + +*span lite* can provide construction from a std::initializer_list<> as a constant set of values as proposed in [p2447](https://wg21.link/p2447). See the table below and section [configuration](#configuration). + +### Construct from container + +To construct a span from a container with compilers that cannot constrain such a single-parameter constructor to containers, *span lite* provides a constructor that takes an additional parameter of type `with_container_t`. Use `with_container` as value for this parameter. See the table below and section [configuration](#configuration). + +### Construct from `std::array` with const data + +*span lite* can provide construction of a span from a `std::array` with const data. See the table below and section [configuration](#configuration). + +### `operator()` + +*span lite* can provide member function call `operator()` for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. + +### `at()` + +*span lite* can provide member function `at()` for element access. Unless exceptions have been disabled, `at()` throws std::out_of_range if the index falls outside the span. With exceptions disabled, `at(index_t)` delegates bounds checking to `operator[](index_t)`. See the table below and sections [configuration](#configuration) and [disable exceptions](#disable-exceptions). + +### `swap()` + +*span lite* can provide a `swap()`member function. See the table below and section [configuration](#configuration). + +### `operator==()` and other comparison functions + +*span lite* can provide functions to compare the content of two spans. However, C++20's span will not provide comparison and _span lite_ will omit comparison at default in the near future. See the table below and section [configuration](#configuration). See also [Revisiting Regular Types](#regtyp). + +### `same()` + +*span lite* can provide function `same()` to determine if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. See the table below and section [configuration](#configuration). + +### `first()`, `last()` and `subspan()` + +*span lite* can provide functions `first()`, `last()` and `subspan()` to avoid having to use the *dot template* syntax when the span is a dependent type. See the table below and section [configuration](#configuration). + +### `make_span()` + +*span lite* can provide `make_span()` creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers. See the table below and section [configuration](#configuration). + +### `byte_span()` + +*span lite* can provide `byte_span()` creator functions to represent an object as a span of bytes. This requires the C++17 type `std::byte` to be available. See the table below and section [configuration](#configuration). + +| Kind | std | Function or method | +|--------------------|------|--------------------| +| **Macro** | | macro **`span_FEATURE_WITH_INITIALIZER_LIST_P2447`** | +| **Constructor**
  | | constexpr explicit **span**( std::initializer_list<value_type> il ) noexcept
explicit for non-dynamic extent | +|   | |   | +| **Macro**
 | | macro **`span_FEATURE_WITH_CONTAINER`**
macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** | +| **Types** | | **with_container_t** type to disambiguate below constructors | +| **Objects** | | **with_container** value to disambiguate below constructors | +| **Constructors** | | macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**| +|   | | template<class Container>
constexpr **span**(with_container_t, Container & cont) | +|   | | template<class Container>
constexpr **span**(with_container_t, Container const & cont) | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** | +|   | | constexpr reference **operator()**(index_t idx) const
Equivalent to **operator[]**(), marked `[[deprecated]]` | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_AT`** | +|   | | constexpr reference **at**(index_t idx) const
May throw std::out_of_range exception | +|   | |   | +| **Methods** | | macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) | +|   | | constexpr reference **back()** const noexcept | +|   | | constexpr reference **front()** const noexcept | +|   | |   | +| **Method** | | macro **`span_FEATURE_MEMBER_SWAP`** | +|   | | constexpr void **swap**(span & other) noexcept | +|   | |   | +| **Free functions** | | macro **`span_FEATURE_COMPARISON`** | +|

== != < > <= >= | | template<class T1, index_t E1, class T2, index_t E2>
constexpr bool
**operator==**( span const & l, span const & r) noexcept | +|   | |   | +| **Free function** | | macro **`span_FEATURE_SAME`** | +|   | | template<class T1, index_t E1, class T2, index_t E2>
constexpr bool
**same**( span const & l, span const & r) noexcept | +|   | |   | +| **Free functions**
 
  | | macros **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`**,
**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`**,
**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** | +|   | |   | +| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`** | +|   |   | template<extent_t Count, class T, extent_t Extent>
constexpr span<T,Count>
**first**(span<T,Extent> spn) | +|   |   | template<class T, extent_t Extent >
constexpr span<T>
**first**(span<T,Extent> spn, size_t count) | +|   |   | template<extent_t Count, class T, extent_t Extent>
constexpr span<T,Count>
**last**(span<T,Extent> spn) | +|   |   | template<class T, extent_t Extent >
constexpr span<T>
**last**(span<T,Extent> spn, size_t count) | +|   |   | template<size_t Offset, extent_t Count, class T, extent_t Extent>
constexpr span<T, Count>
**subspan**(span<T, Extent> spn) | +|   |   | template<class T, extent_t Extent>
constexpr span<T>
**subspan**( span<T, Extent> spn, size_t offset, extent_t count = dynamic_extent) | +|   | |   | +| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** | +|   | >= C++11 | template<extent_t Count, class T>
constexpr auto
**first**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**first**(T & t, index_t count) ->... | +|   | >= C++11 | template<extent_t Count, class T>
constexpr auto
**last**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**last**(T & t, extent_t count) ->... | +|   | >= C++11 | template<index_t Offset, extent_t Count = dynamic_extent, class T>
constexpr auto
**subspan**(T & t) ->... | +|   | >= C++11 | template<class T>
constexpr auto
**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... | +|   |   |   | +| **Free functions**
  | | macro **`span_FEATURE_MAKE_SPAN`**
macro **`span_FEATURE_MAKE_SPAN_TO_STD`** | +|   |   | template<class T>
constexpr span<T>
**make_span**(T \* first, T \* last) noexcept | +|   |   | template<class T>
constexpr span<T>
**make_span**(T \* ptr, index_t count) noexcept | +|   |   | template<class T, size_t N>
constexpr span<T,N>
**make_span**(T (&arr)[N]) noexcept | +|   | >= C++11 | template<class T, size_t N>
constexpr span<T,N>
**make_span**(std::array<T,N> & arr) noexcept | +|   | >= C++11 | template<class T, size_t N>
constexpr span<const T,N>
**make_span**(std::array<T,N > const & arr) noexcept | +|   | >= C++11 | template<class T>
constexpr span<T>
**make_span**(std::initializer_list<T> il) noexcept | +|   | >= C++11 | template<class Container>
constexpr auto
**make_span**(Container & cont) ->
 span<typename Container::value_type> noexcept | +|   | >= C++11 | template<class Container>
constexpr auto
**make_span**(Container const & cont) ->
 span<const typename Container::value_type> noexcept | +|   |   | template<class Container>
span<typename Container::value_type>
**make_span**( with_container_t, Container & cont ) | +|   |   | template<class Container>
span<const typename Container::value_type>
**make_span**( with_container_t, Container const & cont ) | +|   | < C++11 | template<class T, Allocator>
span<T>
**make_span**(std::vector<T, Allocator> & cont) | +|   | < C++11 | template<class T, Allocator>
span<const T>
**make_span**(std::vector<T, Allocator> const & cont) | +|   |   |   | +| **Free functions** | | macro **`span_FEATURE_BYTE_SPAN`** | +|   | >= C++11 | template<class T>
span<T, sizeof(T)>
**byte_span**(T & t) | +|   | >= C++11 | template<class T>
span<const T, sizeof(T)>
**byte_span**(T const & t) | + +## Configuration + +### Tweak header + +If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *span lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/span.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define span_CONFIG_NO_EXCEPTIONS 1`. + +### Standard selection macro + +\-Dspan\_CPLUSPLUS=199711L +Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. + +### Select `std::span` or `nonstd::span` + +At default, *span lite* uses `std::span` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::span` or span lite's `nonstd::span` as `nonstd::span` via the following macros. + +-Dspan\_CONFIG\_SELECT\_SPAN=span_SPAN_DEFAULT +Define this to `span_SPAN_STD` to select `std::span` as `nonstd::span`. Define this to `span_SPAN_NONSTD` to select `nonstd::span` as `nonstd::span`. Default is undefined, which has the same effect as defining to `span_SPAN_DEFAULT`. + +### Select extent type + +-Dspan_CONFIG_EXTENT_TYPE=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). + +### Select size type + +-Dspan_CONFIG_SIZE_TYPE=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). Note `span_CONFIG_SIZE_TYPE` replaces `span_CONFIG_INDEX_TYPE` which is deprecated. + +### Disable exceptions + +-Dspan_CONFIG_NO_EXCEPTIONS=0 +Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Disabling exceptions will force contract violation to use termination, see [contract violation macros](#contract-violation-response-macros). Default is undefined. + +### Provide construction from std::initializer_list (p2447) + +-Dspan_FEATURE_WITH_INITIALIZER_LIST_P2447=0 +Define this to 1 to enable constructing a span from a std::initializer_list<> as a constant set of values. See proposal [p2447](https://wg21.link/p2447). Default is undefined. + +### Provide construction using `with_container_t` + +-Dspan_FEATURE_WITH_CONTAINER=0 +Define this to 1 to enable constructing a span using `with_container_t`. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +-Dspan_FEATURE_WITH_CONTAINER_TO_STD=*n* +Define this to the highest C++ language version for which to enable constructing a span using `with_container_t`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_WITH_CONTAINER` for this. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +### Provide construction from `std::array` with const data + +-Dspan_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE=0 +Define this to 1 to enable constructing a span from a std::array with const data. Default is undefined. + +### Provide `operator()` member function + +-Dspan_FEATURE_MEMBER_CALL_OPERATOR=0 +Define this to 1 to provide member function `operator()`for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. Default is undefined. + +### Provide `at()` member function + +-Dspan_FEATURE_MEMBER_AT=0 +Define this to 1 to provide member function `at()`. Define this to 2 to include index and size in message of std::out_of_range exception. Default is undefined. + +### Provide `back()` and `front()` member functions + +-Dspan_FEATURE_MEMBER_BACK_FRONT=1 _(on since v0.5.0)_ +Define this to 0 to omit member functions `back()` and `front()`. Default is undefined. + +### Provide `swap()` member function + +-Dspan_FEATURE_MEMBER_SWAP=0 +Define this to 1 to provide member function `swap()`. Default is undefined. + +### Provide `operator==()` and other comparison functions + +-Dspan_FEATURE_COMPARISON=0 +Define this to 1 to include the comparison functions to compare the content of two spans. C++20's span does not provide comparison and _span lite_ omits comparison from v0.7.0. Default is undefined. + +### Provide `same()` function + +-Dspan_FEATURE_SAME=0 +Define this to 1 to provide function `same()` to test if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. Default is undefined. + +### Provide `first()`, `last()` and `subspan()` functions + +-Dspan_FEATURE_NON_MEMBER_FIRST_LAST_SUB=0 +Define this to 1 to enable both `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN` and `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`. Default is undefined. + +-Dspan_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN=0 +Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a `span<>` (work with C++98). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined. + +-Dspan_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER=0 +Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a compatible container (requires C++11). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined. + +### Provide `make_span()` functions + +-Dspan_FEATURE_MAKE_SPAN=0 +Define this to 1 to provide creator functions `nonstd::make_span()`. This feature is implied by using `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1`. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +-Dspan_FEATURE_MAKE_SPAN_TO_STD=*n* +Define this to the highest C++ language version for which to provide creator functions `nonstd::make_span()`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_MAKE_SPAN` for this. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +### Provide `byte_span()` functions + +-Dspan_FEATURE_BYTE_SPAN=0 +Define this to 1 to provide creator functions `nonstd::byte_span()`. Default is undefined. + +### Contract violation response macros + +*span-lite* provides contract violation response control as suggested in proposal [N4415](http://wg21.link/n4415). + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_ON (*default*) +Define this macro to include both `span_EXPECTS` and `span_ENSURES` in the code. This is the default case. + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_OFF +Define this macro to exclude both `span_EXPECTS` and `span_ENSURES` from the code. + +\-Dspan\_CONFIG_CONTRACT\_LEVEL\_EXPECTS\_ONLY +Define this macro to include `span_EXPECTS` in the code and exclude `span_ENSURES` from the code. + +\-Dspan\_CONFIG\_CONTRACT\_LEVEL\_ENSURES\_ONLY +Define this macro to exclude `span_EXPECTS` from the code and include `span_ENSURES` in the code. + +\-Dspan\_CONFIG\_CONTRACT\_VIOLATION\_TERMINATES (*default*) +Define this macro to call `std::terminate()` on a contract violation in `span_EXPECTS`, `span_ENSURES`. This is the default case. + +\-Dspan\_CONFIG\_CONTRACT\_VIOLATION\_THROWS +Define this macro to throw an exception of implementation-defined type that is derived from `std::runtime_exception` instead of calling `std::terminate()` on a contract violation in `span_EXPECTS` and `span_ENSURES`. See also [disable exceptions](#disable-exceptions). + +Reported to work with +-------------------- +The table below mentions the compiler versions *span lite* is reported to work with. + +OS | Compiler | Where | Versions | +------------:|:-----------|:--------|:---------| +**GNU/Linux**| Clang/LLVM | Travis | 3.5.0, 3.6.2, 3.7.1, 3.8.0, 3.9.1, 4.0.1 | +   | GCC | Travis | 5.5.0, 6.4.0, 7.3.0 | +**OS X** | ? | Local | ? | +**Windows** | Clang/LLVM | Local | 6.0.0 | +  | GCC | Local | 7.2.0 | +  | Visual C++
(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017) | +  | Visual C++
(Visual Studio)| AppVeyor | 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017) | + +## Building the tests + +To build the tests you need: + +- [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH. +- A [suitable compiler](#reported-to-work-with). + +The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). + +The following steps assume that the [*span lite* source code](https://github.com/martinmoene/span-lite) has been cloned into a directory named `./span-lite`. + +1. Create a directory for the build outputs. + + cd ./span-lite + md build && cd build + +2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). + + cmake -G "Unix Makefiles" -DSPAN_LITE_OPT_BUILD_TESTS=ON .. + +3. Optional. You can control above configuration through the following options: + + `-DSPAN_LITE_OPT_BUILD_TESTS=ON`: build the tests for span, default off + `-DSPAN_LITE_OPT_BUILD_EXAMPLES=OFF`: build the examples, default off + +4. Build the test suite. + + cmake --build . + +5. Run the test suite. + + ctest -V + +All tests should pass, indicating your platform is supported and you are ready to use *span lite*. + +## Other implementations of span + +- *gsl-lite* [span](https://github.com/martinmoene/gsl-lite/blob/73c4f16f2b35fc174fc2f09d44d5ab13e5c638c3/include/gsl/gsl-lite.hpp#L1221). +- Microsoft GSL [span](https://github.com/Microsoft/GSL/blob/master/include/gsl/span). +- Google Abseil [span](https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h). +- Marshall Clow's [libc++ span snippet](https://github.com/mclow/snippets/blob/master/span.cpp). +- Tristan Brindle's [Implementation of C++20's std::span for older compilers](https://github.com/tcbrindle/span). +- [Search _span c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=span+c%2B%2B&type=Repositories&utf8=%E2%9C%93). + +## Notes and references + +*Interface and specification* + +- [span on cppreference](https://en.cppreference.com/w/cpp/container/span). +- [p0122 - C++20 Proposal](http://wg21.link/p0122). +- [span in C++20 Working Draft](http://eel.is/c++draft/views). + +*Presentations* + +- TBD + +*Proposals* + +- [p0122 - span: bounds-safe views for sequences of objects](http://wg21.link/p0122). +- [p1024 - Usability Enhancements for std::span](http://wg21.link/p1024). +- [p1419 - A SFINAE-friendly trait to determine the extent of statically sized containers](http://wg21.link/p1419). +- [p0805 - Comparing Containers](http://wg21.link/p0805). +- [p1085 - Should Span be Regular?](http://wg21.link/p0805). +- [p0091 - Template argument deduction for class templates](http://wg21.link/p0091). +- [p0856 - Restrict Access Property for mdspan and span](http://wg21.link/p0856). +- [p1428 - Subscripts and sizes should be signed](http://wg21.link/p1428). +- [p1089 - Sizes Should Only span Unsigned](http://wg21.link/p1089). +- [p1227 - Signed size() functions](http://wg21.link/p1227). +- [p1872 - span should have size_type, not index_type](http://wg21.link/p1872). +- [p2447 - std::span and the missing constructor](https://wg21.link/p2447). +- [lwg 3101 - span's Container constructors need another constraint](https://cplusplus.github.io/LWG/issue3101). +- [Reddit - 2018-06 Rapperswil ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/8prqzm/2018_rapperswil_iso_c_committee_trip_report/) +- [Reddit - 2018-11 San Diego ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/). +- [Reddit - 2019-02 Kona ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/au0c4x/201902_kona_iso_c_committee_trip_report_c20/). +- [Reddit - 2019-07 Cologne ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/cfk9de/201907_cologne_iso_c_committee_trip_report_the/) +- [Reddit - 2019-11 Belfast ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/) +- Titus Winters. [Revisiting Regular Types](https://abseil.io/blog/20180531-regular-types). Abseil Blog. 31 May 2018. + +## Appendix + +### A.1 Compile-time information + +The version of *span lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. + +### A.2 Span lite test specification + +
+click to expand +

+ +```Text +span<>: Terminates construction from a nullptr and a non-zero size (C++11) +span<>: Terminates construction from two pointers in the wrong order +span<>: Terminates construction from a null pointer and a non-zero size +span<>: Terminates creation of a sub span of the first n elements for n exceeding the span +span<>: Terminates creation of a sub span of the last n elements for n exceeding the span +span<>: Terminates creation of a sub span outside the span +span<>: Terminates access outside the span +span<>: Throws on access outside the span via at(): std::out_of_range [span_FEATURE_MEMBER_AT>0][span_CONFIG_NO_EXCEPTIONS=0] +span<>: Termination throws std::logic_error-derived exception [span_CONFIG_CONTRACT_VIOLATION_THROWS=1] +span<>: Allows to default-construct +span<>: Allows to construct from a nullptr and a zero size (C++11) +span<>: Allows to construct from two pointers +span<>: Allows to construct from two iterators +span<>: Allows to construct from two iterators - empty range +span<>: Allows to construct from two iterators - move-only element +span<>: Allows to construct from an iterator and a size +span<>: Allows to construct from an iterator and a size - empty range +span<>: Allows to construct from an iterator and a size - move-only element +span<>: Allows to construct from two pointers to const +span<>: Allows to construct from a non-null pointer and a size +span<>: Allows to construct from a non-null pointer to const and a size +span<>: Allows to construct from a temporary pointer and a size +span<>: Allows to construct from a temporary pointer to const and a size +span<>: Allows to construct from any pointer and a zero size (C++98) +span<>: Allows to construct from a pointer and a size via a deduction guide (C++17) +span<>: Allows to construct from an iterator and a size via a deduction guide (C++17) +span<>: Allows to construct from two iterators via a deduction guide (C++17) +span<>: Allows to construct from a C-array +span<>: Allows to construct from a C-array via a deduction guide (C++17) +span<>: Allows to construct from a const C-array +span<>: Allows to construct from a C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a const C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a std::initializer_list<> (C++11) +span<>: Allows to construct from a std::initializer_list<> as a constant set of values (C++11, p2447) +span<>: Allows to construct from a std::array<> (C++11) +span<>: Allows to construct from a std::array via a deduction guide (C++17) +span<>: Allows to construct from a std::array<> with const data (C++11, span_FEATURE_CONSTR..._ELEMENT_TYPE=1) +span<>: Allows to construct from an empty std::array<> (C++11) +span<>: Allows to construct from a container (std::vector<>) +span<>: Allows to construct from a container via a deduction guide (std::vector<>, C++17) +span<>: Allows to tag-construct from a container (std::vector<>) +span<>: Allows to tag-construct from a const container (std::vector<>) +span<>: Allows to copy-construct from another span of the same type +span<>: Allows to copy-construct from another span of a compatible type +span<>: Allows to copy-construct from a temporary span of the same type (C++11) +span<>: Allows to copy-assign from another span of the same type +span<>: Allows to copy-assign from a temporary span of the same type (C++11) +span<>: Allows to create a sub span of the first n elements +span<>: Allows to create a sub span of the last n elements +span<>: Allows to create a sub span starting at a given offset +span<>: Allows to create a sub span starting at a given offset with a given length +span<>: Allows to observe an element via array indexing +span<>: Allows to observe an element via call indexing +span<>: Allows to observe an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to observe an element via data() +span<>: Allows to observe the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to observe the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change an element via array indexing +span<>: Allows to change an element via call indexing +span<>: Allows to change an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to change an element via data() +span<>: Allows to change the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to swap with another span [span_FEATURE_MEMBER_SWAP=1] +span<>: Allows forward iteration +span<>: Allows const forward iteration +span<>: Allows reverse iteration +span<>: Allows const reverse iteration +span<>: Allows to identify if a span is the same as another span [span_FEATURE_SAME=1] +span<>: Allows to compare equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare unequal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare to another span of the same type and different cv-ness [span_FEATURE_SAME=0] +span<>: Allows to compare empty spans as equal [span_FEATURE_COMPARISON=1] +span<>: Allows to test for empty span via empty(), empty case +span<>: Allows to test for empty span via empty(), non-empty case +span<>: Allows to obtain the number of elements via size() +span<>: Allows to obtain the number of elements via ssize() +span<>: Allows to obtain the number of bytes via size_bytes() +span<>: Allows to view the elements as read-only bytes +span<>: Allows to view and change the elements as writable bytes +make_span() [span_FEATURE_MAKE_SPAN_TO_STD=99] +make_span(): Allows building from two pointers +make_span(): Allows building from two const pointers +make_span(): Allows building from a non-null pointer and a size +make_span(): Allows building from a non-null const pointer and a size +make_span(): Allows building from a C-array +make_span(): Allows building from a const C-array +make_span(): Allows building from a std::initializer_list<> (C++11) +make_span(): Allows building from a std::initializer_list<> as a constant set of values (C++11) +make_span(): Allows building from a std::array<> (C++11) +make_span(): Allows building from a const std::array<> (C++11) +make_span(): Allows building from a container (std::vector<>) +make_span(): Allows building from a const container (std::vector<>) +make_span(): Allows building from a container (with_container_t, std::vector<>) +make_span(): Allows building from a const container (with_container_t, std::vector<>) +byte_span() [span_FEATURE_BYTE_SPAN=1] +byte_span(): Allows building a span of std::byte from a single object (C++17, byte-lite) +byte_span(): Allows building a span of const std::byte from a single const object (C++17, byte-lite) +first(), last(), subspan() [span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1] +first(): Allows to create a sub span of the first n elements (span, template parameter) +first(): Allows to create a sub span of the first n elements (span, function parameter) +first(): Allows to create a sub span of the first n elements (compatible container, template parameter) +first(): Allows to create a sub span of the first n elements (compatible container, function parameter) +last(): Allows to create a sub span of the last n elements (span, template parameter) +last(): Allows to create a sub span of the last n elements (span, function parameter) +last(): Allows to create a sub span of the last n elements (compatible container, template parameter) +last(): Allows to create a sub span of the last n elements (compatible container, function parameter) +subspan(): Allows to create a sub span starting at a given offset (span, template parameter) +subspan(): Allows to create a sub span starting at a given offset (span, function parameter) +subspan(): Allows to create a sub span starting at a given offset (compatible container, template parameter) +subspan(): Allows to create a sub span starting at a given offset (compatible container, function parameter) +size(): Allows to obtain the number of elements via size() +ssize(): Allows to obtain the number of elements via ssize() +tuple_size<>: Allows to obtain the number of elements via std::tuple_size<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element_t<> (C++11) +get(spn): Allows to access an element via std::get<>() +tweak header: reads tweak header if supported [tweak] +``` + +

+
diff --git a/src/libs/3rdparty/span/span.hpp b/src/libs/3rdparty/span/span.hpp index 51bdf1da303..fa6e51735f9 100644 --- a/src/libs/3rdparty/span/span.hpp +++ b/src/libs/3rdparty/span/span.hpp @@ -1,633 +1,1947 @@ - -/* -This is an implementation of C++20's std::span -http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf -*/ - -// Copyright Tristan Brindle 2018. +// +// span for C++98 and later. +// Based on http://wg21.link/p0122r7 +// For more information see https://github.com/martinmoene/span-lite +// +// Copyright 2018-2021 Martin Moene +// // Distributed under the Boost Software License, Version 1.0. -// (See accompanying file ../../LICENSE_1_0.txt or copy at -// https://www.boost.org/LICENSE_1_0.txt) +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef TCB_SPAN_HPP_INCLUDED -#define TCB_SPAN_HPP_INCLUDED +#ifndef NONSTD_SPAN_HPP_INCLUDED +#define NONSTD_SPAN_HPP_INCLUDED -#include -#include -#include -#include +#define span_lite_MAJOR 0 +#define span_lite_MINOR 10 +#define span_lite_PATCH 3 -#ifndef TCB_SPAN_NO_EXCEPTIONS -// Attempt to discover whether we're being compiled with exception support -#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) -#define TCB_SPAN_NO_EXCEPTIONS -#endif -#endif +#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH) -#ifndef TCB_SPAN_NO_EXCEPTIONS -#include -#include +#define span_STRINGIFY( x ) span_STRINGIFY_( x ) +#define span_STRINGIFY_( x ) #x + +// span configuration: + +#define span_SPAN_DEFAULT 0 +#define span_SPAN_NONSTD 1 +#define span_SPAN_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define span_HAVE_TWEAK_HEADER 1 #else -#include // for std::terminate +#define span_HAVE_TWEAK_HEADER 0 +//# pragma message("span.hpp: Note: Tweak header not supported.") #endif -// Various feature test macros +// span selection and configuration: -#ifndef TCB_SPAN_NAMESPACE_NAME -#define TCB_SPAN_NAMESPACE_NAME tcb +#define span_HAVE( feature ) ( span_HAVE_##feature ) + +#ifndef span_CONFIG_SELECT_SPAN +# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD ) #endif -#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -#define TCB_SPAN_HAVE_CPP17 +#ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t #endif -#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -#define TCB_SPAN_HAVE_CPP14 +#ifndef span_CONFIG_SIZE_TYPE +# define span_CONFIG_SIZE_TYPE std::size_t #endif -namespace TCB_SPAN_NAMESPACE_NAME { +#ifdef span_CONFIG_INDEX_TYPE +# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`. +#endif -// Establish default contract checking behavior -#if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ - !defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ - !defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14) -#define TCB_SPAN_NO_CONTRACT_CHECKING +// span configuration (features): + +#ifndef span_FEATURE_WITH_INITIALIZER_LIST_P2447 +# define span_FEATURE_WITH_INITIALIZER_LIST_P2447 0 +#endif + +#ifndef span_FEATURE_WITH_CONTAINER +#ifdef span_FEATURE_WITH_CONTAINER_TO_STD +# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD ) #else -#define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION +# define span_FEATURE_WITH_CONTAINER 0 +# define span_FEATURE_WITH_CONTAINER_TO_STD 0 #endif #endif -#if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) -struct contract_violation_error : std::logic_error { - explicit contract_violation_error(const char* msg) : std::logic_error(msg) - {} -}; - -inline void contract_violation(const char* msg) -{ - throw contract_violation_error(msg); -} - -#elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) -[[noreturn]] inline void contract_violation(const char* /*unused*/) -{ - std::terminate(); -} +#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE +# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0 #endif -#if !defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#define TCB_SPAN_STRINGIFY(cond) #cond -#define TCB_SPAN_EXPECT(cond) \ - cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond)) +#ifndef span_FEATURE_MEMBER_AT +# define span_FEATURE_MEMBER_AT 0 +#endif + +#ifndef span_FEATURE_MEMBER_BACK_FRONT +# define span_FEATURE_MEMBER_BACK_FRONT 1 +#endif + +#ifndef span_FEATURE_MEMBER_CALL_OPERATOR +# define span_FEATURE_MEMBER_CALL_OPERATOR 0 +#endif + +#ifndef span_FEATURE_MEMBER_SWAP +# define span_FEATURE_MEMBER_SWAP 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0 +#elif span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 1 +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 1 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 0 +#endif + +#ifndef span_FEATURE_COMPARISON +# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison +#endif + +#ifndef span_FEATURE_SAME +# define span_FEATURE_SAME 0 +#endif + +#if span_FEATURE_SAME && !span_FEATURE_COMPARISON +# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON` +#endif + +#ifndef span_FEATURE_MAKE_SPAN +#ifdef span_FEATURE_MAKE_SPAN_TO_STD +# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD ) #else -#define TCB_SPAN_EXPECT(cond) +# define span_FEATURE_MAKE_SPAN 0 +# define span_FEATURE_MAKE_SPAN_TO_STD 0 +#endif #endif -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) -#define TCB_SPAN_INLINE_VAR inline +#ifndef span_FEATURE_BYTE_SPAN +# define span_FEATURE_BYTE_SPAN 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef span_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define span_CONFIG_NO_EXCEPTIONS 0 +# else +# define span_CONFIG_NO_EXCEPTIONS 1 +# undef span_CONFIG_CONTRACT_VIOLATION_THROWS +# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0 +# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 +# endif +#endif + +// Control pre- and postcondition violation behaviour: + +#if defined( span_CONFIG_CONTRACT_LEVEL_ON ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10 #else -#define TCB_SPAN_INLINE_VAR +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 #endif -#if defined(TCB_SPAN_HAVE_CPP14) || \ - (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) -#define TCB_SPAN_HAVE_CPP14_CONSTEXPR -#endif - -#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) -#define TCB_SPAN_CONSTEXPR14 constexpr +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS #else -#define TCB_SPAN_CONSTEXPR14 +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 #endif -#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) && \ - (!defined(_MSC_VER) || _MSC_VER > 1900) -#define TCB_SPAN_CONSTEXPR_ASSIGN constexpr +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \ + defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both. +#endif + +// C++ language version detection (C++23 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef span_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define span_CPLUSPLUS __cplusplus +# endif +#endif + +#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L ) +#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L ) +#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L ) +#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L ) +#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202002L ) +#define span_CPP23_OR_GREATER ( span_CPLUSPLUS >= 202300L ) + +// C++ language version (represent 98 as 3): + +#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V ) + +#define span_CONFIG( feature ) ( span_CONFIG_##feature ) +#define span_FEATURE( feature ) ( span_FEATURE_##feature ) +#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) ) + +// Use C++20 std::span if available and requested: + +#if span_CPP20_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define span_HAVE_STD_SPAN 1 +# else +# define span_HAVE_STD_SPAN 0 +# endif #else -#define TCB_SPAN_CONSTEXPR_ASSIGN +# define span_HAVE_STD_SPAN 0 #endif -#if defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#define TCB_SPAN_CONSTEXPR11 constexpr +#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) ) + +// +// Use C++20 std::span: +// + +#if span_USES_STD_SPAN + +#include + +namespace nonstd { + +using std::span; +using std::dynamic_extent; + +// Note: C++20 does not provide comparison +// using std::operator==; +// using std::operator!=; +// using std::operator<; +// using std::operator<=; +// using std::operator>; +// using std::operator>=; +} // namespace nonstd + +#else // span_USES_STD_SPAN + +#include + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define span_COMPILER_MSVC_VER (_MSC_VER ) +# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) #else -#define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14 +# define span_COMPILER_MSVC_VER 0 +# define span_COMPILER_MSVC_VERSION 0 #endif -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) -#define TCB_SPAN_HAVE_DEDUCTION_GUIDES -#endif +#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) -#define TCB_SPAN_HAVE_STD_BYTE -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) -#define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC -#endif - -#if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) -#define TCB_SPAN_ARRAY_CONSTEXPR constexpr +#if defined(__clang__) +# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else -#define TCB_SPAN_ARRAY_CONSTEXPR +# define span_COMPILER_CLANG_VERSION 0 #endif -#ifdef TCB_SPAN_HAVE_STD_BYTE -using byte = std::byte; +#if defined(__GNUC__) && !defined(__clang__) +# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else -using byte = unsigned char; +# define span_COMPILER_GNUC_VERSION 0 #endif -#if defined(TCB_SPAN_HAVE_CPP17) -#define TCB_SPAN_NODISCARD [[nodiscard]] +// half-open range [lo..hi): +#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wmismatched-tags" +# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" ) + +#elif span_COMPILER_MSVC_VER >= 1900 +# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define span_RESTORE_WARNINGS() __pragma(warning(pop )) + +// Suppress the following MSVC GSL warnings: +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narrow +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26490: gsl::t.1 : don't use reinterpret_cast + +span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 ) + #else -#define TCB_SPAN_NODISCARD +# define span_RESTORE_WARNINGS() /*empty*/ #endif -TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX; +// Presence of language and library features: -template +#ifdef _HAS_CPP0X +# define span_HAS_CPP0X _HAS_CPP0X +#else +# define span_HAS_CPP0X 0 +#endif + +#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400) +#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500) +#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600) +#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700) +#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP14_000 (span_CPP14_OR_GREATER) +#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP17_000 (span_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140 +#define span_HAVE_AUTO span_CPP11_100 +#define span_HAVE_CONSTEXPR_11 span_CPP11_140 +#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120 +#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140 +#define span_HAVE_INITIALIZER_LIST span_CPP11_120 +#define span_HAVE_IS_DEFAULT span_CPP11_140 +#define span_HAVE_IS_DELETE span_CPP11_140 +#define span_HAVE_NOEXCEPT span_CPP11_140 +#define span_HAVE_NULLPTR span_CPP11_100 +#define span_HAVE_STATIC_ASSERT span_CPP11_100 + +// Presence of C++14 language features: + +#define span_HAVE_CONSTEXPR_14 span_CPP14_000 + +// Presence of C++17 language features: + +#define span_HAVE_DEPRECATED span_CPP17_000 +#define span_HAVE_NODISCARD span_CPP17_000 +#define span_HAVE_NORETURN span_CPP17_000 + +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#if defined(__cpp_deduction_guides) +# define span_HAVE_DEDUCTION_GUIDES 1 +#else +# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 )) +#endif + +// Presence of C++ library features: + +#define span_HAVE_ADDRESSOF span_CPP17_000 +#define span_HAVE_ARRAY span_CPP11_110 +#define span_HAVE_BYTE span_CPP17_000 +#define span_HAVE_CONDITIONAL span_CPP11_120 +#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X )) +#define span_HAVE_DATA span_CPP17_000 +#define span_HAVE_LONGLONG span_CPP11_80 +#define span_HAVE_REMOVE_CONST span_CPP11_110 +#define span_HAVE_SNPRINTF span_CPP11_140 +#define span_HAVE_STRUCT_BINDING span_CPP11_120 +#define span_HAVE_TYPE_TRAITS span_CPP11_90 + +// Presence of byte-lite: + +#ifdef NONSTD_BYTE_LITE_HPP +# define span_HAVE_NONSTD_BYTE 1 +#else +# define span_HAVE_NONSTD_BYTE 0 +#endif + +// C++ feature usage: + +#if span_HAVE_ADDRESSOF +# define span_ADDRESSOF(x) std::addressof(x) +#else +# define span_ADDRESSOF(x) (&x) +#endif + +#if span_HAVE_CONSTEXPR_11 +# define span_constexpr constexpr +#else +# define span_constexpr /*span_constexpr*/ +#endif + +#if span_HAVE_CONSTEXPR_14 +# define span_constexpr14 constexpr +#else +# define span_constexpr14 /*span_constexpr*/ +#endif + +#if span_HAVE_EXPLICIT_CONVERSION +# define span_explicit explicit +#else +# define span_explicit /*explicit*/ +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete = delete +#else +# define span_is_delete +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete_access public +#else +# define span_is_delete_access private +#endif + +#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V +# define span_noexcept noexcept +#else +# define span_noexcept /*noexcept*/ +#endif + +#if span_HAVE_NULLPTR +# define span_nullptr nullptr +#else +# define span_nullptr NULL +#endif + +#if span_HAVE_DEPRECATED +# define span_deprecated(msg) [[deprecated(msg)]] +#else +# define span_deprecated(msg) /*[[deprecated]]*/ +#endif + +#if span_HAVE_NODISCARD +# define span_nodiscard [[nodiscard]] +#else +# define span_nodiscard /*[[nodiscard]]*/ +#endif + +#if span_HAVE_NORETURN +# define span_noreturn [[noreturn]] +#else +# define span_noreturn /*[[noreturn]]*/ +#endif + +// Other features: + +#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG +#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG + +// Additional includes: + +#if span_HAVE( ADDRESSOF ) +# include +#endif + +#if span_HAVE( ARRAY ) +# include +#endif + +#if span_HAVE( BYTE ) +# include +#endif + +#if span_HAVE( DATA ) +# include // for std::data(), std::size() +#endif + +#if span_HAVE( TYPE_TRAITS ) +# include +#endif + +#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) +# include +#endif + +#if span_FEATURE( MEMBER_AT ) > 1 +# include +#endif + +#if ! span_CONFIG( NO_EXCEPTIONS ) +# include +#endif + +// Contract violation + +#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if span_ELIDE_CONTRACT_EXPECTS +# define span_constexpr_exp span_constexpr +# define span_EXPECTS( cond ) /* Expect elided */ +#else +# define span_constexpr_exp span_constexpr14 +# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond ) +#endif + +#if span_ELIDE_CONTRACT_ENSURES +# define span_constexpr_ens span_constexpr +# define span_ENSURES( cond ) /* Ensures elided */ +#else +# define span_constexpr_ens span_constexpr14 +# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond ) +#endif + +#define span_CONTRACT_CHECK( type, cond ) \ + cond ? static_cast< void >( 0 ) \ + : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." ) + +#ifdef __GNUG__ +# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line ) +#else +# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")" +#endif + +// Method enabling + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +#define span_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if::type = 0 > + +# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 ) +// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments +# define span_REQUIRES_T(VA) \ + , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type +# else +# define span_REQUIRES_T(VA) \ + , typename std::enable_if< (VA), int >::type = 0 +# endif + +#define span_REQUIRES_R(R, VA) \ + typename std::enable_if< (VA), R>::type + +#define span_REQUIRES_A(VA) \ + , typename std::enable_if< (VA), void*>::type = nullptr + +#else + +# define span_REQUIRES_0(VA) /*empty*/ +# define span_REQUIRES_T(VA) /*empty*/ +# define span_REQUIRES_R(R, VA) R +# define span_REQUIRES_A(VA) /*empty*/ + +#endif + +namespace nonstd { +namespace span_lite { + +// [views.constants], constants + +typedef span_CONFIG_EXTENT_TYPE extent_t; +typedef span_CONFIG_SIZE_TYPE size_t; + +span_constexpr const extent_t dynamic_extent = static_cast( -1 ); + +template< class T, extent_t Extent = dynamic_extent > class span; -namespace detail { +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): -template -struct span_storage { - constexpr span_storage() noexcept = default; +struct with_container_t { span_constexpr with_container_t() span_noexcept {} }; +const span_constexpr with_container_t with_container; - constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept - : ptr(p_ptr) - {} +// C++11 emulation: - E* ptr = nullptr; - static constexpr std::size_t size = S; +namespace std11 { + +#if span_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const< T const > { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile< T volatile > { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type; }; -template -struct span_storage { - constexpr span_storage() noexcept = default; +#endif // span_HAVE( REMOVE_CONST ) - constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept - : ptr(p_ptr), size(p_size) - {} +#if span_HAVE( TYPE_TRAITS ) - E* ptr = nullptr; - std::size_t size = 0; -}; +using std::is_same; +using std::is_signed; +using std::integral_constant; +using std::true_type; +using std::false_type; +using std::remove_reference; + +#else + +template< class T, T v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< bool, true > true_type; +typedef integral_constant< bool, false > false_type; + +template< class T, class U > struct is_same : false_type{}; +template< class T > struct is_same : true_type{}; + +template< typename T > struct is_signed : false_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; + +#endif + +} // namespace std11 + +// C++17 emulation: + +namespace std17 { + +template< bool v > struct bool_constant : std11::integral_constant{}; + +#if span_CPP11_120 + +template< class...> +using void_t = void; + +#endif + +#if span_HAVE( DATA ) -// Reimplementation of C++17 std::size() and std::data() -#if defined(TCB_SPAN_HAVE_CPP17) || \ - defined(__cpp_lib_nonmember_container_access) using std::data; using std::size; -#else -template -constexpr auto size(const C& c) -> decltype(c.size()) -{ - return c.size(); -} -template -constexpr std::size_t size(const T (&)[N]) noexcept +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< typename T, std::size_t N > +inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t { return N; } -template -constexpr auto data(C& c) -> decltype(c.data()) +template< typename C > +inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() ) { - return c.data(); + return cont.size(); } -template -constexpr auto data(const C& c) -> decltype(c.data()) +template< typename T, std::size_t N > +inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T* { - return c.data(); + return &arr[0]; } -template -constexpr T* data(T (&array)[N]) noexcept +template< typename C > +inline span_constexpr auto data( C & cont ) -> decltype( cont.data() ) { - return array; + return cont.data(); } -template -constexpr const E* data(std::initializer_list il) noexcept +template< typename C > +inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename E > +inline span_constexpr auto data( std::initializer_list il ) span_noexcept -> E const * { return il.begin(); } -#endif // TCB_SPAN_HAVE_CPP17 -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) -using std::void_t; -#else -template -using void_t = void; +#endif // span_HAVE( DATA ) + +#if span_HAVE( BYTE ) +using std::byte; +#elif span_HAVE( NONSTD_BYTE ) +using nonstd::byte; #endif -template -using uncvref_t = - typename std::remove_cv::type>::type; +} // namespace std17 -template -struct is_span : std::false_type {}; +// C++20 emulation: -template -struct is_span> : std::true_type {}; +namespace std20 { -template -struct is_std_array : std::false_type {}; +#if span_HAVE( DEDUCTION_GUIDES ) +template< class T > +using iter_reference_t = decltype( *std::declval() ); +#endif -template -struct is_std_array> : std::true_type {}; +} // namespace std20 -template -struct has_size_and_data : std::false_type {}; +// Implementation details: -template -struct has_size_and_data())), - decltype(detail::data(std::declval()))>> - : std::true_type {}; +namespace detail { -template > -struct is_container { - static constexpr bool value = - !is_span::value && !is_std_array::value && - !std::is_array::value && has_size_and_data::value; +/*enum*/ struct enabler{}; + +template< typename T > +span_constexpr bool is_positive( T x ) +{ + return std11::is_signed::value ? x >= 0 : true; +} + +#if span_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std::false_type{}; + +template< class T, span_CONFIG_EXTENT_TYPE Extent > +struct is_span_oracle< span > : std::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_std_array_oracle : std::false_type{}; + +#if span_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array > : std::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_array : std::false_type {}; + +template< class T > +struct is_array : std::true_type {}; + +template< class T, std::size_t N > +struct is_array : std::true_type {}; + +#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 ) + +template< class, class = void > +struct has_size_and_data : std::false_type{}; + +template< class C > +struct has_size_and_data +< + C, std17::void_t< + decltype( std17::size(std::declval()) ), + decltype( std17::data(std::declval()) ) > +> : std::true_type{}; + +template< class, class, class = void > +struct is_compatible_element : std::false_type {}; + +template< class C, class E > +struct is_compatible_element +< + C, E, std17::void_t< + decltype( std17::data(std::declval()) ) > +> : std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >{}; + +template< class C > +struct is_container : std17::bool_constant +< + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && has_size_and_data< C >::value +>{}; + +template< class C, class E > +struct is_compatible_container : std17::bool_constant +< + is_container::value + && is_compatible_element::value +>{}; + +#else // span_CPP11_140 + +template< + class C, class E + span_REQUIRES_T(( + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && ( std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >::value) + // && has_size_and_data< C >::value + )) + , class = decltype( std17::size(std::declval()) ) + , class = decltype( std17::data(std::declval()) ) +> +struct is_compatible_container : std::true_type{}; + +#endif // span_CPP11_140 + +#endif // span_HAVE( TYPE_TRAITS ) + +#if ! span_CONFIG( NO_EXCEPTIONS ) +#if span_FEATURE( MEMBER_AT ) > 1 + +// format index and size: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wlong-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wformat=ll" +# pragma GCC diagnostic ignored "-Wlong-long" +#endif + +span_noreturn inline void throw_out_of_range( size_t idx, size_t size ) +{ + const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)"; + char buffer[ 2 * 20 + sizeof fmt ]; + sprintf( buffer, fmt, static_cast(idx), static_cast(size) ); + + throw std::out_of_range( buffer ); +} + +#else // MEMBER_AT + +span_noreturn inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ ) +{ + throw std::out_of_range( "span::at(): index outside span" ); +} +#endif // MEMBER_AT +#endif // NO_EXCEPTIONS + +#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +struct contract_violation : std::logic_error +{ + explicit contract_violation( char const * const message ) + : std::logic_error( message ) + {} }; -template -using remove_pointer_t = typename std::remove_pointer::type; +inline void report_contract_violation( char const * msg ) +{ + throw contract_violation( msg ); +} -template -struct is_container_element_type_compatible : std::false_type {}; +#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) -template -struct is_container_element_type_compatible< - T, E, - typename std::enable_if< - !std::is_same()))>::type, - void>::value>::type> - : std::is_convertible< - remove_pointer_t()))> (*)[], - E (*)[]> {}; +span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept +{ + std::terminate(); +} -template -struct is_complete : std::false_type {}; +#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) -template -struct is_complete : std::true_type {}; +} // namespace detail -} // namespace detail +// Prevent signed-unsigned mismatch: -template -class span { - static_assert(std::is_object::value, - "A span's ElementType must be an object type (not a " - "reference type or void)"); - static_assert(detail::is_complete::value, - "A span's ElementType must be a complete type (not a forward " - "declaration)"); - static_assert(!std::is_abstract::value, - "A span's ElementType cannot be an abstract class type"); +#define span_sizeof(T) static_cast( sizeof(T) ) - using storage_type = detail::span_storage; +template< class T > +inline span_constexpr size_t to_size( T size ) +{ + return static_cast( size ); +} +// +// [views.span] - A view over a contiguous, single-dimension sequence of objects +// +template< class T, extent_t Extent /*= dynamic_extent*/ > +class span +{ public: // constants and types - using element_type = ElementType; - using value_type = typename std::remove_cv::type; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = element_type*; - using const_pointer = const element_type*; - using reference = element_type&; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - static constexpr size_type extent = Extent; + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; - // [span.cons], span constructors, copy, assignment, and destructor - template < - std::size_t E = Extent, - typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> - constexpr span() noexcept + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef size_t size_type; + typedef extent_t extent_type; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + +// static constexpr extent_type extent = Extent; + enum { extent = Extent }; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + span_REQUIRES_0( + ( Extent == 0 ) || + ( Extent == dynamic_extent ) + ) + span_constexpr span() span_noexcept + : data_( span_nullptr ) + , size_( 0 ) + { + // span_EXPECTS( data() == span_nullptr ); + // span_EXPECTS( size() == 0 ); + } + +#if span_HAVE( ITERATOR_CTOR ) + // Didn't yet succeed in combining the next two constructors: + + span_constexpr_exp span( std::nullptr_t, size_type count ) + : data_( span_nullptr ) + , size_( count ) + { + span_EXPECTS( data_ == span_nullptr && count == 0 ); + } + + template< typename It + span_REQUIRES_T(( + std::is_convertible()), element_type &>::value + )) + > + span_constexpr_exp span( It first, size_type count ) + : data_( to_address( first ) ) + , size_( count ) + { + span_EXPECTS( + ( data_ == span_nullptr && count == 0 ) || + ( data_ != span_nullptr && detail::is_positive( count ) ) + ); + } +#else + span_constexpr_exp span( pointer ptr, size_type count ) + : data_( ptr ) + , size_( count ) + { + span_EXPECTS( + ( ptr == span_nullptr && count == 0 ) || + ( ptr != span_nullptr && detail::is_positive( count ) ) + ); + } +#endif + +#if span_HAVE( ITERATOR_CTOR ) + template< typename It, typename End + span_REQUIRES_T(( + std::is_convertible()), element_type *>::value + && ! std::is_convertible::value + )) + > + span_constexpr_exp span( It first, End last ) + : data_( to_address( first ) ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#else + span_constexpr_exp span( pointer first, pointer last ) + : data_( first ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#endif + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > + span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept + : data_( span_ADDRESSOF( arr[0] ) ) + , size_( N ) {} - TCB_SPAN_CONSTEXPR11 span(pointer ptr, size_type count) - : storage_(ptr, count) - { - TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent); - } +#if span_HAVE( ARRAY ) - TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) - : storage_(first_elem, last_elem - first_elem) - { - TCB_SPAN_EXPECT(extent == dynamic_extent || - last_elem - first_elem == - static_cast(extent)); - } - - template ::value, - int>::type = 0> - constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > +# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE ) + span_constexpr span( std::array< element_type, N > & arr ) span_noexcept +# else + span_constexpr span( std::array< value_type, N > & arr ) span_noexcept +# endif + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) {} - template &, ElementType>::value, - int>::type = 0> - TCB_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept - : storage_(arr.data(), N) + template< std::size_t N +# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) +# endif + > + span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) {} - template &, ElementType>::value, - int>::type = 0> - TCB_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept - : storage_(arr.data(), N) +#endif // span_HAVE( ARRAY ) + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + span_REQUIRES_T(( + detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) {} - template < - typename Container, std::size_t E = Extent, - typename std::enable_if< - E == dynamic_extent && detail::is_container::value && - detail::is_container_element_type_compatible< - Container&, ElementType>::value, - int>::type = 0> - constexpr span(Container& cont) - : storage_(detail::data(cont), detail::size(cont)) + template< class Container + span_REQUIRES_T(( + std::is_const< element_type >::value + && detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container const & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) {} - template < - typename Container, std::size_t E = Extent, - typename std::enable_if< - E == dynamic_extent && detail::is_container::value && - detail::is_container_element_type_compatible< - const Container&, ElementType>::value, - int>::type = 0> - constexpr span(const Container& cont) - : storage_(detail::data(cont), detail::size(cont)) +#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +#if span_FEATURE( WITH_CONTAINER ) + + template< class Container > + span_constexpr span( with_container_t, Container & cont ) + : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) ) + , size_( to_size( cont.size() ) ) {} - constexpr span(const span& other) noexcept = default; + template< class Container > + span_constexpr span( with_container_t, Container const & cont ) + : data_( cont.size() == 0 ? span_nullptr : const_cast( span_ADDRESSOF( cont[0] ) ) ) + , size_( to_size( cont.size() ) ) + {} +#endif - template ::value, - int>::type = 0> - constexpr span(const span& other) noexcept - : storage_(other.data(), other.size()) +#if span_FEATURE( WITH_INITIALIZER_LIST_P2447 ) && span_HAVE( INITIALIZER_LIST ) + + // constexpr explicit(extent != dynamic_extent) span(std::initializer_list il) noexcept; + +#if !span_BETWEEN( span_COMPILER_MSVC_VERSION, 120, 130 ) + + template< extent_t U = Extent + span_REQUIRES_T(( + U != dynamic_extent + )) + > +#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime" + span_constexpr14 explicit span( std::initializer_list il ) span_noexcept + { + data_ = il.begin(); + size_ = il.size(); + } +#else + span_constexpr explicit span( std::initializer_list il ) span_noexcept + : data_( il.begin() ) + , size_( il.size() ) + {} +#endif + +#endif // MSVC 120 (VS2013) + + template< extent_t U = Extent + span_REQUIRES_T(( + U == dynamic_extent + )) + > +#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime" + span_constexpr14 /*explicit*/ span( std::initializer_list il ) span_noexcept + { + data_ = il.begin(); + size_ = il.size(); + } +#else + span_constexpr /*explicit*/ span( std::initializer_list il ) span_noexcept + : data_( il.begin() ) + , size_( il.size() ) + {} +#endif + +#endif // P2447 + +#if span_HAVE( IS_DEFAULT ) + span_constexpr span( span const & other ) span_noexcept = default; + + ~span() span_noexcept = default; + + span_constexpr14 span & operator=( span const & other ) span_noexcept = default; +#else + span_constexpr span( span const & other ) span_noexcept + : data_( other.data_ ) + , size_( other.size_ ) {} - ~span() noexcept = default; + ~span() span_noexcept + {} - TCB_SPAN_CONSTEXPR_ASSIGN span& - operator=(const span& other) noexcept = default; - - // [span.sub], span subviews - template - TCB_SPAN_CONSTEXPR11 span first() const + span_constexpr14 span & operator=( span const & other ) span_noexcept { - TCB_SPAN_EXPECT(Count <= size()); - return {data(), Count}; + data_ = other.data_; + size_ = other.size_; + + return *this; + } +#endif + + template< class OtherElementType, extent_type OtherExtent + span_REQUIRES_T(( + (Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent) + && std::is_convertible::value + )) + > + span_constexpr_exp span( span const & other ) span_noexcept + : data_( other.data() ) + , size_( other.size() ) + { + span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) ); } - template - TCB_SPAN_CONSTEXPR11 span last() const + // 26.7.3.3 Subviews [span.sub] + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + first() const { - TCB_SPAN_EXPECT(Count <= size()); - return {data() + (size() - Count), Count}; + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data(), Count ); } - template - using subspan_return_t = - span; - - template - TCB_SPAN_CONSTEXPR11 subspan_return_t subspan() const + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + last() const { - TCB_SPAN_EXPECT(Offset <= size() && - (Count == dynamic_extent || Offset + Count <= size())); - return {data() + Offset, - Count != dynamic_extent ? Count : size() - Offset}; + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data() + (size() - Count), Count ); } - TCB_SPAN_CONSTEXPR11 span - first(size_type count) const +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< size_type Offset, extent_type Count = dynamic_extent > +#else + template< size_type Offset, extent_type Count /*= dynamic_extent*/ > +#endif + span_constexpr_exp span< element_type, Count > + subspan() const { - TCB_SPAN_EXPECT(count <= size()); - return {data(), count}; + span_EXPECTS( + ( detail::is_positive( Offset ) && Offset <= size() ) && + ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) ) + ); + + return span< element_type, Count >( + data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) ); } - TCB_SPAN_CONSTEXPR11 span - last(size_type count) const + span_constexpr_exp span< element_type, dynamic_extent > + first( size_type count ) const { - TCB_SPAN_EXPECT(count <= size()); - return {data() + (size() - count), count}; + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data(), count ); } - TCB_SPAN_CONSTEXPR11 span - subspan(size_type offset, size_type count = dynamic_extent) const + span_constexpr_exp span< element_type, dynamic_extent > + last( size_type count ) const { - TCB_SPAN_EXPECT(offset <= size() && - (count == dynamic_extent || offset + count <= size())); - return {data() + offset, - count == dynamic_extent ? size() - offset : count}; + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data() + ( size() - count ), count ); } - // [span.obs], span observers - constexpr size_type size() const noexcept { return storage_.size; } - - constexpr size_type size_bytes() const noexcept + span_constexpr_exp span< element_type, dynamic_extent > + subspan( size_type offset, size_type count = static_cast(dynamic_extent) ) const { - return size() * sizeof(element_type); + span_EXPECTS( + ( ( detail::is_positive( offset ) && offset <= size() ) ) && + ( count == static_cast(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) ) + ); + + return span< element_type, dynamic_extent >( + data() + offset, count == static_cast(dynamic_extent) ? size() - offset : count ); } - TCB_SPAN_NODISCARD constexpr bool empty() const noexcept + // 26.7.3.4 Observers [span.obs] + + span_constexpr size_type size() const span_noexcept + { + return size_; + } + + span_constexpr std::ptrdiff_t ssize() const span_noexcept + { + return static_cast( size_ ); + } + + span_constexpr size_type size_bytes() const span_noexcept + { + return size() * to_size( sizeof( element_type ) ); + } + + span_nodiscard span_constexpr bool empty() const span_noexcept { return size() == 0; } - // [span.elem], span element access - TCB_SPAN_CONSTEXPR11 reference operator[](size_type idx) const + // 26.7.3.5 Element access [span.elem] + + span_constexpr_exp reference operator[]( size_type idx ) const { - TCB_SPAN_EXPECT(idx < size()); - return *(data() + idx); + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); } - TCB_SPAN_CONSTEXPR11 reference front() const +#if span_FEATURE( MEMBER_CALL_OPERATOR ) + span_deprecated("replace operator() with operator[]") + + span_constexpr_exp reference operator()( size_type idx ) const { - TCB_SPAN_EXPECT(!empty()); + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } +#endif + +#if span_FEATURE( MEMBER_AT ) + span_constexpr14 reference at( size_type idx ) const + { +#if span_CONFIG( NO_EXCEPTIONS ) + return this->operator[]( idx ); +#else + if ( !detail::is_positive( idx ) || size() <= idx ) + { + detail::throw_out_of_range( idx, size() ); + } + return *( data() + idx ); +#endif + } +#endif + + span_constexpr pointer data() const span_noexcept + { + return data_; + } + +#if span_FEATURE( MEMBER_BACK_FRONT ) + + span_constexpr_exp reference front() const span_noexcept + { + span_EXPECTS( ! empty() ); + return *data(); } - TCB_SPAN_CONSTEXPR11 reference back() const + span_constexpr_exp reference back() const span_noexcept { - TCB_SPAN_EXPECT(!empty()); - return *(data() + (size() - 1)); + span_EXPECTS( ! empty() ); + + return *( data() + size() - 1 ); } - constexpr pointer data() const noexcept { return storage_.ptr; } +#endif - // [span.iterators], span iterator support - constexpr iterator begin() const noexcept { return data(); } + // xx.x.x.x Modifiers [span.modifiers] - constexpr iterator end() const noexcept { return data() + size(); } +#if span_FEATURE( MEMBER_SWAP ) - constexpr const_iterator cbegin() const noexcept { return begin(); } - - constexpr const_iterator cend() const noexcept { return end(); } - - TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept + span_constexpr14 void swap( span & other ) span_noexcept { - return reverse_iterator(end()); + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } +#endif + + // 26.7.3.6 Iterator support [span.iterators] + + span_constexpr iterator begin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return iterator( data() ); +#endif } - TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept + span_constexpr iterator end() const span_noexcept { - return reverse_iterator(begin()); +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return iterator( data() + size() ); +#endif } - TCB_SPAN_ARRAY_CONSTEXPR const_reverse_iterator crbegin() const noexcept + span_constexpr const_iterator cbegin() const span_noexcept { - return const_reverse_iterator(cend()); +#if span_CPP11_OR_GREATER + return { data() }; +#else + return const_iterator( data() ); +#endif } - TCB_SPAN_ARRAY_CONSTEXPR const_reverse_iterator crend() const noexcept + span_constexpr const_iterator cend() const span_noexcept { - return const_reverse_iterator(cbegin()); +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return const_iterator( data() + size() ); +#endif } - friend constexpr iterator begin(span s) noexcept { return s.begin(); } + span_constexpr reverse_iterator rbegin() const span_noexcept + { + return reverse_iterator( end() ); + } - friend constexpr iterator end(span s) noexcept { return s.end(); } + span_constexpr reverse_iterator rend() const span_noexcept + { + return reverse_iterator( begin() ); + } + + span_constexpr const_reverse_iterator crbegin() const span_noexcept + { + return const_reverse_iterator ( cend() ); + } + + span_constexpr const_reverse_iterator crend() const span_noexcept + { + return const_reverse_iterator( cbegin() ); + } private: - storage_type storage_{}; + + // Note: C++20 has std::pointer_traits::to_address( it ); + +#if span_HAVE( ITERATOR_CTOR ) + static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept + { + return nullptr; + } + + template< typename U > + static inline span_constexpr U * to_address( U * p ) span_noexcept + { + return p; + } + + template< typename Ptr + span_REQUIRES_T(( ! std::is_pointer::value )) + > + static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept + { + return to_address( it.operator->() ); + } +#endif // span_HAVE( ITERATOR_CTOR ) + +private: + pointer data_; + size_type size_; }; -#ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES +// class template argument deduction guides: -/* Deduction Guides */ -template -span(T (&)[N])->span; +#if span_HAVE( DEDUCTION_GUIDES ) -template -span(std::array&)->span; +template< class T, size_t N > +span( T (&)[N] ) -> span(N)>; -template -span(const std::array&)->span; +template< class T, size_t N > +span( std::array & ) -> span(N)>; -template -span(Container&)->span; +template< class T, size_t N > +span( std::array const & ) -> span(N)>; -template -span(const Container&)->span; +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) -#endif // TCB_HAVE_DEDUCTION_GUIDES +template< class Container > +span( Container& ) -> span; -template -constexpr span -make_span(span s) noexcept +template< class Container > +span( Container const & ) -> span; + +#endif + +// iterator: constraints: It satisfies contiguous_­iterator. + +template< class It, class EndOrSize > +span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t >::type >; + +#endif // span_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool same( span const & l, span const & r ) span_noexcept { - return s; + return std11::is_same::value + && l.size() == r.size() + && static_cast( l.data() ) == r.data(); } -template -constexpr span make_span(T (&arr)[N]) noexcept +#endif + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator==( span const & l, span const & r ) { - return {arr}; + return +#if span_FEATURE( SAME ) + same( l, r ) || +#endif + ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) ); } -template -TCB_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<( span const & l, span const & r ) { - return {arr}; + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); } -template -TCB_SPAN_ARRAY_CONSTEXPR span -make_span(const std::array& arr) noexcept +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator!=( span const & l, span const & r ) { - return {arr}; + return !( l == r ); } -template -constexpr span make_span(Container& cont) +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<=( span const & l, span const & r ) { - return {cont}; + return !( r < l ); } -template -constexpr span -make_span(const Container& cont) +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>( span const & l, span const & r ) { - return {cont}; + return ( r < l ); } -template -span -as_bytes(span s) noexcept +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>=( span const & l, span const & r ) { - return {reinterpret_cast(s.data()), s.size_bytes()}; + return !( l < r ); } -template < - class ElementType, size_t Extent, - typename std::enable_if::value, int>::type = 0> -span -as_writable_bytes(span s) noexcept +#endif // span_FEATURE( COMPARISON ) + +// 26.7.2.6 views of object representation [span.objectrep] + +#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow: + +template< typename T, extent_t Extent > +struct BytesExtent { - return {reinterpret_cast(s.data()), s.size_bytes()}; +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = span_sizeof(T) * Extent }; +#else + enum ET { value = span_sizeof(T) * Extent }; +#endif +}; + +template< typename T > +struct BytesExtent< T, dynamic_extent > +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = dynamic_extent }; +#else + enum ET { value = dynamic_extent }; +#endif +}; + +template< class T, extent_t Extent > +inline span_constexpr span< const std17::byte, BytesExtent::value > +as_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() }; +#else + return span< const std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif } -template -constexpr auto get(span s) -> decltype(s[N]) +template< class T, extent_t Extent > +inline span_constexpr span< std17::byte, BytesExtent::value > +as_writable_bytes( span spn ) span_noexcept { - return s[N]; +#if 0 + return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() }; +#else + return span< std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif } -} // namespace TCB_SPAN_NAMESPACE_NAME +#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// 27.8 Container and view access [iterator.container] + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::size_t size( span const & spn ) +{ + return static_cast( spn.size() ); +} + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::ptrdiff_t ssize( span const & spn ) +{ + return static_cast( spn.size() ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { + +using span_lite::dynamic_extent; + +using span_lite::span; + +using span_lite::with_container; + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) +using span_lite::same; +#endif + +using span_lite::operator==; +using span_lite::operator!=; +using span_lite::operator<; +using span_lite::operator<=; +using span_lite::operator>; +using span_lite::operator>=; +#endif + +#if span_HAVE( BYTE ) +using span_lite::as_bytes; +using span_lite::as_writable_bytes; +#endif + +using span_lite::size; +using span_lite::ssize; + +} // namespace nonstd + +#endif // span_USES_STD_SPAN + +// make_span() [span-lite extension]: + +#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +#if span_USES_STD_SPAN +# define span_constexpr constexpr +# define span_noexcept noexcept +# define span_nullptr nullptr +# ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +# endif +using extent_t = span_CONFIG_EXTENT_TYPE; +#endif // span_USES_STD_SPAN + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr span +make_span( T * ptr, size_t count ) span_noexcept +{ + return span( ptr, count ); +} + +template< class T > +inline span_constexpr span +make_span( T * first, T * last ) span_noexcept +{ + return span( first, last ); +} + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( T ( &arr )[ N ] ) span_noexcept +{ + return span(N)>( &arr[ 0 ], N ); +} + +#if span_USES_STD_SPAN || span_HAVE( ARRAY ) + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( std::array< T, N > & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +template< class T, std::size_t N > +inline span_constexpr span< const T, static_cast(N) > +make_span( std::array< T, N > const & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +#endif // span_HAVE( ARRAY ) + +#if span_USES_STD_SPAN || span_HAVE( INITIALIZER_LIST ) + +template< class T > +inline span_constexpr span< const T > +make_span( std::initializer_list il ) span_noexcept +{ + return span( il.begin(), il.size() ); +} + +#endif // span_HAVE( INITIALIZER_LIST ) + +#if span_USES_STD_SPAN + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO ) + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#else + +template< class T > +inline span_constexpr span +make_span( span spn ) span_noexcept +{ + return spn; +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector const & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +#endif // span_USES_STD_SPAN || ( ... ) + +#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container & cont ) span_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container const & cont ) span_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +// extensions: non-member views: +// this feature implies the presence of make_span() + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +first( span spn ) +{ + return spn.template first(); +} + +template< class T, extent_t Extent > +span_constexpr span +first( span spn, size_t count ) +{ + return spn.first( count ); +} + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +last( span spn ) +{ + return spn.template last(); +} + +template< class T, extent_t Extent > +span_constexpr span +last( span spn, size_t count ) +{ + return spn.last( count ); +} + +template< size_t Offset, extent_t Count, class T, extent_t Extent > +span_constexpr span +subspan( span spn ) +{ + return spn.template subspan(); +} + +template< class T, extent_t Extent > +span_constexpr span +subspan( span spn, size_t offset, extent_t count = dynamic_extent ) +{ + return spn.subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 + +template< extent_t Count, class T > +span_constexpr auto +first( T & t ) -> decltype( make_span(t).template first() ) +{ + return make_span( t ).template first(); +} + +template< class T > +span_constexpr auto +first( T & t, size_t count ) -> decltype( make_span(t).first(count) ) +{ + return make_span( t ).first( count ); +} + +template< extent_t Count, class T > +span_constexpr auto +last( T & t ) -> decltype( make_span(t).template last() ) +{ + return make_span(t).template last(); +} + +template< class T > +span_constexpr auto +last( T & t, extent_t count ) -> decltype( make_span(t).last(count) ) +{ + return make_span( t ).last( count ); +} + +template< size_t Offset, extent_t Count = dynamic_extent, class T > +span_constexpr auto +subspan( T & t ) -> decltype( make_span(t).template subspan() ) +{ + return make_span( t ).template subspan(); +} + +template< class T > +span_constexpr auto +subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) ) +{ + return make_span( t ).subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::make_span; + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 ) + +using span_lite::first; +using span_lite::last; +using span_lite::subspan; + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] ) + +} // namespace nonstd + +#endif // #if span_FEATURE_TO_STD( MAKE_SPAN ) + +#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) ) + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr auto +byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) > +{ + return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) ); +} + +template< class T > +inline span_constexpr auto +byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) > +{ + return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::byte_span; +} // namespace nonstd + +#endif // span_FEATURE( BYTE_SPAN ) + +#if span_HAVE( STRUCT_BINDING ) + +#if span_CPP14_OR_GREATER +# include +#elif span_CPP11_OR_GREATER +# include +namespace std { + template< std::size_t I, typename T > + using tuple_element_t = typename tuple_element::type; +} +#else +namespace std { + template< typename T > + class tuple_size; /*undefined*/ + + template< std::size_t I, typename T > + class tuple_element; /* undefined */ +} +#endif // span_CPP14_OR_GREATER namespace std { -template -class tuple_size> - : public integral_constant {}; +// 26.7.X Tuple interface -template -class tuple_size>; // not defined +// std::tuple_size<>: -template -class tuple_element> { +template< typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_size< nonstd::span > : public integral_constant(Extent)> {}; + +// std::tuple_size<>: Leave undefined for dynamic extent: + +template< typename ElementType > +class tuple_size< nonstd::span >; + +// std::tuple_element<>: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_element< I, nonstd::span > +{ public: - static_assert(Extent != TCB_SPAN_NAMESPACE_NAME::dynamic_extent && - I < Extent, - ""); +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element: dynamic extent or index out of range" ); +#endif using type = ElementType; }; +// std::get<>(), 2 variants: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType & get( nonstd::span & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType const & get( nonstd::span const & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + } // end namespace std -#endif // TCB_SPAN_HPP_INCLUDED +#endif // span_HAVE( STRUCT_BINDING ) + +#if ! span_USES_STD_SPAN +span_RESTORE_WARNINGS() +#endif // span_USES_STD_SPAN + +#endif // NONSTD_SPAN_HPP_INCLUDED diff --git a/src/libs/advanceddockingsystem/CMakeLists.txt b/src/libs/advanceddockingsystem/CMakeLists.txt index 2ec5c94b422..66608852137 100644 --- a/src/libs/advanceddockingsystem/CMakeLists.txt +++ b/src/libs/advanceddockingsystem/CMakeLists.txt @@ -1,5 +1,5 @@ add_qtc_library(AdvancedDockingSystem - DEPENDS Qt::Widgets Qt::Core Qt::Gui Qt::Xml Utils + DEPENDS Qt::Widgets Qt::Core Qt::Gui Qt::Xml Qt::QuickWidgets Utils SOURCES ads_globals.cpp ads_globals.h advanceddockingsystemtr.h diff --git a/src/libs/advanceddockingsystem/autohidetab.cpp b/src/libs/advanceddockingsystem/autohidetab.cpp index c6bfd7bdb8b..1d8f7a50a65 100644 --- a/src/libs/advanceddockingsystem/autohidetab.cpp +++ b/src/libs/advanceddockingsystem/autohidetab.cpp @@ -408,7 +408,8 @@ void AutoHideTab::mouseReleaseEvent(QMouseEvent *event) case DraggingFloatingWidget: event->accept(); d->m_floatingWidget->finishDragging(); - if (d->m_dockWidget->isAutoHide() && d->m_dragStartOrientation != orientation()) + if (d->m_dockWidget->autoHideDockContainer() + && d->m_dragStartOrientation != orientation()) d->m_dockWidget->autoHideDockContainer()->resetToInitialDockWidgetSize(); break; diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp index e1ac0c9b861..57e7c52da78 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.cpp +++ b/src/libs/advanceddockingsystem/dockareatabbar.cpp @@ -112,6 +112,14 @@ void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab) setCurrentIndex(index); emit tabBarClicked(index); + + // QDS: Focus the actual content widget on tab click + DockWidgetTab *tab = currentTab(); + if (tab && tab->dockWidget() && tab->dockWidget()->widget()) { + QMetaObject::invokeMethod(tab->dockWidget()->widget(), + QOverload<>::of(&QWidget::setFocus), + Qt::QueuedConnection); + } } void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab) @@ -272,7 +280,7 @@ int DockAreaTabBar::currentIndex() const DockWidgetTab *DockAreaTabBar::currentTab() const { - if (d->m_currentIndex < 0) + if (d->m_currentIndex < 0 || d->m_currentIndex >= d->m_tabsLayout->count()) return nullptr; else return qobject_cast(d->m_tabsLayout->itemAt(d->m_currentIndex)->widget()); diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp index 2c0d306ea74..0b2a03d1348 100644 --- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp +++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp @@ -460,7 +460,6 @@ void DockContainerWidgetPrivate::dropIntoCenterOfSection(FloatingDockContainer * for (int i = 0; i < newDockWidgets.count(); ++i) { DockWidget *dockWidget = newDockWidgets[i]; targetArea->insertDockWidget(tabIndex + i, dockWidget, false); - targetArea->insertDockWidget(i, dockWidget, false); // If the floating widget contains multiple visible dock areas, then we simply pick the // first visible open dock widget and make it the current one. if (newCurrentIndex < 0 && !dockWidget->isClosed()) diff --git a/src/libs/advanceddockingsystem/dockfocuscontroller.cpp b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp index b35963c6db2..ab665ea0ae7 100644 --- a/src/libs/advanceddockingsystem/dockfocuscontroller.cpp +++ b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp @@ -231,6 +231,9 @@ void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget return; DockWidget *dockWidget = qobject_cast(focusedNow); + + bool focusActual = dockWidget && dockWidget->widget(); + if (!dockWidget) dockWidget = internal::findParent(focusedNow); @@ -243,6 +246,12 @@ void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget #endif d->updateDockWidgetFocus(dockWidget); + + if (focusActual) { + // QDS: Focus the actual content widget when dockWidget gets focus + QMetaObject::invokeMethod(dockWidget->widget(), QOverload<>::of(&QWidget::setFocus), + Qt::QueuedConnection); + } } void DockFocusController::setDockWidgetTabFocused(DockWidgetTab *tab) diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 73b475ee5a4..ce85293fef8 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -714,7 +713,8 @@ bool DockManager::eventFilter(QObject *obj, QEvent *event) QWindowStateChangeEvent *ev = static_cast(event); if (ev->oldState().testFlag(Qt::WindowMinimized)) { d->m_isLeavingMinimized = true; - QMetaObject::invokeMethod(this, "endLeavingMinimizedState", Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, [this] { endLeavingMinimizedState(); }, Qt::QueuedConnection); } } return Super::eventFilter(obj, event); diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index 229995b9322..f2e0e72249f 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -6,7 +6,6 @@ #include "ads_globals.h" #include "ads_globals_p.h" #include "autohidedockcontainer.h" -#include "autohidesidebar.h" #include "autohidetab.h" #include "dockareawidget.h" #include "dockcomponentsfactory.h" @@ -20,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +27,7 @@ #include #include #include +#include namespace ADS { /** @@ -50,6 +51,7 @@ public: DockAreaWidget *m_dockArea = nullptr; QAction *m_toggleViewAction = nullptr; bool m_closed = false; + bool m_focused = false; QScrollArea *m_scrollArea = nullptr; QToolBar *m_toolBar = nullptr; Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly; @@ -161,6 +163,11 @@ void DockWidgetPrivate::hideDockWidget() closeAutoHideDockWidgetsIfNeeded(); if (m_features.testFlag(DockWidget::DeleteContentOnClose)) { + if (m_scrollArea) { + m_scrollArea->takeWidget(); + delete m_scrollArea; + m_scrollArea = nullptr; + } m_widget->deleteLater(); m_widget = nullptr; } @@ -219,6 +226,7 @@ void DockWidgetPrivate::setupScrollArea() m_scrollArea = new QScrollArea(q); m_scrollArea->setObjectName("dockWidgetScrollArea"); m_scrollArea->setWidgetResizable(true); + m_scrollArea->setProperty("focused", q->isFocused()); m_layout->addWidget(m_scrollArea); } @@ -439,6 +447,45 @@ bool DockWidget::isClosed() const return d->m_closed; } +void DockWidget::setFocused(bool focused) +{ + if (d->m_focused == focused) + return; + + d->m_focused = focused; + + if (d->m_scrollArea) + d->m_scrollArea->setProperty("focused", focused); + + const QString customObjectName = QString("__mainSrollView"); + + QList quickWidgets = d->m_widget->findChildren(); + + for (const auto &quickWidget : std::as_const(quickWidgets)) { + QQuickItem *rootItem = quickWidget->rootObject(); + if (!rootItem) + continue; + + if (rootItem->objectName() == customObjectName) { + rootItem->setProperty("adsFocus", focused); + continue; + } + + QQuickItem *scrollView = rootItem->findChild(customObjectName); + if (!scrollView) + continue; + + scrollView->setProperty("adsFocus", focused); + } + + emit focusedChanged(); +} + +bool DockWidget::isFocused() const +{ + return d->m_focused; +} + QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; diff --git a/src/libs/advanceddockingsystem/dockwidget.h b/src/libs/advanceddockingsystem/dockwidget.h index 4f9d13be290..65495cc04d6 100644 --- a/src/libs/advanceddockingsystem/dockwidget.h +++ b/src/libs/advanceddockingsystem/dockwidget.h @@ -32,6 +32,8 @@ class AutoHideSideBar; class ADS_EXPORT DockWidget : public QFrame { Q_OBJECT + Q_PROPERTY(bool focused READ isFocused WRITE setFocused NOTIFY focusedChanged) + private: DockWidgetPrivate *d; ///< private data (pimpl) friend class DockWidgetPrivate; @@ -367,6 +369,16 @@ public: */ bool isClosed() const; + /** + * Sets the focus property for widget + */ + void setFocused(bool focused); + + /** + * Returns true if this dock widget is focused. + */ + bool isFocused() const; + /** * Returns a checkable action that can be used to show or close this dock widget. * The action's text is set to the dock widget's window title. @@ -627,6 +639,8 @@ signals: * The features parameter gives the new value of the property. */ void featuresChanged(DockWidgetFeatures features); + + void focusedChanged(); }; // class DockWidget } // namespace ADS diff --git a/src/libs/googletest/CMakeLists.txt b/src/libs/googletest/CMakeLists.txt index 50e82e1edf6..3ccd2c44ca8 100644 --- a/src/libs/googletest/CMakeLists.txt +++ b/src/libs/googletest/CMakeLists.txt @@ -10,7 +10,7 @@ add_qtc_library(Googletest STATIC CONDITION GOOGLETEST_SUBMODULE_IS_CHECKED_OUT DEPENDS Threads::Threads - PUBLIC_INCLUDES + PUBLIC_SYSTEM_INCLUDES "${GOOGLETEST_DIR}/googletest/include" "${GOOGLETEST_DIR}/googlemock/include" INCLUDES diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index c1cf61d9dcb..9aaf5c02a19 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -675,11 +675,16 @@ QList Check::defaultDisabledMessagesForNonQuickUi() ErrBehavioursNotSupportedInQmlUi, ErrStatesOnlyInRootItemInQmlUi, ErrReferenceToParentItemNotSupportedInQmlUi, - ErrDoNotMixTranslationFunctionsInQmlUi, + WarnDoNotMixTranslationFunctionsInQmlUi, }); return disabled; } +bool Check::incompatibleDesignerQmlId(const QString &id) +{ + return idsThatShouldNotBeUsedInDesigner->contains(id); +} + Check::Check(Document::Ptr doc, const ContextPtr &context, Utils::QtcSettings *qtcSettings) : _doc(doc) , _context(context) @@ -769,7 +774,7 @@ void Check::enableQmlDesignerUiFileChecks() enableMessage(ErrBehavioursNotSupportedInQmlUi); enableMessage(ErrStatesOnlyInRootItemInQmlUi); enableMessage(ErrReferenceToParentItemNotSupportedInQmlUi); - enableMessage(ErrDoNotMixTranslationFunctionsInQmlUi); + enableMessage(WarnDoNotMixTranslationFunctionsInQmlUi); } void Check::disableQmlDesignerUiFileChecks() @@ -781,7 +786,7 @@ void Check::disableQmlDesignerUiFileChecks() disableMessage(ErrBehavioursNotSupportedInQmlUi); disableMessage(ErrStatesOnlyInRootItemInQmlUi); disableMessage(ErrReferenceToParentItemNotSupportedInQmlUi); - disableMessage(ErrDoNotMixTranslationFunctionsInQmlUi); + disableMessage(WarnDoNotMixTranslationFunctionsInQmlUi); } bool Check::preVisit(Node *ast) @@ -1099,7 +1104,7 @@ bool Check::visit(UiScriptBinding *ast) return false; } - if (idsThatShouldNotBeUsedInDesigner->contains(id)) { + if (incompatibleDesignerQmlId(id)) { addMessage(ErrInvalidIdeInVisualDesigner, loc); } @@ -1932,7 +1937,7 @@ bool Check::visit(CallExpression *ast) if (lastTransLationfunction != noTranslationfunction && lastTransLationfunction != translationFunction) - addMessage(ErrDoNotMixTranslationFunctionsInQmlUi, location); + addMessage(WarnDoNotMixTranslationFunctionsInQmlUi, location); lastTransLationfunction = translationFunction; } diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 60484fc43f1..1fb96d251b5 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -39,6 +39,7 @@ public: static QList defaultDisabledMessages(); static QList defaultDisabledMessagesForNonQuickUi(); + static bool incompatibleDesignerQmlId(const QString &id); protected: bool preVisit(AST::Node *ast) override; diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp index ef24d648c41..98bff3a4b69 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp +++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp @@ -218,7 +218,8 @@ StaticAnalysisMessages::StaticAnalysisMessages() Tr::tr("States are only supported in the root item in a UI file (.ui.qml).")); newMsg(ErrReferenceToParentItemNotSupportedInQmlUi, Error, Tr::tr("Referencing the parent of the root item is not supported in a UI file (.ui.qml).")); - newMsg(ErrDoNotMixTranslationFunctionsInQmlUi, Error, + newMsg(WarnDoNotMixTranslationFunctionsInQmlUi, + Warning, Tr::tr("Do not mix translation functions in a UI file (.ui.qml).")); newMsg(StateCannotHaveChildItem, Error, Tr::tr("A State cannot have a child item (%1)."), 1); diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.h b/src/libs/qmljs/qmljsstaticanalysismessage.h index 404eb1676c8..01b3b52d698 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.h +++ b/src/libs/qmljs/qmljsstaticanalysismessage.h @@ -83,7 +83,7 @@ enum Type { ErrBehavioursNotSupportedInQmlUi = 224, ErrStatesOnlyInRootItemInQmlUi = 225, ErrReferenceToParentItemNotSupportedInQmlUi = 226, - ErrDoNotMixTranslationFunctionsInQmlUi = 227, + WarnDoNotMixTranslationFunctionsInQmlUi = 227, ErrUnknownComponent = 300, ErrCouldNotResolvePrototypeOf = 301, ErrCouldNotResolvePrototype = 302, diff --git a/src/libs/qmlpuppetcommunication/commands/createscenecommand.h b/src/libs/qmlpuppetcommunication/commands/createscenecommand.h index ada540829c3..f052da1eaaa 100644 --- a/src/libs/qmlpuppetcommunication/commands/createscenecommand.h +++ b/src/libs/qmlpuppetcommunication/commands/createscenecommand.h @@ -38,9 +38,7 @@ public: const QString &language, QSize captureImageMinimumSize, QSize captureImageMaximumSize, - qint32 stateInstanceId, - const QList &edit3dBackgroundColor, - const QColor &edit3dGridColor) + qint32 stateInstanceId) : instances(instanceContainer) , reparentInstances(reparentContainer) , ids(idVector) @@ -56,8 +54,6 @@ public: , captureImageMinimumSize(captureImageMinimumSize) , captureImageMaximumSize(captureImageMaximumSize) , stateInstanceId{stateInstanceId} - , edit3dBackgroundColor{edit3dBackgroundColor} - , edit3dGridColor{edit3dGridColor} {} friend QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) @@ -77,8 +73,6 @@ public: out << command.stateInstanceId; out << command.captureImageMinimumSize; out << command.captureImageMaximumSize; - out << command.edit3dBackgroundColor; - out << command.edit3dGridColor; return out; } @@ -100,8 +94,6 @@ public: in >> command.stateInstanceId; in >> command.captureImageMinimumSize; in >> command.captureImageMaximumSize; - in >> command.edit3dBackgroundColor; - in >> command.edit3dGridColor; return in; } @@ -122,8 +114,6 @@ public: QSize captureImageMinimumSize; QSize captureImageMaximumSize; qint32 stateInstanceId = 0; - QList edit3dBackgroundColor; - QColor edit3dGridColor; }; QDebug operator<<(QDebug debug, const CreateSceneCommand &command); diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 67f0fa4c5c2..6e25b3b419c 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -44,9 +44,6 @@ enum class View3DActionType { ParticlesPlay, ParticlesRestart, ParticlesSeek, - SelectBackgroundColor, - SelectGridColor, - ResetBackgroundColor, SyncBackgroundColor, GetNodeAtPos, SetBakeLightsView3D diff --git a/src/libs/qmlpuppetcommunication/types/enumeration.h b/src/libs/qmlpuppetcommunication/types/enumeration.h index f3127ab9075..57bfe6c0ef4 100644 --- a/src/libs/qmlpuppetcommunication/types/enumeration.h +++ b/src/libs/qmlpuppetcommunication/types/enumeration.h @@ -37,7 +37,7 @@ public: { m_enumerationName.reserve(scope.size() + 1 + name.size()); m_enumerationName.append(scope); - m_enumerationName.append(1); + m_enumerationName.append('.'); m_enumerationName.append(name); } diff --git a/src/libs/sqlite/sqlitealgorithms.h b/src/libs/sqlite/sqlitealgorithms.h index f54c02e50e0..a410e59aa90 100644 --- a/src/libs/sqlite/sqlitealgorithms.h +++ b/src/libs/sqlite/sqlitealgorithms.h @@ -34,7 +34,7 @@ void insertUpdateDelete(SqliteRange &&sqliteRange, auto endSqliteIterator = sqliteRange.end(); auto currentValueIterator = values.begin(); auto endValueIterator = values.end(); - std::optional> lastValue; + auto lastValueIterator = endValueIterator; while (true) { bool hasMoreValues = currentValueIterator != endValueIterator; @@ -47,10 +47,10 @@ void insertUpdateDelete(SqliteRange &&sqliteRange, UpdateChange updateChange = updateCallback(sqliteValue, value); switch (updateChange) { case UpdateChange::Update: - lastValue = value; + lastValueIterator = currentValueIterator; break; case UpdateChange::No: - lastValue.reset(); + lastValueIterator = endValueIterator; break; } ++currentSqliteIterator; @@ -59,10 +59,10 @@ void insertUpdateDelete(SqliteRange &&sqliteRange, insertCallback(value); ++currentValueIterator; } else if (compare < 0) { - if (lastValue) { - if (compareKey(sqliteValue, *lastValue) != 0) + if (lastValueIterator != endValueIterator) { + if (compareKey(sqliteValue, *lastValueIterator) != 0) deleteCallback(sqliteValue); - lastValue.reset(); + lastValueIterator = endValueIterator; } else { deleteCallback(sqliteValue); } @@ -73,10 +73,10 @@ void insertUpdateDelete(SqliteRange &&sqliteRange, ++currentValueIterator; } else if (hasMoreSqliteValues) { auto &&sqliteValue = *currentSqliteIterator; - if (lastValue) { - if (compareKey(sqliteValue, *lastValue) != 0) + if (lastValueIterator != endValueIterator) { + if (compareKey(sqliteValue, *lastValueIterator) != 0) deleteCallback(sqliteValue); - lastValue.reset(); + lastValueIterator = endValueIterator; } else { deleteCallback(sqliteValue); } diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index d9d677c3959..ae0a60913d1 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -325,11 +325,6 @@ QString BaseStatement::columnName(int column) const return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column)); } -Database &BaseStatement::database() const -{ - return m_database; -} - namespace { template StringType textForColumn(sqlite3_stmt *sqlStatment, int column) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 54d08260b7c..3c490293447 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -23,6 +23,7 @@ #include #include #include +#include using std::int64_t; @@ -116,7 +117,7 @@ public: QString columnName(int column) const; - Database &database() const; + Database &database() const { return m_database; } protected: ~BaseStatement() = default; @@ -172,12 +173,28 @@ public: BaseStatement::next(); } - template - auto values(std::size_t reserveSize, const QueryTypes &...queryValues) + template + struct is_container : std::false_type + {}; + template + struct is_container> : std::true_type + {}; + template + struct is_container> : std::true_type + {}; + template + struct is_container> : std::true_type + {}; + + template::value>, + typename... QueryTypes> + auto values(const QueryTypes &...queryValues) { Resetter resetter{this}; - std::vector resultValues; - resultValues.reserve(std::max(reserveSize, m_maximumResultCount)); + Container resultValues; + resultValues.reserve(std::max(capacity, m_maximumResultCount)); bindValues(queryValues...); @@ -189,6 +206,16 @@ public: return resultValues; } + template typename Container = std::vector, + typename = std::enable_if_t::value>, + typename... QueryTypes> + auto values(const QueryTypes &...queryValues) + { + return values, capacity>(queryValues...); + } + template auto value(const QueryTypes &...queryValues) { @@ -398,8 +425,10 @@ private: Resetter(StatementImplementation *statement) : statement(statement) { +#ifndef QT_NO_DEBUG if (statement && !statement->database().isLocked()) throw DatabaseIsNotLocked{}; +#endif } Resetter(Resetter &) = delete; @@ -409,7 +438,7 @@ private: : statement{std::exchange(other.statement, nullptr)} {} - void reset() + void reset() noexcept { if (statement) statement->reset(); diff --git a/src/libs/sqlite/sqlitedatabasebackend.cpp b/src/libs/sqlite/sqlitedatabasebackend.cpp index 0fd18dfaa19..1cca1f61791 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.cpp +++ b/src/libs/sqlite/sqlitedatabasebackend.cpp @@ -275,7 +275,7 @@ void DatabaseBackend::checkCanOpenDatabase(Utils::SmallStringView databaseFilePa if (databaseFilePath.isEmpty()) throw DatabaseFilePathIsEmpty("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because the file path is empty!"); - if (!QFileInfo::exists(QFileInfo(QString(databaseFilePath)).path())) + if (!QFileInfo::exists(QFileInfo(QString{databaseFilePath}).path())) throw WrongFilePath(Utils::SmallString(databaseFilePath)); if (databaseIsOpen()) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 6296f84718a..d64e4d9645a 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -78,3 +78,14 @@ auto toIntegers(const Container &container) } } // namespace Sqlite + +namespace std { +template +struct hash> +{ + auto operator()(const Sqlite::BasicId &id) const + { + return std::hash{}(id.internalId()); + } +}; +} // namespace std diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 3df47efc741..04e4a37cf70 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -34,7 +34,7 @@ public: template auto valueWithTransaction(const QueryTypes &...queryValues) { - return withDeferredTransaction(Base::database(), [&] { + return withImplicitTransaction(Base::database(), [&] { return Base::template value(queryValues...); }); } @@ -42,23 +42,23 @@ public: template auto optionalValueWithTransaction(const QueryTypes &...queryValues) { - return withDeferredTransaction(Base::database(), [&] { + return withImplicitTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); }); } - template - auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) + template + auto valuesWithTransaction(const QueryTypes &...queryValues) { - return withDeferredTransaction(Base::database(), [&] { - return Base::template values(reserveSize, queryValues...); + return withImplicitTransaction(Base::database(), [&] { + return Base::template values(queryValues...); }); } template void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { - withDeferredTransaction(Base::database(), [&] { + withImplicitTransaction(Base::database(), [&] { Base::readCallback(std::forward(callable), queryValues...); }); } @@ -66,7 +66,7 @@ public: template void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { - withDeferredTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); + withImplicitTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } protected: diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 08f1aeda04c..2b3e6c32477 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -47,11 +47,13 @@ public: }); } - template - auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) + template + auto valuesWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { - return Base::template values(reserveSize, queryValues...); + return Base::template values(queryValues...); }); } diff --git a/src/libs/sqlite/sqlitesessions.cpp b/src/libs/sqlite/sqlitesessions.cpp index 96119cd20fb..fd81b17edee 100644 --- a/src/libs/sqlite/sqlitesessions.cpp +++ b/src/libs/sqlite/sqlitesessions.cpp @@ -110,7 +110,7 @@ void Sessions::revert() " ORDER BY id DESC"}), database}; - auto changeSets = selectChangeSets.values(1024); + auto changeSets = selectChangeSets.values(); for (auto &changeSet : changeSets) { int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(), @@ -134,7 +134,7 @@ void Sessions::apply() " ORDER BY id"}), database}; - auto changeSets = selectChangeSets.values(1024); + auto changeSets = selectChangeSets.values(); for (auto &changeSet : changeSets) { int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(), @@ -170,7 +170,7 @@ SessionChangeSets Sessions::changeSets() const " ORDER BY id DESC"}), database}; - return selectChangeSets.values(1024); + return selectChangeSets.values(); } void Sessions::Deleter::operator()(sqlite3_session *session) diff --git a/src/libs/sqlite/sqlitetransaction.h b/src/libs/sqlite/sqlitetransaction.h index 2cc4a7bf5fe..85228eb0507 100644 --- a/src/libs/sqlite/sqlitetransaction.h +++ b/src/libs/sqlite/sqlitetransaction.h @@ -67,6 +67,24 @@ protected: bool m_rollback = false; }; +template +class ImplicitTransaction +{ +public: + using Transaction = TransactionInterface; + + ~ImplicitTransaction() = default; + ImplicitTransaction(TransactionInterface &transactionInterface) + : m_locker(transactionInterface) + {} + + ImplicitTransaction(const ImplicitTransaction &) = delete; + ImplicitTransaction &operator=(const ImplicitTransaction &) = delete; + +protected: + std::unique_lock m_locker; +}; + template class AbstractThrowingSessionTransaction { @@ -201,6 +219,18 @@ auto withTransaction(TransactionInterface &transactionInterface, Callable &&call } } +template +auto withImplicitTransaction(TransactionInterface &transactionInterface, Callable &&callable) +{ + ImplicitTransaction transaction{transactionInterface}; + + if constexpr (std::is_void_v>) { + callable(); + } else { + return callable(); + } +} + template auto withDeferredTransaction(TransactionInterface &transactionInterface, Callable &&callable) { diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index bd4f708fed1..16e872d5afa 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -50,29 +50,23 @@ public: using const_reverse_iterator = std::reverse_iterator; using size_type = std::size_t; - static_assert(Size < 64 - ? sizeof(Internal::StringDataLayout) == Size + 1 - : sizeof(Internal::StringDataLayout) == Size + 2, + static_assert(Size < 64 ? sizeof(Internal::StringDataLayout) == Size + 1 + : sizeof(Internal::StringDataLayout) == Size + 2, "Size is wrong"); - constexpr - BasicSmallString() noexcept - : m_data(Internal::StringDataLayout()) - { - } - constexpr - BasicSmallString(const BasicSmallStringLiteral &stringReference) + BasicSmallString() noexcept = default; + + constexpr BasicSmallString(const BasicSmallStringLiteral &stringReference) noexcept : m_data(stringReference.m_data) { } template - constexpr - BasicSmallString(const char(&string)[ArraySize]) + constexpr BasicSmallString(const char (&string)[ArraySize]) noexcept : m_data(string) {} - BasicSmallString(const char *string, size_type size, size_type capacity) + BasicSmallString(const char *string, size_type size, size_type capacity) noexcept { if (Q_LIKELY(capacity <= shortStringCapacity())) { m_data.control = Internal::ControlBlock(size, false, false); @@ -84,52 +78,52 @@ public: } } - explicit BasicSmallString(SmallStringView stringView) + explicit BasicSmallString(SmallStringView stringView) noexcept : BasicSmallString(stringView.data(), stringView.size(), stringView.size()) {} - BasicSmallString(const char *string, size_type size) + BasicSmallString(const char *string, size_type size) noexcept : BasicSmallString(string, size, size) {} - explicit BasicSmallString(const_iterator begin, const_iterator end) - : BasicSmallString{std::addressof(*begin), static_cast(std::distance(begin, end))} + explicit BasicSmallString(const_iterator begin, const_iterator end) noexcept + : BasicSmallString{std::addressof(*begin), + static_cast(std::distance(begin, end))} {} - explicit BasicSmallString(iterator begin, iterator end) + explicit BasicSmallString(iterator begin, iterator end) noexcept - : BasicSmallString{std::addressof(*begin), static_cast(std::distance(begin, end))} + : BasicSmallString{std::addressof(*begin), + static_cast(std::distance(begin, end))} {} template::value>> - BasicSmallString(Type characterPointer) + BasicSmallString(Type characterPointer) noexcept : BasicSmallString(characterPointer, std::char_traits::length(characterPointer)) { static_assert(!std::is_array::value, "Input type is array and not char pointer!"); } - BasicSmallString(const QString &qString) { append(qString); } + BasicSmallString(const QString &qString) noexcept { append(qString); } - BasicSmallString(const QStringView qStringView) { append(qStringView); } + BasicSmallString(const QStringView qStringView) noexcept { append(qStringView); } - BasicSmallString(const QByteArray &qByteArray) + BasicSmallString(const QByteArray &qByteArray) noexcept : BasicSmallString(qByteArray.constData(), qByteArray.size()) {} - template = 0> - BasicSmallString(const String &string) + template = 0> + BasicSmallString(const String &string) noexcept : BasicSmallString(string.data(), string.size()) { } - BasicSmallString(const std::wstring &wstring) { append(wstring); } + BasicSmallString(const std::wstring &wstring) noexcept { append(wstring); } template::value> - > - BasicSmallString(BeginIterator begin, EndIterator end) + typename = std::enable_if_t::value>> + BasicSmallString(BeginIterator begin, EndIterator end) noexcept : BasicSmallString(&(*begin), size_type(end - begin)) {} @@ -139,7 +133,7 @@ public: Memory::deallocate(m_data.reference.pointer); } - BasicSmallString(const BasicSmallString &other) + BasicSmallString(const BasicSmallString &other) noexcept { if (Q_LIKELY(other.isShortString() || other.isReadOnlyReference())) m_data = other.m_data; @@ -147,7 +141,7 @@ public: new (this) BasicSmallString{other.data(), other.size()}; } - BasicSmallString &operator=(const BasicSmallString &other) + BasicSmallString &operator=(const BasicSmallString &other) noexcept { if (Q_LIKELY(this != &other)) { this->~BasicSmallString(); @@ -180,7 +174,7 @@ public: } BasicSmallString take() noexcept { return std::move(*this); } - BasicSmallString clone() const + BasicSmallString clone() const noexcept { BasicSmallString clonedString(m_data); @@ -204,26 +198,22 @@ public: return QByteArray(data(), int(size())); } - QString toQString() const + QString toQString() const noexcept { return QString::fromUtf8(data(), int(size())); } + + SmallStringView toStringView() const noexcept { return SmallStringView(data(), size()); } + + operator SmallStringView() const noexcept { return SmallStringView(data(), size()); } + explicit operator QLatin1StringView() const noexcept { - return QString::fromUtf8(data(), int(size())); + return QLatin1StringView(data(), Utils::ssize(*this)); } - SmallStringView toStringView() const + operator QUtf8StringView() const noexcept { - return SmallStringView(data(), size()); + return QUtf8StringView(data(), Utils::ssize(*this)); } - operator SmallStringView() const - { - return SmallStringView(data(), size()); - } - - explicit - operator QString() const - { - return toQString(); - } + explicit operator QString() const noexcept { return toQString(); } explicit operator std::string() const @@ -231,13 +221,12 @@ public: return std::string(data(), size()); } - static - BasicSmallString fromUtf8(const char *characterPointer) + static BasicSmallString fromUtf8(const char *characterPointer) noexcept { return BasicSmallString(characterPointer, std::char_traits::length(characterPointer)); } - void reserve(size_type newCapacity) + void reserve(size_type newCapacity) noexcept { if (fitsNotInCapacity(newCapacity)) { if (Q_UNLIKELY(hasAllocatedMemory())) { @@ -250,7 +239,7 @@ public: } } - void resize(size_type newSize) + void resize(size_type newSize) noexcept { reserve(newSize); setSize(newSize); @@ -322,7 +311,7 @@ public: return end(); } - static BasicSmallString fromQString(const QString &qString) + static BasicSmallString fromQString(const QString &qString) noexcept { BasicSmallString string; string.append(qString); @@ -330,7 +319,7 @@ public: return string; } - static BasicSmallString fromQStringView(QStringView qStringView) + static BasicSmallString fromQStringView(QStringView qStringView) noexcept { BasicSmallString string; string.append(qStringView); @@ -338,18 +327,18 @@ public: return string; } - static BasicSmallString fromQByteArray(const QByteArray &utf8ByteArray) + static BasicSmallString fromQByteArray(const QByteArray &utf8ByteArray) noexcept { return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size())); } - bool contains(SmallStringView subStringToSearch) const + bool contains(SmallStringView subStringToSearch) const noexcept { return SmallStringView{*this}.find(subStringToSearch) != SmallStringView::npos; } - bool contains(char characterToSearch) const + bool contains(char characterToSearch) const noexcept { auto found = std::char_traits::find(data(), size(), characterToSearch); @@ -430,17 +419,20 @@ public: return SmallStringView(data() + position, length); } - void append(SmallStringView string) + void append(SmallStringView string) noexcept { size_type oldSize = size(); size_type newSize = oldSize + string.size(); reserve(optimalCapacity(newSize)); + QT_WARNING_PUSH + QT_WARNING_DISABLE_CLANG("-Wunsafe-buffer-usage") std::char_traits::copy(data() + oldSize, string.data(), string.size()); + QT_WARNING_POP setSize(newSize); } - void append(QStringView string) + void append(QStringView string) noexcept { QStringEncoder encoder{QStringEncoder::Utf8}; @@ -472,28 +464,28 @@ public: setSize(newEnd - data()); } - BasicSmallString &operator+=(SmallStringView string) + BasicSmallString &operator+=(SmallStringView string) noexcept { append(string); return *this; } - BasicSmallString &operator+=(QStringView string) + BasicSmallString &operator+=(QStringView string) noexcept { append(string); return *this; } - BasicSmallString &operator+=(std::initializer_list list) + BasicSmallString &operator+=(std::initializer_list list) noexcept { appendInitializerList(list, size()); return *this; } - void replace(SmallStringView fromText, SmallStringView toText) + void replace(SmallStringView fromText, SmallStringView toText) noexcept { if (toText.size() == fromText.size()) replaceEqualSized(fromText, toText); @@ -503,14 +495,14 @@ public: replaceLargerSized(fromText, toText); } - void replace(char fromCharacter, char toCharacter) + void replace(char fromCharacter, char toCharacter) noexcept { reserve(size()); std::replace(begin(), end(), fromCharacter, toCharacter); } - void replace(size_type position, size_type length, SmallStringView replacementText) + void replace(size_type position, size_type length, SmallStringView replacementText) noexcept { size_type newSize = size() - length + replacementText.size(); @@ -528,7 +520,7 @@ public: setSize(newSize); } - BasicSmallString toCarriageReturnsStripped() const + BasicSmallString toCarriageReturnsStripped() const noexcept { BasicSmallString text = *this; @@ -551,10 +543,12 @@ public: return size; } - constexpr size_type shortStringSize() const { return m_data.control.shortStringSize(); } + constexpr size_type shortStringSize() const noexcept + { + return m_data.control.shortStringSize(); + } - static - BasicSmallString join(std::initializer_list list) + static BasicSmallString join(std::initializer_list list) noexcept { size_type totalSize = 0; for (SmallStringView string : list) @@ -584,8 +578,7 @@ public: #endif } - static - BasicSmallString number(long long int number) + static BasicSmallString number(long long int number) noexcept { #ifdef __cpp_lib_to_chars // +1 for the sign, +1 for the extra digit @@ -599,23 +592,14 @@ public: #endif } - static - BasicSmallString number(double number) - { - return std::to_string(number); - } + static BasicSmallString number(double number) noexcept { return std::to_string(number); } - char &operator[](std::size_t index) - { - return *(data() + index); - } + char &operator[](std::size_t index) noexcept { return *(data() + index); } - char operator[](std::size_t index) const - { - return *(data() + index); - } + char operator[](std::size_t index) const noexcept { return *(data() + index); } - friend BasicSmallString operator+(const BasicSmallString &first, const BasicSmallString &second) + friend BasicSmallString operator+(const BasicSmallString &first, + const BasicSmallString &second) noexcept { BasicSmallString text; text.reserve(first.size() + second.size()); @@ -626,7 +610,7 @@ public: return text; } - friend BasicSmallString operator+(const BasicSmallString &first, SmallStringView second) + friend BasicSmallString operator+(const BasicSmallString &first, SmallStringView second) noexcept { BasicSmallString text; text.reserve(first.size() + second.size()); @@ -637,7 +621,7 @@ public: return text; } - friend BasicSmallString operator+(SmallStringView first, const BasicSmallString &second) + friend BasicSmallString operator+(SmallStringView first, const BasicSmallString &second) noexcept { BasicSmallString text; text.reserve(first.size() + second.size()); @@ -649,14 +633,16 @@ public: } template - friend BasicSmallString operator+(const BasicSmallString &first, const char(&second)[ArraySize]) + friend BasicSmallString operator+(const BasicSmallString &first, + const char (&second)[ArraySize]) noexcept { return operator+(first, SmallStringView(second)); } template - friend BasicSmallString operator+(const char(&first)[ArraySize], const BasicSmallString &second) + friend BasicSmallString operator+(const char (&first)[ArraySize], + const BasicSmallString &second) noexcept { return operator+(SmallStringView(first), second); } @@ -686,8 +672,7 @@ unittest_public: || (!isShortString() && capacity > m_data.reference.capacity); } - static - size_type optimalHeapCapacity(const size_type size) + static size_type optimalHeapCapacity(const size_type size) noexcept { const size_type cacheLineSize = 64; @@ -696,7 +681,7 @@ unittest_public: return (cacheLineBlocks + 1) * cacheLineSize; } - size_type countOccurrence(SmallStringView text) + size_type countOccurrence(SmallStringView text) noexcept { auto found = begin(); @@ -723,7 +708,8 @@ private: { } - void appendInitializerList(std::initializer_list list, std::size_t initialSize) + void appendInitializerList(std::initializer_list list, + std::size_t initialSize) noexcept { auto addSize = [] (std::size_t size, SmallStringView string) { return size + string.size(); @@ -742,7 +728,7 @@ private: } } - constexpr void initializeLongString(char *pointer, size_type size, size_type capacity) + constexpr void initializeLongString(char *pointer, size_type size, size_type capacity) noexcept { m_data.control = Internal::ControlBlock(0, false, true); m_data.reference.pointer = pointer; @@ -750,17 +736,11 @@ private: m_data.reference.capacity = capacity; } - char &at(size_type index) - { - return *(data() + index); - } + char &at(size_type index) noexcept { return *(data() + index); } - char at(size_type index) const - { - return *(data() + index); - } + char at(size_type index) const noexcept { return *(data() + index); } - void replaceEqualSized(SmallStringView fromText, SmallStringView toText) + void replaceEqualSized(SmallStringView fromText, SmallStringView toText) noexcept { reserve(size()); @@ -783,7 +763,7 @@ private: } } - void replaceSmallerSized(SmallStringView fromText, SmallStringView toText) + void replaceSmallerSized(SmallStringView fromText, SmallStringView toText) noexcept { auto found = std::search(begin(), end(), @@ -827,7 +807,7 @@ private: iterator replaceLargerSizedRecursive(size_type startIndex, SmallStringView fromText, SmallStringView toText, - size_type sizeDifference) + size_type sizeDifference) noexcept { auto found = std::search(begin() + startIndex, end(), @@ -864,7 +844,7 @@ private: return begin() + foundIndex; } - void replaceLargerSized(SmallStringView fromText, SmallStringView toText) + void replaceLargerSized(SmallStringView fromText, SmallStringView toText) noexcept { size_type sizeDifference = 0; size_type startIndex = 0; @@ -880,7 +860,7 @@ private: } } - void setSize(size_type size) + void setSize(size_type size) noexcept { if (isShortString()) m_data.control.setShortStringSize(size); @@ -922,8 +902,7 @@ Type clone(const Type &vector) using SmallString = BasicSmallString<31>; using PathString = BasicSmallString<190>; -inline -SmallString operator+(SmallStringView first, SmallStringView second) +inline SmallString operator+(SmallStringView first, SmallStringView second) noexcept { SmallString text; text.reserve(first.size() + second.size()); @@ -935,8 +914,7 @@ SmallString operator+(SmallStringView first, SmallStringView second) } template -inline -SmallString operator+(SmallStringView first, const char(&second)[Size]) +inline SmallString operator+(SmallStringView first, const char (&second)[Size]) noexcept { return operator+(first, SmallStringView(second)); } diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 6235995d944..0d87bd101ba 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -11,6 +11,10 @@ #include #include +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wgnu-anonymous-struct") +QT_WARNING_DISABLE_CLANG("-Wnested-anon-types") + namespace Utils { namespace Internal { @@ -31,45 +35,29 @@ struct ControlBlock , m_isReference(isReference) {} - constexpr void setShortStringSize(size_type size) + constexpr void setShortStringSize(size_type size) noexcept { m_shortStringSize = static_cast(size); } - constexpr size_type shortStringSize() const { return m_shortStringSize; } + constexpr size_type shortStringSize() const noexcept { return m_shortStringSize; } - constexpr void setIsReadOnlyReference(bool isReadOnlyReference) + constexpr void setIsReadOnlyReference(bool isReadOnlyReference) noexcept { m_isReadOnlyReference = isReadOnlyReference; } - constexpr void setIsReference(bool isReference) { m_isReference = isReference; } + constexpr void setIsReference(bool isReference) noexcept { m_isReference = isReference; } - constexpr void setIsShortString(bool isShortString) { m_isReference = !isShortString; } + constexpr void setIsShortString(bool isShortString) noexcept { m_isReference = !isShortString; } - constexpr - SizeType stringSize() const - { - return m_shortStringSize; - } + constexpr SizeType stringSize() const noexcept { return m_shortStringSize; } - constexpr - bool isReadOnlyReference() const - { - return m_isReadOnlyReference; - } + constexpr bool isReadOnlyReference() const noexcept { return m_isReadOnlyReference; } - constexpr - bool isReference() const - { - return m_isReference; - } + constexpr bool isReference() const noexcept { return m_isReference; } - constexpr - bool isShortString() const - { - return !m_isReference; - } + constexpr bool isShortString() const noexcept { return !m_isReference; } private: ControlType m_shortStringSize : (sizeof(ControlType) * 8) - 2; @@ -95,23 +83,30 @@ struct alignas(16) StringDataLayout "Size + 1 must be dividable by 16 if under 64 and Size + 2 must be dividable by " "16 if over 64!"); - StringDataLayout() noexcept { reset(); } + constexpr StringDataLayout() noexcept { reset(); } constexpr StringDataLayout(const char *string, size_type size) noexcept - : control{0, true, true} + : controlReference{0, true, true} , reference{{string}, size, 0} {} template constexpr StringDataLayout(const char (&string)[Size]) noexcept { - if constexpr (Size + 1 <= MaximumShortStringDataAreaSize) { - control = {Size - 1, false, false}; - for (size_type i = 0; i < Size; ++i) + constexpr auto size = Size - 1; + if constexpr (size <= MaximumShortStringDataAreaSize) { + control = {size, false, false}; + for (size_type i = 0; i < size; ++i) shortString[i] = string[i]; +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = size; i < MaximumShortStringDataAreaSize; ++i) + shortString[i] = 0; + } +#endif } else { control = {0, true, true}; - reference = {{string}, Size - 1, 0}; + reference = {{string}, size, 0}; } } @@ -120,20 +115,29 @@ struct alignas(16) StringDataLayout return MaximumShortStringDataAreaSize; } - constexpr void reset() { control = ControlBlock(); } + constexpr void reset() noexcept + { + control = ControlBlock(); +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = 0; i < MaximumShortStringDataAreaSize; ++i) + shortString[i] = 0; + } +#endif + } -#pragma pack(push) -#pragma pack(1) - ControlBlock control; union { - char shortString[MaximumShortStringDataAreaSize]; struct { - char dummy[sizeof(void *) - sizeof(ControlBlock)]; + ControlBlock control; + char shortString[MaximumShortStringDataAreaSize]; + }; + struct + { + ControlBlock controlReference; ReferenceLayout reference; }; }; -#pragma pack(pop) }; template @@ -147,34 +151,46 @@ struct alignas(16) StringDataLayout constexpr StringDataLayout(const char (&string)[Size]) noexcept { - if constexpr (Size + 1 <= MaximumShortStringDataAreaSize) { - control = {Size - 1, false, false}; - for (size_type i = 0; i < Size; ++i) + constexpr auto size = Size - 1; + if constexpr (size <= MaximumShortStringDataAreaSize) { + control = {size, false, false}; + for (size_type i = 0; i < size; ++i) shortString[i] = string[i]; +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = size; i < MaximumShortStringDataAreaSize; ++i) + shortString[i] = 0; + } +#endif } else { control = {0, true, true}; - reference = {{string}, Size - 1, 0}; + reference = {{string}, size, 0}; } } - StringDataLayout(const StringDataLayout &other) noexcept { *this = other; } - - StringDataLayout &operator=(const StringDataLayout &other) noexcept + void copyHere(const StringDataLayout &other) noexcept { constexpr auto controlBlockSize = sizeof(ControlBlock); auto shortStringLayoutSize = other.control.stringSize() + controlBlockSize; constexpr auto referenceLayoutSize = sizeof(ReferenceLayout); std::memcpy(this, &other, std::max(shortStringLayoutSize, referenceLayoutSize)); + } + + StringDataLayout(const StringDataLayout &other) noexcept { copyHere(other); } + + StringDataLayout &operator=(const StringDataLayout &other) noexcept + { + copyHere(other); return *this; } @@ -184,21 +200,32 @@ struct alignas(16) StringDataLayout(); } + constexpr void reset() noexcept + { + control = ControlBlock(); +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = 0; i < MaximumShortStringDataAreaSize; ++i) + shortString[i] = 0; + } +#endif + } -#pragma pack(push) -#pragma pack(1) - ControlBlock control; union { - char shortString[MaximumShortStringDataAreaSize]; struct { - char dummy[sizeof(void *) - sizeof(ControlBlock)]; + ControlBlock control; + char shortString[MaximumShortStringDataAreaSize]; + }; + struct + { + ControlBlock controlReference; ReferenceLayout reference; }; }; -#pragma pack(pop) }; } // namespace Internal } // namespace Utils + +QT_WARNING_POP diff --git a/src/libs/utils/smallstringliteral.h b/src/libs/utils/smallstringliteral.h index c7f410d79ae..3ca6420c379 100644 --- a/src/libs/utils/smallstringliteral.h +++ b/src/libs/utils/smallstringliteral.h @@ -20,9 +20,10 @@ public: using const_reverse_iterator = std::reverse_iterator; using size_type = std::size_t; + constexpr BasicSmallStringLiteral() noexcept = default; + template - constexpr - BasicSmallStringLiteral(const char(&string)[ArraySize]) noexcept + constexpr BasicSmallStringLiteral(const char (&string)[ArraySize]) noexcept : m_data(string) { static_assert(ArraySize >= 1, "Invalid string literal! Length is zero!"); @@ -80,11 +81,7 @@ public: return m_data.control.isReadOnlyReference(); } - constexpr - operator SmallStringView() const - { - return SmallStringView(data(), size()); - } + constexpr operator SmallStringView() const noexcept { return SmallStringView(data(), size()); } private: BasicSmallStringLiteral(const Internal::StringDataLayout &data) noexcept diff --git a/src/libs/utils/smallstringview.h b/src/libs/utils/smallstringview.h index 982192c7a44..28f8b781501 100644 --- a/src/libs/utils/smallstringview.h +++ b/src/libs/utils/smallstringview.h @@ -3,6 +3,7 @@ #pragma once +#include "algorithm.h" #include "smallstringfwd.h" #include "smallstringiterator.h" @@ -76,11 +77,17 @@ public: return QString::fromUtf8(data(), int(size())); } - explicit operator QByteArray() const + explicit operator QByteArray() const { return QByteArray(data(), int(size())); } + + explicit operator QLatin1StringView() const noexcept { - return QByteArray(data(), int(size())); + return QLatin1StringView(data(), Utils::ssize(*this)); } + operator QUtf8StringView() const noexcept + { + return QUtf8StringView(data(), Utils::ssize(*this)); + } constexpr bool startsWith(SmallStringView subStringToSearch) const noexcept { if (size() >= subStringToSearch.size()) diff --git a/src/libs/utils/span.h b/src/libs/utils/span.h index d38dbe87728..b1fba814472 100644 --- a/src/libs/utils/span.h +++ b/src/libs/utils/span.h @@ -3,6 +3,8 @@ #pragma once +#include + #if __cplusplus >= 202002L #include @@ -13,6 +15,17 @@ using std::get; using std::span; } // namespace Utils #else -#define TCB_SPAN_NAMESPACE_NAME Utils -#include <3rdparty/span/span.hpp> +QT_WARNING_PUSH + +#if defined(Q_CC_MSVC) +#pragma system_header +#elif defined(Q_CC_GNU) || defined(Q_CC_CLANG) +#pragma GCC system_header +#endif +#include <3rdparty/span/span.hpp> +namespace Utils { +using namespace nonstd; +} + +QT_WARNING_POP #endif diff --git a/src/libs/utils/styleanimator.cpp b/src/libs/utils/styleanimator.cpp index 05973677323..02d829711d0 100644 --- a/src/libs/utils/styleanimator.cpp +++ b/src/libs/utils/styleanimator.cpp @@ -5,12 +5,17 @@ #include "algorithm.h" +#include +#include #include #include #include using namespace Utils; +static const qreal ScrollBarFadeOutDuration = 200.0; +static const qreal ScrollBarFadeOutDelay = 450.0; + Animation * StyleAnimator::widgetAnimation(const QWidget *widget) const { if (!widget) @@ -125,3 +130,187 @@ void StyleAnimator::startAnimation(Animation *t) if (animations.size() > 0 && !animationTimer.isActive()) animationTimer.start(35, this); } + +QStyleAnimation::QStyleAnimation(QObject *target) + : QAbstractAnimation(target) + , m_delay(0) + , m_duration(-1) + , m_startTime(QTime::currentTime()) + , m_fps(ThirtyFps) + , m_skip(0) +{} + +QStyleAnimation::~QStyleAnimation() {} + +QObject *QStyleAnimation::target() const +{ + return parent(); +} + +int QStyleAnimation::duration() const +{ + return m_duration; +} + +void QStyleAnimation::setDuration(int duration) +{ + m_duration = duration; +} + +int QStyleAnimation::delay() const +{ + return m_delay; +} + +void QStyleAnimation::setDelay(int delay) +{ + m_delay = delay; +} + +QTime QStyleAnimation::startTime() const +{ + return m_startTime; +} + +void QStyleAnimation::setStartTime(const QTime &time) +{ + m_startTime = time; +} + +QStyleAnimation::FrameRate QStyleAnimation::frameRate() const +{ + return m_fps; +} + +void QStyleAnimation::setFrameRate(FrameRate fps) +{ + m_fps = fps; +} + +void QStyleAnimation::updateTarget() +{ + QEvent event(QEvent::StyleAnimationUpdate); + event.setAccepted(false); + QCoreApplication::sendEvent(target(), &event); + if (!event.isAccepted()) + stop(); +} + +void QStyleAnimation::start() +{ + m_skip = 0; + QAbstractAnimation::start(DeleteWhenStopped); +} + +bool QStyleAnimation::isUpdateNeeded() const +{ + return currentTime() > m_delay; +} + +void QStyleAnimation::updateCurrentTime(int) +{ + if (++m_skip >= m_fps) { + m_skip = 0; + if (target() && isUpdateNeeded()) + updateTarget(); + } +} + +QNumberStyleAnimation::QNumberStyleAnimation(QObject *target) + : QStyleAnimation(target) + , m_start(0.0) + , m_end(1.0) + , m_prev(0.0) +{ + setDuration(250); +} + +qreal QNumberStyleAnimation::startValue() const +{ + return m_start; +} + +void QNumberStyleAnimation::setStartValue(qreal value) +{ + m_start = value; +} + +qreal QNumberStyleAnimation::endValue() const +{ + return m_end; +} + +void QNumberStyleAnimation::setEndValue(qreal value) +{ + m_end = value; +} + +qreal QNumberStyleAnimation::currentValue() const +{ + qreal step = qreal(currentTime() - delay()) / (duration() - delay()); + return m_start + qMax(qreal(0), step) * (m_end - m_start); +} + +bool QNumberStyleAnimation::isUpdateNeeded() const +{ + if (QStyleAnimation::isUpdateNeeded()) { + qreal current = currentValue(); + if (!qFuzzyCompare(m_prev, current)) { + m_prev = current; + return true; + } + } + return false; +} + +QScrollbarStyleAnimation::QScrollbarStyleAnimation(Mode mode, QObject *target) + : QNumberStyleAnimation(target) + , m_mode(mode) + , m_active(false) +{ + switch (mode) { + case Activating: + setDuration(ScrollBarFadeOutDuration); + setStartValue(0.0); + setEndValue(1.0); + break; + case Deactivating: + setDuration(ScrollBarFadeOutDelay + ScrollBarFadeOutDuration); + setDelay(ScrollBarFadeOutDelay); + setStartValue(1.0); + setEndValue(0.0); + break; + } +} + +QScrollbarStyleAnimation::Mode QScrollbarStyleAnimation::mode() const +{ + return m_mode; +} + +bool QScrollbarStyleAnimation::wasActive() const +{ + return m_active; +} + +bool QScrollbarStyleAnimation::wasAdjacent() const +{ + return m_adjacent; +} + +void QScrollbarStyleAnimation::setActive(bool active) +{ + m_active = active; +} + +void QScrollbarStyleAnimation::setAdjacent(bool adjacent) +{ + m_adjacent = adjacent; +} + +void QScrollbarStyleAnimation::updateCurrentTime(int time) +{ + QNumberStyleAnimation::updateCurrentTime(time); + if (m_mode == Deactivating && qFuzzyIsNull(currentValue())) + target()->setProperty("visible", false); +} diff --git a/src/libs/utils/styleanimator.h b/src/libs/utils/styleanimator.h index 76576be8208..6745890d148 100644 --- a/src/libs/utils/styleanimator.h +++ b/src/libs/utils/styleanimator.h @@ -5,6 +5,7 @@ #include "utils_global.h" +#include #include #include #include @@ -13,6 +14,7 @@ QT_BEGIN_NAMESPACE class QPainter; class QStyleOption; +class QTimerEvent; QT_END_NAMESPACE namespace Utils { @@ -74,4 +76,78 @@ private: QBasicTimer animationTimer; QList animations; }; -} + +class QTCREATOR_UTILS_EXPORT QStyleAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QStyleAnimation(QObject *target); + virtual ~QStyleAnimation(); + QObject *target() const; + int duration() const override; + void setDuration(int duration); + int delay() const; + void setDelay(int delay); + QTime startTime() const; + void setStartTime(const QTime &time); + enum FrameRate { DefaultFps, SixtyFps, ThirtyFps, TwentyFps, FifteenFps }; + FrameRate frameRate() const; + void setFrameRate(FrameRate fps); + void updateTarget(); +public Q_SLOTS: + void start(); + +protected: + virtual bool isUpdateNeeded() const; + virtual void updateCurrentTime(int time) override; + +private: + int m_delay; + int m_duration; + QTime m_startTime; + FrameRate m_fps; + int m_skip; +}; + +class QTCREATOR_UTILS_EXPORT QNumberStyleAnimation : public QStyleAnimation +{ + Q_OBJECT +public: + QNumberStyleAnimation(QObject *target); + qreal startValue() const; + void setStartValue(qreal value); + qreal endValue() const; + void setEndValue(qreal value); + qreal currentValue() const; + +protected: + bool isUpdateNeeded() const override; + +private: + qreal m_start; + qreal m_end; + mutable qreal m_prev; +}; + +class QTCREATOR_UTILS_EXPORT QScrollbarStyleAnimation : public QNumberStyleAnimation +{ + Q_OBJECT +public: + enum Mode { Activating, Deactivating }; + QScrollbarStyleAnimation(Mode mode, QObject *target); + Mode mode() const; + bool wasActive() const; + bool wasAdjacent() const; + void setActive(bool active); + void setAdjacent(bool adjacent); + +private slots: + void updateCurrentTime(int time) override; + +private: + Mode m_mode; + bool m_active; + bool m_adjacent; +}; + +} // namespace Utils diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 2604780a2d5..c3aa8842818 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -323,6 +323,28 @@ public: DSstateControlBackgroundColor_globalHover, DSplaceholderTextColor, DSplaceholderTextColorInteraction, + DSpopoutBackground, + DSpopoutControlBackground_idle, + DSpopoutControlBackground_hover, + DSpopoutControlBackground_globalHover, + DSpopoutControlBackground_interaction, + DSpopoutControlBackground_disabled, + DSpopoutPopupBackground, + DSpopoutControlBorder_idle, + DSpopoutControlBorder_hover, + DSpopoutControlBorder_interaction, + DSpopoutControlBorder_disabled, + DSpopoutButtonBackground_idle, + DSpopoutButtonBackground_hover, + DSpopoutButtonBackground_interaction, + DSpopoutButtonBackground_disabled, + DSpopoutButtonBorder_idle, + DSpopoutButtonBorder_hover, + DSpopoutButtonBorder_interaction, + DSpopoutButtonBorder_disabled, + DSscrollBarTrack, + DSscrollBarHandle, + DSscrollBarHandle_idle, /*Legacy QtDS*/ DSiconColor, @@ -346,8 +368,6 @@ public: DSsliderHandleHover, DSsliderHandleFocus, DSsliderHandleInteraction, - DSscrollBarTrack, - DSscrollBarHandle, DSsectionHeadBackground, DSstateDefaultHighlight, DSstateSeparatorColor, diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp index 844d48de3ad..3a0a9153769 100644 --- a/src/libs/utils/transientscroll.cpp +++ b/src/libs/utils/transientscroll.cpp @@ -12,6 +12,8 @@ using namespace Utils; static constexpr char transientScrollAreaSupportName[] = "transientScrollAreSupport"; +static constexpr char focusedPropertyName[] = "focused"; +static constexpr char skipChildPropertyName[] = "qds_transient_skipChildArea"; class Utils::ScrollAreaPrivate { @@ -19,14 +21,23 @@ public: ScrollAreaPrivate(QAbstractScrollArea *area) : area(area) { - verticalScrollBar = new ScrollBar(area); - area->setVerticalScrollBar(verticalScrollBar); + verticalScrollBar = dynamic_cast(area->verticalScrollBar()); + if (!verticalScrollBar) { + verticalScrollBar = new ScrollBar(area); + area->setVerticalScrollBar(verticalScrollBar); + } - horizontalScrollBar = new ScrollBar(area); - area->setHorizontalScrollBar(horizontalScrollBar); + horizontalScrollBar = dynamic_cast(area->horizontalScrollBar()); + if (!horizontalScrollBar) { + horizontalScrollBar = new ScrollBar(area); + area->setHorizontalScrollBar(horizontalScrollBar); + } - area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + if (area->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) + area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + if (area->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff) + area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } inline QRect scrollBarRect(ScrollBar *scrollBar) @@ -40,15 +51,30 @@ public: return rect.adjusted(0, mDiff, 0, mDiff); } } + inline QPointer adjacentScrollBar(QPointer scrollBar) + { + if (scrollBar == verticalScrollBar) + return horizontalScrollBar; + + if (scrollBar == horizontalScrollBar) + return verticalScrollBar; + + return {}; + } inline bool checkToFlashScroll(QPointer scrollBar, const QPoint &pos) { if (scrollBar.isNull()) return false; - if (!scrollBar->style()->styleHint( - QStyle::SH_ScrollBar_Transient, - nullptr, scrollBar)) + if (!scrollBar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, scrollBar)) + return false; + + Qt::ScrollBarPolicy policy = (scrollBar->orientation() == Qt::Horizontal) + ? area->horizontalScrollBarPolicy() + : area->verticalScrollBarPolicy(); + + if (policy == Qt::ScrollBarAlwaysOff) return false; if (scrollBarRect(scrollBar).contains(pos)) { @@ -59,16 +85,120 @@ public: return false; } + inline bool setAdjacentHovered(QObject *w, bool setHovered) + { + if (!w) + return false; + + QPointer scrollBar; + if (w == verticalScrollBar) + scrollBar = verticalScrollBar; + else if (w == horizontalScrollBar) + scrollBar = horizontalScrollBar; + + if (!scrollBar) + return false; + + QPointer adjacent = adjacentScrollBar(scrollBar); + if (!adjacent) + return false; + + return adjacent->setAdjacentHovered(setHovered); + } + + inline bool setAdjacentVisible(QObject *changedObject, bool setVisible) + { + if (!changedObject) + return false; + + QPointer scrollBar; + if (changedObject == verticalScrollBar) { + scrollBar = verticalScrollBar; + } else if (changedObject == horizontalScrollBar) { + scrollBar = horizontalScrollBar; + } else if (changedObject == area) { + if (setVisible && verticalScrollBar && horizontalScrollBar) { + bool anyChange = false; + anyChange |= verticalScrollBar->setAdjacentVisible(horizontalScrollBar->isVisible()); + anyChange |= horizontalScrollBar->setAdjacentVisible(verticalScrollBar->isVisible()); + return anyChange; + } + } + + if (!scrollBar) + return false; + + QPointer adjacent = adjacentScrollBar(scrollBar); + if (!adjacent) + return false; + + return adjacent->setAdjacentVisible(setVisible); + } + inline bool checkToFlashScroll(const QPoint &pos) { bool coversScroll = checkToFlashScroll(verticalScrollBar, pos); + if (!coversScroll) coversScroll |= checkToFlashScroll(horizontalScrollBar, pos); return coversScroll; } - inline void installViewPort(QObject *eventHandler) { + inline bool canSetTransientProperty(QPointer scrollBar) const + { + if (scrollBar.isNull()) + return false; + + if (!scrollBar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, scrollBar)) + return false; + + Qt::ScrollBarPolicy policy = (scrollBar->orientation() == Qt::Horizontal) + ? area->horizontalScrollBarPolicy() + : area->verticalScrollBarPolicy(); + + if (policy == Qt::ScrollBarAlwaysOff) + return false; + + return true; + } + + inline bool setFocus(QPointer scrollBar, const bool &focus) + { + if (!canSetTransientProperty(scrollBar)) + return false; + + return scrollBar->setFocused(focus); + } + + inline bool setFocus(const bool &focus) + { + bool flashChanged = false; + flashChanged |= setFocus(verticalScrollBar, focus); + flashChanged |= setFocus(horizontalScrollBar, focus); + + return flashChanged; + } + + inline bool setViewPortIntraction(QPointer scrollBar, const bool &hovered) + { + if (!canSetTransientProperty(scrollBar)) + return false; + + return scrollBar->setViewPortInteraction(hovered); + } + + inline bool setViewPortIntraction(const bool &hovered) + { + bool interactionChanged = false; + interactionChanged |= setViewPortIntraction(verticalScrollBar, hovered); + interactionChanged |= setViewPortIntraction(horizontalScrollBar, hovered); + + return interactionChanged; + } + + inline void installViewPort(QObject *eventHandler) + { QWidget *viewPort = area->viewport(); if (viewPort && viewPort != this->viewPort @@ -76,14 +206,30 @@ public: && (area->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff || area->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) { viewPort->installEventFilter(eventHandler); + + if (verticalScrollBar) + verticalScrollBar->installEventFilter(eventHandler); + + if (horizontalScrollBar) + horizontalScrollBar->installEventFilter(eventHandler); + this->viewPort = viewPort; + setViewPortIntraction(true); } } inline void uninstallViewPort(QObject *eventHandler) { if (viewPort) { viewPort->removeEventFilter(eventHandler); + + if (verticalScrollBar) + verticalScrollBar->removeEventFilter(eventHandler); + + if (horizontalScrollBar) + horizontalScrollBar->removeEventFilter(eventHandler); + this->viewPort = nullptr; + setViewPortIntraction(false); } } @@ -111,6 +257,13 @@ void TransientScrollAreaSupport::support(QAbstractScrollArea *scrollArea) ); } +void TransientScrollAreaSupport::supportWidget(QWidget *widget) +{ + for (QAbstractScrollArea *area : widget->findChildren()) { + support(area); + } +} + TransientScrollAreaSupport::~TransientScrollAreaSupport() { delete d; @@ -122,23 +275,65 @@ bool TransientScrollAreaSupport::eventFilter(QObject *watched, QEvent *event) case QEvent::Enter: { if (watched == d->area) d->installViewPort(this); - } - break; + } break; case QEvent::Leave: { if (watched == d->area) d->uninstallViewPort(this); - } - break; + } break; case QEvent::MouseMove: { - if (watched == d->viewPort){ + if (watched == d->viewPort) { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { if (d->checkToFlashScroll(mouseEvent->pos())) return true; } } - } - break; + } break; + case QEvent::HoverEnter: + case QEvent::HoverMove: { + QHoverEvent *hoverEvent = static_cast(event); + if (watched == d->horizontalScrollBar || watched == d->verticalScrollBar) { + if (hoverEvent) + d->setAdjacentHovered(watched, true); + } + } break; + case QEvent::HoverLeave: { + QHoverEvent *hoverEvent = static_cast(event); + if (watched == d->horizontalScrollBar || watched == d->verticalScrollBar) { + if (hoverEvent) + d->setAdjacentHovered(watched, false); + } + } break; + case QEvent::DynamicPropertyChange: { + if (watched == d->area) { + auto *pEvent = static_cast(event); + if (!pEvent || pEvent->propertyName() != focusedPropertyName) + break; + + bool focused = d->area->property(focusedPropertyName).toBool(); + d->setFocus(focused); + + if (!d->area->property(skipChildPropertyName).toBool() && d->area->viewport()) { + const QList scrollChildren + = d->area->viewport()->findChildren(); + for (QAbstractScrollArea *area : scrollChildren) { + area->setProperty(skipChildPropertyName, true); + area->setProperty(focusedPropertyName, focused); + area->setProperty(skipChildPropertyName, false); + } + } + } + } break; + case QEvent::Hide: + d->setAdjacentVisible(watched, false); + break; + case QEvent::Show: + d->setAdjacentVisible(watched, true); + break; + case QEvent::Resize: { + if (watched == d->area) + d->setAdjacentVisible(watched, true); + } break; default: break; } @@ -149,6 +344,12 @@ class Utils::ScrollBarPrivate { public: bool flashed = false; int flashTimer = 0; + bool focused = false; + bool viewPortIntraction = false; + bool adjacentHovered = false; + bool adjacentVisible = false; + bool isHandleUnderCursor = false; + bool isGrooveUnderCursor = false; }; ScrollBar::ScrollBar(QWidget *parent) @@ -162,34 +363,25 @@ ScrollBar::~ScrollBar() delete d; } -QSize ScrollBar::sizeHint() const -{ - QSize sh = QScrollBar::sizeHint(); - if (style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { - constexpr int thickness = 10; - if (orientation() == Qt::Horizontal) - sh.setHeight(thickness); - else - sh.setWidth(thickness); - } else { - constexpr int thickness = 12; - if (orientation() == Qt::Horizontal) - sh.setHeight(thickness); - else - sh.setWidth(thickness); - } - return sh; -} - void ScrollBar::flash() { - if (!d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { + if (!style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) + return; + + if (!isEnabled()) { + d->flashed = false; + hide(); + return; + } + + if (!d->flashed) { d->flashed = true; if (!isVisible()) show(); else update(); } + if (!d->flashTimer) d->flashTimer = startTimer(0); } @@ -197,14 +389,57 @@ void ScrollBar::flash() void ScrollBar::initStyleOption(QStyleOptionSlider *option) const { QScrollBar::initStyleOption(option); + if (style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { + if (d->flashed || d->focused || d->viewPortIntraction) + option->state |= QStyle::State_On; - if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) - option->state |= QStyle::State_On; + if (d->isGrooveUnderCursor || d->isHandleUnderCursor || d->adjacentHovered) + option->subControls |= QStyle::SC_ScrollBarGroove; + + option->styleObject->setProperty("adjacentScroll", d->adjacentHovered); + + if (d->isHandleUnderCursor) + option->activeSubControls |= QStyle::SC_ScrollBarSlider; + + if (d->adjacentVisible) { + int scrollExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, option, this); + if (option->orientation == Qt::Horizontal) + option->rect.adjust(0, 0, -scrollExtent, 0); + else + option->rect.adjust(0, 0, 0, -scrollExtent); + } + } } bool ScrollBar::event(QEvent *event) { switch (event->type()) { + case QEvent::HoverEnter: + case QEvent::HoverMove: { + QHoverEvent *hoverEvent = static_cast(event); + if (hoverEvent) { + QStyleOptionSlider option; + option.initFrom(this); + d->isHandleUnderCursor = style() + ->subControlRect(QStyle::CC_ScrollBar, + &option, + QStyle::SC_ScrollBarSlider, + this) + .contains(hoverEvent->pos()); + + d->isGrooveUnderCursor = !d->isHandleUnderCursor + && style() + ->subControlRect(QStyle::CC_ScrollBar, + &option, + QStyle::SC_ScrollBarGroove, + this) + .contains(hoverEvent->pos()); + } + } break; + case QEvent::HoverLeave: + d->isHandleUnderCursor = false; + d->isGrooveUnderCursor = false; + break; case QEvent::Timer: if (static_cast(event)->timerId() == d->flashTimer) { if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) { @@ -213,6 +448,7 @@ bool ScrollBar::event(QEvent *event) } killTimer(d->flashTimer); d->flashTimer = 0; + return true; } break; default: @@ -220,3 +456,99 @@ bool ScrollBar::event(QEvent *event) } return QScrollBar::event(event); } + +bool ScrollBar::setFocused(const bool &focused) +{ + if (d->focused == focused) + return false; + + d->focused = focused; + + if (d->focused) + flash(); + else + update(); + + return true; +} + +bool ScrollBar::setAdjacentVisible(const bool &visible) +{ + if (d->adjacentVisible == visible) + return false; + + d->adjacentVisible = visible; + update(); + return true; +} + +bool ScrollBar::setAdjacentHovered(const bool &hovered) +{ + if (d->adjacentHovered == hovered) + return false; + + d->adjacentHovered = hovered; + update(); + return true; +} + +bool ScrollBar::setViewPortInteraction(const bool &hovered) +{ + if (d->viewPortIntraction == hovered) + return false; + + d->viewPortIntraction = hovered; + + if (d->viewPortIntraction) + flash(); + else + update(); + + return true; +} + +void GlobalTransientSupport::support(QWidget *widget) +{ + if (!widget) + return; + + widget->installEventFilter(instance()); + QAbstractScrollArea *area = dynamic_cast(widget); + if (area) + TransientScrollAreaSupport::support(area); + + for (QWidget *childWidget : widget->findChildren(Qt::FindChildOption::FindDirectChildrenOnly)) + support(childWidget); +} + +GlobalTransientSupport::GlobalTransientSupport() + : QObject(nullptr) +{} + +GlobalTransientSupport *GlobalTransientSupport::instance() +{ + static GlobalTransientSupport *gVal = nullptr; + if (!gVal) + gVal = new GlobalTransientSupport; + + return gVal; +} + +bool GlobalTransientSupport::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::ChildAdded: { + QChildEvent *childEvent = static_cast(event); + + if (!childEvent || !childEvent->child() || !childEvent->child()->isWidgetType()) + break; + + QWidget *widget = dynamic_cast(childEvent->child()); + if (widget) + support(widget); + } + default: + break; + } + return QObject::eventFilter(watched, event); +} diff --git a/src/libs/utils/transientscroll.h b/src/libs/utils/transientscroll.h index 98140764499..b10f77541d6 100644 --- a/src/libs/utils/transientscroll.h +++ b/src/libs/utils/transientscroll.h @@ -20,6 +20,7 @@ class QTCREATOR_UTILS_EXPORT TransientScrollAreaSupport : public QObject { public: static void support(QAbstractScrollArea *scrollArea); + static void supportWidget(QWidget *widget); virtual ~TransientScrollAreaSupport(); protected: @@ -33,20 +34,38 @@ private: class QTCREATOR_UTILS_EXPORT ScrollBar : public QScrollBar { + + friend class ScrollAreaPrivate; + public: explicit ScrollBar(QWidget *parent = nullptr); virtual ~ScrollBar(); - QSize sizeHint() const override; - virtual void flash(); + bool setFocused(const bool &focused); + protected: - virtual void initStyleOption(QStyleOptionSlider *option) const override; + void initStyleOption(QStyleOptionSlider *option) const override; bool event(QEvent *event) override; private: + bool setAdjacentVisible(const bool &visible); + bool setAdjacentHovered(const bool &hovered); + bool setViewPortInteraction(const bool &hovered); + ScrollBarPrivate *d = nullptr; }; +class QTCREATOR_UTILS_EXPORT GlobalTransientSupport : public QObject +{ + Q_OBJECT +public: + static void support(QWidget *widget); + +private: + GlobalTransientSupport(); + static GlobalTransientSupport *instance(); + virtual bool eventFilter(QObject *watched, QEvent *event) override; +}; } diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 1a55c74fef2..801a7248a0a 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverviewer.cpp b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverviewer.cpp index a075904c529..dbe371eeb7d 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverviewer.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverviewer.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h index 08088613428..92ce69276d0 100644 --- a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h +++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h @@ -2,11 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once -#if defined(__linux) -/* Needed for glibc < 2.12 */ -// NOLINTNEXTLINE(bugprone-reserved-identifier) -# define _XOPEN_SOURCE 600 -#endif #if !defined(_POSIX_C_SOURCE) && !defined(_WIN32) && !defined(__sun) && \ !defined(__OpenBSD__) /* POSIX APIs are needed */ diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt index 679102c922b..f1989cec6c7 100644 --- a/src/plugins/cmakeprojectmanager/CMakeLists.txt +++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt @@ -2,6 +2,7 @@ add_qtc_plugin(CMakeProjectManager PLUGIN_CLASS CMakeProjectPlugin DEPENDS QmlJS PLUGIN_DEPENDS Core CppEditor ProjectExplorer TextEditor QtSupport + SYSTEM_INCLUDES 3dparty/cmake SOURCES builddirparameters.cpp builddirparameters.h cmake_global.h diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 4ca9764efa8..f1a5d3cfbeb 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -674,7 +674,7 @@ FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const const QString generatedFileName = "ui_" + sourceFile.completeBaseName() + ".h"; auto targetNode = this->project()->nodeForFilePath(sourceFile); - while (!dynamic_cast(targetNode)) + while (targetNode && !dynamic_cast(targetNode)) targetNode = targetNode->parentFolderNode(); FilePaths generatedFilePaths; diff --git a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp index f3b1e20dd21..47a857b26af 100644 --- a/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp +++ b/src/plugins/coreplugin/find/highlightscrollbarcontroller.cpp @@ -31,11 +31,10 @@ class HighlightScrollBarOverlay : public QWidget public: HighlightScrollBarOverlay(HighlightScrollBarController *scrollBarController) : QWidget(scrollBarController->scrollArea()) - , m_scrollBar(scrollBarController->scrollBar()) , m_highlightController(scrollBarController) { setAttribute(Qt::WA_TransparentForMouseEvents); - m_scrollBar->parentWidget()->installEventFilter(this); + scrollBar()->parentWidget()->installEventFilter(this); doResize(); doMove(); show(); @@ -43,12 +42,12 @@ public: void doResize() { - resize(m_scrollBar->size()); + resize(scrollBar()->size()); } void doMove() { - move(parentWidget()->mapFromGlobal(m_scrollBar->mapToGlobal(m_scrollBar->pos()))); + move(parentWidget()->mapFromGlobal(scrollBar()->mapToGlobal(scrollBar()->pos()))); } void scheduleUpdate(); @@ -71,7 +70,7 @@ private: // line start to line end QMap>> m_highlightCache; - QScrollBar *m_scrollBar; + inline QScrollBar *scrollBar() const { return m_highlightController->scrollBar(); } HighlightScrollBarController *m_highlightController; bool m_isCacheUpdateScheduled = true; }; @@ -115,8 +114,8 @@ void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent) gRect.width() + marginH, gRect.height() - hRect.height() + gRect.y() - hRect.y()); - const int aboveValue = m_scrollBar->value(); - const int belowValue = m_scrollBar->maximum() - m_scrollBar->value(); + const int aboveValue = scrollBar()->value(); + const int belowValue = scrollBar()->maximum() - scrollBar()->value(); const int sizeDocAbove = int(aboveValue * m_highlightController->lineHeight()); const int sizeDocBelow = int(belowValue * m_highlightController->lineHeight()); const int sizeDocVisible = int(m_highlightController->visibleRange()); @@ -303,14 +302,14 @@ void HighlightScrollBarOverlay::updateCache() QRect HighlightScrollBarOverlay::overlayRect() const { - QStyleOptionSlider opt = qt_qscrollbarStyleOption(m_scrollBar); - return m_scrollBar->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, m_scrollBar); + QStyleOptionSlider opt = qt_qscrollbarStyleOption(scrollBar()); + return scrollBar()->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, scrollBar()); } QRect HighlightScrollBarOverlay::handleRect() const { - QStyleOptionSlider opt = qt_qscrollbarStyleOption(m_scrollBar); - return m_scrollBar->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, m_scrollBar); + QStyleOptionSlider opt = qt_qscrollbarStyleOption(scrollBar()); + return scrollBar()->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, scrollBar()); } diff --git a/src/plugins/coreplugin/themechooser.cpp b/src/plugins/coreplugin/themechooser.cpp index 4839a129130..fe569c1bc4a 100644 --- a/src/plugins/coreplugin/themechooser.cpp +++ b/src/plugins/coreplugin/themechooser.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp index feb24cddb1c..282020349e0 100644 --- a/src/plugins/insight/insightmodel.cpp +++ b/src/plugins/insight/insightmodel.cpp @@ -605,8 +605,11 @@ void InsightModel::parseDefaultConfig() const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); if (target) { const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); - m_defaultConfig = readJSON(qtVersion->dataPath().toString() + "/" + dataFolder + "/" - + insightConfFile); + + if (qtVersion) { + m_defaultConfig = readJSON(qtVersion->dataPath().toString() + "/" + dataFolder + "/" + + insightConfFile); + } } } diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 5c18b017927..406ddcb76df 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/projectexplorer/userfileaccessor.h b/src/plugins/projectexplorer/userfileaccessor.h index 3c37da84b22..4e48a223789 100644 --- a/src/plugins/projectexplorer/userfileaccessor.h +++ b/src/plugins/projectexplorer/userfileaccessor.h @@ -6,6 +6,8 @@ #include #include +#include +#include namespace ProjectExplorer { diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 405ad5c00a3..cc47700ee03 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -13,7 +13,7 @@ add_compile_options("$<$:-Wno-error=maybe-uninitial add_qtc_library(QmlDesignerUtils STATIC DEPENDS - Qt::Gui Utils Qt::QmlPrivate + Qt::Gui Utils Qt::QmlPrivate Core DEFINES QMLDESIGNERUTILS_LIBRARY PUBLIC_DEFINES $<$:QMLDESIGNER_STATIC_LIBRARY> @@ -21,6 +21,7 @@ add_qtc_library(QmlDesignerUtils STATIC SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES asset.cpp asset.h + designeralgorithm.h filedownloader.cpp filedownloader.h multifiledownloader.cpp multifiledownloader.h fileextractor.cpp fileextractor.h @@ -36,7 +37,7 @@ extend_qtc_library(QmlDesignerUtils ) add_qtc_library(QmlDesignerCore STATIC - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET QmlDesignerBase AND TARGET Qt6::QmlPrivate AND TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate + CONDITION TARGET QmlDesignerBase AND TARGET Qt6::QmlPrivate AND TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate EXCLUDE_FROM_INSTALL PROPERTIES SKIP_AUTOUIC ON DEPENDS @@ -175,6 +176,7 @@ extend_qtc_library(QmlDesignerCore meshimagecachecollector.cpp meshimagecachecollector.h synchronousimagecache.cpp + taskqueue.h textureimagecachecollector.cpp textureimagecachecollector.h timestampprovider.cpp @@ -427,7 +429,7 @@ extend_qtc_library(QmlDesignerCore add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg + CONDITION TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg PLUGIN_DEPENDS Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager QtSupport @@ -444,6 +446,7 @@ add_qtc_plugin(QmlDesigner INCLUDES ${CMAKE_CURRENT_LIST_DIR}/components ${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary + ${CMAKE_CURRENT_LIST_DIR}/components/collectioneditor ${CMAKE_CURRENT_LIST_DIR}/components/debugview ${CMAKE_CURRENT_LIST_DIR}/components/edit3d ${CMAKE_CURRENT_LIST_DIR}/components/formeditor @@ -456,6 +459,7 @@ add_qtc_plugin(QmlDesigner ${CMAKE_CURRENT_LIST_DIR}/components/propertyeditor ${CMAKE_CURRENT_LIST_DIR}/components/stateseditor ${CMAKE_CURRENT_LIST_DIR}/components/texteditor + ${CMAKE_CURRENT_LIST_DIR}/components/effectmaker PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/designercore #can not be a public dependency -> EXCLUDE_FROM_INSTALL in QmlDesignerCore @@ -482,7 +486,7 @@ add_qtc_plugin(QmlDesigner editorproxy.cpp editorproxy.h EXPLICIT_MOC components/propertyeditor/propertyeditorvalue.h - components/connectioneditor/connectionviewwidget.h + qmldesignerplugin.h EXTRA_TRANSLATIONS "${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner" PROPERTIES @@ -595,9 +599,10 @@ extend_qtc_plugin(QmlDesigner edit3dwidget.cpp edit3dwidget.h edit3dcanvas.cpp edit3dcanvas.h edit3dactions.cpp edit3dactions.h - edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h + edit3dtoolbarmenu.cpp edit3dtoolbarmenu.h backgroundcolorselection.cpp backgroundcolorselection.h bakelights.cpp bakelights.h + snapconfiguration.cpp snapconfiguration.h bakelightsdatamodel.cpp bakelightsdatamodel.h bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h edit3d.qrc @@ -704,6 +709,24 @@ extend_qtc_plugin(QmlDesigner assetslibraryiconprovider.cpp assetslibraryiconprovider.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/effectmaker + SOURCES + effectmakerwidget.cpp effectmakerwidget.h + effectmakerview.cpp effectmakerview.h + effectmakermodel.cpp effectmakermodel.h + effectmakernodesmodel.cpp effectmakernodesmodel.h + effectmakeruniformsmodel.cpp effectmakeruniformsmodel.h + effectnode.cpp effectnode.h + effectnodescategory.cpp effectnodescategory.h + compositionnode.cpp compositionnode.h + uniform.cpp uniform.h + effectutils.cpp effectutils.h + effectmakercontextobject.cpp effectmakercontextobject.h + shaderfeatures.cpp shaderfeatures.h + syntaxhighlighterdata.cpp syntaxhighlighterdata.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/navigator SOURCES @@ -779,6 +802,15 @@ extend_qtc_plugin(QmlDesigner materialeditor.qrc ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/collectioneditor + SOURCES + collectionmodel.cpp collectionmodel.h + collectionview.cpp collectionview.h + collectionwidget.cpp collectionwidget.h + singlecollectionmodel.cpp singlecollectionmodel.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/textureeditor SOURCES @@ -917,13 +949,17 @@ extend_qtc_plugin(QmlDesigner addnewbackenddialog.cpp addnewbackenddialog.h addnewbackenddialog.ui backendmodel.cpp backendmodel.h bindingmodel.cpp bindingmodel.h + bindingmodelitem.cpp bindingmodelitem.h connectioneditor.qrc + connectioneditorevaluator.cpp connectioneditorevaluator.h + connectioneditorstatements.cpp connectioneditorstatements.h connectionmodel.cpp connectionmodel.h connectionview.cpp connectionview.h - connectionviewwidget.cpp connectionviewwidget.h connectionviewwidget.ui - delegates.cpp delegates.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h + dynamicpropertiesitem.cpp dynamicpropertiesitem.h + connectioneditorutils.cpp connectioneditorutils.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h + propertytreemodel.cpp propertytreemodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index 64845068970..cef86f26968 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 9a8a611c664..48d10a34d20 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -151,6 +151,8 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon // init the first load of the QML UI elements reloadQmlSource(); + + setFocusProxy(m_assetsWidget->quickWidget()); } void AssetsLibraryWidget::contextHelp(const Core::IContext::HelpCallback &callback) const diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 10e8181d8d5..76f5cc7e211 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -190,7 +190,7 @@ bool compareTypes(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var"); - return isType(variantTypes, target) || isType(variantTypes, source) + return isType(variantTypes, target) || isType(variantTypes, source) || target == source || targetType == sourceType || isType(target, source, "double", "real", "int") || isType(target, source, "QColor", "color") || isType(target, source, "QString", "string"); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp new file mode 100644 index 00000000000..18e651fc2e2 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp @@ -0,0 +1,262 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "collectionmodel.h" + +#include "abstractview.h" +#include "variantproperty.h" + +#include + +namespace QmlDesigner { +CollectionModel::CollectionModel() {} + +int CollectionModel::rowCount(const QModelIndex &) const +{ + return m_collections.size(); +} + +QVariant CollectionModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid(), return {}); + + const ModelNode *collection = &m_collections.at(index.row()); + + switch (role) { + case IdRole: + return collection->id(); + case NameRole: + return collection->variantProperty("objectName").value(); + case SelectedRole: + return index.row() == m_selectedIndex; + } + + return {}; +} + +bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + ModelNode collection = m_collections.at(index.row()); + switch (role) { + case IdRole: { + if (collection.id() == value) + return false; + + bool duplicatedId = Utils::anyOf(std::as_const(m_collections), + [&collection, &value](const ModelNode &otherCollection) { + return (otherCollection.id() == value + && otherCollection != collection); + }); + if (duplicatedId) + return false; + + collection.setIdWithRefactoring(value.toString()); + } break; + case Qt::DisplayRole: + case NameRole: { + auto collectionName = collection.variantProperty("objectName"); + if (collectionName.value() == value) + return false; + + collectionName.setValue(value.toString()); + } break; + case SelectedRole: { + if (value.toBool() != index.data(SelectedRole).toBool()) + setSelectedIndex(value.toBool() ? index.row() : -1); + else + return false; + } break; + default: + return false; + } + + return true; +} + +bool CollectionModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) +{ + const int rowMax = std::min(row + count, rowCount()); + + if (row >= rowMax || row < 0) + return false; + + AbstractView *view = m_collections.at(row).view(); + if (!view) + return false; + + count = rowMax - row; + + bool selectionUpdateNeeded = m_selectedIndex >= row && m_selectedIndex < rowMax; + + // It's better to remove the group of nodes here because of the performance issue for the list, + // and update issue for the view + beginRemoveRows({}, row, rowMax - 1); + + view->executeInTransaction(Q_FUNC_INFO, [row, count, this]() { + for (ModelNode node : Utils::span(m_collections).subspan(row, count)) { + m_collectionsIndexHash.remove(node.internalId()); + node.destroy(); + } + }); + + m_collections.remove(row, count); + + int idx = row; + for (const ModelNode &node : Utils::span(m_collections).subspan(row)) + m_collectionsIndexHash.insert(node.internalId(), ++idx); + + endRemoveRows(); + + if (selectionUpdateNeeded) + updateSelectedCollection(); + + updateEmpty(); + return true; +} + +QHash CollectionModel::roleNames() const +{ + static QHash roles; + if (roles.isEmpty()) { + roles.insert(Super::roleNames()); + roles.insert({ + {IdRole, "collectionId"}, + {NameRole, "collectionName"}, + {SelectedRole, "collectionIsSelected"}, + }); + } + return roles; +} + +void CollectionModel::setCollections(const ModelNodes &collections) +{ + beginResetModel(); + bool wasEmpty = isEmpty(); + m_collections = collections; + m_collectionsIndexHash.clear(); + int i = 0; + for (const ModelNode &collection : collections) + m_collectionsIndexHash.insert(collection.internalId(), i++); + + if (wasEmpty != isEmpty()) + emit isEmptyChanged(isEmpty()); + + endResetModel(); + + updateSelectedCollection(true); +} + +void CollectionModel::removeCollection(const ModelNode &node) +{ + int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); + if (nodePlace < 0) + return; + + removeRow(nodePlace); +} + +int CollectionModel::collectionIndex(const ModelNode &node) const +{ + return m_collectionsIndexHash.value(node.internalId(), -1); +} + +void CollectionModel::selectCollection(const ModelNode &node) +{ + int nodePlace = m_collectionsIndexHash.value(node.internalId(), -1); + if (nodePlace < 0) + return; + + selectCollectionIndex(nodePlace, true); +} + +QmlDesigner::ModelNode CollectionModel::collectionNodeAt(int idx) +{ + QModelIndex data = index(idx); + if (!data.isValid()) + return {}; + + return m_collections.at(idx); +} + +bool CollectionModel::isEmpty() const +{ + return m_collections.isEmpty(); +} + +void CollectionModel::selectCollectionIndex(int idx, bool selectAtLeastOne) +{ + int collectionCount = m_collections.size(); + int prefferedIndex = -1; + if (collectionCount) { + if (selectAtLeastOne) + prefferedIndex = std::max(0, std::min(idx, collectionCount - 1)); + else if (idx > -1 && idx < collectionCount) + prefferedIndex = idx; + } + + setSelectedIndex(prefferedIndex); +} + +void CollectionModel::deselect() +{ + setSelectedIndex(-1); +} + +void CollectionModel::updateSelectedCollection(bool selectAtLeastOne) +{ + int idx = m_selectedIndex; + m_selectedIndex = -1; + selectCollectionIndex(idx, selectAtLeastOne); +} + +void CollectionModel::updateNodeName(const ModelNode &node) +{ + QModelIndex index = indexOfNode(node); + emit dataChanged(index, index, {NameRole, Qt::DisplayRole}); +} + +void CollectionModel::updateNodeId(const ModelNode &node) +{ + QModelIndex index = indexOfNode(node); + emit dataChanged(index, index, {IdRole}); +} + +void CollectionModel::setSelectedIndex(int idx) +{ + idx = (idx > -1 && idx < m_collections.count()) ? idx : -1; + + if (m_selectedIndex != idx) { + QModelIndex previousIndex = index(m_selectedIndex); + QModelIndex newIndex = index(idx); + + m_selectedIndex = idx; + + if (previousIndex.isValid()) + emit dataChanged(previousIndex, previousIndex, {SelectedRole}); + + if (newIndex.isValid()) + emit dataChanged(newIndex, newIndex, {SelectedRole}); + + emit selectedIndexChanged(idx); + } +} + +void CollectionModel::updateEmpty() +{ + bool isEmptyNow = isEmpty(); + if (m_isEmpty != isEmptyNow) { + m_isEmpty = isEmptyNow; + emit isEmptyChanged(m_isEmpty); + + if (m_isEmpty) + setSelectedIndex(-1); + } +} + +QModelIndex CollectionModel::indexOfNode(const ModelNode &node) const +{ + return index(m_collectionsIndexHash.value(node.internalId(), -1)); +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h new file mode 100644 index 00000000000..17831732543 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h @@ -0,0 +1,72 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "modelnode.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QJsonArray; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class CollectionModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + +public: + enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; + + explicit CollectionModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex &index, + const QVariant &value, + int role = Qt::EditRole) override; + + Q_INVOKABLE virtual bool removeRows(int row, + int count = 1, + const QModelIndex &parent = QModelIndex()) override; + + virtual QHash roleNames() const override; + + void setCollections(const ModelNodes &collections); + void removeCollection(const ModelNode &node); + int collectionIndex(const ModelNode &node) const; + void selectCollection(const ModelNode &node); + + ModelNode collectionNodeAt(int idx); + + Q_INVOKABLE bool isEmpty() const; + Q_INVOKABLE void selectCollectionIndex(int idx, bool selectAtLeastOne = false); + Q_INVOKABLE void deselect(); + Q_INVOKABLE void updateSelectedCollection(bool selectAtLeastOne = false); + void updateNodeName(const ModelNode &node); + void updateNodeId(const ModelNode &node); + +signals: + void selectedIndexChanged(int idx); + void renameCollectionTriggered(const QmlDesigner::ModelNode &collection, const QString &newName); + void addNewCollectionTriggered(); + void isEmptyChanged(bool); + +private: + void setSelectedIndex(int idx); + void updateEmpty(); + + using Super = QAbstractListModel; + + QModelIndex indexOfNode(const ModelNode &node) const; + ModelNodes m_collections; + QHash m_collectionsIndexHash; // internalId -> index + int m_selectedIndex = -1; + bool m_isEmpty = true; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp new file mode 100644 index 00000000000..aaf2605b64f --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -0,0 +1,605 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "collectionview.h" +#include "collectionmodel.h" +#include "collectionwidget.h" +#include "designmodecontext.h" +#include "nodelistproperty.h" +#include "nodemetainfo.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.h" +#include "singlecollectionmodel.h" +#include "variantproperty.h" + +#include +#include +#include + +#include +#include +#include + +namespace { +using Data = std::variant; +using DataRecord = QMap; + +struct DataHeader +{ + enum class Type { Unknown, Bool, Numeric, String, DateTime }; + Type type; + QString name; +}; + +using DataHeaderMap = QMap; // Lowercase Name - Header Data + +inline constexpr QStringView BoolDataType{u"Bool"}; +inline constexpr QStringView NumberDataType{u"Number"}; +inline constexpr QStringView StringDataType{u"String"}; +inline constexpr QStringView DateTimeDataType{u"Date/Time"}; + +QString removeSpaces(QString string) +{ + string.replace(" ", "_"); + string.replace("-", "_"); + return string; +} + +DataHeader getDataType(const QString &type, const QString &name) +{ + static const QMap typeMap = { + {BoolDataType.toString().toLower(), DataHeader::Type::Bool}, + {NumberDataType.toString().toLower(), DataHeader::Type::Numeric}, + {StringDataType.toString().toLower(), DataHeader::Type::String}, + {DateTimeDataType.toString().toLower(), DataHeader::Type::DateTime}}; + if (name.isEmpty()) + return {}; + + if (type.isEmpty()) + return {DataHeader::Type::String, removeSpaces(name)}; + + return {typeMap.value(type.toLower(), DataHeader::Type::Unknown), removeSpaces(name)}; +} + +struct JsonDocumentError : public std::exception +{ + enum Error { + InvalidDocumentType, + InvalidCollectionName, + InvalidCollectionId, + InvalidCollectionObject, + InvalidArrayPosition, + InvalidLiteralType, + InvalidCollectionHeader, + IsNotJsonArray, + CollectionHeaderNotFound + }; + + const Error error; + + JsonDocumentError(Error error) + : error(error) + {} + + const char *what() const noexcept override + { + switch (error) { + case InvalidDocumentType: + return "Current JSON document contains errors."; + case InvalidCollectionName: + return "Invalid collection name."; + case InvalidCollectionId: + return "Invalid collection Id."; + case InvalidCollectionObject: + return "A collection should be a json object."; + case InvalidArrayPosition: + return "Arrays are not supported inside the collection."; + case InvalidLiteralType: + return "Invalid literal type for collection items"; + case InvalidCollectionHeader: + return "Invalid Collection Header"; + case IsNotJsonArray: + return "Json file should be an array"; + case CollectionHeaderNotFound: + return "Collection Header not found"; + default: + return "Unknown Json Error"; + } + } +}; + +struct CsvDocumentError : public std::exception +{ + enum Error { + HeaderNotFound, + DataNotFound, + }; + + const Error error; + + CsvDocumentError(Error error) + : error(error) + {} + + const char *what() const noexcept override + { + switch (error) { + case HeaderNotFound: + return "CSV Header not found"; + case DataNotFound: + return "CSV data not found"; + default: + return "Unknown CSV Error"; + } + } +}; + +Data getLiteralDataValue(const QVariant &value, const DataHeader &header, bool *typeWarningCheck = nullptr) +{ + if (header.type == DataHeader::Type::Bool) + return value.toBool(); + + if (header.type == DataHeader::Type::Numeric) + return value.toDouble(); + + if (header.type == DataHeader::Type::String) + return value.toString(); + + if (header.type == DataHeader::Type::DateTime) { + QDateTime dateTimeStr = QDateTime::fromString(value.toString()); + if (dateTimeStr.isValid()) + return dateTimeStr; + } + + if (typeWarningCheck) + *typeWarningCheck = true; + + return value.toString(); +} + +void loadJsonHeaders(QList &collectionHeaders, + DataHeaderMap &headerDataMap, + const QJsonObject &collectionJsonObject) +{ + const QJsonArray collectionHeader = collectionJsonObject.value("headers").toArray(); + for (const QJsonValue &headerValue : collectionHeader) { + const QJsonObject headerJsonObject = headerValue.toObject(); + DataHeader dataHeader = getDataType(headerJsonObject.value("type").toString(), + headerJsonObject.value("name").toString()); + + if (dataHeader.type == DataHeader::Type::Unknown) + throw JsonDocumentError{JsonDocumentError::InvalidCollectionHeader}; + + collectionHeaders.append(dataHeader); + headerDataMap.insert(dataHeader.name.toLower(), dataHeader); + } + + if (collectionHeaders.isEmpty()) + throw JsonDocumentError{JsonDocumentError::CollectionHeaderNotFound}; +} + +void loadJsonRecords(QList &collectionItems, + DataHeaderMap &headerDataMap, + const QJsonObject &collectionJsonObject) +{ + auto addItemFromValue = [&headerDataMap, &collectionItems](const QJsonValue &jsonValue) { + const QVariantMap dataMap = jsonValue.toObject().toVariantMap(); + DataRecord recordData; + for (const auto &dataPair : dataMap.asKeyValueRange()) { + const DataHeader correspondingHeader = headerDataMap.value(removeSpaces( + dataPair.first.toLower()), + {}); + + const QString &fieldName = correspondingHeader.name; + if (fieldName.size()) + recordData.insert(fieldName, + getLiteralDataValue(dataPair.second, correspondingHeader)); + } + if (!recordData.isEmpty()) + collectionItems.append(recordData); + }; + + const QJsonValue jsonDataValue = collectionJsonObject.value("data"); + if (jsonDataValue.isObject()) { + addItemFromValue(jsonDataValue); + } else if (jsonDataValue.isArray()) { + const QJsonArray jsonDataArray = jsonDataValue.toArray(); + for (const QJsonValue &jsonItem : jsonDataArray) { + if (jsonItem.isObject()) + addItemFromValue(jsonItem); + } + } +} + +inline bool isCollectionLib(const QmlDesigner::ModelNode &node) +{ + return node.parentProperty().parentModelNode().isRootNode() + && node.id() == QmlDesigner::Constants::COLLECTION_LIB_ID; +} + +inline bool isListModel(const QmlDesigner::ModelNode &node) +{ + return node.metaInfo().isQtQuickListModel(); +} + +inline bool isListElement(const QmlDesigner::ModelNode &node) +{ + return node.metaInfo().isQtQuickListElement(); +} + +inline bool isCollection(const QmlDesigner::ModelNode &node) +{ + return isCollectionLib(node.parentProperty().parentModelNode()) && isListModel(node); +} + +inline bool isCollectionElement(const QmlDesigner::ModelNode &node) +{ + return isListElement(node) && isCollection(node.parentProperty().parentModelNode()); +} + +} // namespace + +namespace QmlDesigner { + +struct Collection +{ + QString name; + QString id; + QList headers; + QList items; +}; + +CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) + : AbstractView(externalDependencies) +{} + +bool CollectionView::loadJson(const QByteArray &data) +{ + try { + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(data, &parseError); + if (parseError.error != QJsonParseError::NoError) + throw JsonDocumentError{JsonDocumentError::InvalidDocumentType}; + + QList collections; + if (document.isArray()) { + const QJsonArray collectionsJsonArray = document.array(); + + for (const QJsonValue &collectionJson : collectionsJsonArray) { + Collection collection; + if (!collectionJson.isObject()) + throw JsonDocumentError{JsonDocumentError::InvalidCollectionObject}; + + QJsonObject collectionJsonObject = collectionJson.toObject(); + + const QString &collectionName = collectionJsonObject.value(u"name").toString(); + if (!collectionName.size()) + throw JsonDocumentError{JsonDocumentError::InvalidCollectionName}; + + const QString &collectionId = collectionJsonObject.value(u"id").toString(); + if (!collectionId.size()) + throw JsonDocumentError{JsonDocumentError::InvalidCollectionId}; + + DataHeaderMap headerDataMap; + + loadJsonHeaders(collection.headers, headerDataMap, collectionJsonObject); + loadJsonRecords(collection.items, headerDataMap, collectionJsonObject); + + if (collection.items.count()) + collections.append(collection); + } + } else { + throw JsonDocumentError{JsonDocumentError::InvalidDocumentType}; + } + + addLoadedModel(collections); + } catch (const std::exception &error) { + m_widget->warn("Json Import Problem", QString::fromLatin1(error.what())); + return false; + } + + return true; +} + +bool CollectionView::loadCsv(const QString &collectionName, const QByteArray &data) +{ + QTextStream stream(data); + Collection collection; + collection.name = collectionName; + + try { + if (!stream.atEnd()) { + const QStringList recordData = stream.readLine().split(','); + for (const QString &name : recordData) + collection.headers.append(getDataType({}, name)); + } + if (collection.headers.isEmpty()) + throw CsvDocumentError{CsvDocumentError::HeaderNotFound}; + + while (!stream.atEnd()) { + const QStringList recordDataList = stream.readLine().split(','); + DataRecord recordData; + int column = -1; + for (const QString &cellData : recordDataList) { + if (++column == collection.headers.size()) + break; + recordData.insert(collection.headers.at(column).name, cellData); + } + if (recordData.count()) + collection.items.append(recordData); + } + + if (collection.items.isEmpty()) + throw CsvDocumentError{CsvDocumentError::DataNotFound}; + + addLoadedModel({collection}); + } catch (const std::exception &error) { + m_widget->warn("Json Import Problem", QString::fromLatin1(error.what())); + return false; + } + + return true; +} + +bool CollectionView::hasWidget() const +{ + return true; +} + +QmlDesigner::WidgetInfo CollectionView::widgetInfo() +{ + if (m_widget.isNull()) { + m_widget = new CollectionWidget(this); + + auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); + Core::ICore::addContextObject(collectionEditorContext); + CollectionModel *collectionModel = m_widget->collectionModel().data(); + + connect(collectionModel, &CollectionModel::selectedIndexChanged, this, [&](int selectedIndex) { + m_widget->singleCollectionModel()->setCollection( + m_widget->collectionModel()->collectionNodeAt(selectedIndex)); + }); + } + + return createWidgetInfo(m_widget.data(), + "CollectionEditor", + WidgetInfo::LeftPane, + 0, + tr("Collection Editor"), + tr("Collection Editor view")); +} + +void CollectionView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + refreshModel(); +} + +void CollectionView::nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + [[maybe_unused]] PropertyChangeFlags propertyChange) +{ + if (!isListModel(node)) + return; + + ModelNode newParentNode = newPropertyParent.parentModelNode(); + ModelNode oldParentNode = oldPropertyParent.parentModelNode(); + bool added = isCollectionLib(newParentNode); + bool removed = isCollectionLib(oldParentNode); + + if (!added && !removed) + return; + + refreshModel(); + + if (isCollection(node)) + m_widget->collectionModel()->selectCollection(node); +} + +void CollectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) +{ + // removing the collections lib node + if (isCollectionLib(removedNode)) { + m_widget->collectionModel()->setCollections({}); + return; + } + + if (isCollection(removedNode)) + m_widget->collectionModel()->removeCollection(removedNode); +} + +void CollectionView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + [[maybe_unused]] PropertyChangeFlags propertyChange) +{ + if (parentProperty.parentModelNode().id() != Constants::COLLECTION_LIB_ID) + return; + + m_widget->collectionModel()->updateSelectedCollection(true); +} + +void CollectionView::variantPropertiesChanged(const QList &propertyList, + [[maybe_unused]] PropertyChangeFlags propertyChange) +{ + for (const VariantProperty &property : propertyList) { + ModelNode node(property.parentModelNode()); + if (isCollection(node)) { + if (property.name() == "objectName") + m_widget->collectionModel()->updateNodeName(node); + else if (property.name() == "id") + m_widget->collectionModel()->updateNodeId(node); + } + } +} + +void CollectionView::selectedNodesChanged(const QList &selectedNodeList, + [[maybe_unused]] const QList &lastSelectedNodeList) +{ + QList selectedCollections = Utils::filtered(selectedNodeList, &isCollection); + + // More than one collections are selected. So ignore them + if (selectedCollections.size() > 1) + return; + + if (selectedCollections.size() == 1) { // If exactly one collection is selected + m_widget->collectionModel()->selectCollection(selectedCollections.first()); + return; + } + + // If no collection is selected, check the elements + QList selectedElements = Utils::filtered(selectedNodeList, &isCollectionElement); + if (selectedElements.size()) { + const ModelNode parentElement = selectedElements.first().parentProperty().parentModelNode(); + bool haveSameParent = Utils::allOf(selectedElements, [&parentElement](const ModelNode &element) { + return element.parentProperty().parentModelNode() == parentElement; + }); + if (haveSameParent) + m_widget->collectionModel()->selectCollection(parentElement); + } +} + +void CollectionView::addNewCollection(const QString &name) +{ + executeInTransaction(__FUNCTION__, [&] { + ensureCollectionLibraryNode(); + ModelNode collectionLib = collectionLibraryNode(); + if (!collectionLib.isValid()) + return; + + NodeMetaInfo listModelMetaInfo = model()->qtQmlModelsListModelMetaInfo(); + ModelNode collectionNode = createModelNode(listModelMetaInfo.typeName(), + listModelMetaInfo.majorVersion(), + listModelMetaInfo.minorVersion()); + QString collectionName = name.isEmpty() ? "Collection" : name; + renameCollection(collectionNode, collectionName); + + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); + + auto headersProperty = collectionNode.variantProperty("headers"); + headersProperty.setDynamicTypeNameAndValue("string", {}); + + collectionLib.defaultNodeListProperty().reparentHere(collectionNode); + }); +} + +void CollectionView::refreshModel() +{ + if (!model()) + return; + + ModelNode collectionLib = modelNodeForId(Constants::COLLECTION_LIB_ID); + ModelNodes collections; + + if (collectionLib.isValid()) { + const QList collectionLibNodes = collectionLib.directSubModelNodes(); + for (const ModelNode &node : collectionLibNodes) { + if (isCollection(node)) + collections.append(node); + } + } + + m_widget->collectionModel()->setCollections(collections); +} + +ModelNode CollectionView::getNewCollectionNode(const Collection &collection) +{ + QTC_ASSERT(model(), return {}); + ModelNode collectionNode; + executeInTransaction(__FUNCTION__, [&] { + NodeMetaInfo listModelMetaInfo = model()->qtQmlModelsListModelMetaInfo(); + collectionNode = createModelNode(listModelMetaInfo.typeName(), + listModelMetaInfo.majorVersion(), + listModelMetaInfo.minorVersion()); + QString collectionName = collection.name.isEmpty() ? "Collection" : collection.name; + renameCollection(collectionNode, collectionName); + QStringList headers; + for (const DataHeader &header : collection.headers) + headers.append(header.name); + + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); + + auto headersProperty = collectionNode.variantProperty("headers"); + headersProperty.setDynamicTypeNameAndValue("string", headers.join(",")); + + NodeMetaInfo listElementMetaInfo = model()->qtQmlModelsListElementMetaInfo(); + for (const DataRecord &item : collection.items) { + ModelNode elementNode = createModelNode(listElementMetaInfo.typeName(), + listElementMetaInfo.majorVersion(), + listElementMetaInfo.minorVersion()); + for (const auto &headerMapElement : item.asKeyValueRange()) { + auto property = elementNode.variantProperty(headerMapElement.first.toLatin1()); + QVariant value = std::visit([](const auto &data) + -> QVariant { return QVariant::fromValue(data); }, + headerMapElement.second); + property.setValue(value); + } + collectionNode.defaultNodeListProperty().reparentHere(elementNode); + } + }); + return collectionNode; +} + +void CollectionView::addLoadedModel(const QList &newCollection) +{ + executeInTransaction(__FUNCTION__, [&] { + ensureCollectionLibraryNode(); + ModelNode collectionLib = collectionLibraryNode(); + if (!collectionLib.isValid()) + return; + + for (const Collection &collection : newCollection) { + ModelNode collectionNode = getNewCollectionNode(collection); + collectionLib.defaultNodeListProperty().reparentHere(collectionNode); + } + }); +} + +void CollectionView::renameCollection(ModelNode &collection, const QString &newName) +{ + QTC_ASSERT(collection.isValid(), return); + + QVariant objName = collection.variantProperty("objectName").value(); + if (objName.isValid() && objName.toString() == newName) + return; + + executeInTransaction(__FUNCTION__, [&] { + collection.setIdWithRefactoring(model()->generateIdFromName(newName, "collection")); + + VariantProperty objNameProp = collection.variantProperty("objectName"); + objNameProp.setValue(newName); + }); +} + +void CollectionView::ensureCollectionLibraryNode() +{ + ModelNode collectionLib = modelNodeForId(Constants::COLLECTION_LIB_ID); + if (collectionLib.isValid() + || (!rootModelNode().metaInfo().isQtQuick3DNode() + && !rootModelNode().metaInfo().isQtQuickItem())) { + return; + } + + executeInTransaction(__FUNCTION__, [&] { + // Create collection library node +#ifdef QDS_USE_PROJECTSTORAGE + TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; + matLib = createModelNode(nodeTypeName, -1, -1); +#else + auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() + ? model()->qtQuick3DNodeMetaInfo() + : model()->qtQuickItemMetaInfo(); + collectionLib = createModelNode(nodeType.typeName(), + nodeType.majorVersion(), + nodeType.minorVersion()); +#endif + collectionLib.setIdWithoutRefactoring(Constants::COLLECTION_LIB_ID); + rootModelNode().defaultNodeListProperty().reparentHere(collectionLib); + }); +} + +ModelNode CollectionView::collectionLibraryNode() +{ + return modelNodeForId(Constants::COLLECTION_LIB_ID); +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h new file mode 100644 index 00000000000..9372c74150f --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -0,0 +1,59 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include "abstractview.h" +#include "modelnode.h" + +#include + +namespace QmlDesigner { + +struct Collection; +class CollectionWidget; + +class CollectionView : public AbstractView +{ + Q_OBJECT + +public: + explicit CollectionView(ExternalDependenciesInterface &externalDependencies); + + bool loadJson(const QByteArray &data); + bool loadCsv(const QString &collectionName, const QByteArray &data); + + bool hasWidget() const override; + WidgetInfo widgetInfo() override; + + void modelAttached(Model *model) override; + + void nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + PropertyChangeFlags propertyChange) override; + + void nodeAboutToBeRemoved(const ModelNode &removedNode) override; + + void nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override; + + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + + void selectedNodesChanged(const QList &selectedNodeList, + const QList &lastSelectedNodeList) override; + + void addNewCollection(const QString &name); + +private: + void refreshModel(); + ModelNode getNewCollectionNode(const Collection &collection); + void addLoadedModel(const QList &newCollection); + void renameCollection(ModelNode &material, const QString &newName); + void ensureCollectionLibraryNode(); + ModelNode collectionLibraryNode(); + + QPointer m_widget; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp new file mode 100644 index 00000000000..7e65516143c --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -0,0 +1,171 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "collectionwidget.h" +#include "collectionmodel.h" +#include "collectionview.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.h" +#include "singlecollectionmodel.h" +#include "theme.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +QString collectionViewResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource"; +#endif + return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); +} +} // namespace + +namespace QmlDesigner { +CollectionWidget::CollectionWidget(CollectionView *view) + : QFrame() + , m_view(view) + , m_model(new CollectionModel) + , m_singleCollectionModel(new SingleCollectionModel) + , m_quickWidget(new StudioQuickWidget(this)) +{ + setWindowTitle(tr("Collection View", "Title of collection view widget")); + + Core::IContext *icontext = nullptr; + Core::Context context(Constants::C_QMLMATERIALBROWSER); + icontext = new Core::IContext(this); + icontext->setContext(context); + icontext->setWidget(this); + + m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports"); + m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); + + Theme::setupTheme(m_quickWidget->engine()); + m_quickWidget->quickWidget()->installEventFilter(this); + + auto layout = new QVBoxLayout(this); + layout->setContentsMargins({}); + layout->setSpacing(0); + layout->addWidget(m_quickWidget.data()); + + qmlRegisterAnonymousType("CollectionEditorBackend", 1); + auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); + map->setProperties( + {{"rootView", QVariant::fromValue(this)}, + {"model", QVariant::fromValue(m_model.data())}, + {"singleCollectionModel", QVariant::fromValue(m_singleCollectionModel.data())}}); + + auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); + connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); + + reloadQmlSource(); +} + +void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const +{ + if (m_view) + QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId()); + else + callback({}); +} + +QPointer CollectionWidget::collectionModel() const +{ + return m_model; +} + +QPointer CollectionWidget::singleCollectionModel() const +{ + return m_singleCollectionModel; +} + +void CollectionWidget::reloadQmlSource() +{ + const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; + + QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return); + + m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); +} + +bool CollectionWidget::loadJsonFile(const QString &jsonFileAddress) +{ + QUrl jsonUrl(jsonFileAddress); + QString fileAddress = jsonUrl.isLocalFile() ? jsonUrl.toLocalFile() : jsonUrl.toString(); + QFile file(fileAddress); + if (file.open(QFile::ReadOnly)) + return m_view->loadJson(file.readAll()); + + warn("Unable to open the file", file.errorString()); + return false; +} + +bool CollectionWidget::loadCsvFile(const QString &collectionName, const QString &csvFileAddress) +{ + QUrl csvUrl(csvFileAddress); + QString fileAddress = csvUrl.isLocalFile() ? csvUrl.toLocalFile() : csvUrl.toString(); + QFile file(fileAddress); + if (file.open(QFile::ReadOnly)) + return m_view->loadCsv(collectionName, file.readAll()); + + warn("Unable to open the file", file.errorString()); + return false; +} + +bool CollectionWidget::isJsonFile(const QString &jsonFileAddress) const +{ + QUrl jsonUrl(jsonFileAddress); + QString fileAddress = jsonUrl.isLocalFile() ? jsonUrl.toLocalFile() : jsonUrl.toString(); + QFile file(fileAddress); + + if (!file.exists() || !file.open(QFile::ReadOnly)) + return false; + + QJsonParseError error; + QJsonDocument::fromJson(file.readAll(), &error); + if (error.error) + return false; + + return true; +} + +bool CollectionWidget::isCsvFile(const QString &csvFileAddress) const +{ + QUrl csvUrl(csvFileAddress); + QString fileAddress = csvUrl.isLocalFile() ? csvUrl.toLocalFile() : csvUrl.toString(); + QFile file(fileAddress); + + if (!file.exists()) + return false; + + // TODO: Evaluate the csv file + return true; +} + +bool CollectionWidget::addCollection(const QString &collectionName) const +{ + m_view->addNewCollection(collectionName); + return true; +} + +void CollectionWidget::warn(const QString &title, const QString &body) +{ + QMetaObject::invokeMethod(m_quickWidget->rootObject(), + "showWarning", + Q_ARG(QVariant, title), + Q_ARG(QVariant, body)); +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h new file mode 100644 index 00000000000..e76237d0c14 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include + +#include + +class StudioQuickWidget; + +namespace QmlDesigner { + +class CollectionModel; +class CollectionView; +class SingleCollectionModel; + +class CollectionWidget : public QFrame +{ + Q_OBJECT + +public: + CollectionWidget(CollectionView *view); + void contextHelp(const Core::IContext::HelpCallback &callback) const; + + QPointer collectionModel() const; + QPointer singleCollectionModel() const; + + void reloadQmlSource(); + + Q_INVOKABLE bool loadJsonFile(const QString &jsonFileAddress); + Q_INVOKABLE bool loadCsvFile(const QString &collectionName, const QString &csvFileAddress); + Q_INVOKABLE bool isJsonFile(const QString &jsonFileAddress) const; + Q_INVOKABLE bool isCsvFile(const QString &csvFileAddress) const; + Q_INVOKABLE bool addCollection(const QString &collectionName) const; + + void warn(const QString &title, const QString &body); + +private: + QPointer m_view; + QPointer m_model; + QPointer m_singleCollectionModel; + QScopedPointer m_quickWidget; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp new file mode 100644 index 00000000000..040f9307b67 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "singlecollectionmodel.h" + +#include "nodemetainfo.h" +#include "variantproperty.h" + +#include + +namespace { +inline bool isListElement(const QmlDesigner::ModelNode &node) +{ + return node.metaInfo().isQtQuickListElement(); +} + +inline QByteArrayList getHeaders(const QByteArray &headersValue) +{ + QByteArrayList result; + const QByteArrayList initialHeaders = headersValue.split(','); + for (QByteArray header : initialHeaders) { + header = header.trimmed(); + if (header.size()) + result.append(header); + } + return result; +} +} // namespace + +namespace QmlDesigner { +SingleCollectionModel::SingleCollectionModel(QObject *parent) + : QAbstractTableModel(parent) +{} + +int SingleCollectionModel::rowCount([[maybe_unused]] const QModelIndex &parent) const +{ + return m_elements.count(); +} + +int SingleCollectionModel::columnCount([[maybe_unused]] const QModelIndex &parent) const +{ + return m_headers.count(); +} + +QVariant SingleCollectionModel::data(const QModelIndex &index, int) const +{ + if (!index.isValid()) + return {}; + + const QByteArray &propertyName = m_headers.at(index.column()); + const ModelNode &elementNode = m_elements.at(index.row()); + + if (elementNode.hasVariantProperty(propertyName)) + return elementNode.variantProperty(propertyName).value(); + + return {}; +} + +bool SingleCollectionModel::setData(const QModelIndex &, const QVariant &, int) +{ + return false; +} + +Qt::ItemFlags SingleCollectionModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return {}; + + return {Qt::ItemIsSelectable | Qt::ItemIsEnabled}; +} + +QVariant SingleCollectionModel::headerData(int section, + Qt::Orientation orientation, + [[maybe_unused]] int role) const +{ + if (orientation == Qt::Horizontal) + return m_headers.at(section); + + return {}; +} + +void SingleCollectionModel::setCollection(const ModelNode &collection) +{ + beginResetModel(); + m_collectionNode = collection; + updateCollectionName(); + + QTC_ASSERT(collection.isValid() && collection.hasVariantProperty("headers"), { + m_headers.clear(); + m_elements.clear(); + endResetModel(); + return; + }); + + m_headers = getHeaders(collection.variantProperty("headers").value().toByteArray()); + m_elements = Utils::filtered(collection.allSubModelNodes(), &isListElement); + endResetModel(); +} + +void SingleCollectionModel::updateCollectionName() +{ + QString newCollectionName = m_collectionNode.isValid() + ? m_collectionNode.variantProperty("objectName").value().toString() + : ""; + if (m_collectionName != newCollectionName) { + m_collectionName = newCollectionName; + emit this->collectionNameChanged(m_collectionName); + } +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h new file mode 100644 index 00000000000..8d2c6c41b4f --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include "modelnode.h" + +#include + +QT_BEGIN_NAMESPACE +class QJsonArray; +QT_END_NAMESPACE + +namespace QmlDesigner { +class SingleCollectionModel : public QAbstractTableModel +{ + Q_OBJECT + + Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) + +public: + explicit SingleCollectionModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant headerData(int section, + Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + void setCollection(const ModelNode &collection); + +signals: + void collectionNameChanged(const QString &collectionName); + +private: + void updateCollectionName(); + + QByteArrayList m_headers; + ModelNodes m_elements; + ModelNode m_collectionNode; + QString m_collectionName; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 7cac4c49349..228934b2f83 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -296,6 +296,11 @@ QIcon DesignerActionManager::contextIcon(int contextId) const return m_designerIcons->icon(DesignerIcons::IconId(contextId), DesignerIcons::ContextMenuArea); } +QIcon DesignerActionManager::toolbarIcon(int contextId) const +{ + return m_designerIcons->icon(DesignerIcons::IconId(contextId), DesignerIcons::ToolbarArea); +} + void DesignerActionManager::addAddActionCallback(ActionAddedInterface callback) { m_callBacks.append(callback); @@ -587,9 +592,9 @@ QList getSlotsLists(const ModelNode &node) ModelNode createNewConnection(ModelNode targetNode) { NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQuickConnectionsMetaInfo(); - ModelNode newConnectionNode = targetNode.view()->createModelNode(connectionsMetaInfo.typeName(), - connectionsMetaInfo.majorVersion(), - connectionsMetaInfo.minorVersion()); + const auto typeName = useProjectStorage() ? "Connections" : "QtQuick.Connections"; + ModelNode newConnectionNode = targetNode.view()->createModelNode( + typeName, connectionsMetaInfo.majorVersion(), connectionsMetaInfo.minorVersion()); if (QmlItemNode::isValidQmlItemNode(targetNode)) { targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode); } else { @@ -886,14 +891,18 @@ public: NodeMetaInfo elementMetaInfo = view->model()->metaInfo("ListElement"); ListModelEditorModel model{[&] { - return view->createModelNode(modelMetaInfo.typeName(), + return view->createModelNode(useProjectStorage() + ? "ListModel" + : "QtQml.Models.ListModel", modelMetaInfo.majorVersion(), modelMetaInfo.minorVersion()); }, [&] { - return view->createModelNode(elementMetaInfo.typeName(), - elementMetaInfo.majorVersion(), - elementMetaInfo.minorVersion()); + return view->createModelNode( + useProjectStorage() ? "ListElement" + : "QtQml.Models.ListElement", + elementMetaInfo.majorVersion(), + elementMetaInfo.minorVersion()); }, [&](const ModelNode &node) { bool isNowInComponent = ModelNodeOperations::goIntoComponent( diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index e1544de114d..16d6219cd69 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -123,6 +123,7 @@ public: bool externalDragHasSupportedAssets(const QMimeData *data) const; QHash handleExternalAssetsDrop(const QMimeData *data) const; QIcon contextIcon(int contextId) const; + QIcon toolbarIcon(int contextId) const; void addAddActionCallback(ActionAddedInterface callback); diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.cpp b/src/plugins/qmldesigner/components/componentcore/designericons.cpp index 053a188328f..31e7b45fcdb 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designericons.cpp @@ -238,6 +238,36 @@ struct JsonMap> } }; +void jsonParseErrorOffset(int &line, + int &offset, + const QJsonParseError &jpe, + const QString &filePath) +{ + line = -1; + offset = -1; + if (!jpe.error || jpe.offset < 0) + return; + + QFile errorFile(filePath); + if (!errorFile.open(QFile::ReadOnly)) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot open file" << filePath + << "to get error line"; + return; + } + line = 0; + QByteArray data; + do { + int linePos = errorFile.pos(); + data = errorFile.readLine(); + line++; + if (jpe.offset < errorFile.pos()) { + offset = jpe.offset - linePos; + break; + } + } while (!errorFile.atEnd()); + errorFile.close(); +} + } // End of blank namespace class QmlDesigner::DesignerIconsPrivate @@ -253,11 +283,12 @@ public: QCache DesignerIconsPrivate::cache(100); -IconFontHelper::IconFontHelper(Theme::Icon themeIcon, Theme::Color color, const QSize &size, QIcon::Mode mode, QIcon::State state) - : Super(Theme::getIconUnicode(themeIcon), - Theme::getColor(color), - size, - mode, state) +IconFontHelper::IconFontHelper(Theme::Icon themeIcon, + Theme::Color color, + const QSize &size, + QIcon::Mode mode, + QIcon::State state) + : Super(Theme::getIconUnicode(themeIcon), Theme::getColor(color), size, mode, state) , mThemeIcon(themeIcon) , mThemeColor(color) {} @@ -341,17 +372,55 @@ void DesignerIcons::loadIconSettings(const QString &fileName) QJsonDocument jsonDoc = QJsonDocument::fromJson(designerIconFile.readAll(), &parseError); if (parseError.error != QJsonParseError::NoError) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Json Parse Error - " << parseError.errorString() << "---\t File: " << fileName; + int line = 0; + int offset = 0; + jsonParseErrorOffset(line, offset, parseError, fileName); + qWarning() << Q_FUNC_INFO << __LINE__ << "Json Parse Error - " << parseError.errorString() + << "---\t File: " << fileName << "---\t Line:" << line + << "---\t File Offset:" << offset; return; } - if (!jsonDoc.isObject()) { + if (!jsonDoc.isObject() && !jsonDoc.isArray()) { qWarning() << Q_FUNC_INFO << __LINE__ << "Invalid Json Array in file: " << fileName; return; } clearAll(); - d->icons = JsonMap::value(jsonDoc.object(), {}); + if (jsonDoc.isObject()) { + d->icons = JsonMap::value(jsonDoc.object(), {}); + } else if (jsonDoc.isArray()) { + DesignerIcons::IconsMap singleAreaMap; + const QJsonArray jArray = jsonDoc.array(); + for (const QJsonValue &areaPack : jArray) { + if (areaPack.isObject()) { + QJsonObject areaPackObject = areaPack.toObject(); + singleAreaMap = JsonMap::value(areaPackObject, {}); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + for (const auto &mapItem : singleAreaMap.asKeyValueRange()) { + const IconId &id = mapItem.first; + const AreaMap &areaMap = mapItem.second; +#else + const auto mapKeys = singleAreaMap.keys(); + for (const IconId &id : mapKeys) { + const AreaMap &areaMap = singleAreaMap.value(id); +#endif + if (d->icons.contains(id)) { + AreaMap &oldAreaMap = d->icons[id]; + for (const auto &areaMapItem : areaMap.asKeyValueRange()) + oldAreaMap.insert(areaMapItem.first, areaMapItem.second); + } else { + d->icons.insert(id, areaMap); + } + } + } else { + qWarning() << Q_FUNC_INFO << __LINE__ << "Invalid Json Array in file: " << fileName; + return; + } + } + } + d->cache.insert(fileName, new IconsMap(d->icons), 1); } diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index 749e4e1cce4..6539462f8f7 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -62,11 +62,14 @@ public: CreateIcon, DeleteIcon, DuplicateIcon, + EditColorIcon, EditComponentIcon, EditIcon, + EditLightIcon, EnterComponentIcon, EventListIcon, FitSelectedIcon, + FitToViewIcon, GroupSelectionIcon, ImportedModelsIcon, LayoutsIcon, @@ -74,6 +77,7 @@ public: LightDirectionalIcon, LightPointIcon, LightSpotIcon, + LocalOrientIcon, MakeComponentIcon, MaterialIcon, MergeWithTemplateIcon, @@ -83,25 +87,29 @@ public: ModelCylinderIcon, ModelPlaneIcon, ModelSphereIcon, + MoveToolIcon, ParentIcon, + ParticlesAnimationIcon, + ParticlesPlayIcon, + ParticlesRestartIcon, PasteIcon, PositionsersIcon, PrimitivesIcon, ResetViewIcon, + RotateToolIcon, + ScaleToolIcon, SelecionIcon, ShowBoundsIcon, SimpleCheckIcon, SnappingIcon, + SnappingConfIcon, TimelineIcon, ToggleGroupIcon, VisibilityIcon }; Q_ENUM(IconId) - enum Area { - TopToolbarArea, - ContextMenuArea - }; + enum Area { ContextMenuArea, ToolbarArea, TopToolbarArea }; Q_ENUM(Area) enum Mode { diff --git a/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp b/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp index f60e0ee5e33..7bd2f793d37 100644 --- a/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp @@ -6,6 +6,7 @@ #include "nodeabstractproperty.h" #include "nodelistproperty.h" +#include #include using namespace QmlDesigner; @@ -14,7 +15,7 @@ namespace { bool selectionsAreSiblings(const QList &selectedItems) { - const QList prunedSelectedItems = ModelNode::pruneChildren(selectedItems); + const QList prunedSelectedItems = ModelUtils::pruneChildren(selectedItems); if (prunedSelectedItems.size() < 2) return false; @@ -57,7 +58,7 @@ ModelNode availableGroupNode(const SelectionContext &selection) QList allSiblingNodes = parentNode.directSubModelNodes(); - QList selectedNodes = ModelNode::pruneChildren(selection.selectedModelNodes()); + QList selectedNodes = ModelUtils::pruneChildren(selection.selectedModelNodes()); if (selectedNodes.size() != allSiblingNodes.size()) return {}; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 485130d6e9f..27f196dc5ad 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1205,21 +1205,20 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ NodeMetaInfo effectMetaInfo = view->model()->metaInfo("FlowView." + typeName, -1, -1); QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return); - view->executeInTransaction("DesignerActionManager:addFlowEffect", - [view, container, effectMetaInfo](){ + view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() { + if (container.hasProperty("effect")) + container.removeProperty("effect"); - if (container.hasProperty("effect")) - container.removeProperty("effect"); + if (effectMetaInfo.isQtObject()) { + ModelNode effectNode = view->createModelNode(useProjectStorage() + ? typeName + : effectMetaInfo.typeName(), + effectMetaInfo.majorVersion(), + effectMetaInfo.minorVersion()); - if (effectMetaInfo.isQtObject()) { - ModelNode effectNode = - view->createModelNode(effectMetaInfo.typeName(), - effectMetaInfo.majorVersion(), - effectMetaInfo.minorVersion()); - - container.nodeProperty("effect").reparentHere(effectNode); - view->setSelectedModelNode(effectNode); - } + container.nodeProperty("effect").reparentHere(effectNode); + view->setSelectedModelNode(effectNode); + } }); } @@ -1404,21 +1403,20 @@ void addCustomFlowEffect(const SelectionContext &selectionContext) NodeMetaInfo effectMetaInfo = view->model()->metaInfo(typeName, -1, -1); QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return); - view->executeInTransaction("DesignerActionManager:addFlowEffect", - [view, container, effectMetaInfo](){ + view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() { + if (container.hasProperty("effect")) + container.removeProperty("effect"); - if (container.hasProperty("effect")) - container.removeProperty("effect"); + if (effectMetaInfo.isValid()) { + ModelNode effectNode = view->createModelNode(useProjectStorage() + ? typeName + : effectMetaInfo.typeName(), + effectMetaInfo.majorVersion(), + effectMetaInfo.minorVersion()); - if (effectMetaInfo.isValid()) { - ModelNode effectNode = - view->createModelNode(effectMetaInfo.typeName(), - effectMetaInfo.majorVersion(), - effectMetaInfo.minorVersion()); - - container.nodeProperty("effect").reparentHere(effectNode); - view->setSelectedModelNode(effectNode); - } + container.nodeProperty("effect").reparentHere(effectNode); + view->setSelectedModelNode(effectNode); + } }); } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index a51d6798473..94cd54c1ea3 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -4,6 +4,7 @@ #pragma once #include "selectioncontext.h" +#include #include @@ -98,7 +99,9 @@ void addItemToStackedContainer(const SelectionContext &selectionContext); void increaseIndexOfStackedContainer(const SelectionContext &selectionContext); void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext); void addTabBarToStackedContainer(const SelectionContext &selectionContext); -QMLDESIGNERCORE_EXPORT AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDir, bool showDialog = true); +QMLDESIGNERCOMPONENTS_EXPORT AddFilesResult addFilesToProject(const QStringList &fileNames, + const QString &defaultDir, + bool showDialog = true); AddFilesResult addImageToProject(const QStringList &fileNames, const QString &directory, bool showDialog = true); AddFilesResult addFontToProject(const QStringList &fileNames, const QString &directory, bool showDialog = true); AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &directory, bool showDialog = true); @@ -119,8 +122,8 @@ void addMouseAreaFill(const SelectionContext &selectionContext); void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); -QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsImportDirectory(); -QMLDESIGNERCORE_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir); +QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory(); +QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir); void openEffectMaker(const QString &filePath); QString getEffectIcon(const QString &effectPath); bool useLayerEffect(); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index fba2f25a05c..1124e9b4e84 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -82,6 +82,7 @@ public: closeFile_large, closeLink, close_small, + code, colorPopupClose, colorSelection_medium, columnsAndRows, @@ -133,6 +134,7 @@ public: downloadUnavailable, downloadUpdate, downloaded, + dragmarks, duplicate_small, edit, editComponent_large, @@ -162,6 +164,7 @@ public: gridView, grid_medium, group_small, + help, home_large, idAliasOff, idAliasOn, @@ -170,6 +173,7 @@ public: importedModels_small, infinity, invisible_medium, + invisible_small, keyframe, languageList_medium, layouts_small, @@ -274,6 +278,8 @@ public: selection_small, settings_medium, signal_small, + snapping_conf_medium, + snapping_medium, snapping_small, sphere_medium, sphere_small, diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index fc30ab99f9d..8c06085393c 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +24,11 @@ #include #include #include -#include #include #include #include #include +#include #include @@ -50,9 +52,11 @@ public: : connectionManager, externalDependencies, true) + , collectionView{externalDependencies} , contentLibraryView{externalDependencies} , componentView{externalDependencies} , edit3DView{externalDependencies} + , effectMakerView{externalDependencies} , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} @@ -71,9 +75,11 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; + CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; Edit3DView edit3DView; + EffectMakerView effectMakerView; FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; @@ -206,6 +212,12 @@ QList ViewManager::standardViews() const .toBool()) list.append(&d->debugView); + if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) + list.append(&d->effectMakerView); + + if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) + list.append(&d->collectionView); + #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) list.append(&d->contentLibraryView); @@ -381,6 +393,12 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); widgetInfoList.append(d->statesEditorView.widgetInfo()); + if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) + widgetInfoList.append(d->effectMakerView.widgetInfo()); + + if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) + widgetInfoList.append(d->collectionView.widgetInfo()); + #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp index 5778d8242f7..93c151978f8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp @@ -232,7 +232,11 @@ void BackendModel::addNewBackend() int majorVersion = metaInfo.majorVersion(); if (dialog.localDefinition()) { - ModelNode newNode = m_connectionView->createModelNode(metaInfo.typeName(), majorVersion, minorVersion); + ModelNode newNode = m_connectionView->createModelNode(useProjectStorage() + ? typeName.toUtf8() + : metaInfo.typeName(), + majorVersion, + minorVersion); m_connectionView->rootModelNode().nodeProperty(propertyName.toUtf8()).setDynamicTypeNameAndsetModelNode( typeName.toUtf8(), newNode); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index a5562059d67..0b38b9d35d9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -2,111 +2,28 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "bindingmodel.h" - +#include "bindingmodelitem.h" #include "connectionview.h" +#include "connectioneditorutils.h" +#include "modelfwd.h" +#include #include #include -#include -#include -#include #include -#include +#include +#include #include -#include -#include - namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *parent) - : QStandardItemModel(parent), m_connectionView(parent), - m_delegate(new BindingModelBackendDelegate(this)) + : QStandardItemModel(parent) + , m_connectionView(parent) + , m_delegate(new BindingModelBackendDelegate(this)) { - connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged); -} - -void BindingModel::resetModel() -{ - beginResetModel(); - clear(); - setHorizontalHeaderLabels( - QStringList({tr("Item"), tr("Property"), tr("Source Item"), tr("Source Property")})); - - if (connectionView()->isAttached()) { - for (const ModelNode &modelNode : connectionView()->selectedModelNodes()) - addModelNode(modelNode); - } - - endResetModel(); -} - -void BindingModel::add() -{ - addBindingForCurrentNode(); -} - -void BindingModel::remove(int row) -{ - deleteBindindByRow(row); -} - -int BindingModel::currentIndex() const -{ - return m_currentIndex; -} - -void BindingModel::setCurrentIndex(int i) -{ - if (m_currentIndex == i) - return; - - m_currentIndex = i; - - emit currentIndexChanged(); -} - -void BindingModel::bindingChanged(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - QList selectedNodes = connectionView()->selectedModelNodes(); - if (!selectedNodes.contains(bindingProperty.parentModelNode())) - return; - if (!m_lock) { - int rowNumber = findRowForBinding(bindingProperty); - - if (rowNumber == -1) { - addBindingProperty(bindingProperty); - } else { - updateBindingProperty(rowNumber); - } - } - - m_handleDataChanged = true; -} - -void BindingModel::bindingRemoved(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - QList selectedNodes = connectionView()->selectedModelNodes(); - if (!selectedNodes.contains(bindingProperty.parentModelNode())) - return; - if (!m_lock) { - int rowNumber = findRowForBinding(bindingProperty); - removeRow(rowNumber); - } - - m_handleDataChanged = true; -} - -void BindingModel::selectionChanged([[maybe_unused]] const QList &selectedNodes) -{ - m_handleDataChanged = false; - resetModel(); - m_handleDataChanged = true; + setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } ConnectionView *BindingModel::connectionView() const @@ -114,461 +31,269 @@ ConnectionView *BindingModel::connectionView() const return m_connectionView; } -BindingProperty BindingModel::bindingPropertyForRow(int rowNumber) const -{ - - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.bindingProperty(targetPropertyName.toLatin1()); - - return BindingProperty(); -} - -QStringList BindingModel::possibleTargetProperties(const BindingProperty &bindingProperty) const -{ - const ModelNode modelNode = bindingProperty.parentModelNode(); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node"; - return {}; - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - const auto properties = metaInfo.properties(); - QStringList writableProperties; - writableProperties.reserve(static_cast(properties.size())); - for (const auto &property : properties) { - if (property.isWritable()) - writableProperties.push_back(QString::fromUtf8(property.name())); - } - - return writableProperties; - } - - return {}; -} - -QStringList BindingModel::possibleSourceProperties(const BindingProperty &bindingProperty) const -{ - const QString expression = bindingProperty.expression(); - const QStringList stringlist = expression.split(QLatin1String(".")); - QStringList possibleProperties; - - NodeMetaInfo type; - - if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) - type = metaInfo.property(bindingProperty.name()).propertyType(); - else - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node"; - - const QString &id = stringlist.constFirst(); - - ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode()); - - if (!modelNode.isValid()) { - //if it's not a valid model node, maybe it's a singleton - if (RewriterView* rv = connectionView()->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty() && data.typeName == id) { - NodeMetaInfo metaInfo = connectionView()->model()->metaInfo(data.typeName.toUtf8()); - - if (metaInfo.isValid()) { - for (const auto &property : metaInfo.properties()) { - //without check for now - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - - return possibleProperties; - } - } - } - } - - qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node"; - return {}; - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - for (const VariantProperty &variantProperty : modelNode.variantProperties()) { - if (variantProperty.isDynamic()) - possibleProperties << QString::fromUtf8(variantProperty.name()); - } - - for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) { - if (bindingProperty.isDynamic()) - possibleProperties << QString::fromUtf8((bindingProperty.name())); - } - - if (metaInfo.isValid()) { - for (const auto &property : metaInfo.properties()) { - if (property.propertyType() == type) //### todo proper check - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - } else { - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node"; - } - - return possibleProperties; -} - -void BindingModel::deleteBindindByRow(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); - } - - resetModel(); -} - -static PropertyName unusedProperty(const ModelNode &modelNode) -{ - PropertyName propertyName = "none"; - if (modelNode.metaInfo().isValid()) { - for (const auto &property : modelNode.metaInfo().properties()) { - if (property.isWritable() && !modelNode.hasProperty(propertyName)) - return property.name(); - } - } - - return propertyName; -} - -void BindingModel::addBindingForCurrentNode() -{ - if (connectionView()->selectedModelNodes().size() == 1) { - const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst(); - if (modelNode.isValid()) { - try { - modelNode.bindingProperty(unusedProperty(modelNode)).setExpression(QLatin1String("none.none")); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &BindingModel::handleException); - } - } - } else { - qWarning() << " BindingModel::addBindingForCurrentNode not one node selected"; - } -} - -static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property) -{ - item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole); - item->setData(property.name(), BindingModel::TargetPropertyNameRole); - - const AbstractProperty source = property.resolveToProperty(); - - if (source.isValid()) { - item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole); - item->setData(source.name(), BindingModel::SourcePropertyNameRole); - } -} - -void BindingModel::addBindingProperty(const BindingProperty &property) -{ - QStandardItem *idItem; - QStandardItem *targetPropertyNameItem; - QStandardItem *sourceIdItem; - QStandardItem *sourcePropertyNameItem; - - QString idLabel = property.parentModelNode().id(); - if (idLabel.isEmpty()) - idLabel = property.parentModelNode().simplifiedTypeName(); - idItem = new QStandardItem(idLabel); - updateCustomData(idItem, property); - targetPropertyNameItem = new QStandardItem(QString::fromUtf8(property.name())); - QList items; - - items.append(idItem); - updateDisplayRoles(idItem, property); - items.append(targetPropertyNameItem); - - QString sourceNodeName; - QString sourcePropertyName; - getExpressionStrings(property, &sourceNodeName, &sourcePropertyName); - - sourceIdItem = new QStandardItem(sourceNodeName); - sourcePropertyNameItem = new QStandardItem(sourcePropertyName); - - items.append(sourceIdItem); - items.append(sourcePropertyNameItem); - appendRow(items); -} - -void BindingModel::updateBindingProperty(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - QStandardItem *idItem = item(rowNumber, 0); - if (idItem) - updateDisplayRoles(idItem, bindingProperty); - - QString targetPropertyName = QString::fromUtf8(bindingProperty.name()); - updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName); - QString sourceNodeName; - QString sourcePropertyName; - getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); - updateDisplayRole(rowNumber, SourceModelNodeRow, sourceNodeName); - updateDisplayRole(rowNumber, SourcePropertyNameRow, sourcePropertyName); - } -} - -void BindingModel::addModelNode(const ModelNode &modelNode) -{ - const QList bindingProperties = modelNode.bindingProperties(); - for (const BindingProperty &bindingProperty : bindingProperties) { - addBindingProperty(bindingProperty); - } -} - -void BindingModel::updateExpression(int row) -{ - const QString sourceNode = data(index(row, SourceModelNodeRow)).toString().trimmed(); - const QString sourceProperty = data(index(row, SourcePropertyNameRow)).toString().trimmed(); - - QString expression; - if (sourceProperty.isEmpty()) { - expression = sourceNode; - } else { - expression = sourceNode + QLatin1String(".") + sourceProperty; - } - - connectionView()->executeInTransaction("BindingModel::updateExpression", [this, row, expression](){ - BindingProperty bindingProperty = bindingPropertyForRow(row); - bindingProperty.setExpression(expression.trimmed()); - }); -} - -void BindingModel::updatePropertyName(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - const PropertyName newName = data(index(rowNumber, TargetPropertyNameRow)).toString().toUtf8(); - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); - ModelNode targetNode = bindingProperty.parentModelNode(); - - if (!newName.isEmpty()) { - RewriterTransaction transaction = - connectionView()->beginRewriterTransaction(QByteArrayLiteral("BindingModel::updatePropertyName")); - try { - if (bindingProperty.isDynamic()) { - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression); - } else { - targetNode.bindingProperty(newName).setExpression(expression); - } - targetNode.removeProperty(bindingProperty.name()); - transaction.commit(); //committing in the try block - } catch (Exception &e) { //better save then sorry - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &BindingModel::handleException); - } - - QStandardItem* idItem = item(rowNumber, 0); - BindingProperty newBindingProperty = targetNode.bindingProperty(newName); - updateCustomData(idItem, newBindingProperty); - - } else { - qWarning() << "BindingModel::updatePropertyName invalid property name"; - } -} - -ModelNode BindingModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const -{ - ModelNode modelNode; - - if (id != QLatin1String("parent")) { - modelNode = connectionView()->modelNodeForId(id); - } else { - if (targetNode.hasParentProperty()) { - modelNode = targetNode.parentProperty().parentModelNode(); - } - } - return modelNode; -} - -void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty) -{ - item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1); - item->setData(bindingProperty.name(), Qt::UserRole + 2); - updateDisplayRoles(item, bindingProperty); -} - -int BindingModel::findRowForBinding(const BindingProperty &bindingProperty) -{ - for (int i=0; i < rowCount(); i++) { - if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty)) - return i; - } - //not found - return -1; -} - -bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty) -{ - //TODO reimplement using existing helper functions - - //### todo we assume no expressions yet - - const QString expression = bindingProperty.expression(); - - if (true) { - const QStringList stringList = expression.split(QLatin1String(".")); - - *sourceNode = stringList.constFirst(); - - QString propertyName; - - for (int i = 1; i < stringList.size(); i++) { - propertyName += stringList.at(i); - if (i != stringList.size() - 1) - propertyName += QLatin1String("."); - } - *sourceProperty = propertyName; - } - return true; -} - -void BindingModel::updateDisplayRole(int row, int columns, const QString &string) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex).toString() != string) - setData(modelIndex, string); -} - -void BindingModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - if (!m_handleDataChanged) - return; - - if (topLeft != bottomRight) { - qWarning() << "BindingModel::handleDataChanged multi edit?"; - return; - } - - m_lock = true; - - int currentColumn = topLeft.column(); - int currentRow = topLeft.row(); - - switch (currentColumn) { - case TargetModelNodeRow: { - //updating user data - } break; - case TargetPropertyNameRow: { - updatePropertyName(currentRow); - } break; - case SourceModelNodeRow: { - updateExpression(currentRow); - } break; - case SourcePropertyNameRow: { - updateExpression(currentRow); - } break; - - default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn; - } - - m_lock = false; -} - -void BindingModel::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - resetModel(); -} - -QHash BindingModel::roleNames() const -{ - static QHash roleNames{{TargetNameRole, "target"}, - {TargetPropertyNameRole, "targetProperty"}, - {SourceNameRole, "source"}, - {SourcePropertyNameRole, "sourceProperty"}}; - - return roleNames; -} - BindingModelBackendDelegate *BindingModel::delegate() const { return m_delegate; } -BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent) +int BindingModel::currentIndex() const +{ + return m_currentIndex; +} + +BindingProperty BindingModel::currentProperty() const +{ + return propertyForRow(m_currentIndex); +} + +BindingProperty BindingModel::propertyForRow(int row) const +{ + if (!m_connectionView) + return {}; + + if (!m_connectionView->isAttached()) + return {}; + + if (auto *item = itemForRow(row)) { + int internalId = item->internalId(); + if (ModelNode node = m_connectionView->modelNodeForInternalId(internalId); node.isValid()) + return node.bindingProperty(item->targetPropertyName()); + } + + return {}; +} + +static PropertyName unusedProperty(const ModelNode &modelNode) +{ + if (modelNode.metaInfo().isValid()) { + for (const auto &property : modelNode.metaInfo().properties()) { + if (property.isWritable() && !modelNode.hasProperty(property.name())) + return property.name(); + } + } + return "none"; +} + +void BindingModel::add() +{ + if (const QList nodes = connectionView()->selectedModelNodes(); nodes.size() == 1) { + const ModelNode modelNode = nodes.constFirst(); + if (modelNode.isValid()) { + try { + PropertyName name = unusedProperty(modelNode); + modelNode.bindingProperty(name).setExpression(QLatin1String("none.none")); + } catch (RewritingException &e) { + showErrorMessage(e.description()); + reset(); + } + } + } else { + qWarning() << __FUNCTION__ << " Requires exactly one selected node"; + } +} + +void BindingModel::remove(int row) +{ + if (BindingProperty property = propertyForRow(row); property.isValid()) { + ModelNode node = property.parentModelNode(); + node.removeProperty(property.name()); + } + + reset(); +} + +void BindingModel::reset(const QList &nodes) +{ + if (!connectionView()) + return; + + if (!connectionView()->isAttached()) + return; + + AbstractProperty current = currentProperty(); + + clear(); + + if (!nodes.isEmpty()) { + for (const ModelNode &modelNode : nodes) + addModelNode(modelNode); + } else { + for (const ModelNode &modelNode : connectionView()->selectedModelNodes()) + addModelNode(modelNode); + } + + setCurrentProperty(current); +} + +void BindingModel::setCurrentIndex(int i) +{ + if (m_currentIndex != i) { + m_currentIndex = i; + emit currentIndexChanged(); + } + m_delegate->update(currentProperty(), m_connectionView); +} + +void BindingModel::setCurrentProperty(const AbstractProperty &property) +{ + if (auto index = rowForProperty(property)) + setCurrentIndex(*index); +} + +void BindingModel::updateItem(const BindingProperty &property) +{ + if (auto *item = itemForProperty(property)) + item->updateProperty(property); + else + appendRow(new BindingModelItem(property)); + + m_delegate->update(currentProperty(), m_connectionView); +} + +void BindingModel::removeItem(const AbstractProperty &property) +{ + AbstractProperty current = currentProperty(); + if (auto index = rowForProperty(property)) + static_cast(removeRow(*index)); + + setCurrentProperty(current); + emit currentIndexChanged(); +} + +void BindingModel::commitExpression(int row, const QString &expression) +{ + QTC_ASSERT(connectionView(), return); + + BindingProperty bindingProperty = propertyForRow(row); + if (!bindingProperty.isValid()) + return; + + connectionView()->executeInTransaction(__FUNCTION__, [&bindingProperty, expression]() { + if (bindingProperty.isDynamic()) { + TypeName type = bindingProperty.dynamicTypeName(); + bindingProperty.setDynamicTypeNameAndExpression(type, expression); + } else { + bindingProperty.setExpression(expression.trimmed()); + } + }); +} + +void BindingModel::commitPropertyName(int row, const PropertyName &name) +{ + QTC_ASSERT(connectionView(), return); + + BindingProperty bindingProperty = propertyForRow(row); + if (!bindingProperty.isValid()) + return; + + connectionView()->executeInTransaction(__FUNCTION__, [&]() { + const TypeName type = bindingProperty.dynamicTypeName(); + const QString expression = bindingProperty.expression(); + + ModelNode node = bindingProperty.parentModelNode(); + node.removeProperty(bindingProperty.name()); + if (bindingProperty.isDynamic()) + node.bindingProperty(name).setDynamicTypeNameAndExpression(type, expression); + else + node.bindingProperty(name).setExpression(expression); + }); +} + +QHash BindingModel::roleNames() const +{ + return BindingModelItem::roleNames(); +} + +std::optional BindingModel::rowForProperty(const AbstractProperty &property) const +{ + PropertyName name = property.name(); + int internalId = property.parentModelNode().internalId(); + + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->targetPropertyName() == name && item->internalId() == internalId) + return i; + } + } + return std::nullopt; +} + +BindingModelItem *BindingModel::itemForRow(int row) const +{ + if (QModelIndex idx = index(row, 0); idx.isValid()) + return dynamic_cast(itemFromIndex(idx)); + return nullptr; +} + +BindingModelItem *BindingModel::itemForProperty(const AbstractProperty &property) const +{ + if (auto row = rowForProperty(property)) + return itemForRow(*row); + return nullptr; +} + +void BindingModel::addModelNode(const ModelNode &node) +{ + if (!node.isValid()) + return; + + const QList bindingProperties = node.bindingProperties(); + for (const BindingProperty &property : bindingProperties) + appendRow(new BindingModelItem(property)); +} + +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) + : QObject(parent) + , m_targetNode() + , m_property() + , m_sourceNode() + , m_sourceNodeProperty() { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() { - handleSourceNodeChanged(); + expressionChanged(); }); connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { - handleSourcePropertyChanged(); + expressionChanged(); + }); + + connect(&m_property, &StudioQmlComboBoxBackend::activated, this, [this]() { + propertyNameChanged(); }); } -int BindingModelBackendDelegate::currentRow() const +void BindingModelBackendDelegate::update(const BindingProperty &property, AbstractView *view) { - return m_currentRow; -} - -void BindingModelBackendDelegate::setCurrentRow(int i) -{ - // See BindingDelegate::createEditor - - if (m_currentRow == i) + if (!property.isValid()) return; - m_currentRow = i; + auto addName = [](QStringList&& list, const QString& name) { + if (!list.contains(name)) + list.prepend(name); + return std::move(list); + }; - //setup + auto [sourceNodeName, sourcePropertyName] = splitExpression(property.expression()); - BindingModel *model = qobject_cast(parent()); + QStringList sourceNodes = {}; + if (!sourceNodeName.isEmpty()) + sourceNodes = addName(availableSources(view), sourceNodeName); - QTC_ASSERT(model, return ); - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - - QString idLabel = bindingProperty.parentModelNode().id(); - if (idLabel.isEmpty()) - idLabel = bindingProperty.parentModelNode().simplifiedTypeName(); - - m_targetNode = idLabel; - - emit targetNodeChanged(); - - m_property.setModel(model->possibleTargetProperties(bindingProperty)); - m_property.setCurrentText(QString::fromUtf8(bindingProperty.name())); - - QStringList sourceNodes; - - for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) - sourceNodes.append(modelNode.id()); - } - - std::sort(sourceNodes.begin(), sourceNodes.end()); m_sourceNode.setModel(sourceNodes); - - QString sourceNodeName; - QString sourcePropertyName; - model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); - m_sourceNode.setCurrentText(sourceNodeName); - m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty)); + auto sourceproperties = addName(availableSourceProperties(property, view), sourcePropertyName); + m_sourceNodeProperty.setModel(sourceproperties); m_sourceNodeProperty.setCurrentText(sourcePropertyName); -} -void BindingModelBackendDelegate::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - //reset + QString targetName = QString::fromUtf8(property.name()); + m_targetNode = idOrTypeName(property.parentModelNode()); + + auto targetProperties = addName(availableTargetProperties(property), targetName); + m_property.setModel(targetProperties); + m_property.setCurrentText(targetName); + + emit targetNodeChanged(); } QString BindingModelBackendDelegate::targetNode() const @@ -591,54 +316,38 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() return &m_sourceNodeProperty; } -void BindingModelBackendDelegate::handleSourceNodeChanged() +void BindingModelBackendDelegate::expressionChanged() const { - BindingModel *model = qobject_cast(parent()); + auto commit = [this]() { + BindingModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView(), return ); + const QString sourceNode = m_sourceNode.currentText(); + const QString sourceProperty = m_sourceNodeProperty.currentText(); + QString expression; + if (sourceProperty.isEmpty()) + expression = sourceNode; + else + expression = sourceNode + QLatin1String(".") + sourceProperty; - const QString sourceNode = m_sourceNode.currentText(); - const QString sourceProperty = m_sourceNodeProperty.currentText(); + int row = model->currentIndex(); + model->commitExpression(row, expression); + }; - QString expression; - if (sourceProperty.isEmpty()) { - expression = sourceNode; - } else { - expression = sourceNode + QLatin1String(".") + sourceProperty; - } - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - model->connectionView()->executeInTransaction("BindingModel::updateExpression", - [&bindingProperty, expression]() { - bindingProperty.setExpression( - expression.trimmed()); - }); + callLater(commit); } -void BindingModelBackendDelegate::handleSourcePropertyChanged() +void BindingModelBackendDelegate::propertyNameChanged() const { - BindingModel *model = qobject_cast(parent()); + auto commit = [this]() { + BindingModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); + const PropertyName propertyName = m_property.currentText().toUtf8(); + int row = model->currentIndex(); + model->commitPropertyName(row, propertyName); + }; - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView(), return ); - - const QString sourceNode = m_sourceNode.currentText(); - const QString sourceProperty = m_sourceNodeProperty.currentText(); - - QString expression; - if (sourceProperty.isEmpty()) { - expression = sourceNode; - } else { - expression = sourceNode + QLatin1String(".") + sourceProperty; - } - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - model->connectionView()->executeInTransaction("BindingModel::updateExpression", - [&bindingProperty, expression]() { - bindingProperty.setExpression( - expression.trimmed()); - }); + callLater(commit); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 1f469876852..4c3dd1210e5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -3,132 +3,100 @@ #pragma once -#include +#include #include -#include - +#include #include +#include #include namespace QmlDesigner { -class ConnectionView; class BindingModelBackendDelegate; +class BindingModelItem; +class ConnectionView; class BindingModel : public QStandardItemModel { Q_OBJECT +signals: + void currentIndexChanged(); + +public: Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT) public: - enum ColumnRoles { - TargetModelNodeRow = 0, - TargetPropertyNameRow = 1, - SourceModelNodeRow = 2, - SourcePropertyNameRow = 3 - }; - - enum UserRoles { - InternalIdRole = Qt::UserRole + 2, - TargetNameRole, - TargetPropertyNameRole, - SourceNameRole, - SourcePropertyNameRole - }; - BindingModel(ConnectionView *parent = nullptr); - void bindingChanged(const BindingProperty &bindingProperty); - void bindingRemoved(const BindingProperty &bindingProperty); - void selectionChanged(const QList &selectedNodes); ConnectionView *connectionView() const; - BindingProperty bindingPropertyForRow(int rowNumber) const; - QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const; - QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const; - void deleteBindindByRow(int rowNumber); - void addBindingForCurrentNode(); - void resetModel(); + BindingModelBackendDelegate *delegate() const; + + int currentIndex() const; + BindingProperty currentProperty() const; + BindingProperty propertyForRow(int row) const; Q_INVOKABLE void add(); Q_INVOKABLE void remove(int row); - int currentIndex() const; + void reset(const QList &selectedNodes = {}); void setCurrentIndex(int i); - bool getExpressionStrings(const BindingProperty &bindingProperty, - QString *sourceNode, - QString *sourceProperty); + void setCurrentProperty(const AbstractProperty &property); -signals: - void currentIndexChanged(); + void updateItem(const BindingProperty &property); + void removeItem(const AbstractProperty &property); + + void commitExpression(int row, const QString &expression); + void commitPropertyName(int row, const PropertyName &name); protected: - void addBindingProperty(const BindingProperty &property); - void updateBindingProperty(int rowNumber); - void addModelNode(const ModelNode &modelNode); - void updateExpression(int row); - void updatePropertyName(int rowNumber); - ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; - void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty); - int findRowForBinding(const BindingProperty &bindingProperty); - void updateDisplayRole(int row, int columns, const QString &string); - QHash roleNames() const override; - BindingModelBackendDelegate *delegate() const; private: - void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); - void handleException(); + std::optional rowForProperty(const AbstractProperty &property) const; + BindingModelItem *itemForRow(int row) const; + BindingModelItem *itemForProperty(const AbstractProperty &property) const; + + void addModelNode(const ModelNode &modelNode); private: - ConnectionView *m_connectionView; - bool m_lock = false; - bool m_handleDataChanged = false; - QString m_exceptionError; - int m_currentIndex = 0; + ConnectionView *m_connectionView = nullptr; BindingModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; }; class BindingModelBackendDelegate : public QObject { Q_OBJECT - Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) - Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) +signals: + void targetNodeChanged(); + public: BindingModelBackendDelegate(BindingModel *parent = nullptr); -signals: - void currentRowChanged(); - //void nameChanged(); - void targetNodeChanged(); + void update(const BindingProperty &property, AbstractView *view); private: - int currentRow() const; - void setCurrentRow(int i); - void handleException(); QString targetNode() const; + void expressionChanged() const; + void propertyNameChanged() const; StudioQmlComboBoxBackend *property(); StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceProperty(); - void handleSourceNodeChanged(); - void handleSourcePropertyChanged(); - + QString m_targetNode; StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_sourceNode; StudioQmlComboBoxBackend m_sourceNodeProperty; - QString m_exceptionError; - int m_currentRow = -1; - QString m_targetNode; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp new file mode 100644 index 00000000000..9e9fd92f528 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "bindingmodelitem.h" +#include "connectioneditorutils.h" + +#include +#include + +namespace QmlDesigner { + +QHash BindingModelItem::roleNames() +{ + return {{TargetNameRole, "target"}, + {TargetPropertyNameRole, "targetProperty"}, + {SourceNameRole, "source"}, + {SourcePropertyNameRole, "sourceProperty"}}; +} + +QStringList BindingModelItem::headerLabels() +{ + return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Source Item"), Tr::tr("Source Property")}; +} + +BindingModelItem::BindingModelItem(const BindingProperty &property) + : QStandardItem(idOrTypeName(property.parentModelNode())) +{ + updateProperty(property); +} + +int BindingModelItem::internalId() const +{ + return data(InternalIdRole).toInt(); +} + +PropertyName BindingModelItem::targetPropertyName() const +{ + return data(TargetPropertyNameRole).toString().toUtf8(); +} + +void BindingModelItem::updateProperty(const BindingProperty &property) +{ + setData(property.parentModelNode().internalId(), InternalIdRole); + setData(idOrTypeName(property.parentModelNode()), TargetNameRole); + setData(property.name(), TargetPropertyNameRole); + + // TODO: Make this safe when the new codemodel allows it. + if (auto expression = property.expression(); !expression.isEmpty()) { + auto [nodeName, propertyName] = splitExpression(expression); + setData(nodeName, SourceNameRole); + setData(propertyName, SourcePropertyNameRole); + } +} + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h new file mode 100644 index 00000000000..2e52905611c --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +namespace QmlDesigner { + +class BindingModelItem : public QStandardItem +{ +public: + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + TargetPropertyNameRole, + SourceNameRole, + SourcePropertyNameRole + }; + + static QHash roleNames(); + static QStringList headerLabels(); + + BindingModelItem(const BindingProperty &property); + + int internalId() const; + PropertyName targetPropertyName() const; + + void updateProperty(const BindingProperty &property); +}; + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp new file mode 100644 index 00000000000..d44077e4ff6 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -0,0 +1,1106 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "connectioneditorevaluator.h" +#include "qmljs/parser/qmljsast_p.h" +#include "qmljs/qmljsdocument.h" + +#include + +#include + +using namespace QmlDesigner; + +using QmlJS::AST::Node; +using Kind = Node::Kind; +using ConnectionEditorStatements::ConditionalStatement; +using ConnectionEditorStatements::ConditionToken; +using ConnectionEditorStatements::MatchedCondition; +using ConnectionEditorStatements::MatchedStatement; + +namespace { +enum class TrackingArea { No, Condition, Ok, Ko }; + +template +struct Overload : Ts... +{ + using Ts::operator()...; +}; +template +Overload(Ts...) -> Overload; + +inline ConditionToken operator2ConditionToken(const int &op) +{ + using QSOperator::Op; + switch (op) { + case Op::Le: + return ConditionToken::SmallerEqualsThan; + case Op::Lt: + return ConditionToken::SmallerThan; + case Op::Ge: + return ConditionToken::LargerEqualsThan; + case Op::Gt: + return ConditionToken::LargerThan; + case Op::Equal: + return ConditionToken::Equals; + case Op::NotEqual: + return ConditionToken::Not; + case Op::And: + return ConditionToken::And; + case Op::Or: + return ConditionToken::Or; + case Op::StrictEqual: + return ConditionToken::Equals; + case Op::StrictNotEqual: + return ConditionToken::Not; + default: + return ConditionToken::Unknown; + } +} + +inline bool isStatementNode(const int &kind) +{ + return kind == Kind::Kind_CallExpression || kind == Kind::Kind_FieldMemberExpression + || kind == Kind::Kind_IdentifierExpression; +} + +inline bool isAcceptedIfBinaryOperator(const int &operation) +{ + switch (operation) { + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::And: + case QSOperator::Or: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: + case QSOperator::InplaceXor: + return true; + default: + return false; + } +} + +class NodeStatus +{ +public: + NodeStatus() + : m_kind(Kind::Kind_Undefined) + , m_children(0) + , m_isStatementNode(false) + {} + + NodeStatus(Node *node) + : m_kind(Kind(node->kind)) + , m_children(0) + , m_isStatementNode(::isStatementNode(m_kind)) + {} + + int childId() const { return m_children - 1; } + + int children() const { return m_children; } + + operator Kind() { return m_kind; } + + bool operator==(int kind) const { return m_kind == kind; } + + bool operator==(Kind kind) const { return m_kind == kind; } + + bool operator!=(Kind kind) const { return m_kind != kind; } + + int increaseChildNo() { return m_children++; } + + bool isStatementNode() const { return m_isStatementNode; } + +private: + Kind m_kind; + int m_children = 0; + bool m_isStatementNode = false; +}; + +class BoolCondition : public QmlJS::AST::Visitor +{ +public: + MatchedCondition matchedCondition() const { return m_condition; } + + void reset() + { + m_failed = false; + m_depth = 0; + m_fields.clear(); + m_identifier.clear(); + } + + bool isValid() { return !m_failed; } + + const QString &errorString() const { return m_failureString; } + +protected: + bool preVisit(QmlJS::AST::Node *node) override + { + if (m_failed) + return false; + + switch (node->kind) { + case Kind::Kind_BinaryExpression: + case Kind::Kind_FieldMemberExpression: + case Kind::Kind_IdentifierExpression: + case Kind::Kind_StringLiteral: + case Kind::Kind_NumericLiteral: + case Kind::Kind_TrueLiteral: + case Kind::Kind_FalseLiteral: + return true; + default: { + checkValidityAndReturn(false, "Invalid node type."); + return false; + } + } + } + + bool visit(QmlJS::AST::BinaryExpression *binaryExpression) override + { + if (m_failed) + return false; + + if (binaryExpression->op == QSOperator::Equal) + return checkValidityAndReturn(false, "Use \"===\" for comparing two expressions."); + + if (binaryExpression->op == QSOperator::NotEqual) + return checkValidityAndReturn(false, "Use \"!==\" for comparing two field member expressions."); + + if (!isAcceptedIfBinaryOperator(binaryExpression->op)) + return checkValidityAndReturn(false, "Invalid binary operator"); + + if (binaryExpression->left->kind == Kind::Kind_StringLiteral) + return checkValidityAndReturn(false, "Left hand string literal"); + + if (binaryExpression->left->kind == Kind::Kind_NumericLiteral) + return checkValidityAndReturn(false, "Left hand numeric literal"); + + acceptBoolOperand(binaryExpression->left); + m_condition.tokens.append(operator2ConditionToken(binaryExpression->op)); + acceptBoolOperand(binaryExpression->right); + + return false; + } + + bool visit([[maybe_unused]] QmlJS::AST::IdentifierExpression *identifier) override + { + if (m_failed) + return false; + + ++m_depth; + return false; + } + + bool visit([[maybe_unused]] QmlJS::AST::FieldMemberExpression *field) override + { + if (m_failed) + return false; + + ++m_depth; + return true; + } + + void endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) override + { + if (m_failed) + return; + + m_fields << fieldExpression->name.toString(); + checkAndResetVariable(); + } + + void endVisit(QmlJS::AST::IdentifierExpression *identifier) override + { + if (m_failed) + return; + + m_identifier = identifier->name.toString(); + checkAndResetVariable(); + } + + void endVisit(QmlJS::AST::StringLiteral *stringLiteral) override + { + if (m_failed) + return; + m_condition.statements.push_back(stringLiteral->value.toString()); + } + + void endVisit(QmlJS::AST::NumericLiteral *numericLiteral) override + { + if (m_failed) + return; + m_condition.statements.push_back(numericLiteral->value); + } + + void endVisit([[maybe_unused]] QmlJS::AST::TrueLiteral *trueLiteral) override + { + if (m_failed) + return; + m_condition.statements.push_back(true); + } + + void endVisit([[maybe_unused]] QmlJS::AST::FalseLiteral *falseLiteral) override + { + if (m_failed) + return; + m_condition.statements.push_back(false); + } + + void throwRecursionDepthError() override + { + checkValidityAndReturn(false, "Recursion depth problem"); + qDebug() << Q_FUNC_INFO << this; + } + + void checkAndResetVariable() + { + if (--m_depth == 0) { + m_condition.statements.push_back( + ConnectionEditorStatements::Variable{m_identifier, m_fields.join(".")}); + m_identifier.clear(); + m_fields.clear(); + } + } + + void acceptBoolOperand(Node *operand) + { + BoolCondition boolEvaluator; + operand->accept(&boolEvaluator); + if (!checkValidityAndReturn(boolEvaluator.isValid(), boolEvaluator.errorString())) + return; + + m_condition.statements.append(boolEvaluator.m_condition.statements); + m_condition.tokens.append(boolEvaluator.m_condition.tokens); + } + +private: + bool m_failed = false; + int m_depth = 0; + QString m_identifier; + QStringList m_fields; + QString m_failureString; + MatchedCondition m_condition; + + bool checkValidityAndReturn(bool valid, const QString &error) + { + if (!m_failed && !valid) { + m_failed = true; + m_failureString = error; + } + return !m_failed; + } +}; + +class RightHandVisitor : public QmlJS::AST::Visitor +{ +public: + ConnectionEditorStatements::RightHandSide rhs() const { return m_rhs; } + + void reset() + { + m_failed = false; + m_specified = false; + m_depth = 0; + m_fields.clear(); + m_identifier.clear(); + } + + bool isValid() const { return !m_failed && m_specified; } + + bool isLiteralType() const + { + if (!isValid()) + return false; + + return ConnectionEditorStatements::isLiteralType(rhs()); + } + + bool couldBeLHS() const + { + if (!isValid()) + return false; + return std::holds_alternative(m_rhs); + } + + inline bool couldBeVariable() const { return couldBeLHS(); } + + ConnectionEditorStatements::Literal literal() const + { + if (!isLiteralType()) + return {}; + + return std::visit( + Overload{[](const bool &var) -> ConnectionEditorStatements::Literal { return var; }, + [](const double &var) -> ConnectionEditorStatements::Literal { return var; }, + [](const QString &var) -> ConnectionEditorStatements::Literal { return var; }, + [](const auto &) -> ConnectionEditorStatements::Literal { return false; }}, + m_rhs); + } + + ConnectionEditorStatements::Variable lhs() const + { + if (!isValid()) + return {}; + + if (auto rhs = std::get_if(&m_rhs)) + return *rhs; + + return {}; + } + + ConnectionEditorStatements::Variable variable() const + { + if (!isValid()) + return {}; + + if (auto rhs = std::get_if(&m_rhs)) + return *rhs; + + return {}; + } + +protected: + bool preVisit(QmlJS::AST::Node *node) override + { + if (skipIt()) + setFailed(); + + if (m_failed) + return false; + + switch (node->kind) { + case Kind::Kind_CallExpression: + case Kind::Kind_FieldMemberExpression: + case Kind::Kind_IdentifierExpression: + case Kind::Kind_StringLiteral: + case Kind::Kind_NumericLiteral: + case Kind::Kind_TrueLiteral: + case Kind::Kind_FalseLiteral: + return true; + default: { + setFailed(); + return false; + } + } + } + + bool visit([[maybe_unused]] QmlJS::AST::CallExpression *call) override + { + if (skipIt()) + return false; + + ++m_depth; + return true; + } + + bool visit([[maybe_unused]] QmlJS::AST::IdentifierExpression *identifier) override + { + if (skipIt()) + return false; + + ++m_depth; + return false; + } + + bool visit([[maybe_unused]] QmlJS::AST::FieldMemberExpression *field) override + { + if (skipIt()) + return false; + + ++m_depth; + return true; + } + + void endVisit([[maybe_unused]] QmlJS::AST::CallExpression *call) override + { + if (skipIt()) + return; + + checkAndResetCal(); + } + + void endVisit(QmlJS::AST::IdentifierExpression *identifier) override + { + if (skipIt()) + return; + + m_identifier = identifier->name.toString(); + checkAndResetNonCal(); + } + + void endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) override + { + if (skipIt()) + return; + + m_fields << fieldExpression->name.toString(); + checkAndResetNonCal(); + } + + void endVisit(QmlJS::AST::StringLiteral *stringLiteral) override + { + if (skipIt()) + return; + m_rhs = stringLiteral->value.toString(); + m_specified = true; + } + + void endVisit(QmlJS::AST::NumericLiteral *numericLiteral) override + { + if (skipIt()) + return; + m_rhs = numericLiteral->value; + m_specified = true; + } + + void endVisit([[maybe_unused]] QmlJS::AST::TrueLiteral *trueLiteral) override + { + if (skipIt()) + return; + m_rhs = true; + m_specified = true; + } + + void endVisit([[maybe_unused]] QmlJS::AST::FalseLiteral *falseLiteral) override + { + if (skipIt()) + return; + m_rhs = false; + m_specified = true; + } + + void throwRecursionDepthError() override + { + setFailed(); + qDebug() << Q_FUNC_INFO << this; + } + + void checkAndResetCal() + { + if (--m_depth == 0) { + m_rhs = ConnectionEditorStatements::MatchedFunction{m_identifier, m_fields.join(".")}; + m_specified = true; + + m_identifier.clear(); + m_fields.clear(); + } + } + + void checkAndResetNonCal() + { + if (--m_depth == 0) { + m_rhs = ConnectionEditorStatements::Variable{m_identifier, m_fields.join(".")}; + m_specified = true; + + m_identifier.clear(); + m_fields.clear(); + } + } + + bool skipIt() { return m_failed || m_specified; } + + void setFailed() { m_failed = true; } + +private: + bool m_failed = false; + bool m_specified = false; + int m_depth = 0; + QString m_identifier; + QStringList m_fields; + ConnectionEditorStatements::RightHandSide m_rhs; +}; + +MatchedStatement checkForStateSet(const MatchedStatement ¤tState) +{ + using namespace ConnectionEditorStatements; + return std::visit( + Overload{[](const PropertySet &propertySet) -> MatchedStatement { + if (propertySet.lhs.nodeId.size() && propertySet.lhs.propertyName == u"state" + && std::holds_alternative(propertySet.rhs)) + return StateSet{propertySet.lhs.nodeId, + ConnectionEditorStatements::toString(propertySet.rhs)}; + return propertySet; + }, + [](const auto &pSet) -> MatchedStatement { return pSet; }}, + currentState); +} + +class ConsoleLogEvaluator : public QmlJS::AST::Visitor +{ +public: + bool isValid() { return m_completed; } + + ConnectionEditorStatements::ConsoleLog expression() + { + return ConnectionEditorStatements::ConsoleLog{m_arg}; + } + +protected: + bool preVisit(QmlJS::AST::Node *node) override + { + if (m_failed) + return false; + + switch (m_stepId) { + case 0: { + if (node->kind != Kind::Kind_CallExpression) + m_failed = true; + } break; + case 1: { + if (node->kind != Kind::Kind_FieldMemberExpression) + m_failed = true; + } break; + case 2: { + if (node->kind != Kind::Kind_IdentifierExpression) + m_failed = true; + } break; + case 3: { + if (node->kind != Kind::Kind_ArgumentList) + m_failed = true; + } break; + } + + m_stepId++; + if (m_failed || m_completed) + return false; + + return true; + } + + bool visit(QmlJS::AST::IdentifierExpression *identifier) override + { + if (m_completed) + return true; + + const QStringView identifierName = identifier->name; + if (identifierName != u"console") { + m_failed = true; + return false; + } + return true; + } + + bool visit(QmlJS::AST::FieldMemberExpression *fieldExpression) override + { + if (m_completed) + return true; + + const QStringView fieldName = fieldExpression->name; + if (fieldName != u"log") { + m_failed = true; + return false; + } + return true; + } + + bool visit(QmlJS::AST::ArgumentList *arguments) override + { + if (m_completed) + return true; + + if (arguments->next) { + m_failed = true; + return false; + } + m_completed = true; + RightHandVisitor rVis; + arguments->expression->accept(&rVis); + m_arg = rVis.rhs(); + return true; + } + + void throwRecursionDepthError() override + { + m_failed = true; + qDebug() << Q_FUNC_INFO << this; + } + +private: + bool m_failed = false; + bool m_completed = false; + int m_stepId = 0; + ConnectionEditorStatements::RightHandSide m_arg; +}; + +struct StatementReply +{ + const TrackingArea area; + + MatchedStatement *operator()(MatchedStatement &handler) const { return &handler; } + + MatchedStatement *operator()(ConditionalStatement &handler) const + { + switch (area) { + case TrackingArea::Ko: + return &handler.ko; + case TrackingArea::Ok: + return &handler.ok; + case TrackingArea::Condition: + default: + return nullptr; + } + } +}; + +} // namespace + +class QmlDesigner::ConnectionEditorEvaluatorPrivate +{ + friend class ConnectionEditorEvaluator; + using Status = ConnectionEditorEvaluator::Status; + +public: + bool checkValidityAndReturn(bool valid, const QString &parseError = {}); + + void setStatus(Status status) { m_checkStatus = status; } + + NodeStatus parentNodeStatus() const { return nodeStatus(1); } + + NodeStatus nodeStatus(int reverseLevel = 0) const; + + TrackingArea trackingArea() const { return m_trackingArea; } + + void setTrackingArea(bool ifFound, int ifCurrentChildren) + { + if (!ifFound) { + m_trackingArea = TrackingArea::No; + return; + } + switch (ifCurrentChildren) { + case 1: + m_trackingArea = TrackingArea::Condition; + break; + case 2: + m_trackingArea = TrackingArea::Ok; + break; + case 3: + m_trackingArea = TrackingArea::Ko; + break; + default: + m_trackingArea = TrackingArea::No; + } + } + + int childLevelOfTheClosestItem(Kind kind) + { + for (const NodeStatus &status : m_nodeHierarchy | Utils::views::reverse) { + if (status == kind) + return status.childId(); + } + return -1; + } + + bool isInIfCondition() const { return m_trackingArea == TrackingArea::Condition; } + + MatchedStatement *currentStatement() + { + return std::visit(StatementReply{m_trackingArea}, m_handler); + } + + MatchedCondition *currentCondition() + { + if (auto handler = std::get_if(&m_handler)) + return &handler->condition; + return nullptr; + } + + void addVariableCondition(Node *node) + { + if (MatchedCondition *currentCondition = this->currentCondition()) { + if (currentCondition->statements.isEmpty()) { + RightHandVisitor varVisitor; + node->accept(&varVisitor); + if (varVisitor.couldBeVariable()) + currentCondition->statements.append(varVisitor.variable()); + } + } + } + +private: + int m_ifStatement = 0; + int m_consoleLogCount = 0; + int m_consoleIdentifierCount = 0; + bool m_acceptLogArgument = false; + TrackingArea m_trackingArea = TrackingArea::No; + QString m_errorString; + Status m_checkStatus = Status::UnStarted; + QList m_nodeHierarchy; + ConnectionEditorStatements::Handler m_handler; +}; + +ConnectionEditorEvaluator::ConnectionEditorEvaluator() + : d(std::make_unique()) +{} + +ConnectionEditorEvaluator::~ConnectionEditorEvaluator() {} + +ConnectionEditorEvaluator::Status ConnectionEditorEvaluator::status() const +{ + return d->m_checkStatus; +} + +ConnectionEditorStatements::Handler ConnectionEditorEvaluator::resultNode() const +{ + return (d->m_checkStatus == Succeeded) ? d->m_handler : ConnectionEditorStatements::EmptyBlock{}; +} + +QString ConnectionEditorEvaluator::getDisplayStringForType(const QString &statement) +{ + ConnectionEditorEvaluator evaluator; + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( + ""), + QmlJS::Dialect::JavaScript); + + newDoc->setSource(statement); + newDoc->parseJavaScript(); + + if (!newDoc->isParsedCorrectly()) + return ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + + newDoc->ast()->accept(&evaluator); + + const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; + + if (!valid) + return ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + + auto result = evaluator.resultNode(); + + return QmlDesigner::ConnectionEditorStatements::toDisplayName(result); +} + +ConnectionEditorStatements::Handler ConnectionEditorEvaluator::parseStatement(const QString &statement) +{ + ConnectionEditorEvaluator evaluator; + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( + ""), + QmlJS::Dialect::JavaScript); + + newDoc->setSource(statement); + newDoc->parseJavaScript(); + + if (!newDoc->isParsedCorrectly()) + return ConnectionEditorStatements::EmptyBlock{}; + + newDoc->ast()->accept(&evaluator); + + const bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; + + if (!valid) + return ConnectionEditorStatements::EmptyBlock{}; + + return evaluator.resultNode(); +} + +bool ConnectionEditorEvaluator::preVisit(Node *node) +{ + if (d->m_nodeHierarchy.size()) { + NodeStatus &parentNode = d->m_nodeHierarchy.last(); + parentNode.increaseChildNo(); + if (parentNode == Kind::Kind_IfStatement) + d->setTrackingArea(true, parentNode.children()); + } + + d->m_nodeHierarchy.append(node); + + switch (node->kind) { + case Kind::Kind_Program: + return true; + case Kind::Kind_StatementList: + case Kind::Kind_IfStatement: + case Kind::Kind_Block: + case Kind::Kind_IdentifierExpression: + case Kind::Kind_BinaryExpression: + case Kind::Kind_FieldMemberExpression: + case Kind::Kind_ExpressionStatement: + case Kind::Kind_Expression: + case Kind::Kind_CallExpression: + case Kind::Kind_TrueLiteral: + case Kind::Kind_FalseLiteral: + case Kind::Kind_StringLiteral: + case Kind::Kind_NumericLiteral: + case Kind::Kind_ArgumentList: + return status() == UnFinished; + default: + return false; + } +} + +void ConnectionEditorEvaluator::postVisit(QmlJS::AST::Node *node) +{ + if (d->m_nodeHierarchy.isEmpty()) { + d->checkValidityAndReturn(false, "Unexpected post visiting"); + return; + } + if (d->m_nodeHierarchy.last() == node->kind) { + d->m_nodeHierarchy.removeLast(); + } else { + d->checkValidityAndReturn(false, "Post visiting kind does not match"); + return; + } + + if (node->kind == Kind::Kind_IfStatement) { + bool ifFound = false; + int closestIfChildren = 0; + for (const NodeStatus &nodeStatus : d->m_nodeHierarchy | Utils::views::reverse) { + if (nodeStatus == Kind::Kind_IfStatement) { + ifFound = true; + closestIfChildren = nodeStatus.children(); + break; + } + } + d->setTrackingArea(ifFound, closestIfChildren); + } +} + +bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Program *program) +{ + d->setStatus(UnFinished); + d->setTrackingArea(false, 0); + d->m_ifStatement = 0; + d->m_consoleLogCount = 0; + d->m_consoleIdentifierCount = 0; + d->m_handler = ConnectionEditorStatements::EmptyBlock{}; + return true; +} + +bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement) +{ + if (d->m_ifStatement++) + return d->checkValidityAndReturn(false, "Nested if conditions are not supported"); + + if (ifStatement->ok->kind != Kind::Kind_Block) + return d->checkValidityAndReturn(false, "True block should be in a curly bracket."); + + if (ifStatement->ko && ifStatement->ko->kind != Kind::Kind_Block) + return d->checkValidityAndReturn(false, "False block should be in a curly bracket."); + + d->m_handler = ConditionalStatement{}; + return d->checkValidityAndReturn(true); +} + +bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifier) +{ + if (d->parentNodeStatus() == Kind::Kind_FieldMemberExpression) + if (d->m_consoleLogCount) + d->m_consoleIdentifierCount++; + + d->addVariableCondition(identifier); + + return d->checkValidityAndReturn(true); +} + +bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression) +{ + if (d->isInIfCondition()) { + if (binaryExpression->op == QSOperator::Assign) + return d->checkValidityAndReturn(false, "Assignment as a condition."); + + if (!isAcceptedIfBinaryOperator(binaryExpression->op)) + return d->checkValidityAndReturn(false, "Operator is not supported."); + + MatchedCondition *matchedCondition = d->currentCondition(); + if (!matchedCondition) + return d->checkValidityAndReturn(false, "Matched condition is not available."); + + BoolCondition conditionVisitor; + binaryExpression->accept(&conditionVisitor); + + if (conditionVisitor.isValid()) { + *matchedCondition = conditionVisitor.matchedCondition(); + } else { + return d->checkValidityAndReturn(false, + "Matched condition is not valid. \"" + + conditionVisitor.errorString() + "\""); + } + + return false; + } else { + MatchedStatement *currentStatement = d->currentStatement(); + if (currentStatement && ConnectionEditorStatements::isEmptyStatement(*currentStatement) + && d->parentNodeStatus().childId() == 0) { + if (binaryExpression->op == QSOperator::Assign) { + RightHandVisitor variableVisitor; + binaryExpression->left->accept(&variableVisitor); + + if (!variableVisitor.couldBeLHS()) + return d->checkValidityAndReturn(false, "Invalid left hand."); + + ConnectionEditorStatements::Variable lhs = variableVisitor.lhs(); + + variableVisitor.reset(); + binaryExpression->right->accept(&variableVisitor); + + if (variableVisitor.couldBeLHS()) { + ConnectionEditorStatements::Assignment assignment{lhs, variableVisitor.variable()}; + *currentStatement = assignment; + } else if (variableVisitor.isLiteralType()) { + ConnectionEditorStatements::PropertySet propSet{lhs, variableVisitor.literal()}; + *currentStatement = propSet; + } else { + return d->checkValidityAndReturn(false, "Invalid RHS"); + } + + *currentStatement = checkForStateSet(*currentStatement); + } + } + } + + return d->checkValidityAndReturn(true); +} + +bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldExpression) +{ + if (d->parentNodeStatus() == Kind::Kind_CallExpression && fieldExpression->name == u"log") + d->m_consoleLogCount++; + + d->addVariableCondition(fieldExpression); + + return d->checkValidityAndReturn(true); +} + +bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression) +{ + if (d->isInIfCondition()) + d->checkValidityAndReturn(false, "Functions are not allowd in the expressions"); + + MatchedStatement *currentStatement = d->currentStatement(); + if (!currentStatement) + d->checkValidityAndReturn(false, "Invalid place to call an expression"); + + if (ConnectionEditorStatements::isEmptyStatement(*currentStatement)) { + if (d->parentNodeStatus().childId() == 0) { + ConsoleLogEvaluator logEvaluator; + callExpression->accept(&logEvaluator); + if (logEvaluator.isValid()) { + *currentStatement = logEvaluator.expression(); // Console Log + } else { + RightHandVisitor callVisitor; + + callExpression->accept(&callVisitor); + if (callVisitor.isValid()) { + ConnectionEditorStatements::RightHandSide rhs = callVisitor.rhs(); + if (auto rhs_ = std::get_if(&rhs)) + *currentStatement = *rhs_; + else + return d->checkValidityAndReturn(false, "Invalid Matched Function type."); + } else { + return d->checkValidityAndReturn(false, "Invalid Matched Function"); + } + } + } + } + return d->checkValidityAndReturn(true); +} + +bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Block *block) +{ + Kind parentKind = d->parentNodeStatus(); + + if (parentKind == Kind::Kind_IfStatement) { + return d->checkValidityAndReturn(true); + } else if (d->parentNodeStatus() == Kind::Kind_StatementList) { + if (d->nodeStatus(2) == Kind::Kind_Program) + return d->checkValidityAndReturn(true); + } + + return d->checkValidityAndReturn(false, "Block count ptoblem"); +} + +bool ConnectionEditorEvaluator::visit(QmlJS::AST::ArgumentList *arguments) +{ + if (d->trackingArea() == TrackingArea::Condition) + return d->checkValidityAndReturn(false, "Arguments are not supported in if condition"); + + auto currentStatement = d->currentStatement(); + if (!currentStatement) + return d->checkValidityAndReturn(false, "No statement found for argument"); + + if (!ConnectionEditorStatements::isConsoleLog(*currentStatement)) + return d->checkValidityAndReturn(false, "Arguments are only supported for console.log"); + + if (d->m_acceptLogArgument && !arguments->next) + return d->checkValidityAndReturn(true); + + return d->checkValidityAndReturn(false, "The only supported argument is in console.log"); +} + +void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::Program *program) +{ + if (status() == UnFinished) + d->setStatus(Succeeded); +} + +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) +{ + if (status() != UnFinished) + return; + + if (fieldExpression->name == u"log") { + if (d->m_consoleIdentifierCount != d->m_consoleLogCount) { + d->m_acceptLogArgument = false; + } else { + d->m_consoleIdentifierCount--; + d->m_acceptLogArgument = true; + } + --(d->m_consoleLogCount); + } +} + +void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::CallExpression *callExpression) +{ + d->m_acceptLogArgument = false; +} + +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/) +{ + if (status() != UnFinished) + return; + + if (auto statement = std::get_if(&d->m_handler)) { + if (!isEmptyStatement(statement->ok) && !isEmptyStatement(statement->ko)) { + if (statement->ok.index() != statement->ko.index()) + d->checkValidityAndReturn(false, "Matched statements types are mismatched"); + } + } +} + +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statementList*/) +{ + if (status() != UnFinished) + return; + + if (d->nodeStatus().children() > 1) + d->checkValidityAndReturn(false, "More than one statements are available."); +} + +void ConnectionEditorEvaluator::throwRecursionDepthError() +{ + d->checkValidityAndReturn(false, "Recursion depth problem"); + qDebug() << Q_FUNC_INFO << this; +} + +bool ConnectionEditorEvaluatorPrivate::checkValidityAndReturn(bool valid, const QString &parseError) +{ + if (!valid) { + if (m_checkStatus != Status::Failed) { + setStatus(Status::Failed); + m_errorString = parseError; + qDebug() << Q_FUNC_INFO << "Error: " << parseError; + } + } + + return m_checkStatus; +} + +NodeStatus ConnectionEditorEvaluatorPrivate::nodeStatus(int reverseLevel) const +{ + if (m_nodeHierarchy.size() > reverseLevel) + return m_nodeHierarchy.at(m_nodeHierarchy.size() - reverseLevel - 1); + return {}; +} diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h new file mode 100644 index 00000000000..2cc285ed6c5 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "connectioneditorstatements.h" + +#include "qmldesigner_global.h" + +#include + +namespace QmlDesigner { + +class ConnectionEditorEvaluatorPrivate; + +class QMLDESIGNER_EXPORT ConnectionEditorEvaluator : public QmlJS::AST::Visitor +{ +public: + enum Status { UnStarted, UnFinished, Succeeded, Failed }; + + ConnectionEditorEvaluator(); + virtual ~ConnectionEditorEvaluator(); + + Status status() const; + ConnectionEditorStatements::Handler resultNode() const; + + static QString getDisplayStringForType(const QString &statement); + static ConnectionEditorStatements::Handler parseStatement(const QString &statement); + +protected: + bool preVisit(QmlJS::AST::Node *node) override; + void postVisit(QmlJS::AST::Node *node) override; + + bool visit(QmlJS::AST::Program *program) override; + bool visit(QmlJS::AST::IfStatement *ifStatement) override; + bool visit(QmlJS::AST::IdentifierExpression *identifier) override; + bool visit(QmlJS::AST::BinaryExpression *binaryExpression) override; + bool visit(QmlJS::AST::FieldMemberExpression *fieldExpression) override; + bool visit(QmlJS::AST::CallExpression *callExpression) override; + bool visit(QmlJS::AST::Block *block) override; + bool visit(QmlJS::AST::ArgumentList *arguments) override; + + void endVisit(QmlJS::AST::Program *program) override; + void endVisit(QmlJS::AST::FieldMemberExpression *fieldExpression) override; + void endVisit(QmlJS::AST::CallExpression *callExpression) override; + void endVisit(QmlJS::AST::IfStatement *ifStatement) override; + void endVisit(QmlJS::AST::StatementList *statementList) override; + + void throwRecursionDepthError() override; + +private: + std::unique_ptr d; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp new file mode 100644 index 00000000000..5fbad9acadb --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -0,0 +1,381 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "connectioneditorstatements.h" + +#include + +using namespace QmlDesigner; +using namespace ConnectionEditorStatements; + +namespace { +template +struct Overload : Ts... +{ + using Ts::operator()...; +}; +template +Overload(Ts...) -> Overload; + +inline constexpr QStringView trueString{u"true"}; +inline constexpr QStringView falseString{u"false"}; + +struct StringVisitor +{ + QString operator()(const bool &bVal) const + { + return (bVal ? trueString : falseString).toString(); + } + + QString operator()(const double &dVal) const { return QString::number(dVal); } + + QString operator()(const QString &str) const { return "\"" + str + "\""; } + + QString operator()(const Variable &var) + { + QString propertyName; + if (var.propertyName.size()) + propertyName = "."; + propertyName.append(var.propertyName); + return "Variable{" + var.nodeId + propertyName + "}"; + } + + QString operator()(const ConnectionEditorStatements::MatchedFunction &func) + { + return "MatchedFunction{" + func.nodeId + "." + func.functionName + "}"; + } + + QString operator()(const ConnectionEditorStatements::Assignment &assignment) + { + return "Assignment{" + assignment.lhs.expression() + " = " + StringVisitor()(assignment.rhs) + + "}"; + } + + QString operator()(const ConnectionEditorStatements::PropertySet &propertySet) + { + return "PropertySet{" + propertySet.lhs.expression() + " = " + + std::visit(StringVisitor{}, propertySet.rhs) + "}"; + } + + QString operator()(const ConnectionEditorStatements::StateSet &stateSet) + { + return "StateSet{" + stateSet.nodeId + ".state = " + stateSet.stateName + "}"; + } + + QString operator()(const ConnectionEditorStatements::EmptyBlock &) { return "EmptyBlock{}"; } + + QString operator()(const ConnectionEditorStatements::ConsoleLog &consoleLog) + { + return "ConsoleLog{" + std::visit(StringVisitor{}, consoleLog.argument) + "}"; + } + + QString operator()(const ConditionToken &token) + { + switch (token) { + case ConditionToken::Not: + return "Not"; + case ConditionToken::And: + return "And"; + case ConditionToken::Or: + return "Or"; + case ConditionToken::LargerThan: + return "LargerThan"; + case ConditionToken::LargerEqualsThan: + return "LargerEuqalsThan"; + case ConditionToken::SmallerThan: + return "SmallerThan"; + case ConditionToken::SmallerEqualsThan: + return "SmallerEqualsThan"; + case ConditionToken::Equals: + return "Equals"; + default: + return {}; + } + } + + QString operator()(const ConnectionEditorStatements::MatchedCondition &matched) + { + if (!matched.statements.size() && !matched.tokens.size()) + return "MatchedCondition{}"; + + if (matched.statements.size() != matched.tokens.size() + 1) + return "MatchedCondition{Invalid}"; + + QString value = "MatchedCondition{"; + int i = 0; + for (i = 0; i < matched.tokens.size(); i++) { + const ComparativeStatement &statement = matched.statements[i]; + const ConditionToken &token = matched.tokens[i]; + value += std::visit(StringVisitor{}, statement) + " "; + value += StringVisitor()(token) + " "; + } + value += std::visit(StringVisitor{}, matched.statements[i]); + value += "}"; + return value; + } + + QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) + { + QString value; + value.reserve(200); + value = "IF ("; + value += StringVisitor()(conditional.condition); + value += ") {\n"; + value += std::visit(StringVisitor{}, conditional.ok); + if (!isEmptyStatement(conditional.ko)) { + value += "\n} ELSE {\n"; + value += std::visit(StringVisitor{}, conditional.ko); + } + value += "\n}"; + + return value; + } + + QString operator()(const ConnectionEditorStatements::MatchedStatement &conditional) + { + return std::visit(StringVisitor{}, conditional); + } +}; + +struct JSOverload +{ + QString operator()(const bool &bVal) const + { + return (bVal ? trueString : falseString).toString(); + } + + QString operator()(const double &dVal) const { return QString::number(dVal); } + + QString operator()(const QString &str) const { return "\"" + str + "\""; } + + QString operator()(const Variable &var) + { + QString propertyName; + if (var.propertyName.size()) + propertyName = "."; + propertyName.append(var.propertyName); + return var.nodeId + propertyName; + } + + QString operator()(const ConnectionEditorStatements::MatchedFunction &func) + { + QString funcName; + if (func.functionName.size()) + funcName = "."; + funcName.append(func.functionName); + return func.nodeId + funcName + "()"; + } + + QString operator()(const ConnectionEditorStatements::Assignment &assignment) + { + return JSOverload()(assignment.lhs) + " = " + JSOverload()(assignment.rhs); + } + + QString operator()(const ConnectionEditorStatements::PropertySet &propertySet) + { + return JSOverload()(propertySet.lhs) + " = " + std::visit(JSOverload{}, propertySet.rhs); + } + + QString operator()(const ConnectionEditorStatements::StateSet &stateSet) + { + return stateSet.nodeId + ".state = " + stateSet.stateName; + } + + QString operator()(const ConnectionEditorStatements::EmptyBlock &) { return "{}"; } + + QString operator()(const ConnectionEditorStatements::ConsoleLog &consoleLog) + { + return "console.log(" + std::visit(JSOverload{}, consoleLog.argument) + ")"; + } + + QString operator()(const ConditionToken &token) { return toJavascript(token); } + + QString operator()(const ConnectionEditorStatements::MatchedCondition &matched) + { + if (!matched.statements.size() && !matched.tokens.size()) + return {}; + + if (matched.statements.size() != matched.tokens.size() + 1) + return {}; + + QString value; + int i = 0; + for (i = 0; i < matched.tokens.size(); i++) { + const ComparativeStatement &statement = matched.statements[i]; + const ConditionToken &token = matched.tokens[i]; + value += std::visit(JSOverload{}, statement) + " "; + value += JSOverload()(token) + " "; + } + value += std::visit(JSOverload{}, matched.statements[i]); + return value; + } + + QString operator()(const ConnectionEditorStatements::MatchedStatement &statement) + { + if (isEmptyStatement(statement)) + return {}; + + return std::visit(JSOverload{}, statement); + } + + QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) + { + QString value; + value.reserve(200); + value = "if ("; + value += JSOverload()(conditional.condition); + value += ") {\n"; + + if (!isEmptyStatement(conditional.ok)) + value += JSOverload()(conditional.ok); + + if (!isEmptyStatement(conditional.ko)) { + value += "\n} else {\n"; + value += JSOverload()(conditional.ko); + } + value += "\n}"; + + return value; + } +}; + +} // namespace + +bool ConnectionEditorStatements::isEmptyStatement(const MatchedStatement &stat) +{ + return std::holds_alternative(stat); +} + +QString ConnectionEditorStatements::toString(const ComparativeStatement &stat) +{ + return std::visit(StringVisitor{}, stat); +} + +QString ConnectionEditorStatements::toString(const RightHandSide &rhs) +{ + return std::visit(StringVisitor{}, rhs); +} + +QString ConnectionEditorStatements::toString(const Literal &literal) +{ + return std::visit(StringVisitor{}, literal); +} + +QString ConnectionEditorStatements::toString(const MatchedStatement &statement) +{ + return std::visit(StringVisitor{}, statement); +} + +QString ConnectionEditorStatements::toString(const Handler &handler) +{ + return std::visit(StringVisitor{}, handler); +} + +QString ConnectionEditorStatements::toJavascript(const Handler &handler) +{ + return std::visit(JSOverload{}, handler); +} + +bool ConnectionEditorStatements::isConsoleLog(const MatchedStatement &curState) +{ + return std::holds_alternative(curState); +} + +bool ConnectionEditorStatements::isLiteralType(const RightHandSide &var) +{ + return std::visit(Overload{[](const double &) { return true; }, + [](const bool &) { return true; }, + [](const QString &) { return true; }, + [](const auto &) { return false; }}, + var); +} + +QString ConnectionEditorStatements::toDisplayName(const MatchedStatement &statement) +{ + const char *displayName = std::visit( + Overload{[](const MatchedFunction &) { return FUNCTION_DISPLAY_NAME; }, + [](const Assignment &) { return ASSIGNMENT_DISPLAY_NAME; }, + [](const PropertySet &) { return SETPROPERTY_DISPLAY_NAME; }, + [](const StateSet &) { return SETSTATE_DISPLAY_NAME; }, + [](const ConsoleLog &) { return LOG_DISPLAY_NAME; }, + [](const EmptyBlock &) { return EMPTY_DISPLAY_NAME; }}, + statement); + + return QString::fromLatin1(displayName); +} + +QString ConnectionEditorStatements::toDisplayName(const Handler &handler) +{ + const MatchedStatement &statement = std::visit( + Overload{[](const MatchedStatement &statement) { return statement; }, + [](const ConditionalStatement &statement) { return statement.ok; }}, + handler); + return toDisplayName(statement); +} + +MatchedStatement &ConnectionEditorStatements::okStatement( + ConnectionEditorStatements::Handler &handler) +{ + MatchedStatement statement; + + return std::visit(Overload{[](ConnectionEditorStatements::MatchedStatement &var) + -> MatchedStatement & { return var; }, + [](ConnectionEditorStatements::ConditionalStatement &statement) + -> MatchedStatement & { return statement.ok; }}, + handler); +} + +MatchedStatement &ConnectionEditorStatements::koStatement( + ConnectionEditorStatements::Handler &handler) +{ + static MatchedStatement block; + + if (auto *statement = std::get_if(&handler)) + return statement->ko; + + return block; +} + +MatchedCondition &ConnectionEditorStatements::matchedCondition(Handler &handler) +{ + static MatchedCondition block; + + if (auto *statement = std::get_if(&handler)) + return statement->condition; + + return block; +} + +ConditionalStatement &ConnectionEditorStatements::conditionalStatement( + ConnectionEditorStatements::Handler &handler) +{ + static ConditionalStatement block; + + if (auto *statement = std::get_if(&handler)) + return *statement; + + return block; +} + +QString ConnectionEditorStatements::toJavascript(const ConditionToken &token) +{ + switch (token) { + case ConditionToken::Not: + return "!=="; + case ConditionToken::And: + return "&&"; + case ConditionToken::Or: + return "||"; + case ConditionToken::LargerThan: + return ">"; + case ConditionToken::LargerEqualsThan: + return ">="; + case ConditionToken::SmallerThan: + return "<"; + case ConditionToken::SmallerEqualsThan: + return "<="; + case ConditionToken::Equals: + return "==="; + default: + return {}; + }; +} diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h new file mode 100644 index 00000000000..63d457a84a4 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -0,0 +1,134 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace QmlDesigner { +namespace ConnectionEditorStatements { + +inline constexpr char FUNCTION_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Function"); +inline constexpr char ASSIGNMENT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Assignment"); +inline constexpr char SETPROPERTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Set Property"); +inline constexpr char SETSTATE_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Set State"); +inline constexpr char LOG_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Print"); +inline constexpr char EMPTY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Empty"); +inline constexpr char UNKNOWN_DISPLAY_NAME[] = QT_TRANSLATE_NOOP( + "QmlDesigner::ConnectionEditorStatements", "Unknown"); + +struct Variable; +struct MatchedFunction; +struct Assignment; +struct PropertySet; +struct StateSet; +struct ConsoleLog; +struct MatchedCondition; +struct ConditionalStatement; + +using EmptyBlock = std::monostate; +using Literal = std::variant; +using ComparativeStatement = std::variant; +using RightHandSide = std::variant; +using MatchedStatement = std::variant; +using Handler = std::variant; + +struct Variable +{ //Only one level for now (.) + QString nodeId; + QString propertyName; + + QString expression() const + { + if (propertyName.isEmpty()) + return nodeId; + return nodeId + "." + propertyName; + } +}; + +struct MatchedFunction +{ //First item before "." is considered node + QString nodeId; + QString functionName; +}; + +struct Assignment +{ + Variable lhs; //There always should be a binding property on the left hand side. The first identifier is considered node + Variable rhs; //Similar to function but no function call. Just regular FieldExpression/binding +}; + +struct PropertySet +{ + Variable lhs; //There always should be a binding property on the left hand side. The first identifier is considered node + Literal rhs; +}; + +struct StateSet +{ + QString nodeId; + QString stateName; +}; + +struct ConsoleLog +{ + RightHandSide argument; +}; + +enum class ConditionToken { + Unknown, + Not, + And, + Or, + LargerThan, + LargerEqualsThan, + SmallerThan, + SmallerEqualsThan, + Equals +}; + +struct MatchedCondition +{ + QList tokens; + QList statements; +}; + +struct ConditionalStatement +{ + MatchedStatement ok = EmptyBlock{}; + MatchedStatement ko = EmptyBlock{}; //else statement + MatchedCondition condition; +}; + +QMLDESIGNER_EXPORT bool isEmptyStatement(const MatchedStatement &stat); +QMLDESIGNER_EXPORT QString toString(const ComparativeStatement &stat); +QMLDESIGNER_EXPORT QString toString(const RightHandSide &rhs); +QMLDESIGNER_EXPORT QString toString(const Literal &literal); +QMLDESIGNER_EXPORT QString toString(const MatchedStatement &statement); + +QMLDESIGNER_EXPORT bool isConsoleLog(const MatchedStatement &curState); +QMLDESIGNER_EXPORT bool isLiteralType(const RightHandSide &var); + +QMLDESIGNER_EXPORT QString toString(const Handler &handler); +QMLDESIGNER_EXPORT QString toJavascript(const Handler &handler); +QMLDESIGNER_EXPORT QString toDisplayName(const MatchedStatement &statement); +QMLDESIGNER_EXPORT QString toDisplayName(const Handler &handler); +QMLDESIGNER_EXPORT QString toJavascript(const ConditionToken &token); + +QMLDESIGNER_EXPORT MatchedStatement &okStatement(ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement &koStatement(ConnectionEditorStatements::Handler &handler); + +QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement( + ConnectionEditorStatements::Handler &handler); + +} // namespace ConnectionEditorStatements + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp new file mode 100644 index 00000000000..d43ac648a6f --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -0,0 +1,405 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "connectioneditorutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +void callLater(const std::function &fun) +{ + QTimer::singleShot(0, fun); +} + +void showErrorMessage(const QString &text) +{ + callLater([text]() { QMessageBox::warning(nullptr, Tr::tr("Error"), text); }); +} + +QString idOrTypeName(const ModelNode &modelNode) +{ + QString idLabel = modelNode.id(); + if (idLabel.isEmpty()) + idLabel = modelNode.simplifiedTypeName(); + return idLabel; +} + +PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode) +{ + PropertyName name = suggestion; + if (!modelNode.isValid() || !modelNode.metaInfo().isValid()) + return name; + + int i = 0; + while (true) { + if (!modelNode.hasProperty(name) && !modelNode.metaInfo().hasProperty(name)) + return name; + name = suggestion + QString::number(i++).toLatin1(); + } + return {}; +} + +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property) +{ + // Note: Uses old mechanism to create the NodeMetaInfo and supports + // only types we care about in the connection editor. + // TODO: Support all possible AbstractProperty types and move to the + // AbstractProperty class. + if (property.dynamicTypeName() == "bool") + return property.model()->boolMetaInfo(); + else if (property.dynamicTypeName() == "int") + return property.model()->metaInfo("QML.int"); + else if (property.dynamicTypeName() == "real") + return property.model()->metaInfo("QML.real"); + else if (property.dynamicTypeName() == "color") + return property.model()->metaInfo("QML.color"); + else if (property.dynamicTypeName() == "string") + return property.model()->metaInfo("QML.string"); + else if (property.dynamicTypeName() == "url") + return property.model()->metaInfo("QML.url"); + else if (property.dynamicTypeName() == "variant") + return property.model()->metaInfo("QML.variant"); + else + qWarning() << __FUNCTION__ << " type " << property.dynamicTypeName() << "not found"; + return { }; +} + +bool metaInfoIsCompatibleUnsafe(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType) +{ + if (sourceType.isVariant()) + return true; + + if (sourceType.isBool() && targetType.isBool()) + return true; + + if (sourceType == targetType) + return true; + + if (sourceType.isNumber() && targetType.isNumber()) + return true; + + if (sourceType.isString() && targetType.isString()) + return true; + + if (sourceType.isUrl() && targetType.isUrl()) + return true; + + if (sourceType.isColor() && targetType.isColor()) + return true; + + return false; +} + +bool metaInfoIsCompatible(const NodeMetaInfo &sourceType, const PropertyMetaInfo &metaInfo) +{ + NodeMetaInfo targetType = metaInfo.propertyType(); + return metaInfoIsCompatibleUnsafe(sourceType, targetType); +} + +QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName) +{ + QVariant returnValue = variant; + + if (typeName == "int") { + bool ok; + returnValue = variant.toInt(&ok); + if (!ok) + returnValue = 0; + } else if (typeName == "real") { + bool ok; + returnValue = variant.toReal(&ok); + if (!ok) + returnValue = 0.0; + + } else if (typeName == "string") { + returnValue = variant.toString(); + + } else if (typeName == "bool") { + returnValue = variant.toBool(); + } else if (typeName == "url") { + returnValue = variant.toUrl(); + } else if (typeName == "color") { + if (QColor::isValidColor(variant.toString())) + returnValue = variant.toString(); + else + returnValue = QColor(Qt::black); + } else if (typeName == "vector2d") { + returnValue = "Qt.vector2d(0, 0)"; + } else if (typeName == "vector3d") { + returnValue = "Qt.vector3d(0, 0, 0)"; + } else if (typeName == "vector4d") { + returnValue = "Qt.vector4d(0, 0, 0 ,0)"; + } else if (typeName == "TextureInput") { + returnValue = "null"; + } else if (typeName == "alias") { + returnValue = "null"; + } else if (typeName == "Item") { + returnValue = "null"; + } + + return returnValue; +} + +template +void convertPropertyType(const T &property, const QVariant &value) +{ + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + if (!node.isValid()) + return; + + PropertyName name = property.name(); + TypeName type = property.dynamicTypeName(); + node.removeProperty(name); + + if constexpr (std::is_same_v) { + BindingProperty newProperty = node.bindingProperty(name); + if (newProperty.isValid()) + newProperty.setDynamicTypeNameAndExpression(type, value.toString()); + } else if constexpr (std::is_same_v) { + VariantProperty newProperty = node.variantProperty(name); + if (newProperty.isValid()) + newProperty.setDynamicTypeNameAndValue(type, value); + } +} + +void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value) +{ + convertPropertyType(property, value); +} + +void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value) +{ + convertPropertyType(property, value); +} + +bool isBindingExpression(const QVariant& value) +{ + if (value.metaType().id() != QMetaType::QString) + return false; + + QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"); + QRegularExpressionMatch match = regexp.match(value.toString()); + return match.hasMatch(); +} + +bool isDynamicVariantPropertyType(const TypeName &type) +{ + // "variant" is considered value type as it is initialized as one. + // This may need to change if we provide any kind of proper editor for it. + static const QSet valueTypes{"int", "real", "color", "string", "bool", "url", "variant"}; + return valueTypes.contains(type); +} + +QVariant defaultValueForType(const TypeName &type) +{ + QVariant value; + if (type == "int") + value = 0; + else if (type == "real") + value = 0.0; + else if (type == "color") + value = QColor(255, 255, 255); + else if (type == "string") + value = "This is a string"; + else if (type == "bool") + value = false; + else if (type == "url") + value = ""; + else if (type == "variant") + value = ""; + + return value; +} + +QString defaultExpressionForType(const TypeName &type) +{ + QString expression; + if (type == "alias") + expression = "null"; + else if (type == "TextureInput") + expression = "null"; + else if (type == "vector2d") + expression = "Qt.vector2d(0, 0)"; + else if (type == "vector3d") + expression = "Qt.vector3d(0, 0, 0)"; + else if (type == "vector4d") + expression = "Qt.vector4d(0, 0, 0 ,0)"; + + return expression; +} + +QStringList singletonsFromView(AbstractView *view) +{ + RewriterView *rv = view->rewriterView(); + if (!rv) + return { }; + + QStringList out; + for (const QmlTypeData &data : rv->getQMLTypes()) { + if (data.isSingleton && !data.typeName.isEmpty()) + out.push_back(data.typeName); + } + return out; +} + +QStringList availableSources(AbstractView *view) +{ + QStringList sourceNodes; + for (const ModelNode &modelNode : view->allModelNodes()) { + if (!modelNode.id().isEmpty()) + sourceNodes.append(modelNode.id()); + } + std::sort(sourceNodes.begin(), sourceNodes.end()); + return singletonsFromView(view) + sourceNodes; +} + +QStringList availableTargetProperties(const BindingProperty &bindingProperty) +{ + const ModelNode modelNode = bindingProperty.parentModelNode(); + if (!modelNode.isValid()) { + qWarning() << __FUNCTION__ << " invalid model node"; + return {}; + } + + NodeMetaInfo metaInfo = modelNode.metaInfo(); + if (metaInfo.isValid()) { + const auto properties = metaInfo.properties(); + QStringList writableProperties; + writableProperties.reserve(static_cast(properties.size())); + for (const auto &property : properties) { + if (property.isWritable()) + writableProperties.push_back(QString::fromUtf8(property.name())); + } + + return writableProperties; + } + return { }; +} + +ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const ModelNode &targetNode) +{ + if (id != QLatin1String("parent")) + return view->modelNodeForId(id); + + if (targetNode.hasParentProperty()) + return targetNode.parentProperty().parentModelNode(); + + return {}; +} + +QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view) +{ + const QString expression = bindingProperty.expression(); + const QStringList stringlist = expression.split(QLatin1String(".")); + + const QString &id = stringlist.constFirst(); + ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode()); + + NodeMetaInfo type; + if (bindingProperty.isDynamic()) { + type = dynamicTypeMetaInfo(bindingProperty); + } else if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) { + type = metaInfo.property(bindingProperty.name()).propertyType(); + } else + qWarning() << __FUNCTION__ << " no meta info for target node"; + + QStringList possibleProperties; + if (!modelNode.isValid()) { + QStringList singletons = singletonsFromView(view); + if (singletons.contains(id)) { + Model *model = view->model(); + QTC_ASSERT(model, return {}); + if (NodeMetaInfo metaInfo = model->metaInfo(id.toUtf8()); metaInfo.isValid()) { + for (const auto &property : metaInfo.properties()) { + if (metaInfoIsCompatible(type, property)) + possibleProperties.push_back(QString::fromUtf8(property.name())); + } + } + return possibleProperties; + } + qWarning() << __FUNCTION__ << " invalid model node"; + return {}; + } + + auto isCompatible = [type](const AbstractProperty& other) { + auto otherType = dynamicTypeMetaInfo(other); + return metaInfoIsCompatibleUnsafe(type, otherType); + }; + + for (const VariantProperty &variantProperty : modelNode.variantProperties()) { + if (variantProperty.isDynamic() && isCompatible(variantProperty)) + possibleProperties << QString::fromUtf8(variantProperty.name()); + } + + for (const BindingProperty &bindingProperty : modelNode.bindingProperties()) { + if (bindingProperty.isDynamic() && isCompatible(bindingProperty)) + possibleProperties << QString::fromUtf8((bindingProperty.name())); + } + + NodeMetaInfo metaInfo = modelNode.metaInfo(); + if (metaInfo.isValid()) { + for (const auto &property : metaInfo.properties()) { + if (metaInfoIsCompatible(type, property) ) + possibleProperties.push_back(QString::fromUtf8(property.name())); + } + } else { + qWarning() << __FUNCTION__ << " no meta info for source node"; + } + + return possibleProperties; +} + +QList dynamicPropertiesFromNode(const ModelNode &node) +{ + auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); }; + auto byName = [](const AbstractProperty &a, const AbstractProperty &b) { + return a.name() < b.name(); + }; + + QList dynamicProperties = Utils::filtered(node.properties(), isDynamic); + Utils::sort(dynamicProperties, byName); + return dynamicProperties; +} + +std::pair splitExpression(const QString &expression) +{ + // ### Todo from original code (getExpressionStrings): + // We assume no expressions yet + const QStringList stringList = expression.split(QLatin1String(".")); + + QString sourceNode = stringList.constFirst(); + QString propertyName; + for (int i = 1; i < stringList.size(); ++i) { + propertyName += stringList.at(i); + if (i != stringList.size() - 1) + propertyName += QLatin1String("."); + } + if (propertyName.isEmpty()) + std::swap(sourceNode, propertyName); + + return {sourceNode, propertyName}; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h new file mode 100644 index 00000000000..1f05d654596 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "modelfwd.h" +#include "propertymetainfo.h" + +#include +#include +#include + +namespace QmlDesigner { + +class AbstractView; +class AbstractProperty; +class BindingProperty; +class ModelNode; +class VariantProperty; + +void callLater(const std::function &fun); +void showErrorMessage(const QString &text); + +QString idOrTypeName(const ModelNode &modelNode); +PropertyName uniquePropertyName(const PropertyName &suggestion, const ModelNode &modelNode); + +NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property); +QVariant typeConvertVariant(const QVariant &variant, const QmlDesigner::TypeName &typeName); +void convertVariantToBindingProperty(const VariantProperty &property, const QVariant &value); +void convertBindingToVariantProperty(const BindingProperty &property, const QVariant &value); + +bool isBindingExpression(const QVariant& value); +bool isDynamicVariantPropertyType(const TypeName &type); +QVariant defaultValueForType(const TypeName &type); +QString defaultExpressionForType(const TypeName &type); + +QStringList availableSources(AbstractView *view); +QStringList availableTargetProperties(const BindingProperty &bindingProperty); +QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view); +QList dynamicPropertiesFromNode(const ModelNode &node); + +std::pair splitExpression(const QString &expression); + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 6fe8af59eb7..15ab1341a38 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -3,19 +3,20 @@ #include "connectionmodel.h" #include "connectionview.h" +#include "utils/algorithm.h" #include -#include -#include -#include -#include +#include #include -#include +#include #include -#include #include #include #include +#include +#include +#include +#include #include @@ -26,6 +27,8 @@ namespace { +const char defaultCondition[] = "condition"; + QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList) { QStringList stringList; @@ -46,8 +49,8 @@ bool isConnection(const QmlDesigner::ModelNode &modelNode) namespace QmlDesigner { ConnectionModel::ConnectionModel(ConnectionView *parent) - : QStandardItemModel(parent) - , m_connectionView(parent) + : QStandardItemModel(parent), m_connectionView(parent), + m_delegate(new ConnectionModelBackendDelegate(this)) { connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged); } @@ -79,11 +82,8 @@ void ConnectionModel::resetModel() for (const ModelNode &modelNode : connectionView()->allModelNodes()) addModelNode(modelNode); } - - const int columnWidthTarget = connectionView()->connectionTableView()->columnWidth(0); - connectionView()->connectionTableView()->setColumnWidth(0, columnWidthTarget - 80); - endResetModel(); + m_delegate->update(); } SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const @@ -264,7 +264,10 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP //anything else is assignment // e.g. foo.bal = foo2.bula ; foo.bal = "literal" ; goo.gal = true - item->setData("Assignment", UserRoles::ActionTypeRole); + item->setData(tr(ConnectionEditorEvaluator::getDisplayStringForType( + signalHandlerProperty.source()) + .toLatin1()), + UserRoles::ActionTypeRole); } ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const @@ -332,6 +335,8 @@ void ConnectionModel::addConnection() } newNode.signalHandlerProperty("onClicked").setSource(source); + + selectProperty(newNode.signalHandlerProperty("onClicked")); }); } } @@ -393,12 +398,43 @@ void ConnectionModel::remove(int row) deleteConnectionByRow(row); } +void ConnectionModel::setCurrentIndex(int i) +{ + if (m_currentIndex != i) { + m_currentIndex = i; + emit currentIndexChanged(); + } + m_delegate->setCurrentRow(i); +} + +int ConnectionModel::currentIndex() const +{ + return m_currentIndex; +} + +void ConnectionModel::selectProperty(const SignalHandlerProperty &property) +{ + for (int i = 0; i < rowCount(); i++) { + auto otherProperty = signalHandlerPropertyForRow(i); + + if (property == otherProperty) { + setCurrentIndex(i); + return; + } + } +} + void ConnectionModel::handleException() { QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); resetModel(); } +ConnectionModelBackendDelegate *ConnectionModel::delegate() const +{ + return m_delegate; +} + void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (topLeft != bottomRight) { @@ -553,4 +589,1442 @@ QHash ConnectionModel::roleNames() const return roleNames; } +ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent) + : QObject(parent), m_signalDelegate(parent->connectionView()), m_okStatementDelegate(parent), + m_koStatementDelegate(parent), m_conditionListModel(parent), + m_propertyTreeModel(parent->connectionView()), m_propertyListProxyModel(&m_propertyTreeModel) + +{ + connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() { + handleTargetChanged(); + }); + + connect(&m_okStatementDelegate, + &ConnectionModelStatementDelegate::statementChanged, + this, + [this]() { handleOkStatementChanged(); }); + + connect(&m_koStatementDelegate, + &ConnectionModelStatementDelegate::statementChanged, + this, + [this]() { handleKOStatementChanged(); }); + + connect(&m_conditionListModel, &ConditionListModel::conditionChanged, this, [this]() { + handleConditionChanged(); + }); + + m_signalDelegate.setPropertyType(PropertyTreeModel::SignalType); +} + +QString generateDefaultStatement(ConnectionModelBackendDelegate::ActionType actionType, + const QString &rootId) +{ + switch (actionType) { + case ConnectionModelStatementDelegate::CallFunction: + return "Qt.quit()"; + case ConnectionModelStatementDelegate::Assign: + return QString("%1.visible = %1.visible").arg(rootId); + case ConnectionModelStatementDelegate::ChangeState: + return QString("%1.state = \"\"").arg(rootId); + case ConnectionModelStatementDelegate::SetProperty: + return QString("%1.visible = true").arg(rootId); + case ConnectionModelStatementDelegate::PrintMessage: + return QString("console.log(\"test\")").arg(rootId); + case ConnectionModelStatementDelegate::Custom: + return {}; + }; + + //Qt.quit() + //console.log("test") + //root.state = "" + //root.visible = root.visible + //root.visible = true + + return {}; +} + +void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) +{ + qDebug() << Q_FUNC_INFO << actionType; + QTC_ASSERT(actionType != ConnectionModelStatementDelegate::Custom, return ); + + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + const QString validId = model->connectionView()->rootModelNode().validId(); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + // Do not take ko into account for now + + model->connectionView() + ->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", [&]() { + ConnectionEditorStatements::MatchedStatement &okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + //We expect a valid id on the root node + const QString validId = model->connectionView()->rootModelNode().validId(); + QString statementSource = generateDefaultStatement(actionType, validId); + + auto tempHandler = ConnectionEditorEvaluator::parseStatement(statementSource); + qDebug() << ConnectionEditorStatements::toString(tempHandler); + auto newOkStatement = ConnectionEditorStatements::okStatement(tempHandler); + qDebug() << "newOk" << statementSource; + QTC_ASSERT(!ConnectionEditorStatements::isEmptyStatement(newOkStatement), return ); + + okStatement = newOkStatement; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + signalHandlerProperty.setSource(newSource); + }); + + setSource(signalHandlerProperty.source()); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ConnectionModelBackendDelegate::addCondition() +{ + ConnectionEditorStatements::MatchedStatement okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + ConnectionEditorStatements::MatchedCondition newCondition; + + ConnectionEditorStatements::Variable variable; + variable.nodeId = defaultCondition; + newCondition.statements.append(variable); + + ConnectionEditorStatements::ConditionalStatement conditionalStatement; + + conditionalStatement.ok = okStatement; + conditionalStatement.condition = newCondition; + + m_handler = conditionalStatement; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ConnectionModelBackendDelegate::removeCondition() +{ + ConnectionEditorStatements::MatchedStatement okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + m_handler = okStatement; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ConnectionModelBackendDelegate::addElse() +{ + ConnectionEditorStatements::MatchedStatement okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + auto &condition = ConnectionEditorStatements::conditionalStatement(m_handler); + condition.ko = condition.ok; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + + commitNewSource(newSource); + setupHandlerAndStatements(); +} + +void ConnectionModelBackendDelegate::removeElse() +{ + ConnectionEditorStatements::MatchedStatement okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + auto &condition = ConnectionEditorStatements::conditionalStatement(m_handler); + condition.ko = ConnectionEditorStatements::EmptyBlock(); + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + + commitNewSource(newSource); + setupHandlerAndStatements(); +} + +int ConnectionModelBackendDelegate::currentRow() const +{ + return m_currentRow; +} + +QString removeOnFromSignalName(const QString &signal) +{ + if (signal.isEmpty()) + return {}; + QString ret = signal; + ret.remove(0, 2); + ret[0] = ret.at(0).toLower(); + return ret; +} + +QString addOnToSignalName(const QString &signal) +{ + QString ret = signal; + ret[0] = ret.at(0).toUpper(); + ret.prepend("on"); + return ret; +} + +void ConnectionModelBackendDelegate::setCurrentRow(int i) +{ + if (m_currentRow == i) + return; + + m_currentRow = i; + + update(); +} + +void ConnectionModelBackendDelegate::update() +{ + if (m_blockReflection) + return; + + m_propertyTreeModel.resetModel(); + m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); + + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + if (!model->connectionView()->isAttached()) + return; + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + QStringList targetNodes; + + for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { + if (!modelNode.id().isEmpty()) + targetNodes.append(modelNode.id()); + } + + std::sort(targetNodes.begin(), targetNodes.end()); + + const QString targetNodeName = signalHandlerProperty.parentModelNode() + .bindingProperty("target") + .resolveToModelNode() + .id(); + + if (!targetNodes.contains(targetNodeName)) + targetNodes.append(targetNodeName); + + setSource(signalHandlerProperty.source()); + + m_signalDelegate.setup(targetNodeName, + removeOnFromSignalName(QString::fromUtf8(signalHandlerProperty.name()))); + + setupHandlerAndStatements(); + + qDebug() << Q_FUNC_INFO << ConnectionEditorStatements::toString(m_handler) + << ConnectionEditorStatements::toJavascript(m_handler); + + setupCondition(); + + QTC_ASSERT(model, return ); +} + +void ConnectionModelBackendDelegate::handleException() +{ + QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); +} + +bool ConnectionModelBackendDelegate::hasCondition() const +{ + return m_hasCondition; +} + +bool ConnectionModelBackendDelegate::hasElse() const +{ + return m_hasElse; +} + +void ConnectionModelBackendDelegate::setHasCondition(bool b) +{ + if (b == m_hasCondition) + return; + + m_hasCondition = b; + emit hasConditionChanged(); +} + +void ConnectionModelBackendDelegate::setHasElse(bool b) +{ + if (b == m_hasElse) + return; + + m_hasElse = b; + emit hasElseChanged(); +} + +ConnectionModelBackendDelegate::ActionType ConnectionModelBackendDelegate::actionType() const +{ + return m_actionType; +} + +PropertyTreeModelDelegate *ConnectionModelBackendDelegate::signal() +{ + return &m_signalDelegate; +} + +ConnectionModelStatementDelegate *ConnectionModelBackendDelegate::okStatement() +{ + return &m_okStatementDelegate; +} + +ConnectionModelStatementDelegate *ConnectionModelBackendDelegate::koStatement() +{ + return &m_koStatementDelegate; +} + +ConditionListModel *ConnectionModelBackendDelegate::conditionListModel() +{ + return &m_conditionListModel; +} + +QString ConnectionModelBackendDelegate::source() const +{ + return m_source; +} + +void ConnectionModelBackendDelegate::setSource(const QString &source) +{ + if (source == m_source) + return; + + m_source = source; + emit sourceChanged(); +} + +PropertyTreeModel *ConnectionModelBackendDelegate::propertyTreeModel() +{ + return &m_propertyTreeModel; +} + +PropertyListProxyModel *ConnectionModelBackendDelegate::propertyListProxyModel() +{ + return &m_propertyListProxyModel; +} + +void ConnectionModelBackendDelegate::setupCondition() +{ + auto &condition = ConnectionEditorStatements::matchedCondition(m_handler); + m_conditionListModel.setCondition(ConnectionEditorStatements::matchedCondition(m_handler)); + setHasCondition(!condition.statements.isEmpty()); +} + +void ConnectionModelBackendDelegate::setupHandlerAndStatements() +{ + ConnectionModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return ); + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + if (signalHandlerProperty.source().isEmpty()) { + m_actionType = ConnectionModelStatementDelegate::Custom; + m_handler = ConnectionEditorStatements::EmptyBlock(); + } else { + m_handler = ConnectionEditorEvaluator::parseStatement(signalHandlerProperty.source()); + + const QString statementType = QmlDesigner::ConnectionEditorStatements::toDisplayName( + m_handler); + + if (statementType == ConnectionEditorStatements::EMPTY_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::Custom; + } else if (statementType == ConnectionEditorStatements::ASSIGNMENT_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::Assign; + //setupAssignment(); + } else if (statementType == ConnectionEditorStatements::SETPROPERTY_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::SetProperty; + //setupSetProperty(); + } else if (statementType == ConnectionEditorStatements::FUNCTION_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::CallFunction; + //setupCallFunction(); + } else if (statementType == ConnectionEditorStatements::SETSTATE_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::ChangeState; + //setupChangeState(); + } else if (statementType == ConnectionEditorStatements::LOG_DISPLAY_NAME) { + m_actionType = ConnectionModelStatementDelegate::PrintMessage; + //setupPrintMessage(); + } else { + m_actionType = ConnectionModelStatementDelegate::Custom; + } + } + + ConnectionEditorStatements::MatchedStatement &okStatement + = ConnectionEditorStatements::okStatement(m_handler); + m_okStatementDelegate.setStatement(okStatement); + m_okStatementDelegate.setActionType(m_actionType); + + ConnectionEditorStatements::MatchedStatement &koStatement + = ConnectionEditorStatements::koStatement(m_handler); + + if (!ConnectionEditorStatements::isEmptyStatement(koStatement)) { + m_koStatementDelegate.setStatement(koStatement); + m_koStatementDelegate.setActionType(m_actionType); + } + + ConnectionEditorStatements::isEmptyStatement(koStatement); + setHasElse(!ConnectionEditorStatements::isEmptyStatement(koStatement)); + + emit actionTypeChanged(); +} + +void ConnectionModelBackendDelegate::handleTargetChanged() +{ + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + const PropertyName handlerName = addOnToSignalName(m_signalDelegate.name()).toUtf8(); + + const auto parentModelNode = signalHandlerProperty.parentModelNode(); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + const auto newId = m_signalDelegate.id(); + + const int internalId = signalHandlerProperty.parentModelNode().internalId(); + + model->connectionView() + ->executeInTransaction("ConnectionModelBackendDelegate::handleTargetChanged", [&]() { + const auto oldTargetNodeName + = parentModelNode.bindingProperty("target").resolveToModelNode().id(); + + if (signalHandlerProperty.name() != handlerName) { + const auto expression = signalHandlerProperty.source(); + parentModelNode.removeProperty(signalHandlerProperty.name()); + parentModelNode.signalHandlerProperty(handlerName).setSource(expression); + } + + if (oldTargetNodeName != newId) { + parentModelNode.bindingProperty("target").setExpression(newId); + + const ModelNode parent = parentModelNode.view()->modelNodeForId(newId); + + if (parent.isValid() && QmlItemNode::isValidQmlVisualNode(parent)) + parent.nodeListProperty("data").reparentHere(parentModelNode); + else + model->connectionView()->rootModelNode().nodeListProperty("data").reparentHere( + parentModelNode); + } + }); + + model->selectProperty(model->connectionView() + ->modelNodeForInternalId(internalId) + .signalHandlerProperty(handlerName)); +} + +void ConnectionModelBackendDelegate::handleOkStatementChanged() +{ + ConnectionEditorStatements::MatchedStatement &okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + okStatement = m_okStatementDelegate.statement(); //TODO why? + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ConnectionModelBackendDelegate::handleKOStatementChanged() +{ + ConnectionEditorStatements::MatchedStatement &koStatement + = ConnectionEditorStatements::koStatement(m_handler); + + koStatement = m_koStatementDelegate.statement(); //TODO why? + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ConnectionModelBackendDelegate::handleConditionChanged() +{ + + ConnectionModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + ConnectionEditorStatements::MatchedCondition &condition + = ConnectionEditorStatements::matchedCondition(m_handler); + condition = m_conditionListModel.condition(); //why? + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + commitNewSource(newSource); +} + +void ConnectionModelBackendDelegate::commitNewSource(const QString &source) +{ + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + m_blockReflection = true; + model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::commitNewSource", + [&]() { + signalHandlerProperty.setSource(source); + }); + + setSource(signalHandlerProperty.source()); + m_blockReflection = false; +} + +static ConnectionEditorStatements::MatchedStatement emptyStatement; + +ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *parent) + : QObject(parent), m_functionDelegate(parent->connectionView()), + m_lhsDelegate(parent->connectionView()), m_rhsAssignmentDelegate(parent->connectionView()), + m_statement(emptyStatement), m_model(parent) +{ + m_functionDelegate.setPropertyType(PropertyTreeModel::SlotType); + + connect(&m_functionDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() { + handleFunctionChanged(); + }); + + connect(&m_rhsAssignmentDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() { + handleRhsAssignmentChanged(); + }); + + connect(&m_lhsDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() { + handleLhsChanged(); + }); + + connect(&m_stringArgument, &StudioQmlTextBackend::activated, this, [this]() { + handleStringArgumentChanged(); + }); + + connect(&m_states, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleStateChanged(); + }); + + connect(&m_stateTargets, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleStateTargetsChanged(); + }); +} + +void ConnectionModelStatementDelegate::setActionType(ActionType type) +{ + if (m_actionType == type) + return; + + m_actionType = type; + emit actionTypeChanged(); + setup(); +} + +void ConnectionModelStatementDelegate::setup() +{ + switch (m_actionType) { + case CallFunction: + setupCallFunction(); + break; + case Assign: + setupAssignment(); + break; + case ChangeState: + setupChangeState(); + break; + case SetProperty: + setupSetProperty(); + break; + case PrintMessage: + setupPrintMessage(); + break; + case Custom: + break; + }; +} + +void ConnectionModelStatementDelegate::setStatement( + ConnectionEditorStatements::MatchedStatement &statement) +{ + m_statement = statement; + setup(); +} + +ConnectionEditorStatements::MatchedStatement &ConnectionModelStatementDelegate::statement() +{ + return m_statement; +} + +ConnectionModelStatementDelegate::ActionType ConnectionModelStatementDelegate::actionType() const +{ + return m_actionType; +} + +PropertyTreeModelDelegate *ConnectionModelStatementDelegate::function() +{ + return &m_functionDelegate; +} + +PropertyTreeModelDelegate *ConnectionModelStatementDelegate::lhs() +{ + return &m_lhsDelegate; +} + +PropertyTreeModelDelegate *ConnectionModelStatementDelegate::rhsAssignment() +{ + return &m_rhsAssignmentDelegate; +} + +StudioQmlTextBackend *ConnectionModelStatementDelegate::stringArgument() +{ + return &m_stringArgument; +} + +StudioQmlComboBoxBackend *ConnectionModelStatementDelegate::stateTargets() +{ + return &m_stateTargets; +} + +StudioQmlComboBoxBackend *ConnectionModelStatementDelegate::states() +{ + return &m_states; +} + +void ConnectionModelStatementDelegate::handleFunctionChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + ConnectionEditorStatements::MatchedFunction &functionStatement + = std::get(m_statement); + + functionStatement.functionName = m_functionDelegate.name(); + functionStatement.nodeId = m_functionDelegate.id(); + + emit statementChanged(); +} + +void ConnectionModelStatementDelegate::handleLhsChanged() +{ + if (m_actionType == Assign) { + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + ConnectionEditorStatements::Assignment &assignmentStatement + = std::get(m_statement); + + assignmentStatement.lhs.nodeId = m_lhsDelegate.id(); + assignmentStatement.lhs.propertyName = m_lhsDelegate.name(); + + } else if (m_actionType == SetProperty) { + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + ConnectionEditorStatements::PropertySet &setPropertyStatement + = std::get(m_statement); + + setPropertyStatement.lhs.nodeId = m_lhsDelegate.id(); + setPropertyStatement.lhs.propertyName = m_lhsDelegate.name(); + } else { + QTC_ASSERT(false, return ); + } + + emit statementChanged(); +} + +void ConnectionModelStatementDelegate::handleRhsAssignmentChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + ConnectionEditorStatements::Assignment &assignmentStatement + = std::get(m_statement); + + assignmentStatement.rhs.nodeId = m_rhsAssignmentDelegate.id(); + assignmentStatement.rhs.propertyName = m_rhsAssignmentDelegate.name(); + + setupPropertyType(); + + emit statementChanged(); +} + +static ConnectionEditorStatements::Literal parseTextArgument(const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return false; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + return text; +} + +static ConnectionEditorStatements::ComparativeStatement parseTextArgumentComparativeStatement( + const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return false; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + return text; +} + +static ConnectionEditorStatements::RightHandSide parseLogTextArgument(const QString &text) +{ + if (text.startsWith("\"") && text.endsWith("\"")) { + QString ret = text; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + + if (text == "true") + return true; + + if (text == "false") + return true; + + bool ok = true; + double d = text.toDouble(&ok); + if (ok) + return d; + + //TODO variables and function calls + return text; +} + +void ConnectionModelStatementDelegate::handleStringArgumentChanged() +{ + if (m_actionType == SetProperty) { + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + ConnectionEditorStatements::PropertySet &propertySet + = std::get(m_statement); + + propertySet.rhs = parseTextArgument(m_stringArgument.text()); + + } else if (m_actionType == PrintMessage) { + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + ConnectionEditorStatements::ConsoleLog &consoleLog + = std::get(m_statement); + + consoleLog.argument = parseLogTextArgument(m_stringArgument.text()); + } else { + QTC_ASSERT(false, return ); + } + + emit statementChanged(); +} + +void ConnectionModelStatementDelegate::handleStateChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + ConnectionEditorStatements::StateSet &stateSet = std::get( + m_statement); + + QString stateName = m_states.currentText(); + if (stateName == baseStateName()) + stateName = ""; + stateSet.stateName = "\"" + stateName + "\""; + + emit statementChanged(); +} + +void ConnectionModelStatementDelegate::handleStateTargetsChanged() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + ConnectionEditorStatements::StateSet &stateSet = std::get( + m_statement); + + stateSet.nodeId = m_stateTargets.currentText(); + stateSet.stateName = "\"\""; + + setupStates(); + + emit statementChanged(); +} + +void ConnectionModelStatementDelegate::setupAssignment() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + const auto assignment = std::get(m_statement); + m_lhsDelegate.setup(assignment.lhs.nodeId, assignment.lhs.propertyName); + m_rhsAssignmentDelegate.setup(assignment.rhs.nodeId, assignment.rhs.propertyName); + setupPropertyType(); +} + +void ConnectionModelStatementDelegate::setupSetProperty() +{ + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + const auto propertySet = std::get(m_statement); + m_lhsDelegate.setup(propertySet.lhs.nodeId, propertySet.lhs.propertyName); + m_stringArgument.setText(ConnectionEditorStatements::toString(propertySet.rhs)); +} + +void ConnectionModelStatementDelegate::setupCallFunction() +{ + QTC_ASSERT(std::holds_alternative(m_statement), + return ); + + const auto functionStatement = std::get( + m_statement); + m_functionDelegate.setup(functionStatement.nodeId, functionStatement.functionName); +} + +void ConnectionModelStatementDelegate::setupChangeState() +{ + qDebug() << Q_FUNC_INFO; + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + QTC_ASSERT(m_model->connectionView()->isAttached(), return ); + + auto model = m_model->connectionView()->model(); + const auto items = Utils::filtered(m_model->connectionView()->allModelNodesOfType( + model->qtQuickItemMetaInfo()), + [](const ModelNode &node) { + QmlItemNode item(node); + return node.hasId() && item.isValid() + && !item.allStateNames().isEmpty(); + }); + + QStringList itemIds = Utils::transform(items, [](const ModelNode &node) { return node.id(); }); + const auto groups = m_model->connectionView()->allModelNodesOfType( + model->qtQuickStateGroupMetaInfo()); + + const auto rootId = m_model->connectionView()->rootModelNode().id(); + itemIds.removeAll(rootId); + + QStringList groupIds = Utils::transform(groups, [](const ModelNode &node) { return node.id(); }); + + Utils::sort(itemIds); + Utils::sort(groupIds); + + if (!rootId.isEmpty()) + groupIds.prepend(rootId); + + const QStringList stateGroupModel = groupIds + itemIds; + m_stateTargets.setModel(stateGroupModel); + + const auto stateSet = std::get(m_statement); + + m_stateTargets.setCurrentText(stateSet.nodeId); + setupStates(); +} +QString stripQuotesFromState(const QString &input) +{ + if (input.startsWith("\"") && input.endsWith("\"")) { + QString ret = input; + ret.remove(0, 1); + ret.chop(1); + return ret; + } + return input; +} +void ConnectionModelStatementDelegate::setupStates() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + QTC_ASSERT(m_model->connectionView()->isAttached(), return ); + + const auto stateSet = std::get(m_statement); + + qDebug() << Q_FUNC_INFO << stateSet.stateName; + + const QString nodeId = m_stateTargets.currentText(); + + const ModelNode node = m_model->connectionView()->modelNodeForId(nodeId); + + QStringList states; + if (node.metaInfo().isQtQuickItem()) { + QmlItemNode item(node); + QTC_ASSERT(item.isValid(), return ); + if (item.isRootNode()) + states = item.states().names(); //model + else + states = item.allStateNames(); //instances + } else { + QmlModelStateGroup group(node); + states = group.names(); //model + } + + const QString stateName = stripQuotesFromState(stateSet.stateName); + + states.prepend(baseStateName()); + m_states.setModel(states); + if (stateName.isEmpty()) + m_states.setCurrentText(baseStateName()); + else + m_states.setCurrentText(stateName); +} + +void ConnectionModelStatementDelegate::setupPrintMessage() +{ + QTC_ASSERT(std::holds_alternative(m_statement), return ); + + const auto consoleLog = std::get(m_statement); + m_stringArgument.setText(ConnectionEditorStatements::toString(consoleLog.argument)); +} + +void ConnectionModelStatementDelegate::setupPropertyType() +{ + PropertyTreeModel::PropertyTypes type = PropertyTreeModel::AllTypes; + + const NodeMetaInfo metaInfo = m_rhsAssignmentDelegate.propertyMetaInfo(); + + if (metaInfo.isBool()) + type = PropertyTreeModel::BoolType; + else if (metaInfo.isNumber()) + type = PropertyTreeModel::NumberType; + else if (metaInfo.isColor()) + type = PropertyTreeModel::ColorType; + else if (metaInfo.isString()) + type = PropertyTreeModel::StringType; + else if (metaInfo.isUrl()) + type = PropertyTreeModel::UrlType; + + m_lhsDelegate.setPropertyType(type); +} + +QString ConnectionModelStatementDelegate::baseStateName() const +{ + return tr("Base State"); +} + +static ConnectionEditorStatements::MatchedCondition emptyCondition; + +ConditionListModel::ConditionListModel(ConnectionModel *parent) + : m_connectionModel(parent), m_condition(emptyCondition) +{} + +int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const +{ + return m_tokens.size(); +} + +QHash ConditionListModel::roleNames() const +{ + static QHash roleNames{{Qt::UserRole + 1, "type"}, {Qt::UserRole + 2, "value"}}; + return roleNames; +} + +QVariant ConditionListModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + if (role == Qt::UserRole + 1) { + return m_tokens.at(index.row()).type; + } else if (role == Qt::UserRole + 2) { + return m_tokens.at(index.row()).value; + } + + qWarning() << Q_FUNC_INFO << "invalid role"; + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +void ConditionListModel::setup() +{ + m_tokens.clear(); + + internalSetup(); + + emit validChanged(); + emit emptyChanged(); + + beginResetModel(); + endResetModel(); +} + +void ConditionListModel::setCondition(ConnectionEditorStatements::MatchedCondition &condition) +{ + m_condition = condition; + setup(); +} + +ConnectionEditorStatements::MatchedCondition &ConditionListModel::condition() +{ + return m_condition; +} + +ConditionListModel::ConditionToken ConditionListModel::tokenFromConditionToken( + const ConnectionEditorStatements::ConditionToken &token) +{ + ConditionToken ret; + ret.type = Operator; + ret.value = ConnectionEditorStatements::toJavascript(token); + + return ret; +} + +ConditionListModel::ConditionToken ConditionListModel::tokenFromComparativeStatement( + const ConnectionEditorStatements::ComparativeStatement &token) +{ + ConditionToken ret; + + if (auto *variable = std::get_if(&token)) { + ret.type = Variable; + ret.value = variable->expression(); + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + ret.value = "\"" + *literal + "\""; + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + if (*literal) + ret.value = "true"; + else + ret.value = "false"; + return ret; + } else if (auto *literal = std::get_if(&token)) { + ret.type = Literal; + ret.value = QString::number(*literal); + return ret; + } + + ret.type = Invalid; + ret.value = "invalid"; + return {}; +} + +void ConditionListModel::insertToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + m_tokens.insert(index, valueToToken(value)); + validateAndRebuildTokens(); + + endInsertRows(); + //resetModel(); +} + +void ConditionListModel::updateToken(int index, const QString &value) +{ + m_tokens[index] = valueToToken(value); + validateAndRebuildTokens(); + + dataChanged(createIndex(index, 0), createIndex(index, 0)); + //resetModel(); +} + +void ConditionListModel::appendToken(const QString &value) +{ + beginInsertRows({}, rowCount() - 1, rowCount() - 1); + + insertToken(rowCount(), value); + validateAndRebuildTokens(); + + endInsertRows(); + //resetModel(); +} + +void ConditionListModel::removeToken(int index) +{ + QTC_ASSERT(index < m_tokens.count(), return ); + beginRemoveRows({}, index, index); + + m_tokens.remove(index, 1); + validateAndRebuildTokens(); + + endRemoveRows(); + + //resetModel(); +} + +void ConditionListModel::insertIntermediateToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + ConditionToken token; + token.type = Intermediate; + token.value = value; + + m_tokens.insert(index, token); + + endInsertRows(); + //resetModel(); +} + +void ConditionListModel::insertShadowToken(int index, const QString &value) +{ + beginInsertRows({}, index, index); + + ConditionToken token; + token.type = Shadow; + token.value = value; + + m_tokens.insert(index, token); + + endInsertRows(); + + //resetModel(); +} + +void ConditionListModel::setShadowToken(int index, const QString &value) +{ + m_tokens[index].type = Shadow; + m_tokens[index].value = value; + + dataChanged(createIndex(index, 0), createIndex(index, 0)); + //resetModel(); +} + +bool ConditionListModel::valid() const +{ + return m_valid; +} + +bool ConditionListModel::empty() const +{ + return m_tokens.isEmpty(); +} + +void ConditionListModel::command(const QString &string) +{ + //TODO remove from prodcution code + QStringList list = string.split("%", Qt::SkipEmptyParts); + + qDebug() << Q_FUNC_INFO << string << list.size(); + + if (list.size() < 2) + return; + + if (list.size() == 2) { + if (list.first() == "A") { + qDebug() << "Append" << list.last(); + appendToken(list.last()); + } else if (list.first() == "R") { + bool ok = true; + int index = list.last().toInt(&ok); + + qDebug() << "Remove" << index; + if (ok) + removeToken(index); + } + } + + if (list.size() == 3) { + if (list.first() == "U") { + bool ok = true; + int index = list.at(1).toInt(&ok); + + if (ok) + updateToken(index, list.last()); + qDebug() << "Update" << index << list.last(); + } else if (list.first() == "I") { + bool ok = true; + int index = list.at(1).toInt(&ok); + + if (ok) + insertToken(index, list.last()); + + qDebug() << "Insert" << index << list.last(); + } + } +} + +void ConditionListModel::setInvalid(const QString &errorMessage, int index) +{ + m_valid = false; + m_errorMessage = errorMessage; + + emit errorChanged(); + emit validChanged(); + + if (index != -1) { + m_errorIndex = index; + emit errorIndexChanged(); + } +} + +void ConditionListModel::setValid() +{ + m_valid = true; + m_errorMessage.clear(); + m_errorIndex = -1; + + emit errorChanged(); + emit validChanged(); + emit errorIndexChanged(); +} + +QString ConditionListModel::error() const +{ + return m_errorMessage; +} + +int ConditionListModel::errorIndex() const +{ + return m_errorIndex; +} + +bool ConditionListModel::operatorAllowed(int cursorPosition) +{ + if (m_tokens.empty()) + return false; + + int tokenIdx = cursorPosition - 1; + + if (tokenIdx >= 0 && tokenIdx < m_tokens.length() && m_tokens[tokenIdx].type != Operator) + return true; + + return false; +} + +void ConditionListModel::internalSetup() +{ + setInvalid(tr("No Valid Condition")); + if (!m_condition.statements.size() && !m_condition.tokens.size()) + return; + + if (m_condition.statements.size() != m_condition.tokens.size() + 1) + return; + + if (m_condition.statements.size() == 1 && m_condition.tokens.isEmpty()) { + auto token = tokenFromComparativeStatement(m_condition.statements.first()); + if (token.value == defaultCondition) + return; + } + + auto s_it = m_condition.statements.begin(); + auto o_it = m_condition.tokens.begin(); + + while (o_it != m_condition.tokens.end()) { + m_tokens.append(tokenFromComparativeStatement(*s_it)); + m_tokens.append(tokenFromConditionToken(*o_it)); + + s_it++; + o_it++; + } + m_tokens.append(tokenFromComparativeStatement(*s_it)); + + setValid(); +} + +ConditionListModel::ConditionToken ConditionListModel::valueToToken(const QString &value) +{ + const QStringList operators = {"&&", "||", "===", "!==", ">", ">=", "<", "<="}; + + if (operators.contains(value)) { + ConditionToken token; + token.type = Operator; + token.value = value; + return token; + } + + bool ok = false; + value.toDouble(&ok); + + if (value == "true" || value == "false" || ok + || (value.startsWith("\"") && value.endsWith("\""))) { + ConditionToken token; + token.type = Literal; + token.value = value; + return token; + } + + static QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"); + QRegularExpressionMatch match = regexp.match(value); + + if (match.hasMatch()) { //variable + ConditionToken token; + token.type = Variable; + token.value = value; + return token; + } + + ConditionToken token; + token.type = Invalid; + token.value = value; + + return token; +} + +void ConditionListModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +int ConditionListModel::checkOrder() const +{ + auto it = m_tokens.begin(); + + bool wasOperator = true; + + int ret = 0; + while (it != m_tokens.end()) { + if (wasOperator && it->type == Operator) + return ret; + if (!wasOperator && it->type == Literal) + return ret; + if (!wasOperator && it->type == Variable) + return ret; + wasOperator = it->type == Operator; + it++; + ret++; + } + + if (wasOperator) + return ret; + + return -1; +} + +void ConditionListModel::validateAndRebuildTokens() +{ + /// NEW + auto it = m_tokens.begin(); + + while (it != m_tokens.end()) { + if (it->type == Intermediate) + *it = valueToToken(it->value); + + it++; + } + // NEW + + QString invalidValue; + const bool invalidToken = Utils::contains(m_tokens, + [&invalidValue](const ConditionToken &token) { + if (token.type == Invalid) + invalidValue = token.value; + return token.type == Invalid; + }); + + if (invalidToken) { + setInvalid(tr("Invalid token %1").arg(invalidValue)); + return; + } + + if (int firstError = checkOrder() != -1) { + setInvalid(tr("Invalid order at %1").arg(firstError), firstError); + return; + } + + setValid(); + + rebuildTokens(); +} + +void ConditionListModel::rebuildTokens() +{ + QTC_ASSERT(m_valid, return ); + + m_condition.statements.clear(); + m_condition.tokens.clear(); + + auto it = m_tokens.begin(); + + while (it != m_tokens.end()) { + QTC_ASSERT(it->type != Invalid, return ); + if (it->type == Operator) + m_condition.tokens.append(toOperatorStatement(*it)); + else if (it->type == Literal || it->type == Variable) + m_condition.statements.append(toStatement(*it)); + + it++; + } + + emit conditionChanged(); +} + +ConnectionEditorStatements::ConditionToken ConditionListModel::toOperatorStatement( + const ConditionToken &token) +{ + if (token.value == "&&") + return ConnectionEditorStatements::ConditionToken::And; + + if (token.value == "||") + return ConnectionEditorStatements::ConditionToken::Or; + + if (token.value == "===") + return ConnectionEditorStatements::ConditionToken::Equals; + + if (token.value == "!==") + return ConnectionEditorStatements::ConditionToken::Not; + + if (token.value == ">") + return ConnectionEditorStatements::ConditionToken::LargerThan; + + if (token.value == ">=") + return ConnectionEditorStatements::ConditionToken::LargerEqualsThan; + + if (token.value == "<") + return ConnectionEditorStatements::ConditionToken::SmallerThan; + + if (token.value == "<=") + return ConnectionEditorStatements::ConditionToken::SmallerEqualsThan; + + return ConnectionEditorStatements::ConditionToken::Unknown; +} + +ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement( + const ConditionToken &token) +{ + if (token.type == Variable) { + QStringList list = token.value.split("."); + ConnectionEditorStatements::Variable variable; + + variable.nodeId = list.first(); + if (list.count() > 1) + variable.propertyName = list.last(); + return variable; + } else if (token.type == Literal) { + return parseTextArgumentComparativeStatement(token.value); + } + + return {}; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index fc1108d4035..bef5b79637d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -3,6 +3,11 @@ #pragma once +#include +#include +#include + +#include #include namespace QmlDesigner { @@ -14,10 +19,17 @@ class SignalHandlerProperty; class VariantProperty; class ConnectionView; +class ConnectionModelBackendDelegate; class ConnectionModel : public QStandardItemModel { Q_OBJECT + + Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT) + +public: + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + public: enum ColumnRoles { TargetModelNodeRow = 0, @@ -54,6 +66,14 @@ public: Q_INVOKABLE void add(); Q_INVOKABLE void remove(int row); + void setCurrentIndex(int i); + int currentIndex() const; + + void selectProperty(const SignalHandlerProperty &property); + +signals: + void currentIndexChanged(); + protected: void addModelNode(const ModelNode &modelNode); void addConnection(const ModelNode &modelNode); @@ -71,11 +91,251 @@ protected: private: void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleException(); + ConnectionModelBackendDelegate *delegate() const; private: ConnectionView *m_connectionView; bool m_lock = false; QString m_exceptionError; + ConnectionModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; +}; + +class ConditionListModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool valid READ valid NOTIFY validChanged) + Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged) + Q_PROPERTY(QString error READ error NOTIFY errorChanged) + Q_PROPERTY(int errorIndex READ errorIndex NOTIFY errorIndexChanged) + +public: + enum ConditionType { Intermediate, Invalid, Operator, Literal, Variable, Shadow }; + Q_ENUM(ConditionType) + + struct ConditionToken + { + ConditionType type; + QString value; + }; + + ConditionListModel(ConnectionModel *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + void setup(); + void setCondition(ConnectionEditorStatements::MatchedCondition &condition); + ConnectionEditorStatements::MatchedCondition &condition(); + + static ConditionToken tokenFromConditionToken( + const ConnectionEditorStatements::ConditionToken &token); + + static ConditionToken tokenFromComparativeStatement( + const ConnectionEditorStatements::ComparativeStatement &token); + + Q_INVOKABLE void insertToken(int index, const QString &value); + Q_INVOKABLE void updateToken(int index, const QString &value); + Q_INVOKABLE void appendToken(const QString &value); + Q_INVOKABLE void removeToken(int index); + + Q_INVOKABLE void insertIntermediateToken(int index, const QString &value); + Q_INVOKABLE void insertShadowToken(int index, const QString &value); + Q_INVOKABLE void setShadowToken(int index, const QString &value); + + bool valid() const; + bool empty() const; + + //for debugging + Q_INVOKABLE void command(const QString &string); + + void setInvalid(const QString &errorMessage, int index = -1); + void setValid(); + + QString error() const; + int errorIndex() const; + + Q_INVOKABLE bool operatorAllowed(int cursorPosition); + +signals: + void validChanged(); + void emptyChanged(); + void conditionChanged(); + void errorChanged(); + void errorIndexChanged(); + +private: + void internalSetup(); + ConditionToken valueToToken(const QString &value); + void resetModel(); + int checkOrder() const; + void validateAndRebuildTokens(); + void rebuildTokens(); + + ConnectionEditorStatements::ConditionToken toOperatorStatement(const ConditionToken &token); + ConnectionEditorStatements::ComparativeStatement toStatement(const ConditionToken &token); + + ConnectionModel *m_connectionModel = nullptr; + ConnectionEditorStatements::MatchedCondition &m_condition; + QList m_tokens; + bool m_valid = false; + QString m_errorMessage; + int m_errorIndex = -1; +}; + +class ConnectionModelStatementDelegate : public QObject +{ + Q_OBJECT + +public: + explicit ConnectionModelStatementDelegate(ConnectionModel *parent = nullptr); + + enum ActionType { CallFunction, Assign, ChangeState, SetProperty, PrintMessage, Custom }; + + Q_ENUM(ActionType) + + Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged) + + Q_PROPERTY(PropertyTreeModelDelegate *function READ function CONSTANT) + Q_PROPERTY(PropertyTreeModelDelegate *lhs READ lhs CONSTANT) + Q_PROPERTY(PropertyTreeModelDelegate *rhsAssignment READ rhsAssignment CONSTANT) + Q_PROPERTY(StudioQmlTextBackend *stringArgument READ stringArgument CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *states READ states CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *stateTargets READ stateTargets CONSTANT) + + void setActionType(ActionType type); + void setup(); + void setStatement(ConnectionEditorStatements::MatchedStatement &statement); + ConnectionEditorStatements::MatchedStatement &statement(); + +signals: + void actionTypeChanged(); + void statementChanged(); + +private: + ActionType actionType() const; + PropertyTreeModelDelegate *signal(); + PropertyTreeModelDelegate *function(); + PropertyTreeModelDelegate *lhs(); + PropertyTreeModelDelegate *rhsAssignment(); + StudioQmlTextBackend *stringArgument(); + StudioQmlComboBoxBackend *stateTargets(); + StudioQmlComboBoxBackend *states(); + + void handleFunctionChanged(); + void handleLhsChanged(); + void handleRhsAssignmentChanged(); + void handleStringArgumentChanged(); + void handleStateChanged(); + void handleStateTargetsChanged(); + + void setupAssignment(); + void setupSetProperty(); + void setupCallFunction(); + void setupChangeState(); + void setupStates(); + void setupPrintMessage(); + void setupPropertyType(); + QString baseStateName() const; + + ActionType m_actionType; + PropertyTreeModelDelegate m_functionDelegate; + PropertyTreeModelDelegate m_lhsDelegate; + PropertyTreeModelDelegate m_rhsAssignmentDelegate; + ConnectionEditorStatements::MatchedStatement &m_statement; + ConnectionModel *m_model = nullptr; + StudioQmlTextBackend m_stringArgument; + StudioQmlComboBoxBackend m_stateTargets; + StudioQmlComboBoxBackend m_states; +}; + +class ConnectionModelBackendDelegate : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) + + Q_PROPERTY(ActionType actionType READ actionType NOTIFY actionTypeChanged) + Q_PROPERTY(PropertyTreeModelDelegate *signal READ signal CONSTANT) + Q_PROPERTY(ConnectionModelStatementDelegate *okStatement READ okStatement CONSTANT) + Q_PROPERTY(ConnectionModelStatementDelegate *koStatement READ koStatement CONSTANT) + Q_PROPERTY(ConditionListModel *conditionListModel READ conditionListModel CONSTANT) + Q_PROPERTY(bool hasCondition READ hasCondition NOTIFY hasConditionChanged) + Q_PROPERTY(bool hasElse READ hasElse NOTIFY hasElseChanged) + Q_PROPERTY(QString source READ source NOTIFY sourceChanged) + + Q_PROPERTY(PropertyTreeModel *propertyTreeModel READ propertyTreeModel CONSTANT) + Q_PROPERTY(PropertyListProxyModel *propertyListProxyModel READ propertyListProxyModel CONSTANT) + +public: + explicit ConnectionModelBackendDelegate(ConnectionModel *parent = nullptr); + + using ActionType = ConnectionModelStatementDelegate::ActionType; + + Q_INVOKABLE void changeActionType( + QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType); + + Q_INVOKABLE void addCondition(); + Q_INVOKABLE void removeCondition(); + + Q_INVOKABLE void addElse(); + Q_INVOKABLE void removeElse(); + + void setCurrentRow(int i); + void update(); + +signals: + void currentRowChanged(); + void actionTypeChanged(); + void hasConditionChanged(); + void hasElseChanged(); + void sourceChanged(); + +private: + int currentRow() const; + void handleException(); + bool hasCondition() const; + bool hasElse() const; + void setHasCondition(bool b); + void setHasElse(bool b); + ActionType actionType() const; + PropertyTreeModelDelegate *signal(); + ConnectionModelStatementDelegate *okStatement(); + ConnectionModelStatementDelegate *koStatement(); + ConditionListModel *conditionListModel(); + QString source() const; + void setSource(const QString &source); + + PropertyTreeModel *propertyTreeModel(); + PropertyListProxyModel *propertyListProxyModel(); + + void setupCondition(); + void setupHandlerAndStatements(); + + void handleTargetChanged(); + void handleOkStatementChanged(); + void handleKOStatementChanged(); + void handleConditionChanged(); + + void commitNewSource(const QString &source); + + ActionType m_actionType; + QString m_exceptionError; + int m_currentRow = -1; + ConnectionEditorStatements::Handler m_handler; + PropertyTreeModelDelegate m_signalDelegate; + ConnectionModelStatementDelegate m_okStatementDelegate; + ConnectionModelStatementDelegate m_koStatementDelegate; + ConditionListModel m_conditionListModel; + bool m_hasCondition = false; + bool m_hasElse = false; + QString m_source; + PropertyTreeModel m_propertyTreeModel; + PropertyListProxyModel m_propertyListProxyModel; + bool m_blockReflection = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 9da3fcc7be4..41eee97be48 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -2,20 +2,21 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "connectionview.h" -#include "connectionviewwidget.h" #include "backendmodel.h" #include "bindingmodel.h" #include "connectionmodel.h" #include "dynamicpropertiesmodel.h" +#include "propertytreemodel.h" #include "theme.h" #include #include -#include #include -#include +#include #include +#include +#include #include @@ -58,7 +59,7 @@ public: this, &ConnectionViewQuickWidget::reloadQmlSource); - //setObjectName(Constants::OBJECT_NAME_STATES_EDITOR); + quickWidget()->setObjectName(Constants::OBJECT_NAME_CONNECTION_EDITOR); setResizeMode(QQuickWidget::SizeRootObjectToView); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -77,6 +78,24 @@ public: {{"dynamicPropertiesModel", QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "DynamicPropertiesModelBackendDelegate"); + + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "ConnectionModelStatementDelegate"); + + qmlRegisterType("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel"); + + qmlRegisterType("ConnectionsEditorEditorBackend", 1, 0, "PropertyTreeModel"); + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "PropertyListProxyModel"); + Theme::setupTheme(engine()); setMinimumWidth(195); @@ -122,17 +141,13 @@ private: }; ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()), - m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)), - m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)), - m_backendModel(new BackendModel(this)), - m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) -{ - connectionViewWidget()->setBindingModel(m_bindingModel); - connectionViewWidget()->setConnectionModel(m_connectionModel); - connectionViewWidget()->setDynamicPropertiesModel(m_dynamicPropertiesModel); - connectionViewWidget()->setBackendModel(m_backendModel); -} + : AbstractView{externalDependencies} + , m_connectionModel(new ConnectionModel(this)) + , m_bindingModel(new BindingModel(this)) + , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)) + , m_backendModel(new BackendModel(this)) + , m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) +{} ConnectionView::~ConnectionView() { @@ -142,25 +157,22 @@ ConnectionView::~ConnectionView() void ConnectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); - bindingModel()->selectionChanged(QList()); + bindingModel()->reset(); dynamicPropertiesModel()->reset(); connectionModel()->resetModel(); - connectionViewWidget()->resetItemViews(); backendModel()->resetModel(); } void ConnectionView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - bindingModel()->selectionChanged(QList()); + bindingModel()->reset(); dynamicPropertiesModel()->reset(); connectionModel()->resetModel(); - connectionViewWidget()->resetItemViews(); } void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) { -//bindings connectionModel()->resetModel(); } @@ -180,8 +192,8 @@ void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstra void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/) { connectionModel()->resetModel(); - bindingModel()->resetModel(); - dynamicPropertiesModel()->resetModel(); + bindingModel()->reset(); + dynamicPropertiesModel()->reset(); } void ConnectionView::propertiesRemoved(const QList &propertyList) @@ -198,10 +210,10 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList &p { for (const AbstractProperty &property : propertyList) { if (property.isBindingProperty()) { - bindingModel()->bindingRemoved(property.toBindingProperty()); - dynamicPropertiesModel()->bindingRemoved(property.toBindingProperty()); + bindingModel()->removeItem(property); + dynamicPropertiesModel()->removeItem(property); } else if (property.isVariantProperty()) { - dynamicPropertiesModel()->variantRemoved(property.toVariantProperty()); + dynamicPropertiesModel()->removeItem(property); } else if (property.isSignalHandlerProperty()) { connectionModel()->removeRowFromTable(property.toSignalHandlerProperty()); } @@ -213,7 +225,7 @@ void ConnectionView::variantPropertiesChanged(const QList &prop { for (const VariantProperty &variantProperty : propertyList) { if (variantProperty.isDynamic()) - dynamicPropertiesModel()->variantPropertyChanged(variantProperty); + dynamicPropertiesModel()->updateItem(variantProperty); if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode()) backendModel()->resetModel(); @@ -227,9 +239,9 @@ void ConnectionView::bindingPropertiesChanged(const QList &prop AbstractView::PropertyChangeFlags /*propertyChange*/) { for (const BindingProperty &bindingProperty : propertyList) { - bindingModel()->bindingChanged(bindingProperty); + bindingModel()->updateItem(bindingProperty); if (bindingProperty.isDynamic()) - dynamicPropertiesModel()->bindingPropertyChanged(bindingProperty); + dynamicPropertiesModel()->updateItem(bindingProperty); if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode()) backendModel()->resetModel(); @@ -249,39 +261,8 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVector & selectedNodeList, const QList & /*lastSelectedNodeList*/) { - bindingModel()->selectionChanged(selectedNodeList); + bindingModel()->reset(selectedNodeList); dynamicPropertiesModel()->reset(); - connectionViewWidget()->bindingTableViewSelectionChanged(QModelIndex(), QModelIndex()); - connectionViewWidget()->dynamicPropertiesTableViewSelectionChanged(QModelIndex(), QModelIndex()); - - if (connectionViewWidget()->currentTab() == ConnectionViewWidget::BindingTab - || connectionViewWidget()->currentTab() == ConnectionViewWidget::DynamicPropertiesTab) - emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.size() == 1); -} - -void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node, - AuxiliaryDataKeyView key, - const QVariant &data) -{ - // Check if the auxiliary data is actually the locked property or if it is unlocked - if (key != lockedProperty || !data.toBool()) - return; - - QItemSelectionModel *selectionModel = connectionTableView()->selectionModel(); - if (!selectionModel->hasSelection()) - return; - - QModelIndex modelIndex = selectionModel->currentIndex(); - if (!modelIndex.isValid() || !model()) - return; - - const int internalId = connectionModel()->data(connectionModel()->index(modelIndex.row(), - ConnectionModel::TargetModelNodeRow), - ConnectionModel::UserRoles::InternalIdRole).toInt(); - ModelNode modelNode = modelNodeForInternalId(internalId); - - if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) - selectionModel->clearSelection(); } void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) @@ -296,14 +277,7 @@ void ConnectionView::currentStateChanged(const ModelNode &) WidgetInfo ConnectionView::widgetInfo() { - /* Enable new connection editor here */ - const bool newEditor = false; - - QWidget *widget = m_connectionViewWidget.data(); - if (newEditor) - widget = m_connectionViewQuickWidget.data(); - - return createWidgetInfo(widget, + return createWidgetInfo(m_connectionViewQuickWidget.data(), QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, @@ -320,31 +294,6 @@ bool ConnectionView::isWidgetEnabled() return widgetInfo().widget->isEnabled(); } -QTableView *ConnectionView::connectionTableView() const -{ - return connectionViewWidget()->connectionTableView(); -} - -QTableView *ConnectionView::bindingTableView() const -{ - return connectionViewWidget()->bindingTableView(); -} - -QTableView *ConnectionView::dynamicPropertiesTableView() const -{ - return connectionViewWidget()->dynamicPropertiesTableView(); -} - -QTableView *ConnectionView::backendView() const -{ - return connectionViewWidget()->backendView(); -} - -ConnectionViewWidget *ConnectionView::connectionViewWidget() const -{ - return m_connectionViewWidget.data(); -} - ConnectionModel *ConnectionView::connectionModel() const { return m_connectionModel; @@ -381,7 +330,6 @@ void ConnectionView::setCurrentIndex(int i) ConnectionView *ConnectionView::instance() { - static ConnectionView *s_instance = nullptr; if (s_instance) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 5997f230ad8..b865bf51cc4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -21,6 +21,8 @@ class ConnectionModel; class DynamicPropertiesModel; class BackendModel; class ConnectionViewQuickWidget; +class PropertyTreeModel; +class PropertyListProxyModel; class ConnectionView : public AbstractView { @@ -49,9 +51,6 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; - void auxiliaryDataChanged(const ModelNode &node, - AuxiliaryDataKeyView key, - const QVariant &data) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override; @@ -61,14 +60,8 @@ public: bool hasWidget() const override; bool isWidgetEnabled(); - QTableView *connectionTableView() const; - QTableView *bindingTableView() const; - QTableView *dynamicPropertiesTableView() const; - QTableView *backendView() const; - DynamicPropertiesModel *dynamicPropertiesModel() const; - ConnectionViewWidget *connectionViewWidget() const; ConnectionModel *connectionModel() const; BindingModel *bindingModel() const; BackendModel *backendModel() const; @@ -81,13 +74,13 @@ public: signals: void currentIndexChanged(); -private: //variables - QPointer m_connectionViewWidget; - +private: ConnectionModel *m_connectionModel; BindingModel *m_bindingModel; DynamicPropertiesModel *m_dynamicPropertiesModel; BackendModel *m_backendModel; + PropertyTreeModel *m_propertyTreeModel; + PropertyListProxyModel *m_propertyListProxyModel; int m_currentIndex = 0; QPointer m_connectionViewQuickWidget; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp deleted file mode 100644 index 8a8845b47fd..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "connectionviewwidget.h" -#include "connectionview.h" -#include "ui_connectionviewwidget.h" - -#include "delegates.h" -#include "backendmodel.h" -#include "bindingmodel.h" -#include "connectionmodel.h" -#include "dynamicpropertiesmodel.h" -#include "theme.h" -#include "signalhandlerproperty.h" - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace QmlDesigner { - -ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : - QFrame(parent), - ui(new Ui::ConnectionViewWidget) -{ - m_connectionEditor = new QmlDesigner::ActionEditor(this); - m_bindingEditor = new QmlDesigner::BindingEditor(this); - m_dynamicEditor = new QmlDesigner::BindingEditor(this); - - editorForConnection(); - editorForBinding(); - editorForDynamic(); - - - setWindowTitle(tr("Connections", "Title of connections window")); - ui->setupUi(this); - - QStyle *style = QStyleFactory::create("fusion"); - ui->stackedWidget->setStyle(style); - - //ui->tabWidget->tabBar()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - ui->tabBar->setUsesScrollButtons(true); - ui->tabBar->setElideMode(Qt::ElideRight); - - ui->tabBar->addTab(tr("Connections", "Title of connection tab")); - ui->tabBar->addTab(tr("Bindings", "Title of connection tab")); - ui->tabBar->addTab(tr("Properties", "Title of dynamic properties tab")); - - const Qt::Alignment headerAlignment = Qt::AlignLeft | Qt::AlignVCenter; - ui->connectionView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->bindingView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->dynamicPropertiesView->horizontalHeader()->setDefaultAlignment(headerAlignment); - ui->backendView->horizontalHeader()->setDefaultAlignment(headerAlignment); - - const QList buttons = createToolBarWidgets(); - - ui->toolBar->setFixedHeight(41); - for (auto toolButton : buttons) - ui->toolBar->addWidget(toolButton); - - if (!QmlProjectManager::QmlProject::isQtDesignStudio()) - ui->tabBar->addTab(tr("Backends", "Title of dynamic properties view")); - - ui->tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - - QByteArray sheet = Utils::FileReader::fetchQrc(":/connectionview/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); - setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); - - connect(ui->tabBar, &QTabBar::currentChanged, - ui->stackedWidget, &QStackedWidget::setCurrentIndex); - - connect(ui->tabBar, &QTabBar::currentChanged, - this, &ConnectionViewWidget::handleTabChanged); - - ui->stackedWidget->setCurrentIndex(0); - - ui->stackedWidget->parentWidget()->hide(); -} - -ConnectionViewWidget::~ConnectionViewWidget() -{ - delete m_connectionEditor; - delete m_bindingEditor; - delete m_dynamicEditor; - delete ui; -} - -void ConnectionViewWidget::setBindingModel(BindingModel *model) -{ - ui->bindingView->setModel(model); - ui->bindingView->verticalHeader()->hide(); - ui->bindingView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->bindingView->setItemDelegate(new BindingDelegate); - connect(ui->bindingView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::bindingTableViewSelectionChanged); -} - -void ConnectionViewWidget::setConnectionModel(ConnectionModel *model) -{ - ui->connectionView->setModel(model); - ui->connectionView->verticalHeader()->hide(); - ui->connectionView->horizontalHeader()->setDefaultSectionSize(160); - ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->connectionView->setItemDelegate(new ConnectionDelegate); - - connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::connectionTableViewSelectionChanged); -} - -void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) -{ - auto tablePos = [&](QTableView *targetView) { - // adjusting qpoint to the qtableview entrances: - QPoint posInTable(targetView->mapFromGlobal(mapToGlobal(event->pos()))); - posInTable.ry() -= targetView->horizontalHeader()->height(); - return posInTable; - }; - - switch (currentTab()) { - case ConnectionTab: - if (ui->connectionView != nullptr) { - QTableView *targetView = ui->connectionView; - // making sure that we have source column in our hands: - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(ConnectionModel::SourceRow); - if (!index.isValid()) - return; - - QMenu menu(this); - - menu.addAction(tr("Open Connection Editor"), this, [&]() { - auto *connectionModel = qobject_cast(targetView->model()); - const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row()); - const ModelNode node = property.parentModelNode(); - - const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString() - + "." + property.name(); - - m_connectionEditor->showWidget(); - m_connectionEditor->setConnectionValue(index.data().toString()); - m_connectionEditor->setModelIndex(index); - m_connectionEditor->setModelNode(node); - m_connectionEditor->prepareConnections(); - m_connectionEditor->updateWindowName(targetName); - }); - - QMap data; - data["ModelNode"] = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data(); - data["Signal"] = index.siblingAtColumn(ConnectionModel::TargetPropertyNameRow).data(); - DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->designerActionManager(); - const auto actions = designerActionManager.actionsForTargetView( - ActionInterface::TargetView::ConnectionEditor); - - for (const auto &actionInterface : actions) { - auto *action = actionInterface->action(); - action->setData(data); - menu.addAction(action); - } - - menu.exec(event->globalPos()); - } - break; - - case BindingTab: - if (ui->bindingView != nullptr) { - QTableView *targetView = bindingTableView(); - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(BindingModel::SourcePropertyNameRow); - if (!index.isValid()) - return; - - QMenu menu(this); - - menu.addAction(tr("Open Binding Editor"), this, [&]() { - BindingModel *bindingModel = qobject_cast(targetView->model()); - const BindingProperty property = bindingModel->bindingPropertyForRow(index.row()); - - if (!property.isValid() || !property.isBindingProperty()) - return; - - const ModelNode node = property.parentModelNode(); - auto model = node.model(); - const auto type = property.isDynamic() - ? model->metaInfo(property.dynamicTypeName()) - : node.metaInfo().property(property.name()).propertyType(); - - const QString targetName = node.displayName() + "." + property.name(); - - m_bindingEditor->showWidget(); - m_bindingEditor->setBindingValue(property.expression()); - m_bindingEditor->setModelNode(node); - m_bindingEditor->setBackendValueType(type); - m_bindingEditor->setTargetName(targetName); - m_bindingEditor->prepareBindings(); - m_bindingEditor->updateWindowName(); - - m_bindingIndex = index; - }); - menu.exec(event->globalPos()); - } - break; - - case DynamicPropertiesTab: - if (ui->dynamicPropertiesView != nullptr) { - QTableView *targetView = dynamicPropertiesTableView(); - const QModelIndex index = targetView->indexAt(tablePos(targetView)).siblingAtColumn(DynamicPropertiesModel::PropertyValueRow); - if (!index.isValid()) - return; - - DynamicPropertiesModel *propertiesModel = qobject_cast(targetView->model()); - QMenu menu(this); - - menu.addAction(tr("Open Binding Editor"), this, [&]() { - AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row()); - if (!abstractProperty.isValid()) - return; - - const ModelNode node = abstractProperty.parentModelNode(); - QString newExpression; - - if (abstractProperty.isBindingProperty()) - newExpression = abstractProperty.toBindingProperty().expression(); - else if (abstractProperty.isVariantProperty()) - newExpression = abstractProperty.toVariantProperty().value().toString(); - else - return; - - const QString targetName = node.displayName() + "." + abstractProperty.name(); - auto model = node.model(); - m_dynamicEditor->showWidget(); - m_dynamicEditor->setBindingValue(newExpression); - m_dynamicEditor->setModelNode(node); - m_dynamicEditor->setBackendValueType( - model->metaInfo(abstractProperty.dynamicTypeName())); - m_dynamicEditor->setTargetName(targetName); - m_dynamicEditor->prepareBindings(); - m_dynamicEditor->updateWindowName(); - - m_dynamicIndex = index; - }); - - menu.addAction(tr("Reset Property"), this, [&]() { - propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name()); - }); - - menu.exec(event->globalPos()); - } - break; - default: - break; - - } - -} - -void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model) -{ - ui->dynamicPropertiesView->setModel(model); - ui->dynamicPropertiesView->verticalHeader()->hide(); - ui->dynamicPropertiesView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->dynamicPropertiesView->setItemDelegate(new DynamicPropertiesDelegate); - connect(ui->dynamicPropertiesView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged); -} - -void ConnectionViewWidget::setBackendModel(BackendModel *model) -{ - ui->backendView->setModel(model); - ui->backendView->verticalHeader()->hide(); - ui->backendView->setSelectionMode(QAbstractItemView::SingleSelection); - ui->backendView->setItemDelegate(new BackendDelegate); - model->resetModel(); - connect(ui->backendView->selectionModel(), &QItemSelectionModel::currentRowChanged, - this, &ConnectionViewWidget::backendTableViewSelectionChanged); -} - -QList ConnectionViewWidget::createToolBarWidgets() -{ - QList buttons; - - buttons << new QToolButton(); - buttons.constLast()->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); - buttons.constLast()->setToolTip(tr("Add binding or connection.")); - connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::addButtonClicked); - connect(this, &ConnectionViewWidget::setEnabledAddButton, buttons.constLast(), &QWidget::setEnabled); - - buttons << new QToolButton(); - buttons.constLast()->setIcon(Utils::Icons::MINUS_TOOLBAR.icon()); - buttons.constLast()->setToolTip(tr("Remove selected binding or connection.")); - connect(buttons.constLast(), &QAbstractButton::clicked, this, &ConnectionViewWidget::removeButtonClicked); - connect(this, &ConnectionViewWidget::setEnabledRemoveButton, buttons.constLast(), &QWidget::setEnabled); - - QAction *deleteShortcut = new QAction(this); - this->addAction(deleteShortcut); - deleteShortcut->setShortcuts({QKeySequence::Delete, QKeySequence::Backspace}); - deleteShortcut->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(deleteShortcut, &QAction::triggered, this, &ConnectionViewWidget::removeButtonClicked); - - return buttons; -} - -ConnectionViewWidget::TabStatus ConnectionViewWidget::currentTab() const -{ - switch (ui->stackedWidget->currentIndex()) { - case 0: return ConnectionTab; - case 1: return BindingTab; - case 2: return DynamicPropertiesTab; - case 3: return BackendTab; - default: return InvalidTab; - } -} - -void ConnectionViewWidget::resetItemViews() -{ - if (currentTab() == ConnectionTab) { - ui->connectionView->selectionModel()->clear(); - - } else if (currentTab() == BindingTab) { - ui->bindingView->selectionModel()->clear(); - - } else if (currentTab() == DynamicPropertiesTab) { - ui->dynamicPropertiesView->selectionModel()->clear(); - } else if (currentTab() == BackendTab) { - ui->backendView->selectionModel()->clear(); - } - invalidateButtonStatus(); -} - -void ConnectionViewWidget::invalidateButtonStatus() -{ - if (currentTab() == ConnectionTab) { - emit setEnabledRemoveButton(ui->connectionView->selectionModel()->hasSelection()); - emit setEnabledAddButton(true); - } else if (currentTab() == BindingTab) { - emit setEnabledRemoveButton(ui->bindingView->selectionModel()->hasSelection()); - auto bindingModel = qobject_cast(ui->bindingView->model()); - emit setEnabledAddButton(bindingModel->connectionView()->model() - && bindingModel->connectionView()->selectedModelNodes().size() == 1); - - } else if (currentTab() == DynamicPropertiesTab) { - emit setEnabledRemoveButton(ui->dynamicPropertiesView->selectionModel()->hasSelection()); - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - emit setEnabledAddButton(dynamicPropertiesModel->view()->model() - && dynamicPropertiesModel->selectedNodes().size() == 1); - } else if (currentTab() == BackendTab) { - emit setEnabledAddButton(true); - emit setEnabledRemoveButton(ui->backendView->selectionModel()->hasSelection()); - } -} - -QTableView *ConnectionViewWidget::connectionTableView() const -{ - return ui->connectionView; -} - -QTableView *ConnectionViewWidget::bindingTableView() const -{ - return ui->bindingView; -} - -QTableView *ConnectionViewWidget::dynamicPropertiesTableView() const -{ - return ui->dynamicPropertiesView; -} - -QTableView *ConnectionViewWidget::backendView() const -{ - return ui->backendView; -} - -void ConnectionViewWidget::handleTabChanged(int) -{ - invalidateButtonStatus(); -} - -void ConnectionViewWidget::removeButtonClicked() -{ - if (currentTab() == ConnectionTab) { - if (ui->connectionView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->connectionView->selectionModel()->selectedRows().constFirst().row(); - auto connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel) { - connectionModel->deleteConnectionByRow(currentRow); - } - } else if (currentTab() == BindingTab) { - if (ui->bindingView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->bindingView->selectionModel()->selectedRows().constFirst().row(); - auto bindingModel = qobject_cast(ui->bindingView->model()); - if (bindingModel) { - bindingModel->deleteBindindByRow(currentRow); - } - } else if (currentTab() == DynamicPropertiesTab) { - if (ui->dynamicPropertiesView->selectionModel()->selectedRows().isEmpty()) - return; - int currentRow = ui->dynamicPropertiesView->selectionModel()->selectedRows().constFirst().row(); - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - if (dynamicPropertiesModel) - dynamicPropertiesModel->deleteDynamicPropertyByRow(currentRow); - } else if (currentTab() == BackendTab) { - int currentRow = ui->backendView->selectionModel()->selectedRows().constFirst().row(); - auto backendModel = qobject_cast(ui->backendView->model()); - if (backendModel) - backendModel->deletePropertyByRow(currentRow); - } - - invalidateButtonStatus(); -} - -void ConnectionViewWidget::addButtonClicked() -{ - - if (currentTab() == ConnectionTab) { - auto connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel) { - connectionModel->addConnection(); - } - } else if (currentTab() == BindingTab) { - auto bindingModel = qobject_cast(ui->bindingView->model()); - if (bindingModel) { - bindingModel->addBindingForCurrentNode(); - } - - } else if (currentTab() == DynamicPropertiesTab) { - auto dynamicPropertiesModel = qobject_cast(ui->dynamicPropertiesView->model()); - if (dynamicPropertiesModel) - dynamicPropertiesModel->addDynamicPropertyForCurrentNode(); - } else if (currentTab() == BackendTab) { - auto backendModel = qobject_cast(ui->backendView->model()); - if (backendModel) - backendModel->addNewBackend(); - } - - invalidateButtonStatus(); -} - -void ConnectionViewWidget::editorForConnection() -{ - QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::accepted, - [&]() { - if (m_connectionEditor->hasModelIndex()) { - ConnectionModel *connectionModel = qobject_cast(ui->connectionView->model()); - if (connectionModel->connectionView()->isWidgetEnabled() - && (connectionModel->rowCount() > m_connectionEditor->modelIndex().row())) { - connectionModel->connectionView() - ->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() { - SignalHandlerProperty signalHandler - = connectionModel->signalHandlerPropertyForRow( - m_connectionEditor->modelIndex().row()); - signalHandler.setSource(m_connectionEditor->connectionValue()); - }); - } - m_connectionEditor->resetModelIndex(); - } - - m_connectionEditor->hideWidget(); - }); - QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::rejected, - [&]() { - m_connectionEditor->resetModelIndex(); - m_connectionEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::editorForBinding() -{ - QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::accepted, - [&]() { - BindingModel *bindingModel = qobject_cast(bindingTableView()->model()); - QString newValue = m_bindingEditor->bindingValue().trimmed(); - - if (m_bindingIndex.isValid()) { - if (bindingModel->connectionView()->isWidgetEnabled() - && (bindingModel->rowCount() > m_bindingIndex.row())) { - bindingModel->connectionView()->executeInTransaction( - "ConnectionView::setBindingProperty", [this, bindingModel, newValue]() { - BindingProperty property = bindingModel->bindingPropertyForRow( - m_bindingIndex.row()); - - if (property.isValid()) { - if (property.isBindingProperty()) { - if (property.isDynamic()) { - property - .setDynamicTypeNameAndExpression(property.dynamicTypeName(), - newValue); - } else { - property.setExpression(newValue); - } - } - } - }); - } - } - - m_bindingIndex = QModelIndex(); - m_bindingEditor->hideWidget(); - }); - QObject::connect(m_bindingEditor, &QmlDesigner::BindingEditor::rejected, - [&]() { - m_bindingIndex = QModelIndex(); //invalidating index - m_bindingEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::editorForDynamic() -{ - QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::accepted, - [&]() { - DynamicPropertiesModel *propertiesModel = qobject_cast(dynamicPropertiesTableView()->model()); - QString newValue = m_dynamicEditor->bindingValue().trimmed(); - - if (m_dynamicIndex.isValid()) { - if (qobject_cast(propertiesModel->view())->isWidgetEnabled() - && (propertiesModel->rowCount() > m_dynamicIndex.row())) { - propertiesModel->view()->executeInTransaction( - "ConnectionView::setBinding", [this, propertiesModel, newValue]() { - AbstractProperty abProp = propertiesModel->abstractPropertyForRow( - m_dynamicIndex.row()); - - if (abProp.isValid()) { - if (abProp.isBindingProperty()) { - BindingProperty property = abProp.toBindingProperty(); - property.setDynamicTypeNameAndExpression(property.dynamicTypeName(), - newValue); - } - - //if it's a variant property, then we remove it and replace with binding - else if (abProp.isVariantProperty()) { - VariantProperty property = abProp.toVariantProperty(); - PropertyName name = property.name(); - TypeName type = property.dynamicTypeName(); - - BindingProperty newProperty = propertiesModel - ->replaceVariantWithBinding(name); - if (newProperty.isValid()) { - newProperty.setDynamicTypeNameAndExpression(type, newValue); - } - } - } - }); - } - } - - m_dynamicIndex = QModelIndex(); - m_dynamicEditor->hideWidget(); - }); - QObject::connect(m_dynamicEditor, &QmlDesigner::BindingEditor::rejected, - [&]() { - m_dynamicIndex = QModelIndex(); //invalidating index - m_dynamicEditor->hideWidget(); - }); -} - -void ConnectionViewWidget::bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == BindingTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == ConnectionTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (currentTab() == DynamicPropertiesTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } -} - -void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*revious*/) -{ - if (currentTab() == BackendTab) { - if (current.isValid()) { - emit setEnabledRemoveButton(true); - } else { - emit setEnabledRemoveButton(false); - } - } - -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h deleted file mode 100644 index f71641bd8bc..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -QT_BEGIN_NAMESPACE -class QShortcut; -class QToolButton; -class QTableView; -class QListView; -QT_END_NAMESPACE - -namespace QmlDesigner { - -namespace Ui { class ConnectionViewWidget; } - -class ActionEditor; -class BindingEditor; - -class BindingModel; -class ConnectionModel; -class DynamicPropertiesModel; -class BackendModel; - -class ConnectionViewWidget : public QFrame -{ - Q_OBJECT - -public: - - enum TabStatus { - ConnectionTab, - BindingTab, - DynamicPropertiesTab, - BackendTab, - InvalidTab - }; - - explicit ConnectionViewWidget(QWidget *parent = nullptr); - ~ConnectionViewWidget() override; - - void setBindingModel(BindingModel *model); - void setConnectionModel(ConnectionModel *model); - void setDynamicPropertiesModel(DynamicPropertiesModel *model); - void setBackendModel(BackendModel *model); - - QList createToolBarWidgets(); - - TabStatus currentTab() const; - - void resetItemViews(); - void invalidateButtonStatus(); - - QTableView *connectionTableView() const; - QTableView *bindingTableView() const; - QTableView *dynamicPropertiesTableView() const; - QTableView *backendView() const; - - void bindingTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void connectionTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void dynamicPropertiesTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - void backendTableViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); - -signals: - void setEnabledAddButton(bool enabled); - void setEnabledRemoveButton(bool enabled); - -protected: - void contextMenuEvent(QContextMenuEvent *event) override; - -private: - void handleTabChanged(int i); - void removeButtonClicked(); - void addButtonClicked(); - - //methods to prepare editors - void editorForConnection(); - void editorForBinding(); - void editorForDynamic(); - -private: - Ui::ConnectionViewWidget *ui; - QmlDesigner::ActionEditor *m_connectionEditor; //editor for connections in connection view - QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view - QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view - - QModelIndex m_bindingIndex; - QModelIndex m_dynamicIndex; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui deleted file mode 100644 index 40939468c5e..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui +++ /dev/null @@ -1,265 +0,0 @@ - - - QmlDesigner::ConnectionViewWidget - - - - 0 - 0 - 994 - 611 - - - - Connections - - - - 0 - - - 1 - - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - 0 - - - - - 0 - 2 - - - - - 16777215 - 2 - - - - - - - - 3 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - false - - - - - - - - - - - - stackedWidget - tabBar - widgetSpacer - toolBar - - - - QTabBar - QWidget -
qtabbar.h
- 1 -
-
- - -
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp deleted file mode 100644 index e2c30b0a037..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "delegates.h" - -#include "backendmodel.h" -#include "connectionmodel.h" -#include "bindingmodel.h" -#include "dynamicpropertiesmodel.h" -#include "connectionview.h" -#include "nodemetainfo.h" - -#include - -#include - -#include -#include -#include - -namespace QmlDesigner { - -QStringList prependOnForSignalHandler(const QStringList &signalNames) -{ - QStringList signalHandlerNames; - for (const QString &signalName : signalNames) { - QString signalHandlerName = signalName; - if (!signalHandlerName.isEmpty()) { - QChar firstChar = signalHandlerName.at(0).toUpper(); - signalHandlerName[0] = firstChar; - signalHandlerName.prepend(QLatin1String("on")); - signalHandlerNames.append(signalHandlerName); - } - } - return signalHandlerNames; -} - -PropertiesComboBox::PropertiesComboBox(QWidget *parent) : QComboBox(parent) -{ - setEditable(true); - setValidator(new QRegularExpressionValidator(QRegularExpression(QLatin1String("[a-z|A-Z|0-9|._-]*")), this)); -} - -QString PropertiesComboBox::text() const -{ - return currentText(); -} - -void PropertiesComboBox::setText(const QString &text) -{ - setEditText(text); -} - -void PropertiesComboBox::disableValidator() -{ - setValidator(nullptr); -} - -ConnectionComboBox::ConnectionComboBox(QWidget *parent) : PropertiesComboBox(parent) -{ -} - -QString ConnectionComboBox::text() const -{ - int index = findText(currentText()); - if (index > -1) { - QVariant variantData = itemData(index); - if (variantData.isValid()) - return variantData.toString(); - } - - return currentText(); -} - -ConnectionEditorDelegate::ConnectionEditorDelegate(QWidget *parent) - : QStyledItemDelegate(parent) -{ -} - -void ConnectionEditorDelegate::paint(QPainter *painter, - const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - opt.state &= ~QStyle::State_HasFocus; - QStyledItemDelegate::paint(painter, opt, index); -} - -BindingDelegate::BindingDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ - static QItemEditorFactory *factory = nullptr; - if (factory == nullptr) { - factory = new QItemEditorFactory; - QItemEditorCreatorBase *creator - = new QItemEditorCreator("text"); - factory->registerEditor(QVariant::String, creator); - } - - setItemEditorFactory(factory); -} - -QWidget *BindingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto model = qobject_cast(index.model()); - if (!model) { - qWarning() << "BindingDelegate::createEditor no model"; - return widget; - } - if (!model->connectionView()) { - qWarning() << "BindingDelegate::createEditor no connection view"; - return widget; - } - - model->connectionView()->allModelNodes(); - - auto bindingComboBox = qobject_cast(widget); - if (!bindingComboBox) { - qWarning() << "BindingDelegate::createEditor no bindingComboBox"; - return widget; - } - - BindingProperty bindingProperty = model->bindingPropertyForRow(index.row()); - - switch (index.column()) { - case BindingModel::TargetModelNodeRow: - return nullptr; //no editor - case BindingModel::TargetPropertyNameRow: { - bindingComboBox->addItems(model->possibleTargetProperties(bindingProperty)); - } break; - case BindingModel::SourceModelNodeRow: { - //common items - for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) { - bindingComboBox->addItem(modelNode.id()); - } - } - //singletons: - if (RewriterView* rv = model->connectionView()->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty()) { - bindingComboBox->addItem(data.typeName); - } - } - } - //parent: - if (!bindingProperty.parentModelNode().isRootNode()) - bindingComboBox->addItem(QLatin1String("parent")); - } break; - case BindingModel::SourcePropertyNameRow: { - bindingComboBox->addItems(model->possibleSourceProperties(bindingProperty)); - bindingComboBox->disableValidator(); - } break; - default: qWarning() << "BindingDelegate::createEditor column" << index.column(); - } - - connect(bindingComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(bindingComboBox); - }); - - return widget; -} - -DynamicPropertiesDelegate::DynamicPropertiesDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ -// static QItemEditorFactory *factory = 0; -// if (factory == 0) { -// factory = new QItemEditorFactory; -// QItemEditorCreatorBase *creator -// = new QItemEditorCreator("text"); -// factory->registerEditor(QVariant::String, creator); -// } - -// setItemEditorFactory(factory); -} - -QWidget *DynamicPropertiesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto model = qobject_cast(index.model()); - if (!model) { - qWarning() << "BindingDelegate::createEditor no model"; - return widget; - } - - if (!model->view()) { - qWarning() << "BindingDelegate::createEditor no connection view"; - return widget; - } - model->view()->allModelNodes(); - - switch (index.column()) { - case DynamicPropertiesModel::TargetModelNodeRow: { - return nullptr; //no editor - }; - case DynamicPropertiesModel::PropertyNameRow: { - return QStyledItemDelegate::createEditor(parent, option, index); - }; - case DynamicPropertiesModel::PropertyTypeRow: { - - auto dynamicPropertiesComboBox = new PropertiesComboBox(parent); - connect(dynamicPropertiesComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(dynamicPropertiesComboBox); - }); - - dynamicPropertiesComboBox->addItem(QLatin1String("alias")); - dynamicPropertiesComboBox->addItem(QLatin1String("Item")); - dynamicPropertiesComboBox->addItem(QLatin1String("real")); - dynamicPropertiesComboBox->addItem(QLatin1String("int")); - dynamicPropertiesComboBox->addItem(QLatin1String("string")); - dynamicPropertiesComboBox->addItem(QLatin1String("bool")); - dynamicPropertiesComboBox->addItem(QLatin1String("url")); - dynamicPropertiesComboBox->addItem(QLatin1String("color")); - dynamicPropertiesComboBox->addItem(QLatin1String("variant")); - dynamicPropertiesComboBox->addItem(QLatin1String("TextureInput")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector2d")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector3d")); - dynamicPropertiesComboBox->addItem(QLatin1String("vector4d")); - return dynamicPropertiesComboBox; - }; - case DynamicPropertiesModel::PropertyValueRow: { - return QStyledItemDelegate::createEditor(parent, option, index); - }; - default: qWarning() << "BindingDelegate::createEditor column" << index.column(); - } - - return nullptr; -} - -ConnectionDelegate::ConnectionDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ - static QItemEditorFactory *factory = nullptr; - if (factory == nullptr) { - factory = new QItemEditorFactory; - QItemEditorCreatorBase *creator - = new QItemEditorCreator("text"); - factory->registerEditor(QVariant::String, creator); - } - - setItemEditorFactory(factory); -} - -static QString nameForAction(const QString &input) -{ - QStringList list = input.split('.'); - return list.first(); -} - -QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - const auto connectionModel = qobject_cast(index.model()); - - auto connectionComboBox = qobject_cast(widget); - - if (!connectionModel) { - qWarning() << "ConnectionDelegate::createEditor no model"; - return widget; - } - - if (!connectionModel->connectionView()) { - qWarning() << "ConnectionDelegate::createEditor no connection view"; - return widget; - } - - if (!connectionComboBox) { - qWarning() << "ConnectionDelegate::createEditor no bindingComboBox"; - return widget; - } - - switch (index.column()) { - case ConnectionModel::TargetModelNodeRow: { - - auto addMetaInfoProperties = [&](const NodeMetaInfo& itemMetaInfo, QString itemName){ - if (itemMetaInfo.isValid()) { - for (const auto &property : itemMetaInfo.properties()) { - NodeMetaInfo propertyType = property.propertyType(); - if (propertyType.isValid() && propertyType.isFileComponent()) { - if (!property.isEnumType() && !property.isPrivate() - && !property.isListProperty() && !property.isPointer()) { - if (propertyType.isQtObject()) { - connectionComboBox->addItem(itemName + "." + property.name()); - } - } - } - } - } - }; - - for (const ModelNode &modelNode : connectionModel->connectionView()->allModelNodes()) { - if (!modelNode.id().isEmpty()) { - connectionComboBox->addItem(modelNode.id()); - - for (const BindingProperty &property : modelNode.bindingProperties()) { - if (property.isValid()) { - if (property.isAlias()) { - connectionComboBox->addItem(modelNode.id() - + "." - + QString::fromUtf8(property.name())); - } - } - } - - //Components - if (modelNode.isComponent()) { - NodeMetaInfo componentMetaInfo = modelNode.metaInfo(); - addMetaInfoProperties(componentMetaInfo, modelNode.id()); - } - } - } - //singletons: - if (RewriterView* rv = connectionModel->connectionView()->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty()) { - //singleton itself - connectionComboBox->addItem(data.typeName); - - //its properties, mostly looking for aliases: - NodeMetaInfo metaInfo = connectionModel->connectionView()->model()->metaInfo(data.typeName.toUtf8()); - addMetaInfoProperties(metaInfo, data.typeName); - } - } - } - } break; - case ConnectionModel::TargetPropertyNameRow: { - connectionComboBox->addItems(prependOnForSignalHandler(connectionModel->getSignalsForRow(index.row()))); - } break; - case ConnectionModel::SourceRow: { - ModelNode rootModelNode = connectionModel->connectionView()->rootModelNode(); - if (QmlItemNode::isValidQmlItemNode(rootModelNode) && !rootModelNode.id().isEmpty()) { - - QString itemText = tr("Change to default state"); - QString source = QString::fromLatin1("%1.state = \"\"").arg(rootModelNode.id()); - connectionComboBox->addItem(itemText, source); - connectionComboBox->disableValidator(); - - for (const QmlModelState &state : QmlItemNode(rootModelNode).states().allStates()) { - QString itemText = tr("Change state to %1").arg(state.name()); - QString source = QString::fromLatin1("%1.state = \"%2\"") - .arg(rootModelNode.id()) - .arg(state.name()); - connectionComboBox->addItem(itemText, source); - } - - QStringList trigger = connectionModel->getflowActionTriggerForRow(index.row()); - for (const QString &action : trigger) { - connectionComboBox->addItem(tr("Activate FlowAction %1").arg(nameForAction(action)), action); - } - } - connectionComboBox->disableValidator(); - } break; - - default: qWarning() << "ConnectionDelegate::createEditor column" << index.column(); - } - - connect(connectionComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(connectionComboBox); - }); - - return widget; -} - -BackendDelegate::BackendDelegate(QWidget *parent) : ConnectionEditorDelegate(parent) -{ -} - -QWidget *BackendDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - const auto model = qobject_cast(index.model()); - - model->connectionView()->allModelNodes(); - - QWidget *widget = QStyledItemDelegate::createEditor(parent, option, index); - - QTC_ASSERT(model, return widget); - QTC_ASSERT(model->connectionView(), return widget); - - switch (index.column()) { - case BackendModel::TypeNameColumn: { - auto backendComboBox = new PropertiesComboBox(parent); - backendComboBox->addItems(model->possibleCppTypes()); - connect(backendComboBox, &QComboBox::activated, this, [=] { - auto delegate = const_cast(this); - emit delegate->commitData(backendComboBox); - }); - return backendComboBox; - }; - case BackendModel::PropertyNameColumn: { - return widget; - }; - case BackendModel::IsSingletonColumn: { - return nullptr; //no editor - }; - case BackendModel::IsLocalColumn: { - return nullptr; //no editor - }; - default: qWarning() << "BackendDelegate::createEditor column" << index.column(); - } - - return widget; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.h b/src/plugins/qmldesigner/components/connectioneditor/delegates.h deleted file mode 100644 index 55f886b98a4..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include -#include - -namespace QmlDesigner { - -class PropertiesComboBox : public QComboBox -{ - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText USER true) -public: - PropertiesComboBox(QWidget *parent = nullptr); - - virtual QString text() const; - void setText(const QString &text); - void disableValidator(); -}; - -class ConnectionComboBox : public PropertiesComboBox -{ - Q_OBJECT -public: - ConnectionComboBox(QWidget *parent = nullptr); - QString text() const override; -}; - -class ConnectionEditorDelegate : public QStyledItemDelegate -{ -public: - ConnectionEditorDelegate(QWidget *parent = nullptr); - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; -}; - -class BindingDelegate : public ConnectionEditorDelegate -{ -public: - BindingDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -class DynamicPropertiesDelegate : public ConnectionEditorDelegate -{ -public: - DynamicPropertiesDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - - -class ConnectionDelegate : public ConnectionEditorDelegate -{ - Q_OBJECT -public: - ConnectionDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -class BackendDelegate : public ConnectionEditorDelegate -{ - Q_OBJECT -public: - BackendDelegate(QWidget *parent = nullptr); - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp new file mode 100644 index 00000000000..ae23fab076d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "dynamicpropertiesitem.h" +#include "connectioneditorutils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +QHash DynamicPropertiesItem::roleNames() +{ + return {{TargetNameRole, "target"}, + {PropertyNameRole, "name"}, + {PropertyTypeRole, "type"}, + {PropertyValueRole, "value"}}; +} + +QStringList DynamicPropertiesItem::headerLabels() +{ + return {Tr::tr("Item"), Tr::tr("Property"), Tr::tr("Property Type"), Tr::tr("Property Value")}; +} + +DynamicPropertiesItem::DynamicPropertiesItem(const AbstractProperty &property) + : QStandardItem(idOrTypeName(property.parentModelNode())) +{ + updateProperty(property); +} + +int DynamicPropertiesItem::internalId() const +{ + return data(InternalIdRole).toInt(); +} + +PropertyName DynamicPropertiesItem::propertyName() const +{ + return data(PropertyNameRole).toString().toUtf8(); +} + +std::optional parentIfNotDefaultState(const AbstractProperty &property) +{ + const QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode()); + if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) + return objectNode; + return std::nullopt; +} + +void DynamicPropertiesItem::updateProperty(const AbstractProperty &property) +{ + setData(property.parentModelNode().internalId(), InternalIdRole); + setData(idOrTypeName(property.parentModelNode()), TargetNameRole); + setData(property.name(), PropertyNameRole); + setData(property.dynamicTypeName(), PropertyTypeRole); + + if (property.isVariantProperty()) { + if (std::optional nodeInState = parentIfNotDefaultState(property)) + setData(nodeInState->modelValue(property.name()), PropertyValueRole); + else + setData(property.toVariantProperty().value(), PropertyValueRole); + } else if (property.isBindingProperty()) { + if (std::optional nodeInState = parentIfNotDefaultState(property)) + setData(nodeInState->expression(property.name()), PropertyValueRole); + else + setData(property.toBindingProperty().expression(), PropertyValueRole); + } +} + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h new file mode 100644 index 00000000000..9d95a8d9e4d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class AbstractProperty; + +class DynamicPropertiesItem : public QStandardItem +{ +public: + enum UserRoles { + InternalIdRole = Qt::UserRole + 2, + TargetNameRole, + PropertyNameRole, + PropertyTypeRole, + PropertyValueRole + }; + + static QHash roleNames(); + static QStringList headerLabels(); + + DynamicPropertiesItem(const AbstractProperty &property); + + int internalId() const; + PropertyName propertyName() const; + + void updateProperty(const AbstractProperty &property); +}; + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index bfd5e16be97..69f51a277d2 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -2,164 +2,44 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "dynamicpropertiesmodel.h" +#include "dynamicpropertiesitem.h" +#include "connectioneditorutils.h" -#include "bindingproperty.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "qmlchangeset.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qmlobjectnode.h" -#include "qmltimeline.h" -#include "rewritertransaction.h" -#include "rewritingexception.h" -#include "variantproperty.h" - +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include -#include -#include - -namespace { - -bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProp1, - const QmlDesigner::VariantProperty &variantProp2) -{ - if (variantProp1.parentModelNode() != variantProp2.parentModelNode()) - return false; - if (variantProp1.name() != variantProp2.name()) - return false; - return true; -} - -QString idOrTypeNameForNode(const QmlDesigner::ModelNode &modelNode) -{ - QString idLabel = modelNode.id(); - if (idLabel.isEmpty()) - idLabel = modelNode.simplifiedTypeName(); - - return idLabel; -} - -QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::TypeName &typeName) -{ - QVariant returnValue = variant; - - if (typeName == "int") { - bool ok; - returnValue = variant.toInt(&ok); - if (!ok) - returnValue = 0; - } else if (typeName == "real") { - bool ok; - returnValue = variant.toReal(&ok); - if (!ok) - returnValue = 0.0; - - } else if (typeName == "string") { - returnValue = variant.toString(); - - } else if (typeName == "bool") { - returnValue = variant.toBool(); - } else if (typeName == "url") { - returnValue = variant.toUrl(); - } else if (typeName == "color") { - if (QColor::isValidColor(variant.toString())) { - returnValue = variant.toString(); - } else { - returnValue = QColor(Qt::black); - } - } else if (typeName == "vector2d") { - returnValue = "Qt.vector2d(0, 0)"; - } else if (typeName == "vector3d") { - returnValue = "Qt.vector3d(0, 0, 0)"; - } else if (typeName == "vector4d") { - returnValue = "Qt.vector4d(0, 0, 0 ,0)"; - } else if (typeName == "TextureInput") { - returnValue = "null"; - } else if (typeName == "alias") { - returnValue = "null"; - } else if (typeName == "Item") { - returnValue = "null"; - } - - return returnValue; -} - -} // namespace +#include namespace QmlDesigner { -PropertyName DynamicPropertiesModel::unusedProperty(const ModelNode &modelNode) +DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *parent) + : QStandardItemModel(parent) + , m_view(parent) + , m_delegate(new DynamicPropertiesModelBackendDelegate(this)) + , m_explicitSelection(exSelection) { - PropertyName propertyName = "property"; - int i = 0; - if (modelNode.isValid() && modelNode.metaInfo().isValid()) { - while (true) { - const PropertyName currentPropertyName = propertyName + QString::number(i++).toLatin1(); - if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName)) - return currentPropertyName; - } - } - - return propertyName; + setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); } -bool DynamicPropertiesModel::isValueType(const TypeName &type) +AbstractView *DynamicPropertiesModel::view() const { - // "variant" is considered value type as it is initialized as one. - // This may need to change if we provide any kind of proper editor for it. - static const QSet valueTypes {"int", "real", "color", "string", "bool", "url", "variant"}; - return valueTypes.contains(type); + return m_view; } -QVariant DynamicPropertiesModel::defaultValueForType(const TypeName &type) +DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const { - QVariant value; - if (type == "int") - value = 0; - else if (type == "real") - value = 0.0; - else if (type == "color") - value = QColor(255, 255, 255); - else if (type == "string") - value = "This is a string"; - else if (type == "bool") - value = false; - else if (type == "url") - value = ""; - else if (type == "variant") - value = ""; - - return value; -} - -QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type) -{ - QString expression; - if (type == "alias") - expression = "null"; - else if (type == "TextureInput") - expression = "null"; - else if (type == "vector2d") - expression = "Qt.vector2d(0, 0)"; - else if (type == "vector3d") - expression = "Qt.vector3d(0, 0, 0)"; - else if (type == "vector4d") - expression = "Qt.vector4d(0, 0, 0 ,0)"; - - return expression; -} - -void DynamicPropertiesModel::add() -{ - addDynamicPropertyForCurrentNode(); -} - -void DynamicPropertiesModel::remove(int row) -{ - deleteDynamicPropertyByRow(row); + return m_delegate; } int DynamicPropertiesModel::currentIndex() const @@ -167,112 +47,292 @@ int DynamicPropertiesModel::currentIndex() const return m_currentIndex; } -void DynamicPropertiesModel::setCurrentIndex(int i) +AbstractProperty DynamicPropertiesModel::currentProperty() const { - if (m_currentIndex == i) - return; - - m_currentIndex = i; - - emit currentIndexChanged(); + return propertyForRow(m_currentIndex); } -DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent) - : QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection), - m_delegate(new DynamicPropertiesModelBackendDelegate(this)) +void DynamicPropertiesModel::add() { - connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged); + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); + + if (const QList nodes = selectedNodes(); nodes.size() == 1) { + const ModelNode modelNode = nodes.constFirst(); + if (!modelNode.isValid()) + return; + + try { + PropertyName newName = uniquePropertyName("property", modelNode); + VariantProperty newProperty = modelNode.variantProperty(newName); + newProperty.setDynamicTypeNameAndValue("string", "This is a string"); + } catch (RewritingException &e) { + showErrorMessage(e.description()); + } + } else { + qWarning() << "DynamicPropertiesModel::add not one node selected"; + } } -void DynamicPropertiesModel::resetModel() +void DynamicPropertiesModel::remove(int row) { - beginResetModel(); - const int backIndex = m_currentIndex; + m_view->executeInTransaction(__FUNCTION__, [this, row]() { + if (DynamicPropertiesItem *item = itemForRow(row)) { + PropertyName name = item->propertyName(); + if (ModelNode node = modelNodeForItem(item); node.isValid()) { + node.removeProperty(name); + + QmlObjectNode objectNode = QmlObjectNode(node); + const auto stateOperations = objectNode.allAffectingStatesOperations(); + for (const QmlModelStateOperation &stateOperation : stateOperations) { + if (stateOperation.modelNode().hasProperty(name)) + stateOperation.modelNode().removeProperty(name); + } + for (auto &timelineNode : objectNode.allTimelines()) { + QmlTimeline timeline(timelineNode); + timeline.removeKeyframesForTargetAndProperty(node, name); + } + } + } + }); + reset(); +} + +void DynamicPropertiesModel::reset(const QList &modelNodes) +{ + AbstractProperty current = currentProperty(); + clear(); - setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}); + + if (!modelNodes.isEmpty()) { + for (const ModelNode &modelNode : modelNodes) + addModelNode(modelNode); + return; + } if (m_view->isAttached()) { - const auto nodes = selectedNodes(); - for (const ModelNode &modelNode : nodes) + const QList selected = selectedNodes(); + for (const ModelNode &modelNode : selected) addModelNode(modelNode); } - emit currentIndexChanged(); - endResetModel(); - m_currentIndex = backIndex; + setCurrentProperty(current); } - -// Method creates dynamic BindingProperty with the same name and type as old VariantProperty -// Value copying is optional -BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue) +void DynamicPropertiesModel::setCurrentIndex(int i) { - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - if (modelNode.hasVariantProperty(name)) { - try { - VariantProperty vprop = modelNode.variantProperty(name); - TypeName oldType = vprop.dynamicTypeName(); - QVariant oldValue = vprop.value(); - - modelNode.removeProperty(name); - - BindingProperty bprop = modelNode.bindingProperty(name); - if (bprop.isValid()) { - if (copyValue) - bprop.setDynamicTypeNameAndExpression(oldType, oldValue.toString()); - return bprop; - } - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } - } - } else { - qWarning() << "DynamicPropertiesModel::replaceVariantWithBinding: no selected nodes"; + if (m_currentIndex != i) { + m_currentIndex = i; + emit currentIndexChanged(); } + // Property properties may have changed. + m_delegate->update(currentProperty()); +} +void DynamicPropertiesModel::setCurrentProperty(const AbstractProperty &property) +{ + if (!property.isValid()) + return; + + if (auto index = findRow(property.parentModelNode().internalId(), property.name())) + setCurrentIndex(*index); +} + +void DynamicPropertiesModel::setCurrent(int internalId, const PropertyName &name) +{ + if (internalId < 0) + return; + + if (auto index = findRow(internalId, name)) + setCurrentIndex(*index); +} + +void DynamicPropertiesModel::updateItem(const AbstractProperty &property) +{ + if (!property.isDynamic()) + return; + + if (auto *item = itemForProperty(property)) + item->updateProperty(property); + else + addProperty(property); +} + +void DynamicPropertiesModel::removeItem(const AbstractProperty &property) +{ + if (!property.isValid()) + return; + + AbstractProperty current = currentProperty(); + + if (auto index = findRow(property.parentModelNode().internalId(), property.name())) + static_cast(removeRow(*index)); + + setCurrentProperty(current); +} + +QHash DynamicPropertiesModel::roleNames() const +{ + return DynamicPropertiesItem::roleNames(); +} + +AbstractProperty DynamicPropertiesModel::propertyForRow(int row) const +{ + if (!m_view) + return {}; + + if (!m_view->isAttached()) + return {}; + + if (auto *item = itemForRow(row)) { + int internalId = item->internalId(); + if (ModelNode node = m_view->modelNodeForInternalId(internalId); node.isValid()) + return node.property(item->propertyName()); + } return {}; } -// Finds selected property, and changes it to empty value (QVariant()) -// If it's a BindingProperty, then replaces it with empty VariantProperty -void DynamicPropertiesModel::resetProperty(const PropertyName &name) +std::optional DynamicPropertiesModel::findRow(int nodeId, const PropertyName &name) const { - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - if (modelNode.hasProperty(name)) { - try { - AbstractProperty abProp = modelNode.property(name); + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->propertyName() == name && item->internalId() == nodeId) + return i; + } + } + return std::nullopt; +} - if (abProp.isVariantProperty()) { - VariantProperty property = abProp.toVariantProperty(); - QVariant newValue = convertVariantForTypeName({}, property.dynamicTypeName()); - property.setDynamicTypeNameAndValue(property.dynamicTypeName(), - newValue); - } else if (abProp.isBindingProperty()) { - BindingProperty property = abProp.toBindingProperty(); - TypeName oldType = property.dynamicTypeName(); +DynamicPropertiesItem *DynamicPropertiesModel::itemForRow(int row) const +{ + if (QModelIndex idx = index(row, 0); idx.isValid()) + return dynamic_cast(itemFromIndex(idx)); + return nullptr; +} - // removing old property, to create the new one with the same name - modelNode.removeProperty(name); +DynamicPropertiesItem *DynamicPropertiesModel::itemForProperty(const AbstractProperty &property) const +{ + if (!property.isValid()) + return nullptr; - VariantProperty newProperty = modelNode.variantProperty(name); - QVariant newValue = convertVariantForTypeName({}, oldType); - newProperty.setDynamicTypeNameAndValue(oldType, newValue); - } + if (auto row = findRow(property.parentModelNode().internalId(), property.name())) + return itemForRow(*row); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } + return nullptr; +} + +ModelNode DynamicPropertiesModel::modelNodeForItem(DynamicPropertiesItem *item) +{ + if (!m_view->isAttached()) + return {}; + + return m_view->modelNodeForInternalId(item->internalId()); +} + +void DynamicPropertiesModel::addModelNode(const ModelNode &node) +{ + if (!node.isValid()) + return; + + for (const AbstractProperty &property : dynamicPropertiesFromNode(node)) + addProperty(property); +} + +void DynamicPropertiesModel::addProperty(const AbstractProperty &property) +{ + const PropertyName name = property.name(); + for (int i = 0; i < rowCount(); ++i) { + if (auto *item = itemForRow(i)) { + if (item->propertyName() > name) { + insertRow(i, new DynamicPropertiesItem(property)); + return; } } - } else { - qWarning() << "DynamicPropertiesModel::resetProperty: no selected nodes"; + } + appendRow(new DynamicPropertiesItem(property)); +} + +void DynamicPropertiesModel::commitPropertyType(int row, const TypeName &type) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); + try { + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + const QString expression = binding.expression(); + binding.parentModelNode().removeProperty(binding.name()); + binding.setDynamicTypeNameAndExpression(type, expression); + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + QVariant val = typeConvertVariant(variant.value(), type); + variant.parentModelNode().removeProperty(variant.name()); + variant.setDynamicTypeNameAndValue(type, val); + } + transaction.commit(); + + } catch (Exception &e) { + showErrorMessage(e.description()); + } +} + +void DynamicPropertiesModel::commitPropertyName(int row, const PropertyName &name) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + ModelNode node = property.parentModelNode(); + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + m_view->executeInTransaction(__FUNCTION__, [binding, name, &node]() { + const QString expression = binding.expression(); + const TypeName type = binding.dynamicTypeName(); + node.removeProperty(binding.name()); + node.bindingProperty(name).setDynamicTypeNameAndExpression(type, expression); + }); + + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + m_view->executeInTransaction(__FUNCTION__, [variant, name, &node]() { + const QVariant value = variant.value(); + const TypeName type = variant.dynamicTypeName(); + node.removeProperty(variant.name()); + node.variantProperty(name).setDynamicTypeNameAndValue(type, value); + }); + } +} + +void DynamicPropertiesModel::commitPropertyValue(int row, const QVariant &value) +{ + AbstractProperty property = propertyForRow(row); + if (!property.isValid()) + return; + + RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); + try { + bool isBindingValue = isBindingExpression(value); + if (property.isBindingProperty()) { + BindingProperty binding = property.toBindingProperty(); + if (!isBindingValue) { + convertBindingToVariantProperty(binding, value); + } else { + const QString expression = value.toString(); + const TypeName typeName = binding.dynamicTypeName(); + binding.setDynamicTypeNameAndExpression(typeName, expression); + } + } else if (property.isVariantProperty()) { + VariantProperty variant = property.toVariantProperty(); + if (isBindingValue) + convertVariantToBindingProperty(variant, value); + else + variant.setDynamicTypeNameAndValue(variant.dynamicTypeName(), value); + } + transaction.commit(); + } catch (Exception &e) { + showErrorMessage(e.description()); } } @@ -285,676 +345,13 @@ void DynamicPropertiesModel::dispatchPropertyChanges(const AbstractProperty &abs const PropertyName propertyName = abstractProperty.name(); const AbstractProperty targetProperty = target.variantProperty(propertyName); if (target.hasProperty(propertyName) && targetProperty.isDynamic()) - abstractPropertyChanged(targetProperty); + updateItem(targetProperty); } } } -void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty) -{ - if (!bindingProperty.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(bindingProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForBindingProperty(bindingProperty); - - if (rowNumber == -1) - addBindingProperty(bindingProperty); - else - updateBindingProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::abstractPropertyChanged(const AbstractProperty &property) -{ - if (!property.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(property.parentModelNode())) - return; - - int rowNumber = findRowForProperty(property); - if (rowNumber > -1) { - if (property.isVariantProperty()) - updateVariantProperty(rowNumber); - else - updateBindingProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty) -{ - if (!variantProperty.isDynamic()) - return; - - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(variantProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForVariantProperty(variantProperty); - - if (rowNumber == -1) - addVariantProperty(variantProperty); - else - updateVariantProperty(rowNumber); - } - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProperty) -{ - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(bindingProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForBindingProperty(bindingProperty); - removeRow(rowNumber); - } - - emit currentIndexChanged(); - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProperty) -{ - m_handleDataChanged = false; - - const QList nodes = selectedNodes(); - if (!nodes.contains(variantProperty.parentModelNode())) - return; - - if (!m_lock) { - int rowNumber = findRowForVariantProperty(variantProperty); - removeRow(rowNumber); - } - - emit currentIndexChanged(); - - m_handleDataChanged = true; -} - -void DynamicPropertiesModel::reset() -{ - m_handleDataChanged = false; - resetModel(); - m_handleDataChanged = true; - emit currentIndexChanged(); -} - -void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) -{ - QTC_ASSERT(m_explicitSelection, return); - - if (!node.isValid()) - return; - - m_selectedNodes.clear(); - m_selectedNodes.append(node); - reset(); -} - -AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2) - .toString(); - - if (!m_view->isAttached()) - return AbstractProperty(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.property(targetPropertyName.toUtf8()); - - return {}; -} - -BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.bindingProperty(targetPropertyName.toUtf8()); - - return {}; -} - -VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const -{ - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - - ModelNode modelNode = m_view->modelNodeForInternalId(internalId); - - if (modelNode.isValid()) - return modelNode.variantProperty(targetPropertyName.toUtf8()); - - return {}; -} - -QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const -{ - const ModelNode modelNode = bindingProperty.parentModelNode(); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node"; - return {}; - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - QStringList possibleProperties; - const PropertyMetaInfos props = metaInfo.properties(); - for (const auto &property : props) { - if (property.isWritable()) - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - - return possibleProperties; - } - - return {}; -} - -void DynamicPropertiesModel::addDynamicPropertyForCurrentNode() -{ - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED); - - if (selectedNodes().size() == 1) { - const ModelNode modelNode = selectedNodes().constFirst(); - if (modelNode.isValid()) { - try { - modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", "This is a string"); - } catch (RewritingException &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } - } else { - qWarning() << " BindingModel::addBindingForCurrentNode not one node selected"; - } -} - -QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProperty &bindingProperty) const -{ - const QString expression = bindingProperty.expression(); - const QStringList stringlist = expression.split(QLatin1String(".")); - - NodeMetaInfo type; - - if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) - type = metaInfo.property(bindingProperty.name()).propertyType(); - else - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node"; - - const QString &id = stringlist.constFirst(); - - ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode()); - - if (!modelNode.isValid()) { - qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node"; - return {}; - } - - NodeMetaInfo metaInfo = modelNode.metaInfo(); - - if (metaInfo.isValid()) { - QStringList possibleProperties; - const PropertyMetaInfos props = metaInfo.properties(); - for (const auto &property : props) { - if (property.propertyType() == type) // TODO: proper check - possibleProperties.push_back(QString::fromUtf8(property.name())); - } - return possibleProperties; - } else { - qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node"; - } - - return {}; -} - -void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber) -{ - m_view->executeInTransaction(__FUNCTION__, [this, rowNumber]() { - const AbstractProperty property = abstractPropertyForRow(rowNumber); - const PropertyName propertyName = property.name(); - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - if (bindingProperty.isValid()) { - bindingProperty.parentModelNode().removeProperty(bindingProperty.name()); - } else { - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - if (variantProperty.isValid()) - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - } - - if (property.isValid()) { - QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode()); - const auto stateOperations = objectNode.allAffectingStatesOperations(); - for (const QmlModelStateOperation &stateOperation : stateOperations) { - if (stateOperation.modelNode().hasProperty(propertyName)) - stateOperation.modelNode().removeProperty(propertyName); - } - - const QList timelineNodes = objectNode.allTimelines(); - for (auto &timelineNode : timelineNodes) { - QmlTimeline timeline(timelineNode); - timeline.removeKeyframesForTargetAndProperty(objectNode.modelNode(), - propertyName); - } - } - }); - - resetModel(); -} - -void DynamicPropertiesModel::addProperty(const QVariant &propertyValue, - const QString &propertyType, - const AbstractProperty &abstractProperty) -{ - QList items; - - QStandardItem *idItem; - QStandardItem *propertyNameItem; - QStandardItem *propertyTypeItem; - QStandardItem *propertyValueItem; - - idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode())); - updateCustomData(idItem, abstractProperty); - - const QString propName = QString::fromUtf8(abstractProperty.name()); - propertyNameItem = new QStandardItem(propName); - - items.append(idItem); - items.append(propertyNameItem); - - propertyTypeItem = new QStandardItem(propertyType); - items.append(propertyTypeItem); - - propertyValueItem = new QStandardItem(); - propertyValueItem->setData(propertyValue, Qt::DisplayRole); - items.append(propertyValueItem); - - for (int i = 0; i < rowCount(); ++i) { - if (data(index(i, PropertyNameRow)).toString() > propName) { - insertRow(i, items); - return; - } - } - - appendRow(items); -} - -void DynamicPropertiesModel::addBindingProperty(const BindingProperty &property) -{ - QVariant value = property.expression(); - QString type = QString::fromLatin1(property.dynamicTypeName()); - addProperty(value, type, property); -} - -void DynamicPropertiesModel::addVariantProperty(const VariantProperty &property) -{ - QVariant value = property.value(); - QString type = QString::fromLatin1(property.dynamicTypeName()); - addProperty(value, type, property); -} - -void DynamicPropertiesModel::updateBindingProperty(int rowNumber) -{ - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isValid()) { - updateCustomData(rowNumber, bindingProperty); - - QString propertyName = QString::fromUtf8(bindingProperty.name()); - updateDisplayRole(rowNumber, PropertyNameRow, propertyName); - QString value = bindingProperty.expression(); - QString type = QString::fromUtf8(bindingProperty.dynamicTypeName()); - updateDisplayRole(rowNumber, PropertyTypeRow, type); - updateDisplayRole(rowNumber, PropertyValueRow, value); - - const QmlObjectNode objectNode = QmlObjectNode(bindingProperty.parentModelNode()); - if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) - value = objectNode.expression(bindingProperty.name()); - - updateDisplayRole(rowNumber, PropertyValueRow, value); - } -} - -void DynamicPropertiesModel::updateVariantProperty(int rowNumber) -{ - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isValid()) { - updateCustomData(rowNumber, variantProperty); - QString propertyName = QString::fromUtf8(variantProperty.name()); - updateDisplayRole(rowNumber, PropertyNameRow, propertyName); - QVariant value = variantProperty.value(); - QString type = QString::fromUtf8(variantProperty.dynamicTypeName()); - updateDisplayRole(rowNumber, PropertyTypeRow, type); - const QmlObjectNode objectNode = QmlObjectNode(variantProperty.parentModelNode()); - if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState()) - value = objectNode.modelValue(variantProperty.name()); - - updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value); - } -} - -void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode) -{ - if (!modelNode.isValid()) - return; - - const QList properties = modelNode.properties(); - QList dynamicProperties = Utils::filtered(properties, [](const AbstractProperty &p) { - return p.isDynamic(); - }); - - Utils::sort(dynamicProperties, [](const AbstractProperty &a, const AbstractProperty &b) { - return a.name() < b.name(); - }); - - for (const AbstractProperty &property : std::as_const(dynamicProperties)) { - if (property.isBindingProperty()) - addBindingProperty(property.toBindingProperty()); - else if (property.isVariantProperty()) - addVariantProperty(property.toVariantProperty()); - } -} - -void DynamicPropertiesModel::updateValue(int row) -{ - BindingProperty bindingProperty = bindingPropertyForRow(row); - - if (bindingProperty.isBindingProperty()) { - const QString expression = data(index(row, PropertyValueRow)).toString(); - - RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); - try { - bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - return; - } - - VariantProperty variantProperty = variantPropertyForRow(row); - - if (variantProperty.isVariantProperty()) { - const QVariant value = data(index(row, PropertyValueRow)); - - RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__); - try { - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException); - } - } -} - -void DynamicPropertiesModel::updatePropertyName(int rowNumber) -{ - const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toUtf8(); - QTC_ASSERT(!newName.isEmpty(), return); - - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - ModelNode targetNode = bindingProperty.parentModelNode(); - - if (bindingProperty.isBindingProperty()) { - m_view->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() { - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); - - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression); - targetNode.removeProperty(bindingProperty.name()); - }); - - updateCustomData(rowNumber, targetNode.bindingProperty(newName)); - return; - } - - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); - ModelNode targetNode = variantProperty.parentModelNode(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value); - targetNode.removeProperty(variantProperty.name()); - }); - - updateCustomData(rowNumber, targetNode.variantProperty(newName)); - } -} - -void DynamicPropertiesModel::updatePropertyType(int rowNumber) -{ - const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1(); - QTC_ASSERT(!newType.isEmpty(), return); - - BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); - - if (bindingProperty.isBindingProperty()) { - const QString expression = bindingProperty.expression(); - const PropertyName propertyName = bindingProperty.name(); - ModelNode targetNode = bindingProperty.parentModelNode(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.removeProperty(bindingProperty.name()); - targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression); - }); - - updateCustomData(rowNumber, targetNode.bindingProperty(propertyName)); - return; - } - - VariantProperty variantProperty = variantPropertyForRow(rowNumber); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - ModelNode targetNode = variantProperty.parentModelNode(); - const PropertyName propertyName = variantProperty.name(); - - m_view->executeInTransaction(__FUNCTION__, [=]() { - targetNode.removeProperty(variantProperty.name()); - if (!isValueType(newType)) { - targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression( - newType, convertVariantForTypeName({}, newType).toString()); - } else { - targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue( - newType, convertVariantForTypeName(value, newType)); - } - }); - - updateCustomData(rowNumber, targetNode.variantProperty(propertyName)); - - if (variantProperty.isVariantProperty()) - updateVariantProperty(rowNumber); - else if (bindingProperty.isBindingProperty()) - updateBindingProperty(rowNumber); - } -} - -ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const -{ - if (id != QLatin1String("parent")) - return m_view->modelNodeForId(id); - - if (targetNode.hasParentProperty()) - return targetNode.parentProperty().parentModelNode(); - - return {}; -} - -void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property) -{ - item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1); - item->setData(property.name(), Qt::UserRole + 2); - - item->setData(property.parentModelNode().id(), TargetNameRole); - item->setData(property.name(), PropertyNameRole); - item->setData(property.parentModelNode().id(), TargetNameRole); - item->setData(property.dynamicTypeName(), PropertyTypeRole); - - if (property.isVariantProperty()) - item->setData(property.toVariantProperty().value(), PropertyValueRole); - if (property.isBindingProperty()) - item->setData(property.toBindingProperty().expression(), PropertyValueRole); -} - -void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property) -{ - QStandardItem* idItem = item(row, 0); - updateCustomData(idItem, property); -} - -int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty)) - return i; - } - - return -1; // not found -} - -int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if (compareVariantProperties(variantPropertyForRow(i), variantProperty)) - return i; - } - - return -1; // not found -} - -int DynamicPropertiesModel::findRowForProperty(const AbstractProperty &abstractProperty) const -{ - for (int i = 0; i < rowCount(); ++i) { - if ((abstractPropertyForRow(i).name() == abstractProperty.name())) - return i; - } - - return -1; // not found -} - -bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, - QString *sourceProperty) -{ - // TODO: we assume no expressions yet - - const QString expression = bindingProperty.expression(); - - if (true) { - const QStringList expressionParts = expression.split('.'); - - *sourceNode = expressionParts.constFirst(); - - QString propertyName; - - for (int i = 1; i < expressionParts.size(); ++i) { - propertyName += expressionParts.at(i); - if (i != expressionParts.size() - 1) - propertyName += QLatin1String("."); - } - *sourceProperty = propertyName; - } - - return true; -} - -void DynamicPropertiesModel::updateDisplayRole(int row, int columns, const QString &string) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex).toString() != string) - setData(modelIndex, string); -} - -void DynamicPropertiesModel::updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant) -{ - QModelIndex modelIndex = index(row, columns); - if (data(modelIndex) != variant) - setData(modelIndex, variant); -} - - -void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - if (!m_handleDataChanged) - return; - - if (topLeft != bottomRight) { - qWarning() << __FUNCTION__ << ": multi edit?"; - return; - } - - m_lock = true; - - int currentColumn = topLeft.column(); - int currentRow = topLeft.row(); - - switch (currentColumn) { - case TargetModelNodeRow: { - // updating user data - } break; - case PropertyNameRow: { - updatePropertyName(currentRow); - } break; - case PropertyTypeRow: { - updatePropertyType(currentRow); - } break; - case PropertyValueRow: { - updateValue(currentRow); - } break; - - default: qWarning() << __FUNCTION__ << " column" << currentColumn; - } - - m_lock = false; -} - -void DynamicPropertiesModel::handleException() -{ - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - resetModel(); -} - const QList DynamicPropertiesModel::selectedNodes() const { - // If selected nodes are explicitly set, return those. - // Otherwise return actual selected nodes of the model. if (m_explicitSelection) return m_selectedNodes; @@ -969,52 +366,34 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const return m_view->singleSelectedModelNode(); } -QHash DynamicPropertiesModel::roleNames() const +void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) { - static QHash roleNames{{TargetNameRole, "target"}, - {PropertyNameRole, "name"}, - {PropertyTypeRole, "type"}, - {PropertyValueRole, "value"}}; + QTC_ASSERT(m_explicitSelection, return); - return roleNames; + if (!node.isValid()) + return; + + m_selectedNodes.clear(); + m_selectedNodes.append(node); + reset(); } -DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const -{ - return m_delegate; -} - -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate( - DynamicPropertiesModel *parent) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent) : QObject(parent) + , m_internalNodeId(std::nullopt) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); - connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this]() { handleTypeChanged(); }); connect(&m_name, &StudioQmlTextBackend::activated, this, [this]() { handleNameChanged(); }); connect(&m_value, &StudioQmlTextBackend::activated, this, [this]() { handleValueChanged(); }); } -int DynamicPropertiesModelBackendDelegate::currentRow() const +void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &property) { - return m_currentRow; -} - -void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) -{ - if (m_currentRow == i) + if (!property.isValid()) return; - m_currentRow = i; - - //setup - - DynamicPropertiesModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - - AbstractProperty property = model->abstractPropertyForRow(i); - + m_internalNodeId = property.parentModelNode().internalId(); m_type.setCurrentText(QString::fromUtf8(property.dynamicTypeName())); m_name.setText(QString::fromUtf8(property.name())); @@ -1022,149 +401,69 @@ void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) m_value.setText(property.toVariantProperty().value().toString()); else if (property.isBindingProperty()) m_value.setText(property.toBindingProperty().expression()); + + m_targetNode = property.parentModelNode().id(); + emit targetNodeChanged(); } void DynamicPropertiesModelBackendDelegate::handleTypeChanged() { - //void DynamicPropertiesModel::updatePropertyType(int rowNumber) - const TypeName type = m_type.currentText().toUtf8(); - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); + const PropertyName name = m_name.text().toUtf8(); - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + int current = model->currentIndex(); + const TypeName type = m_type.currentText().toUtf8(); + model->commitPropertyType(current, type); - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - - try { - if (bindingProperty.isBindingProperty() || type == "var") { //var is always a binding - const QString expression = bindingProperty.expression(); - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - bindingProperty.setDynamicTypeNameAndExpression(type, expression); - } else if (variantProperty.isVariantProperty()) { - variantProperty.parentModelNode().removeProperty(variantProperty.name()); - variantProperty.setDynamicTypeNameAndValue(type, variantValue()); - } - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } + // The order might have changed! + model->setCurrent(m_internalNodeId.value_or(-1), name); } void DynamicPropertiesModelBackendDelegate::handleNameChanged() { - //see DynamicPropertiesModel::updatePropertyName - - const PropertyName newName = m_name.text().toUtf8(); - QTC_ASSERT(!newName.isEmpty(), return ); - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); + const PropertyName name = m_name.text().toUtf8(); + QTC_ASSERT(!name.isEmpty(), return); - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); + int current = model->currentIndex(); + model->commitPropertyName(current, name); - ModelNode targetNode = bindingProperty.parentModelNode(); + // The order might have changed! + model->setCurrent(m_internalNodeId.value_or(-1), name); +} - if (bindingProperty.isBindingProperty()) { - model->view()->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() { - const QString expression = bindingProperty.expression(); - const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); +// TODO: Maybe replace with utils typeConvertVariant? +QVariant valueFromText(const QString &value, const QString &type) +{ + if (isBindingExpression(value)) + return value; - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, - expression); - targetNode.removeProperty(bindingProperty.name()); - }); + if (type == "real" || type == "int") + return value.toFloat(); - return; - } + if (type == "bool") + return value == "true"; - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - if (variantProperty.isVariantProperty()) { - const QVariant value = variantProperty.value(); - const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName(); - ModelNode targetNode = variantProperty.parentModelNode(); - - model->view()->executeInTransaction(__FUNCTION__, [=]() { - targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, - value); - targetNode.removeProperty(variantProperty.name()); - }); - } - - AbstractProperty property = targetNode.property(newName); - - //order might have changed because of name change we have to select the correct row - int newRow = model->findRowForProperty(property); - model->setCurrentIndex(newRow); - setCurrentRow(newRow); + return value; } void DynamicPropertiesModelBackendDelegate::handleValueChanged() { - //see void DynamicPropertiesModel::updateValue(int row) - DynamicPropertiesModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - QTC_ASSERT(model, return ); - QTC_ASSERT(model->view(), return ); - - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - - if (bindingProperty.isBindingProperty()) { - const QString expression = m_value.text(); - - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - try { - bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), - expression); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } - return; - } - - VariantProperty variantProperty = model->variantPropertyForRow(currentRow()); - - if (variantProperty.isVariantProperty()) { - RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__); - try { - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), - variantValue()); - transaction.commit(); // committing in the try block - } catch (Exception &e) { - m_exceptionError = e.description(); - QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException); - } - } + int current = model->currentIndex(); + QVariant value = valueFromText(m_value.text(), m_type.currentText()); + model->commitPropertyValue(current, value); } -void DynamicPropertiesModelBackendDelegate::handleException() +QString DynamicPropertiesModelBackendDelegate::targetNode() const { - QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); - //reset -} - -QVariant DynamicPropertiesModelBackendDelegate::variantValue() const -{ - //improve - const QString type = m_type.currentText(); - if (type == "real" || type == "int") - return m_value.text().toFloat(); - - if (type == "bool") - return m_value.text() == "true"; - - return m_value.text(); + return m_targetNode; } StudioQmlComboBoxBackend *DynamicPropertiesModelBackendDelegate::type() diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index c41875dfc09..8ae4abf6d59 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -3,19 +3,17 @@ #pragma once -#include - -#include +#include "dynamicpropertiesitem.h" +#include "nodeinstanceglobal.h" +#include "studioquickwidget.h" #include namespace QmlDesigner { -class AbstractProperty; class AbstractView; -class BindingProperty; +class AbstractProperty; class ModelNode; -class VariantProperty; class DynamicPropertiesModelBackendDelegate; @@ -23,151 +21,103 @@ class DynamicPropertiesModel : public QStandardItemModel { Q_OBJECT +signals: + void currentIndexChanged(); + public: - enum ColumnRoles { - TargetModelNodeRow = 0, - PropertyNameRow = 1, - PropertyTypeRow = 2, - PropertyValueRow = 3 - }; - - enum UserRoles { - InternalIdRole = Qt::UserRole + 2, - TargetNameRole, - PropertyNameRole, - PropertyTypeRole, - PropertyValueRole - }; - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT) DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); - void bindingPropertyChanged(const BindingProperty &bindingProperty); - void abstractPropertyChanged(const AbstractProperty &bindingProperty); - void variantPropertyChanged(const VariantProperty &variantProperty); - void bindingRemoved(const BindingProperty &bindingProperty); - void variantRemoved(const VariantProperty &variantProperty); - void reset(); - void setSelectedNode(const ModelNode &node); - const QList selectedNodes() const; - const ModelNode singleSelectedNode() const; + AbstractView *view() const; + DynamicPropertiesModelBackendDelegate *delegate() const; - AbstractView *view() const { return m_view; } - AbstractProperty abstractPropertyForRow(int rowNumber) const; - BindingProperty bindingPropertyForRow(int rowNumber) const; - VariantProperty variantPropertyForRow(int rowNumber) const; - QStringList possibleTargetProperties(const BindingProperty &bindingProperty) const; - QStringList possibleSourceProperties(const BindingProperty &bindingProperty) const; - void deleteDynamicPropertyByRow(int rowNumber); - - void updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant); - void addDynamicPropertyForCurrentNode(); - void resetModel(); - - BindingProperty replaceVariantWithBinding(const PropertyName &name, bool copyValue = false); - void resetProperty(const PropertyName &name); - - void dispatchPropertyChanges(const AbstractProperty &abstractProperty); - - PropertyName unusedProperty(const ModelNode &modelNode); - - static bool isValueType(const TypeName &type); - static QVariant defaultValueForType(const TypeName &type); - static QString defaultExpressionForType(const TypeName &type); + int currentIndex() const; + AbstractProperty currentProperty() const; + AbstractProperty propertyForRow(int row) const; Q_INVOKABLE void add(); Q_INVOKABLE void remove(int row); - int currentIndex() const; + void reset(const QList &modelNodes = {}); void setCurrentIndex(int i); + void setCurrentProperty(const AbstractProperty &property); + void setCurrent(int internalId, const PropertyName &name); - int findRowForProperty(const AbstractProperty &abstractProperty) const; + void updateItem(const AbstractProperty &property); + void removeItem(const AbstractProperty &property); -signals: - void currentIndexChanged(); + void commitPropertyType(int row, const TypeName &type); + void commitPropertyName(int row, const PropertyName &name); + void commitPropertyValue(int row, const QVariant &value); + + void dispatchPropertyChanges(const AbstractProperty &abstractProperty); protected: - void addProperty(const QVariant &propertyValue, - const QString &propertyType, - const AbstractProperty &abstractProperty); - void addBindingProperty(const BindingProperty &property); - void addVariantProperty(const VariantProperty &property); - void updateBindingProperty(int rowNumber); - void updateVariantProperty(int rowNumber); - void addModelNode(const ModelNode &modelNode); - void updateValue(int row); - void updatePropertyName(int rowNumber); - void updatePropertyType(int rowNumber); - ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; - void updateCustomData(QStandardItem *item, const AbstractProperty &property); - void updateCustomData(int row, const AbstractProperty &property); - int findRowForBindingProperty(const BindingProperty &bindingProperty) const; - int findRowForVariantProperty(const VariantProperty &variantProperty) const; - - bool getExpressionStrings(const BindingProperty &bindingProperty, - QString *sourceNode, - QString *sourceProperty); - - void updateDisplayRole(int row, int columns, const QString &string); - QHash roleNames() const override; - DynamicPropertiesModelBackendDelegate *delegate() const; +private: + std::optional findRow(int nodeId, const PropertyName &name) const; + DynamicPropertiesItem *itemForRow(int row) const; + DynamicPropertiesItem *itemForProperty(const AbstractProperty &property) const; + ModelNode modelNodeForItem(DynamicPropertiesItem *item); + + void addModelNode(const ModelNode &node); + void addProperty(const AbstractProperty &property); + +public: + // TODO: Remove. This is a model for properties. Not nodes. + // Use reset with a list of nodes instead if all properties + // from a set of given nodes should be added. + const QList selectedNodes() const; + void setSelectedNode(const ModelNode &node); + const ModelNode singleSelectedNode() const; private: - void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void handleException(); - AbstractView *m_view = nullptr; - bool m_lock = false; - bool m_handleDataChanged = false; - QString m_exceptionError; - QList m_selectedNodes; - bool m_explicitSelection = false; - int m_currentIndex = 0; - DynamicPropertiesModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; + + // TODO: Remove. + QList m_selectedNodes = {}; + bool m_explicitSelection = false; }; class DynamicPropertiesModelBackendDelegate : public QObject { Q_OBJECT + Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT) - Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged) Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT) Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) - //Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) public: DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr); + void update(const AbstractProperty &property); + signals: - void currentRowChanged(); void nameChanged(); void valueChanged(); + void targetNodeChanged(); private: - int currentRow() const; - void setCurrentRow(int i); void handleTypeChanged(); void handleNameChanged(); void handleValueChanged(); - void handleException(); - QVariant variantValue() const; StudioQmlComboBoxBackend *type(); - StudioQmlTextBackend *name(); StudioQmlTextBackend *value(); + QString targetNode() const; + std::optional m_internalNodeId; StudioQmlComboBoxBackend m_type; StudioQmlTextBackend m_name; StudioQmlTextBackend m_value; - int m_currentRow = -1; - QString m_exceptionError; + QString m_targetNode; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp new file mode 100644 index 00000000000..ae0f8972c5d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -0,0 +1,949 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "propertytreemodel.h" +#include "connectionview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +const std::vector blockListProperties = {"children", + "data", + "childrenRect", + "icon", + "left", + "top", + "bottom", + "right", + "locale", + "objectName", + "transitions", + "states", + "resources", + "data", + "transformOrigin", + "transformOriginPoint", + "verticalCenter", + "horizontalCenter", + "anchors.bottom", + "anchors.top", + "anchors.left", + "anchors.right", + "anchors.fill", + "anchors.horizontalCenter", + "anchors.verticalCenter", + "anchors.centerIn", + "transform", + "visibleChildren"}; + +const std::vector blockListSlots = {"childAt", + "contains", + "destroy", + "dumpItemTree", + "ensurePolished", + "grabToImage", + "mapFromGlobal", + "mapFromItem", + "mapToGlobal", + "mapToItem", + "valueAt", + "toString", + "getText", + "inputMethodQuery", + "positionAt", + "positionToRectangle", + "isRightToLeft" + +}; + +const std::vector priorityListSignals = {"clicked", + "doubleClicked", + "pressed", + "released", + "toggled", + "valueModified", + "valueChanged", + "checkedChanged", + "moved", + "accepted", + "editingFinished", + "entered", + "exited", + "canceled", + "triggered", + "pressAndHold", + "started", + "stopped", + "finished" + "stateChanged", + "enabledChanged", + "visibleChanged", + "opacityChanged", + "rotationChanged"}; + +const std::vector priorityListProperties + = {"opacity", "visible", "value", "x", "y", "width", "height", "rotation", + "color", "scale", "state", "enabled", "z", "text", "pressed", "containsMouse", + "checked", "hovered", "down", "clip", "parent", "from", "true", "focus"}; + +const std::vector priorityListSlots = {"toggle", + "increase", + "decrease", + "clear", + "complete", + "pause", + "restart", + "resume", + "start", + "stop", + "forceActiveFocus"}; + +std::vector properityLists() +{ + std::vector result; + + result.insert(result.end(), priorityListSignals.begin(), priorityListSignals.end()); + result.insert(result.end(), priorityListProperties.begin(), priorityListProperties.end()); + result.insert(result.end(), priorityListSlots.begin(), priorityListSlots.end()); + + return result; +} + +PropertyTreeModel::PropertyTreeModel(ConnectionView *parent) + : QAbstractItemModel(parent), m_connectionView(parent) +{} + +void PropertyTreeModel::resetModel() +{ + beginResetModel(); + + m_sortedAndFilteredPropertyNamesSignalsSlots.clear(); + + m_indexCache.clear(); + m_indexHash.clear(); + m_indexCount = 0; + m_nodeList = allModelNodesWithIdsSortedByDisplayName(); + + if (!m_filter.isEmpty()) { //This could be a bit slow for large projects, but we have to check everynode to "hide" it. + m_nodeList = Utils::filtered(m_nodeList, [this](const ModelNode &node) { + return node.displayName().contains(m_filter) + || !sortedAndFilteredPropertyNamesSignalsSlots(node).empty(); + }); + } + + endResetModel(); +} + +QString stripQualification(const QString &string) +{ + return string.split(".").last(); +} +QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + auto internalId = index.internalId(); + + if (role == InternalIdRole) + return internalId; + + if (role == RowRole) + return index.row(); + + if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole + || role == ChildCountRole) { + if (internalId == internalRootIndex) { + if (role == PropertyNameRole) + return "--root item--"; + if (role == ChildCountRole) + return rowCount(index); + + return {}; + } + + DataCacheItem item = m_indexHash[index.internalId()]; + + if (role == ChildCountRole) + return rowCount(index); + + if (role == ExpressionRole) + return QString(item.modelNode.id() + "." + item.propertyName); + + if (role == PropertyNameRole) { + if (!item.propertyName.isEmpty()) + return stripQualification(QString::fromUtf8(item.propertyName)); + else + return item.modelNode.displayName(); + } + + static const auto priority = properityLists(); + if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end()) + return true; // listed priority properties + + auto dynamic = getDynamicProperties(item.modelNode); + if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end()) + return true; // dynamic properties have priority + + if (item.propertyName.isEmpty()) { + return true; // nodes are always shown + } + + return false; + } + + return {}; +} + +Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const +{ + return Qt::ItemIsEnabled; +} + +QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + auto internalId = parent.internalId(); + if (!m_connectionView->isAttached()) + return {}; + + if (!parent.isValid()) + return createIndex(0, 0, internalRootIndex); + + if (!hasIndex(row, column, parent)) + return {}; + + if (internalId == internalRootIndex) { //root level model node + const ModelNode modelNode = m_nodeList[row]; + return ensureModelIndex(modelNode, row); + } + + //property + + QTC_ASSERT(internalId != internalRootIndex, return {}); + + DataCacheItem item = m_indexHash[internalId]; + QTC_ASSERT(item.modelNode.isValid(), return {}); + + if (!item.propertyName.isEmpty()) { + // "." aka sub property + auto properties = sortedDotPropertyNamesSignalsSlots(item.modelNode.metaInfo(), + item.propertyName); + PropertyName propertyName = properties[row]; + return ensureModelIndex(item.modelNode, propertyName, row); + } + + auto properties = sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode); + + PropertyName propertyName = properties[row]; + + return ensureModelIndex(item.modelNode, propertyName, row); +} + +QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return {}; + + auto internalId = index.internalId(); + + if (internalId == internalRootIndex) + return {}; + + QTC_ASSERT(internalId < m_indexCount, return {}); + + const DataCacheItem item = m_indexHash[index.internalId()]; + + // no property means the parent is the root item + if (item.propertyName.isEmpty()) + return createIndex(0, 0, internalRootIndex); + + if (item.propertyName.contains(".")) { + auto list = item.propertyName.split('.'); + DataCacheItem parent; + parent.modelNode = item.modelNode; + parent.propertyName = list.first(); + if (auto iter = m_indexCache.find(parent); iter != m_indexCache.end()) { + const auto vector = sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode); + QList list(vector.begin(), vector.end()); + int row = list.indexOf(parent.propertyName); + return createIndex(row, 0, iter->internalIndex); + } + } + + // find the parent + + int row = m_nodeList.indexOf(item.modelNode); + return ensureModelIndex(item.modelNode, row); +} + +QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(quintptr internalId, int row) +{ + return createIndex(row, 0, internalId); +} + +int PropertyTreeModel::rowCount(const QModelIndex &parent) const +{ + if (!m_connectionView->isAttached() || parent.column() > 0) + return 0; + + if (!parent.isValid()) + return 1; //m_nodeList.size(); + + auto internalId = parent.internalId(); + + if (internalId == internalRootIndex) + return m_nodeList.size(); + + QTC_ASSERT(internalId < m_indexCount, return 0); + + DataCacheItem item = m_indexHash[internalId]; + if (!item.propertyName.isEmpty()) { + if (item.modelNode.metaInfo().property(item.propertyName).isPointer()) { + auto subProbs = sortedDotPropertyNamesSignalsSlots(item.modelNode.metaInfo(), + item.propertyName); + + return static_cast(subProbs.size()); + } + + return 0; + } + + return static_cast(sortedAndFilteredPropertyNamesSignalsSlots(item.modelNode).size()); +} +int PropertyTreeModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +void PropertyTreeModel::setPropertyType(PropertyTypes type) +{ + if (m_type == type) + return; + + m_type = type; + resetModel(); +} + +void PropertyTreeModel::setFilter(const QString &filter) +{ + if (m_filter == filter) + return; + + m_filter = filter; + resetModel(); +} + +QList PropertyTreeModel::nodeList() const +{ + return m_nodeList; +} + +const std::vector PropertyTreeModel::getProperties(const ModelNode &modelNode) const +{ + return sortedAndFilteredPropertyNamesSignalsSlots(modelNode); +} + +ModelNode PropertyTreeModel::getModelNodeForId(const QString &id) const +{ + if (!m_connectionView->isAttached()) + return {}; + + return m_connectionView->modelNodeForId(id); +} + +QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, int row) const +{ + DataCacheItem item; + item.modelNode = node; + + auto iter = m_indexCache.find(item); + if (iter != m_indexCache.end()) + return createIndex(row, 0, iter->internalIndex); + + item.internalIndex = m_indexCount; + m_indexCount++; + m_indexHash.push_back(item); + m_indexCache.insert(item); + + return createIndex(row, 0, item.internalIndex); +} +QModelIndex PropertyTreeModel::ensureModelIndex(const ModelNode &node, + const PropertyName &name, + int row) const +{ + DataCacheItem item; + item.modelNode = node; + item.propertyName = name; + + auto iter = m_indexCache.find(item); + if (iter != m_indexCache.end()) + return createIndex(row, 0, iter->internalIndex); + + item.internalIndex = m_indexCount; + m_indexCount++; + m_indexHash.push_back(item); + m_indexCache.insert(item); + + return createIndex(row, 0, item.internalIndex); +} + +void PropertyTreeModel::testModel() +{ + qDebug() << Q_FUNC_INFO; + qDebug() << rowCount({}); + + QModelIndex rootIndex = index(0, 0); + + qDebug() << rowCount(rootIndex); + QModelIndex firstItem = index(0, 0, rootIndex); + + qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem); + + firstItem = index(1, 0, rootIndex); + qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem); + + firstItem = index(2, 0, rootIndex); + qDebug() << "fi" << data(firstItem, Qt::DisplayRole) << rowCount(firstItem); + + QModelIndex firstProperty = index(0, 0, firstItem); + + qDebug() << "fp" << data(firstProperty, Qt::DisplayRole) << rowCount(firstProperty); + + qDebug() << m_indexCount << m_indexHash.size() << m_indexCache.size(); +} + +const QList PropertyTreeModel::allModelNodesWithIdsSortedByDisplayName() const +{ + if (!m_connectionView->isAttached()) + return {}; + + return Utils::sorted(ModelUtils::allModelNodesWithId(m_connectionView), + [](const ModelNode &lhs, const ModelNode &rhs) { + return lhs.displayName() < rhs.displayName(); + }); +} + +const std::vector PropertyTreeModel::sortedAndFilteredPropertyNamesSignalsSlots( + const ModelNode &modelNode) const +{ + std::vector returnValue; + + returnValue = m_sortedAndFilteredPropertyNamesSignalsSlots.value(modelNode); + + if (!returnValue.empty()) + return returnValue; + + if (m_type == SignalType) { + returnValue = sortedAndFilteredSignalNames(modelNode.metaInfo()); + } else if (m_type == SlotType) { + returnValue = sortedAndFilteredSlotNames(modelNode.metaInfo()); + } else { + auto list = sortedAndFilteredPropertyNames(modelNode.metaInfo()); + returnValue = getDynamicProperties(modelNode); + std::move(list.begin(), list.end(), std::back_inserter(returnValue)); + } + + if (m_filter.isEmpty() || modelNode.displayName().contains(m_filter)) + return returnValue; + + const auto filtered = Utils::filtered(returnValue, [this](const PropertyName &name) { + return name.contains(m_filter.toUtf8()) || name == m_filter.toUtf8(); + }); + + m_sortedAndFilteredPropertyNamesSignalsSlots.insert(modelNode, filtered); + + return filtered; +} + +const std::vector PropertyTreeModel::getDynamicProperties( + const ModelNode &modelNode) const +{ + QList list = Utils::transform(modelNode.dynamicProperties(), + [](const AbstractProperty &property) { + return property.name(); + }); + + QList filtered + = Utils::filtered(list, [this, modelNode](const PropertyName &propertyName) { + PropertyName propertyType = modelNode.property(propertyName).dynamicTypeName(); + switch (m_type) { + case AllTypes: + return true; + case NumberType: + return propertyType == "float" || propertyType == "double" + || propertyType == "int"; + case StringType: + return propertyType == "string"; + case UrlType: + return propertyType == "url"; + case ColorType: + return propertyType == "color"; + + case BoolType: + return propertyType == "bool"; + default: + break; + } + return true; + }); + + return Utils::sorted(std::vector(filtered.begin(), filtered.end())); +} + +const std::vector PropertyTreeModel::sortedAndFilteredPropertyNames( + const NodeMetaInfo &metaInfo, bool recursive) const +{ + auto filtered = Utils::filtered(metaInfo.properties(), + [this, recursive](const PropertyMetaInfo &metaInfo) { + // if (!metaInfo.isWritable()) - lhs/rhs + + const PropertyName name = metaInfo.name(); + + if (name.contains(".")) + return false; + + if (name.startsWith("icon.")) + return false; + if (name.startsWith("transformOriginPoint.")) + return false; + + return filterProperty(name, metaInfo, recursive); + }); + + auto sorted = Utils::sorted( + Utils::transform(filtered, [](const PropertyMetaInfo &metaInfo) -> PropertyName { + return metaInfo.name(); + })); + + std::set set(std::make_move_iterator(sorted.begin()), + std::make_move_iterator(sorted.end())); + + auto checkedPriorityList = Utils::filtered(priorityListProperties, + [&set](const PropertyName &name) { + auto it = set.find(name); + const bool b = it != set.end(); + if (b) + set.erase(it); + + return b; + }); + + //const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties + + std::vector final(set.begin(), set.end()); + + std::move(final.begin(), final.end(), std::back_inserter(checkedPriorityList)); + + return checkedPriorityList; +} + +const std::vector PropertyTreeModel::sortedAndFilteredSignalNames( + const NodeMetaInfo &metaInfo, bool recursive) const +{ + Q_UNUSED(recursive); + + auto filtered = Utils::filtered(metaInfo.signalNames(), [](const PropertyName &name) { + if (std::find(priorityListSignals.cbegin(), priorityListSignals.cend(), name) + != priorityListSignals.cend()) + return true; + + if (name.endsWith("Changed")) //option? + return false; + + return true; + }); + + auto sorted = Utils::sorted(filtered); + + std::set set(std::make_move_iterator(sorted.begin()), + std::make_move_iterator(sorted.end())); + + auto checkedPriorityList = Utils::filtered(priorityListSignals, + [&set](const PropertyName &name) { + auto it = set.find(name); + const bool b = it != set.end(); + if (b) + set.erase(it); + + return b; + }); + + //const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties + + std::vector finalPropertyList(set.begin(), set.end()); + + std::move(finalPropertyList.begin(), + finalPropertyList.end(), + std::back_inserter(checkedPriorityList)); + + return checkedPriorityList; +} + +const std::vector PropertyTreeModel::sortedAndFilteredSlotNames( + const NodeMetaInfo &metaInfo, bool recursive) const +{ + Q_UNUSED(recursive); + + auto priorityList = priorityListSlots; + auto filtered = Utils::filtered(metaInfo.slotNames(), [priorityList](const PropertyName &name) { + if (std::find(priorityListSlots.begin(), priorityListSlots.end(), name) + != priorityListSlots.end()) + return true; + + if (name.startsWith("_")) + return false; + if (name.startsWith("q_")) + return false; + + if (name.endsWith("Changed")) //option? + return false; + + if (std::find(blockListSlots.begin(), blockListSlots.end(), name) != blockListSlots.end()) + return false; + + return true; + }); + + auto sorted = Utils::sorted(filtered); + + std::set set(std::make_move_iterator(sorted.begin()), + std::make_move_iterator(sorted.end())); + + auto checkedPriorityList = Utils::filtered(priorityListSlots, [&set](const PropertyName &name) { + auto it = set.find(name); + const bool b = it != set.end(); + if (b) + set.erase(it); + + return b; + }); + + //const int priorityLength = checkedPriorityList.size(); We eventually require this to get the prioproperties + + std::vector final(set.begin(), set.end()); + + std::move(final.begin(), final.end(), std::back_inserter(checkedPriorityList)); + + return checkedPriorityList; +} + +const std::vector PropertyTreeModel::sortedDotPropertyNames( + const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const +{ + const PropertyName prefix = propertyName + '.'; + auto filtered = Utils::filtered(metaInfo.properties(), + [this, prefix](const PropertyMetaInfo &metaInfo) { + const PropertyName name = metaInfo.name(); + + if (!name.startsWith(prefix)) + return false; + + return filterProperty(name, metaInfo, true); + }); + + auto sorted = Utils::sorted( + Utils::transform(filtered, [](const PropertyMetaInfo &metaInfo) -> PropertyName { + return metaInfo.name(); + })); + + if (sorted.size() == 0 && metaInfo.property(propertyName).propertyType().isQtObject()) { + return Utils::transform(sortedAndFilteredPropertyNames(metaInfo.property(propertyName) + .propertyType(), + true), + [propertyName](const PropertyName &name) -> PropertyName { + return propertyName + "." + name; + }); + } + + return sorted; +} + +const std::vector PropertyTreeModel::sortedDotPropertySignals( + const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const +{ + return Utils::transform(sortedAndFilteredSignalNames(metaInfo.property(propertyName) + .propertyType(), + true), + [propertyName](const PropertyName &name) -> PropertyName { + return propertyName + "." + name; + }); +} + +const std::vector PropertyTreeModel::sortedDotPropertySlots( + const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const +{ + return Utils::transform(sortedAndFilteredSlotNames(metaInfo.property(propertyName).propertyType(), + true), + [propertyName](const PropertyName &name) -> PropertyName { + return propertyName + "." + name; + }); +} + +const std::vector PropertyTreeModel::sortedDotPropertyNamesSignalsSlots( + const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const +{ + if (m_type == SignalType) { + return sortedDotPropertySignals(metaInfo, propertyName); + } else if (m_type == SlotType) { + return sortedDotPropertySlots(metaInfo, propertyName); + } else { + return sortedDotPropertyNames(metaInfo, propertyName); + } +} + +bool PropertyTreeModel::filterProperty(const PropertyName &name, + const PropertyMetaInfo &metaInfo, + bool recursive) const +{ + if (std::find(blockListProperties.begin(), blockListProperties.end(), name) + != blockListProperties.end()) + return false; + + const NodeMetaInfo propertyType = metaInfo.propertyType(); + + //We want to keep sub items with matching properties + if (!recursive && metaInfo.isPointer() + && sortedAndFilteredPropertyNames(propertyType, true).size() > 0) + return true; + + //TODO no type relaxation atm... + switch (m_type) { + case AllTypes: + return true; + case NumberType: + return propertyType.isNumber(); + case StringType: + return propertyType.isString(); + case UrlType: + return propertyType.isUrl(); + //return propertyType.isString() || propertyType.isUrl(); + case ColorType: + return propertyType.isColor(); + //return propertyType.isString() || propertyType.isColor(); + case BoolType: + return propertyType.isBool(); + default: + break; + } + return true; +} + +QHash PropertyTreeModel::roleNames() const +{ + static QHash roleNames{{PropertyNameRole, "propertyName"}, + {PropertyPriorityRole, "hasPriority"}, + {ExpressionRole, "expression"}, + {ChildCountRole, "childCount"}}; + + return roleNames; +} + +PropertyListProxyModel::PropertyListProxyModel(PropertyTreeModel *parent) + : QAbstractListModel(), m_treeModel(parent) +{} + +void PropertyListProxyModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void PropertyListProxyModel::setRowAndInternalId(int row, quintptr internalId) +{ + QTC_ASSERT(m_treeModel, return ); + + if (internalId == internalRootIndex) + m_parentIndex = m_treeModel->index(0, 0); + else + m_parentIndex = m_treeModel->index(row, 0, m_parentIndex); + //m_parentIndex = m_treeModel->indexForInternalIdAndRow(internalId, row); + + resetModel(); +} + +int PropertyListProxyModel::rowCount(const QModelIndex &) const +{ + QTC_ASSERT(m_treeModel, return 0); + return m_treeModel->rowCount(m_parentIndex); +} + +QVariant PropertyListProxyModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(m_treeModel, return 0); + + auto treeIndex = m_treeModel->index(index.row(), 0, m_parentIndex); + + return m_treeModel->data(treeIndex, role); +} + +QHash PropertyListProxyModel::roleNames() const +{ + return m_treeModel->roleNames(); +} + +void PropertyListProxyModel::goInto(int row) +{ + setRowAndInternalId(row, 0); //m_parentIndex.internalId()); + + emit parentNameChanged(); +} + +void PropertyListProxyModel::goUp() +{ + if (m_parentIndex.internalId() == internalRootIndex) + return; + + m_parentIndex = m_treeModel->parent(m_parentIndex); + resetModel(); + + emit parentNameChanged(); +} + +void PropertyListProxyModel::reset() +{ + setRowAndInternalId(0, internalRootIndex); // TODO ??? + + emit parentNameChanged(); +} + +QString PropertyListProxyModel::parentName() const +{ + if (!m_treeModel->parent(m_parentIndex).isValid()) + return {}; + + return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); +} + +PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent) +{ + connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleNameChanged(); + }); + connect(&m_idCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() { + handleIdChanged(); + }); +} + +void PropertyTreeModelDelegate::setPropertyType(PropertyTreeModel::PropertyTypes type) +{ + m_model.setPropertyType(type); + setupNameComboBox(m_idCombboBox.currentText(), m_nameCombboBox.currentText(), nullptr); +} + +void PropertyTreeModelDelegate::setup(const QString &id, const QString &name, bool *nameExists) +{ + m_model.resetModel(); + QStringList idLists = Utils::transform(m_model.nodeList(), + [](const ModelNode &node) { return node.id(); }); + + if (!idLists.contains(id)) + idLists.prepend(id); + + m_idCombboBox.setModel(idLists); + m_idCombboBox.setCurrentText(id); + setupNameComboBox(id, name, nameExists); +} + +void PropertyTreeModelDelegate::setupNameComboBox(const QString &id, + const QString &name, + bool *nameExists) +{ + const auto modelNode = m_model.getModelNodeForId(id); + //m_nameCombboBox + std::vector nameVector = Utils::transform(m_model.getProperties(modelNode), + [](const PropertyName &name) { + return QString::fromUtf8(name); + }); + QStringList nameList; + nameList.reserve(Utils::ssize(nameVector)); + std::copy(nameVector.begin(), nameVector.end(), std::back_inserter(nameList)); + + if (!nameList.contains(name)) { + if (!nameExists) + nameList.prepend(name); + else + *nameExists = false; + } + + m_nameCombboBox.setModel(nameList); + m_nameCombboBox.setCurrentText(name); +} + +QString PropertyTreeModelDelegate::id() const +{ + return m_idCombboBox.currentText(); +} + +QString PropertyTreeModelDelegate::name() const +{ + return m_nameCombboBox.currentText(); +} + +void PropertyTreeModelDelegate::handleNameChanged() +{ + const auto id = m_idCombboBox.currentText(); + const auto name = m_nameCombboBox.currentText(); + setup(id, name); + + emit commitData(); + + // commit data +} + +NodeMetaInfo PropertyTreeModelDelegate::propertyMetaInfo() const +{ + const auto modelNode = m_model.getModelNodeForId(m_idCombboBox.currentText()); + if (modelNode.isValid()) + return modelNode.metaInfo().property(m_nameCombboBox.currentText().toUtf8()).propertyType(); + return {}; +} + +void PropertyTreeModelDelegate::handleIdChanged() +{ + const auto id = m_idCombboBox.currentText(); + const auto name = m_nameCombboBox.currentText(); + bool exists = true; + setup(id, name, &exists); + if (!exists) { + auto model = m_nameCombboBox.model(); + model.prepend("---"); + m_nameCombboBox.setModel(model); + m_nameCombboBox.setCurrentText("---"); + //We do not commit invalid name + } else { + emit commitData(); + //commit data + } +} + +StudioQmlComboBoxBackend *PropertyTreeModelDelegate::nameCombboBox() +{ + return &m_nameCombboBox; +} + +StudioQmlComboBoxBackend *PropertyTreeModelDelegate::idCombboBox() +{ + return &m_idCombboBox; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h new file mode 100644 index 00000000000..67836cb2698 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -0,0 +1,209 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "propertymetainfo.h" +#include + +#include +#include +#include + +#include +#include + +namespace QmlDesigner { + +inline constexpr quintptr internalRootIndex = std::numeric_limits::max(); + +class AbstractProperty; +class ModelNode; +class BindingProperty; +class SignalHandlerProperty; +class VariantProperty; + +class ConnectionView; + +class PropertyTreeModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum UserRoles { + PropertyNameRole = Qt::DisplayRole, + PropertyPriorityRole = Qt::UserRole + 1, + ExpressionRole, + ChildCountRole, + RowRole, + InternalIdRole + }; + + enum PropertyTypes { + AllTypes, + NumberType, + StringType, + ColorType, + SignalType, + SlotType, + UrlType, + BoolType + }; + + PropertyTreeModel(ConnectionView *parent = nullptr); + + void resetModel(); + + QVariant data(const QModelIndex &index, int role) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + + QPersistentModelIndex indexForInternalIdAndRow(quintptr internalId, int row); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + struct DataCacheItem + { + ModelNode modelNode; + PropertyName propertyName; + std::size_t internalIndex = internalRootIndex; + }; + + void setPropertyType(PropertyTypes type); + Q_INVOKABLE void setFilter(const QString &filter); + + QList nodeList() const; + + const std::vector getProperties(const ModelNode &modelNode) const; + ModelNode getModelNodeForId(const QString &id) const; + + QHash roleNames() const override; + +private: + QModelIndex ensureModelIndex(const ModelNode &node, int row) const; + QModelIndex ensureModelIndex(const ModelNode &node, const PropertyName &name, int row) const; + void testModel(); + const QList allModelNodesWithIdsSortedByDisplayName() const; + const std::vector sortedAndFilteredPropertyNamesSignalsSlots( + const ModelNode &modelNode) const; + + const std::vector getDynamicProperties(const ModelNode &modelNode) const; + const std::vector sortedAndFilteredPropertyNames(const NodeMetaInfo &metaInfo, + bool recursive = false) const; + + const std::vector sortedAndFilteredSignalNames(const NodeMetaInfo &metaInfo, + bool recursive = false) const; + + const std::vector sortedAndFilteredSlotNames(const NodeMetaInfo &metaInfo, + bool recursive = false) const; + + const std::vector sortedDotPropertyNames(const NodeMetaInfo &metaInfo, + const PropertyName &propertyName) const; + + const std::vector sortedDotPropertySignals(const NodeMetaInfo &metaInfo, + const PropertyName &propertyName) const; + + const std::vector sortedDotPropertySlots(const NodeMetaInfo &metaInfo, + const PropertyName &propertyName) const; + + const std::vector sortedDotPropertyNamesSignalsSlots( + const NodeMetaInfo &metaInfo, const PropertyName &propertyName) const; + + bool filterProperty(const PropertyName &name, + const PropertyMetaInfo &metaInfo, + bool recursive) const; + + ConnectionView *m_connectionView; + + mutable std::set m_indexCache; + mutable std::vector m_indexHash; + mutable std::size_t m_indexCount = 0; + QList m_nodeList; + PropertyTypes m_type = AllTypes; + QString m_filter; + mutable QHash> m_sortedAndFilteredPropertyNamesSignalsSlots; +}; + +class PropertyListProxyModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QString parentName READ parentName NOTIFY parentNameChanged) + +public: + PropertyListProxyModel(PropertyTreeModel *parent); + + void resetModel(); + + void setRowAndInternalId(int row, quintptr internalId); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + + QHash roleNames() const override; + + Q_INVOKABLE void goInto(int row); + Q_INVOKABLE void goUp(); + Q_INVOKABLE void reset(); + + QString parentName() const; + +signals: + void parentNameChanged(); + +private: + ModelNode m_modelNode; + PropertyName m_propertyName; + QPersistentModelIndex m_parentIndex; + + PropertyTreeModel *m_treeModel = nullptr; +}; + + +inline bool operator==(const PropertyTreeModel::DataCacheItem &lhs, + const PropertyTreeModel::DataCacheItem &rhs) +{ + return lhs.modelNode == rhs.modelNode && lhs.propertyName == rhs.propertyName; +} + +inline bool operator<(const PropertyTreeModel::DataCacheItem &lhs, + const PropertyTreeModel::DataCacheItem &rhs) +{ + return (lhs.modelNode.id() + lhs.propertyName) < (rhs.modelNode.id() + rhs.propertyName); +} + +class PropertyTreeModelDelegate : public QObject +{ + Q_OBJECT + + Q_PROPERTY(StudioQmlComboBoxBackend *name READ nameCombboBox CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT) + +public: + explicit PropertyTreeModelDelegate(ConnectionView *parent = nullptr); + void setPropertyType(PropertyTreeModel::PropertyTypes type); + void setup(const QString &id, const QString &name, bool *nameExists = nullptr); + void setupNameComboBox(const QString &id, const QString &name, bool *nameExists); + QString id() const; + QString name() const; + NodeMetaInfo propertyMetaInfo() const; + +signals: + void commitData(); + +private: + void handleNameChanged(); + void handleIdChanged(); + + StudioQmlComboBoxBackend *nameCombboBox(); + StudioQmlComboBoxBackend *idCombboBox(); + + StudioQmlComboBoxBackend m_nameCombboBox; + StudioQmlComboBoxBackend m_idCombboBox; + PropertyTreeModel::PropertyTypes m_type; + PropertyTreeModel m_model; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 59ec457d071..49e940ba22f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -209,7 +209,11 @@ void ContentLibraryBundleImporter::handleImportTimer() if (isImport == typeComplete) { m_pendingTypes.remove(pendingType); if (isImport) +#ifdef QDS_USE_PROJECTSTORAGE + emit importFinished(pendingType.toUtf8()); +#else emit importFinished(metaInfo); +#endif else emit unimportFinished(metaInfo); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 02ccf3263e7..3aff09fe343 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -35,7 +35,11 @@ signals: // The metaInfo parameter will be invalid if an error was encountered during // asynchronous part of the import. In this case all remaining pending imports have been // terminated, and will not receive separate importFinished notifications. +#ifdef QDS_USE_PROJECTSTORAGE + void importFinished(const QmlDesigner::TypeName &typeName); +#else void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo); +#endif void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo); private: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 3bfd374bd81..6b1de2d2a73 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -91,13 +92,27 @@ void ContentLibraryEffectsModel::createImporter(const QString &bundlePath, const const QStringList &sharedFiles) { m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); - connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this, +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName) { + m_importerRunning = false; + emit importerRunningChanged(); + if (typeName.size()) + emit bundleItemImported(typeName); + }); +#else + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; emit importerRunningChanged(); if (metaInfo.isValid()) emit bundleItemImported(metaInfo); }); +#endif connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { @@ -116,7 +131,12 @@ void ContentLibraryEffectsModel::loadBundle() if (m_bundleExists || m_probeBundleDir) return; - QDir bundleDir = qEnvironmentVariable("EFFECT_BUNDLE_PATH"); + QDir bundleDir; + + if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty()) + bundleDir.setPath(qEnvironmentVariable("EFFECT_BUNDLE_PATH")); + else if (Utils::HostOsInfo::isMacHost()) + bundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/effect_bundle"); // search for bundleDir from exec dir and up if (bundleDir.dirName() == ".") { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 104d34af2d2..5d67ac3da8b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -57,7 +57,11 @@ public: signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); +#ifdef QDS_USE_PROJECTSTORAGE + void bundleItemImported(const QmlDesigner::TypeName &typeName); +#else void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo); +#endif void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type); void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); void importerRunningChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index f8e284d7bea..dee10006068 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -206,13 +206,27 @@ void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, con const QStringList &sharedFiles) { m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); - connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this, +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName) { + m_importerRunning = false; + emit importerRunningChanged(); + if (typeName.size()) + emit bundleMaterialImported(typeName); + }); +#else + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { m_importerRunning = false; emit importerRunningChanged(); if (metaInfo.isValid()) emit bundleMaterialImported(metaInfo); }); +#endif connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 836ebc6c1e5..21bd3741375 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -64,7 +64,11 @@ signals: void hasModelSelectionChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); +#ifdef QDS_USE_PROJECTSTORAGE + void bundleMaterialImported(const QmlDesigner::TypeName &typeName); +#else void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); +#endif void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); void importerRunningChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index b74c6c32a51..54032d28273 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -79,26 +79,40 @@ WidgetInfo ContentLibraryView::widgetInfo() ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data(); - connect(materialsModel, &ContentLibraryMaterialsModel::applyToSelectedTriggered, this, - [&] (ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) - return; + connect(materialsModel, + &ContentLibraryMaterialsModel::applyToSelectedTriggered, + this, + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->materialsModel()->addToProject(bundleMat); - }); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialsModel()->addToProject(bundleMat); + }); - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialImported, this, - [&] (const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleMaterialsImportedState(); - }); +#ifdef QDS_USE_PROJECTSTORAGE + connect(materialsModel, + &ContentLibraryMaterialsModel::bundleMaterialImported, + this, + [&](const QmlDesigner::TypeName &typeName) { + applyBundleMaterialToDropTarget({}, typeName); + updateBundleMaterialsImportedState(); + }); +#else + connect(materialsModel, + &ContentLibraryMaterialsModel::bundleMaterialImported, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + applyBundleMaterialToDropTarget({}, metaInfo); + updateBundleMaterialsImportedState(); + }); +#endif connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this, [&] (const QmlDesigner::TypeName &type) { @@ -120,30 +134,61 @@ WidgetInfo ContentLibraryView::widgetInfo() ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data(); - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemImported, this, - [&] (const QmlDesigner::NodeMetaInfo &metaInfo) { - QTC_ASSERT(metaInfo.isValid(), return); +#ifdef QDS_USE_PROJECTSTORAGE + connect(effectsModel, + &ContentLibraryEffectsModel::bundleItemImported, + this, + [&](const QmlDesigner::TypeName &typeName) { + QTC_ASSERT(typeName.size(), return); - if (!m_bundleEffectTarget) - m_bundleEffectTarget = active3DSceneNode(); + if (!m_bundleEffectTarget) + m_bundleEffectTarget = active3DSceneNode(); - QTC_ASSERT(m_bundleEffectTarget, return); + QTC_ASSERT(m_bundleEffectTarget, return); - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value(); - ModelNode newEffNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), - metaInfo.minorVersion(), - {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); - clearSelectedModelNodes(); - selectModelNode(newEffNode); - }); + executeInTransaction("ContentLibraryView::widgetInfo", [&] { + QVector3D pos = m_bundleEffectPos.value(); + ModelNode newEffNode = createModelNode( + typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); + m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + clearSelectedModelNodes(); + selectModelNode(newEffNode); + }); - updateBundleEffectsImportedState(); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; - }); + updateBundleEffectsImportedState(); + m_bundleEffectTarget = {}; + m_bundleEffectPos = {}; + }); +#else + connect(effectsModel, + &ContentLibraryEffectsModel::bundleItemImported, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + QTC_ASSERT(metaInfo.isValid(), return); + if (!m_bundleEffectTarget) + m_bundleEffectTarget = active3DSceneNode(); + + QTC_ASSERT(m_bundleEffectTarget, return); + + executeInTransaction("ContentLibraryView::widgetInfo", [&] { + QVector3D pos = m_bundleEffectPos.value(); + ModelNode newEffNode = createModelNode(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion(), + {{"x", pos.x()}, + {"y", pos.y()}, + {"z", pos.z()}}); + m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + clearSelectedModelNodes(); + selectModelNode(newEffNode); + }); + + updateBundleEffectsImportedState(); + m_bundleEffectTarget = {}; + m_bundleEffectPos = {}; + }); +#endif connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this, [&] (const QmlDesigner::TypeName &type) { // delete instances of the bundle effect that is about to be unimported @@ -181,6 +226,7 @@ void ContentLibraryView::modelAttached(Model *model) m_sceneId = model->active3DSceneId(); + m_widget->setHasActive3DScene(m_sceneId != -1); m_widget->clearSearchFilter(); m_widget->effectsModel()->loadBundle(); @@ -214,6 +260,7 @@ void ContentLibraryView::importsChanged(const Imports &addedImports, const Impor void ContentLibraryView::active3DSceneChanged(qint32 sceneId) { m_sceneId = sceneId; + m_widget->setHasActive3DScene(m_sceneId != -1); } void ContentLibraryView::selectedNodesChanged(const QList &selectedNodeList, @@ -228,8 +275,10 @@ void ContentLibraryView::selectedNodesChanged(const QList &selectedNo m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); } -void ContentLibraryView::customNotification(const AbstractView *view, const QString &identifier, - const QList &nodeList, const QList &data) +void ContentLibraryView::customNotification(const AbstractView *view, + const QString &identifier, + const QList &nodeList, + const QList &data) { Q_UNUSED(data) @@ -246,7 +295,11 @@ void ContentLibraryView::customNotification(const AbstractView *view, const QStr ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type()); if (defaultMat.isValid()) { if (m_bundleMaterialTargets.isEmpty()) // if no drop target, create a duplicate material +#ifdef QDS_USE_PROJECTSTORAGE + createMaterial(m_draggedBundleMaterial->type()); +#else createMaterial(defaultMat.metaInfo()); +#endif else applyBundleMaterialToDropTarget(defaultMat); } else { @@ -254,7 +307,7 @@ void ContentLibraryView::customNotification(const AbstractView *view, const QStr } m_draggedBundleMaterial = nullptr; - } else if (identifier == "drop_bundle_texture") { + } else if (identifier == "drop_bundle_texture") { ModelNode matLib = materialLibraryNode(); if (!matLib.isValid()) return; @@ -286,6 +339,57 @@ void ContentLibraryView::nodeAboutToBeRemoved(const ModelNode &removedNode) m_widget->setHasMaterialLibrary(false); } +#ifdef QDS_USE_PROJECTSTORAGE +void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat, + const TypeName &typeName) +{ + if (!bundleMat.isValid() && !typeName.size()) + return; + + executeInTransaction("ContentLibraryView::applyBundleMaterialToDropTarget", [&] { + ModelNode newMatNode = typeName.size() ? createMaterial(typeName) : bundleMat; + + // TODO: unify this logic as it exist elsewhere also + auto expToList = [](const QString &exp) { + QString copy = exp; + copy = copy.remove("[").remove("]"); + + QStringList tmp = copy.split(',', Qt::SkipEmptyParts); + for (QString &str : tmp) + str = str.trimmed(); + + return tmp; + }; + + auto listToExp = [](QStringList &stringList) { + if (stringList.size() > 1) + return QString("[" + stringList.join(",") + "]"); + + if (stringList.size() == 1) + return stringList.first(); + + return QString(); + }; + + for (const ModelNode &target : std::as_const(m_bundleMaterialTargets)) { + if (target.isValid() && target.metaInfo().isQtQuick3DModel()) { + QmlObjectNode qmlObjNode(target); + if (m_bundleMaterialAddToSelected) { + QStringList matList = expToList(qmlObjNode.expression("materials")); + matList.append(newMatNode.id()); + QString updatedExp = listToExp(matList); + qmlObjNode.setBindingProperty("materials", updatedExp); + } else { + qmlObjNode.setBindingProperty("materials", newMatNode.id()); + } + } + + m_bundleMaterialTargets = {}; + m_bundleMaterialAddToSelected = false; + } + }); +} +#else void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo) { @@ -335,6 +439,7 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle } }); } +#endif ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { @@ -342,7 +447,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t if (!matLib.isValid()) return {}; - const QList matLibNodes = matLib.directSubModelNodes(); + const QList matLibNodes = matLib.directSubModelNodes(); for (const ModelNode &mat : matLibNodes) { if (mat.isValid() && mat.type() == type) { bool isDefault = true; @@ -361,15 +466,40 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t return {}; } +#ifdef QDS_USE_PROJECTSTORAGE +ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) +{ + ModelNode matLib = materialLibraryNode(); + if (!matLib.isValid() || !typeName.size()) + return {}; + ModelNode newMatNode = createModelNode(typeName, -1, -1); + matLib.defaultNodeListProperty().reparentHere(newMatNode); + + static QRegularExpression rgx("([A-Z])([a-z]*)"); + QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed(); + if (newName.endsWith(" Material")) + newName.chop(9); // remove trailing " Material" + QString newId = model()->generateIdFromName(newName, "material"); + newMatNode.setIdWithRefactoring(newId); + + VariantProperty objNameProp = newMatNode.variantProperty("objectName"); + objNameProp.setValue(newName); + + emitCustomNotification("focus_material_section", {}); + + return newMatNode; +} +#else ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) { ModelNode matLib = materialLibraryNode(); if (!matLib.isValid() || !metaInfo.isValid()) return {}; - ModelNode newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), - metaInfo.minorVersion()); + ModelNode newMatNode = createModelNode(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); matLib.defaultNodeListProperty().reparentHere(newMatNode); static QRegularExpression rgx("([A-Z])([a-z]*)"); @@ -386,6 +516,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) return newMatNode; } +#endif void ContentLibraryView::updateBundleMaterialsImportedState() { @@ -430,7 +561,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion() bool hasImport = false; int major = -1; int minor = -1; - const QString url {"QtQuick3D"}; + const QString url{"QtQuick3D"}; const auto imports = model()->imports(); for (const auto &import : imports) { if (import.url() == url) { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 741a77759eb..36b579bf759 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -47,10 +47,18 @@ private: void updateBundleMaterialsImportedState(); void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); - void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {}); +#ifdef QDS_USE_PROJECTSTORAGE + void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {}); +#else + void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, + const NodeMetaInfo &metaInfo = {}); +#endif ModelNode getBundleMaterialDefaultInstance(const TypeName &type); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode createMaterial(const TypeName &typeName); +#else ModelNode createMaterial(const NodeMetaInfo &metaInfo); - +#endif QPointer m_widget; QList m_bundleMaterialTargets; ModelNode m_bundleEffectTarget; // target of the dropped bundle effect diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 78bc1f07c3c..cf968a33a89 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -596,6 +596,11 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey); } +QSize ContentLibraryWidget::sizeHint() const +{ + return {420, 420}; +} + QList ContentLibraryWidget::createToolBarWidgets() { return {}; @@ -662,6 +667,20 @@ void ContentLibraryWidget::setHasMaterialLibrary(bool b) m_materialsModel->updateIsEmpty(); } +bool ContentLibraryWidget::hasActive3DScene() const +{ + return m_hasActive3DScene; +} + +void ContentLibraryWidget::setHasActive3DScene(bool b) +{ + if (m_hasActive3DScene == b) + return; + + m_hasActive3DScene = b; + emit hasActive3DSceneChanged(); +} + void ContentLibraryWidget::reloadQmlSource() { const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml"; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 9f9fd167c32..5e41f289141 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -29,8 +29,9 @@ class ContentLibraryWidget : public QFrame { Q_OBJECT - Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport NOTIFY hasQuick3DImportChanged) + Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged) + Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -49,6 +50,9 @@ public: bool hasMaterialLibrary() const; void setHasMaterialLibrary(bool b); + bool hasActive3DScene() const; + void setHasActive3DScene(bool b); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); void setMaterialsModel(QPointer newMaterialsModel); @@ -67,6 +71,8 @@ public: Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void markTextureUpdated(const QString &textureKey); + QSize sizeHint() const override; + signals: void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff); void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); @@ -75,6 +81,7 @@ signals: void updateSceneEnvStateRequested(); void hasQuick3DImportChanged(); void hasMaterialLibraryChanged(); + void hasActive3DSceneChanged(); void isDraggingChanged(); protected: @@ -111,6 +118,7 @@ private: QPoint m_dragStartPoint; bool m_hasMaterialLibrary = false; + bool m_hasActive3DScene = false; bool m_hasQuick3DImport = false; bool m_isDragging = false; QString m_baseUrl; diff --git a/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp index 0886ad42565..712cb32c8da 100644 --- a/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp @@ -47,7 +47,7 @@ AnimationCurve::AnimationCurve( return QPointF(start.x() + slope.x() * pos.x(), start.y() + slope.y() * pos.y()); }; - QVector points = easing.toCubicSpline(); + QList points = easing.toCubicSpline(); int numSegments = points.size() / 3; Keyframe current; diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index d0452fd6e26..6858cc50888 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp @@ -8,6 +8,7 @@ #include "detail/graphicsview.h" #include "detail/treeview.h" +#include #include #include @@ -106,6 +107,7 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent) auto updateTimeline = [this, model](bool validTimeline) { if (validTimeline) { updateStatusLine(); + updateMcuState(); m_view->setCurrentFrame(m_view->model()->currentFrame(), false); m_toolbar->updateBoundsSilent(model->minimumTime(), model->maximumTime()); m_toolbar->show(); @@ -163,4 +165,11 @@ void CurveEditor::updateStatusLine() m_statusLine->setText(currentText); } +void CurveEditor::updateMcuState() +{ + bool isMcu = DesignerMcuManager::instance().isMCUProject(); + m_toolbar->setIsMcuProject(isMcu); + m_view->setIsMcu(isMcu); +} + } // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h index 9965d125731..24d2cd5edd9 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h @@ -38,6 +38,7 @@ protected: private: void updateStatusLine(); + void updateMcuState(); QLabel *m_infoText; diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp index 27a14968c61..773abe1d904 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp @@ -60,7 +60,9 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) , m_startSpin(nullptr) , m_endSpin(nullptr) , m_currentSpin(new QSpinBox) - + , m_stepAction(nullptr) + , m_splineAction(nullptr) + , m_unifyAction(nullptr) { setFloatable(false); setFixedHeight(Theme::toolbarSize()); @@ -68,11 +70,10 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) addSpace(5); - QAction *tangentLinearAction = addAction(Theme::iconFromName(Theme::linear_medium), "Linear"); - QAction *tangentStepAction = addAction(Theme::iconFromName(Theme::step_medium), "Step"); - QAction *tangentSplineAction = addAction(Theme::iconFromName(Theme::bezier_medium), "Spline"); - - QAction *tangentUnifyAction = addAction(Theme::iconFromName(Theme::unify_medium), tr("Unify")); + QAction *tangentLinearAction = addAction(Theme::iconFromName(Theme::linear_medium), tr("Linear")); + m_stepAction = addAction(Theme::iconFromName(Theme::step_medium), tr(m_stepLabel)); + m_splineAction = addAction(Theme::iconFromName(Theme::bezier_medium), tr(m_splineLabel)); + m_unifyAction = addAction(Theme::iconFromName(Theme::unify_medium), tr(m_unifyLabel)); auto setLinearInterpolation = [this]() { emit interpolationClicked(Keyframe::Interpolation::Linear); @@ -88,9 +89,9 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) }; connect(tangentLinearAction, &QAction::triggered, setLinearInterpolation); - connect(tangentStepAction, &QAction::triggered, setStepInterpolation); - connect(tangentSplineAction, &QAction::triggered, setSplineInterpolation); - connect(tangentUnifyAction, &QAction::triggered, toggleUnifyKeyframe); + connect(m_stepAction, &QAction::triggered, setStepInterpolation); + connect(m_splineAction, &QAction::triggered, setSplineInterpolation); + connect(m_unifyAction, &QAction::triggered, toggleUnifyKeyframe); auto validateStart = [this](int val) -> bool { if (m_endSpin==nullptr) @@ -189,6 +190,25 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) addSpace(5); } +void CurveEditorToolBar::setIsMcuProject(bool isMcu) +{ + m_stepAction->setDisabled(isMcu); + m_splineAction->setDisabled(isMcu); + m_unifyAction->setDisabled(isMcu); + + static constexpr const char* notSupportedString = QT_TR_NOOP("Not supported for MCUs"); + + if (isMcu) { + m_stepAction->setText(tr(notSupportedString)); + m_splineAction->setText(tr(notSupportedString)); + m_unifyAction->setText(tr(notSupportedString)); + } else { + m_stepAction->setText(tr(m_stepLabel)); + m_splineAction->setText(tr(m_splineLabel)); + m_unifyAction->setText(tr(m_unifyLabel)); + } +} + void CurveEditorToolBar::setZoom(double zoom) { QSignalBlocker blocker(m_zoomSlider); diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h index c7bb6bc8182..5b796b8e004 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h @@ -50,6 +50,8 @@ signals: public: CurveEditorToolBar(CurveEditorModel *model, QWidget* parent = nullptr); + void setIsMcuProject(bool isMcu); + void setZoom(double zoom); void setCurrentFrame(int current, bool notify); @@ -65,6 +67,13 @@ private: ValidatableSpinBox *m_endSpin; QSpinBox *m_currentSpin; QSlider *m_zoomSlider; + QAction *m_stepAction; + QAction *m_splineAction; + QAction *m_unifyAction; + + static constexpr const char* m_stepLabel = QT_TR_NOOP("Step"); + static constexpr const char* m_splineLabel = QT_TR_NOOP("Spline"); + static constexpr const char* m_unifyLabel = QT_TR_NOOP("Unify"); }; } // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp b/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp index 76fa2a2ffc2..cf90174c999 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp @@ -165,6 +165,13 @@ bool CurveSegment::isLegal() const return ex.size() == 0; } +bool CurveSegment::isLegalMcu() const +{ + if (interpolation() == Keyframe::Interpolation::Linear) + return isValid(); + return false; +} + bool CurveSegment::containsX(double x) const { return m_left.position().x() <= x && m_right.position().x() >= x; @@ -243,7 +250,7 @@ void CurveSegment::extendWithEasingCurve(QPainterPath &path, const QEasingCurve return QPointF(start.x() + slope.x() * pos.x(), start.y() + slope.y() * pos.y()); }; - QVector points = curve.toCubicSpline(); + QList points = curve.toCubicSpline(); int numSegments = points.size() / 3; for (int i = 0; i < numSegments; i++) { QPointF p1 = mapEasing(m_left.position(), m_right.position(), points.at(i * 3)); diff --git a/src/plugins/qmldesigner/components/curveeditor/curvesegment.h b/src/plugins/qmldesigner/components/curveeditor/curvesegment.h index cfb925b2bdc..566cd6f69d8 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curvesegment.h +++ b/src/plugins/qmldesigner/components/curveeditor/curvesegment.h @@ -27,6 +27,8 @@ public: bool isLegal() const; + bool isLegalMcu() const; + bool containsX(double x) const; Keyframe left() const; diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp index 34d811525c1..da59b49884d 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp @@ -23,6 +23,7 @@ CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem , m_component(PropertyTreeItem::Component::Generic) , m_transform() , m_keyframes() + , m_mcu(false) , m_itemDirty(false) { setFlag(QGraphicsItem::ItemIsMovable, false); @@ -96,7 +97,7 @@ void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidg } else { if (locked()) pen.setColor(m_style.lockedColor); - else if (!segment.isLegal()) + else if (!segment.isLegal() || (m_mcu && !segment.isLegalMcu())) pen.setColor(m_style.errorColor); else if (isUnderMouse()) pen.setColor(m_style.hoverColor); @@ -269,14 +270,14 @@ std::vector CurveItem::curves() const return out; } -QVector CurveItem::keyframes() const +QList CurveItem::keyframes() const { return m_keyframes; } -QVector CurveItem::selectedKeyframes() const +QList CurveItem::selectedKeyframes() const { - QVector out; + QList out; for (auto *frame : m_keyframes) { if (frame->selected()) out.push_back(frame); @@ -284,9 +285,9 @@ QVector CurveItem::selectedKeyframes() const return out; } -QVector CurveItem::handles() const +QList CurveItem::handles() const { - QVector out; + QList out; for (auto *frame : m_keyframes) { if (auto *left = frame->leftHandle()) out.push_back(left); @@ -353,6 +354,12 @@ void CurveItem::setDirty(bool dirty) m_itemDirty = dirty; } +void CurveItem::setIsMcu(bool isMcu) +{ + m_mcu = isMcu; + setHandleVisibility(!m_mcu); +} + void CurveItem::setHandleVisibility(bool visible) { for (auto *frame : std::as_const(m_keyframes)) diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h index d028f53404d..eab60023c85 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h @@ -77,11 +77,11 @@ public: std::vector curves() const; - QVector keyframes() const; + QList keyframes() const; - QVector selectedKeyframes() const; + QList selectedKeyframes() const; - QVector handles() const; + QList handles() const; CurveSegment segment(const KeyframeItem *keyframe, HandleItem::Slot slot) const; @@ -89,6 +89,8 @@ public: void setDirty(bool dirty); + void setIsMcu(bool isMcu); + void setHandleVisibility(bool visible); void setValueType(PropertyTreeItem::ValueType type); @@ -126,7 +128,9 @@ private: QTransform m_transform; - QVector m_keyframes; + QList m_keyframes; + + bool m_mcu; bool m_itemDirty; }; diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp index 49c96ed04d8..d981b9cd183 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp @@ -120,14 +120,14 @@ QRectF GraphicsScene::rect() const return rect; } -QVector GraphicsScene::curves() const +QList GraphicsScene::curves() const { return m_curves; } -QVector GraphicsScene::selectedCurves() const +QList GraphicsScene::selectedCurves() const { - QVector out; + QList out; for (auto *curve : m_curves) { if (curve->hasSelectedKeyframe()) out.push_back(curve); @@ -135,18 +135,18 @@ QVector GraphicsScene::selectedCurves() const return out; } -QVector GraphicsScene::keyframes() const +QList GraphicsScene::keyframes() const { - QVector out; + QList out; for (auto *curve : m_curves) out.append(curve->keyframes()); return out; } -QVector GraphicsScene::selectedKeyframes() const +QList GraphicsScene::selectedKeyframes() const { - QVector out; + QList out; for (auto *curve : m_curves) out.append(curve->selectedKeyframes()); @@ -191,6 +191,13 @@ void GraphicsScene::setDirty(bool dirty) m_dirty = dirty; } +void GraphicsScene::setIsMcu(bool isMcu) +{ + m_isMcu = isMcu; + for (auto* curve : curves()) + curve->setIsMcu(isMcu); +} + void GraphicsScene::reset() { m_curves.clear(); @@ -244,6 +251,9 @@ void GraphicsScene::removeCurveItem(unsigned int id) void GraphicsScene::addCurveItem(CurveItem *item) { + if (!item) + return; + for (auto *curve : std::as_const(m_curves)) { if (curve->id() == item->id()) { delete item; @@ -251,6 +261,7 @@ void GraphicsScene::addCurveItem(CurveItem *item) } } + item->setIsMcu(m_isMcu); item->setDirty(false); item->connect(this); addItem(item); @@ -267,6 +278,9 @@ void GraphicsScene::addCurveItem(CurveItem *item) void GraphicsScene::moveToBottom(CurveItem *item) { + if (!item) + return; + if (m_curves.removeAll(item) > 0) { m_curves.push_front(item); resetZValues(); @@ -275,6 +289,9 @@ void GraphicsScene::moveToBottom(CurveItem *item) void GraphicsScene::moveToTop(CurveItem *item) { + if (!item) + return; + if (m_curves.removeAll(item) > 0) { m_curves.push_back(item); resetZValues(); diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h index 182980f62c3..abbc6658453 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h @@ -54,15 +54,15 @@ public: QRectF rect() const; - QVector curves() const; + QList curves() const; - QVector selectedCurves() const; + QList selectedCurves() const; - QVector keyframes() const; + QList keyframes() const; - QVector selectedKeyframes() const; + QList selectedKeyframes() const; - QVector handles() const; + QList handles() const; CurveItem *findCurve(unsigned int id) const; @@ -70,6 +70,8 @@ public: void setDirty(bool dirty); + void setIsMcu(bool isMcu); + void reset(); void deleteSelectedKeyframes(); @@ -120,7 +122,7 @@ private: void resetZValues(); - QVector m_curves; + QList m_curves; mutable bool m_dirty; @@ -129,6 +131,8 @@ private: bool m_doNotMoveItems; QElapsedTimer m_usageTimer; + + bool m_isMcu; }; } // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp index b7548020723..d41a25c43bb 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp @@ -87,11 +87,6 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent) applyZoom(m_zoomX, m_zoomY); update(); - - const QString css = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - horizontalScrollBar()->setStyleSheet(css); - verticalScrollBar()->setStyleSheet(css); } GraphicsView::~GraphicsView() @@ -194,6 +189,11 @@ void GraphicsView::setStyle(const CurveEditorStyle &style) viewport()->update(); } +void GraphicsView::setIsMcu(bool isMcu) +{ + m_scene->setIsMcu(isMcu); +} + void GraphicsView::setLocked(TreeItem *item) { if (item->asNodeItem()) { diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h index 120249b605f..60fb8d08d36 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h @@ -72,6 +72,8 @@ public: QRectF defaultRasterRect() const; + void setIsMcu(bool isMcu); + void setLocked(TreeItem *item); void setPinned(TreeItem *item); diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp index a50966292fa..d0b23f1b128 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -13,13 +13,13 @@ using namespace QmlDesigner; void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *parent, const QByteArray &key, AbstractView *view, - View3DActionType actionType, + const AuxiliaryDataKeyView &auxProp, const std::function &colorSelected) { if (m_dialog) return; - m_dialog = BackgroundColorSelection::createColorDialog(parent, key, view, actionType, colorSelected); + m_dialog = BackgroundColorSelection::createColorDialog(parent, key, view, auxProp, colorSelected); QTC_ASSERT(m_dialog, return); QObject::connect(m_dialog, &QWidget::destroyed, m_dialog, [&]() { @@ -30,7 +30,7 @@ void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *paren QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, const QByteArray &key, AbstractView *view, - View3DActionType actionType, + const AuxiliaryDataKeyView &auxProp, const std::function &colorSelected) { auto dialog = new QColorDialog(parent); @@ -38,13 +38,13 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, dialog->setModal(true); dialog->setAttribute(Qt::WA_DeleteOnClose); - QList oldColorConfig = Edit3DViewConfig::loadColor(key); + QList oldColorConfig = Edit3DViewConfig::loadColors(key); dialog->show(); QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, - [actionType, view](const QColor &color) { - Edit3DViewConfig::setColors(view, actionType, {color}); + [auxProp, view](const QColor &color) { + Edit3DViewConfig::setColors(view, auxProp, {color}); }); QObject::connect(dialog, &QColorDialog::colorSelected, dialog, @@ -57,8 +57,8 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, if (Edit3DViewConfig::colorsValid(oldColorConfig)) { QObject::connect(dialog, &QColorDialog::rejected, dialog, - [actionType, oldColorConfig, view]() { - Edit3DViewConfig::setColors(view, actionType, oldColorConfig); + [auxProp, oldColorConfig, view]() { + Edit3DViewConfig::setColors(view, auxProp, oldColorConfig); }); } diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h index efed2e661ef..8bdc816220a 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include @@ -13,6 +13,11 @@ QT_FORWARD_DECLARE_CLASS(QColorDialog) namespace QmlDesigner { class AbstractView; +inline constexpr AuxiliaryDataKeyView edit3dGridColorProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "edit3dGridColor"}; +inline constexpr AuxiliaryDataKeyView edit3dBgColorProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "edit3dBgColor"}; + class BackgroundColorSelection : public QObject { Q_OBJECT @@ -25,14 +30,14 @@ public: static void showBackgroundColorSelectionWidget(QWidget *parent, const QByteArray &key, AbstractView *view, - View3DActionType actionType, + const AuxiliaryDataKeyView &auxProp, const std::function &colorSelected = {}); private: static QColorDialog *createColorDialog(QWidget *parent, const QByteArray &key, AbstractView *view, - View3DActionType actionType, + const AuxiliaryDataKeyView &auxProp, const std::function &colorSelected); inline static QColorDialog *m_dialog = nullptr; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index 4ffdf0e801c..add61d195de 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -25,10 +25,8 @@ Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, void Edit3DActionTemplate::actionTriggered(bool b) { - if (m_type != View3DActionType::Empty && m_type != View3DActionType::SelectBackgroundColor - && m_type != View3DActionType::SelectGridColor) { + if (m_type != View3DActionType::Empty) m_view->emitView3DAction(m_type, b); - } if (m_action) m_action(m_selectionContext); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.cpp similarity index 54% rename from src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.cpp rename to src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.cpp index c84c4f407ce..42ab811900f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.cpp @@ -1,21 +1,21 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "edit3dvisibilitytogglesmenu.h" +#include "edit3dtoolbarmenu.h" namespace QmlDesigner { -Edit3DVisibilityTogglesMenu::Edit3DVisibilityTogglesMenu(QWidget *parent) : - QMenu(parent) +Edit3DToolbarMenu::Edit3DToolbarMenu(QWidget *parent) + : QMenu(parent) { setToolTipsVisible(true); } -void Edit3DVisibilityTogglesMenu::mouseReleaseEvent(QMouseEvent *e) +void Edit3DToolbarMenu::mouseReleaseEvent(QMouseEvent *e) { QAction *action = activeAction(); - if (action && action->isEnabled()) { - // Prevent the menu from closing on click on any item + if (action && action->isEnabled() && action->isCheckable()) { + // Prevent the menu from closing on click on any checkable item action->setEnabled(false); QMenu::mouseReleaseEvent(e); action->setEnabled(true); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.h b/src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.h similarity index 63% rename from src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.h rename to src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.h index 8b498946934..54e5f311ffb 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dvisibilitytogglesmenu.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dtoolbarmenu.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once @@ -6,12 +6,12 @@ namespace QmlDesigner { -class Edit3DVisibilityTogglesMenu : public QMenu +class Edit3DToolbarMenu : public QMenu { Q_OBJECT public: - explicit Edit3DVisibilityTogglesMenu(QWidget *parent = nullptr); + explicit Edit3DToolbarMenu(QWidget *parent = nullptr); protected: void mouseReleaseEvent(QMouseEvent *e) override; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 5903c530369..c1637bf322b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -9,19 +9,19 @@ #include "designericons.h" #include "designersettings.h" #include "designmodecontext.h" -#include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dviewconfig.h" #include "edit3dwidget.h" #include "materialutils.h" #include "metainfo.h" +#include "nodeabstractproperty.h" #include "nodehints.h" #include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "qmlvisualnode.h" #include "seekerslider.h" -#include "theme.h" +#include "snapconfiguration.h" #include @@ -42,27 +42,14 @@ namespace QmlDesigner { -static inline QIcon contextIcon (const DesignerIcons::IconId &iconId) { +inline static QIcon contextIcon(const DesignerIcons::IconId &iconId) +{ return DesignerActionManager::instance().contextIcon(iconId); }; -static QIcon toolbarIcon (const Theme::Icon &iconOffId, const Theme::Icon &iconnOnId) { - QIcon iconOff = Theme::iconFromName(iconOffId); - QIcon iconOn= Theme::iconFromName(iconnOnId, - Theme::getColor(Theme::Color::DStextSelectedTextColor)); - QIcon retIcon; - - const auto onAvail = iconOff.availableSizes(); // Assume both icons have same sizes available - for (const auto &size : onAvail) { - retIcon.addPixmap(iconOff.pixmap(size), QIcon::Normal, QIcon::Off); - retIcon.addPixmap(iconOn.pixmap(size), QIcon::Normal, QIcon::On); - } - - return retIcon; -}; - -static inline QIcon toolbarIcon (const Theme::Icon &iconOffId) { - return toolbarIcon(iconOffId, iconOffId); +inline static QIcon toolbarIcon(const DesignerIcons::IconId &iconId) +{ + return DesignerActionManager::instance().toolbarIcon(iconId); }; Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies) @@ -133,6 +120,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString cameraFrustumKey = QStringLiteral("showCameraFrustum"); const QString particleEmitterKey = QStringLiteral("showParticleEmitter"); const QString particlesPlayKey = QStringLiteral("particlePlay"); + const QString syncBgColorKey = QStringLiteral("syncBackgroundColor"); if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); @@ -203,6 +191,35 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_particlesPlayAction->action()->setChecked(true); + // Syncing background color only makes sense for children of View3D instances + bool syncValue = false; + bool syncEnabled = false; + bool desiredSyncValue = false; + if (sceneState.contains(syncBgColorKey)) + desiredSyncValue = sceneState[syncBgColorKey].toBool(); + ModelNode checkNode = active3DSceneNode(); + while (checkNode.isValid()) { + if (checkNode.metaInfo().isQtQuick3DView3D()) { + syncValue = desiredSyncValue; + syncEnabled = true; + break; + } + if (checkNode.hasParentProperty()) + checkNode = checkNode.parentProperty().parentModelNode(); + else + break; + } + + if (syncValue != desiredSyncValue) { + // Update actual toolstate as well if we overrode it. + QTimer::singleShot(0, this, [this, syncValue]() { + emitView3DAction(View3DActionType::SyncBackgroundColor, syncValue); + }); + } + + m_syncBackgroundColorAction->action()->setChecked(syncValue); + m_syncBackgroundColorAction->action()->setEnabled(syncEnabled); + // Selection context change updates visible and enabled states SelectionContext selectionContext(this); selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast); @@ -214,6 +231,15 @@ void Edit3DView::modelAttached(Model *model) { AbstractView::modelAttached(model); + syncSnapAuxPropsToSettings(); + + rootModelNode().setAuxiliaryData(edit3dGridColorProperty, + QVariant::fromValue(Edit3DViewConfig::loadColor( + DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR))); + rootModelNode().setAuxiliaryData(edit3dBgColorProperty, + QVariant::fromValue(Edit3DViewConfig::loadColors( + DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR))); + checkImports(); auto cachedImage = m_canvasCache.take(model); if (cachedImage) { @@ -242,7 +268,7 @@ void Edit3DView::onEntriesChanged() void Edit3DView::registerEdit3DAction(Edit3DAction *action) { if (action->actionType() != View3DActionType::Empty) - m_edit3DActions.insert(action->actionType(), QSharedPointer(action)); + m_edit3DActions.insert(action->actionType(), action); } void Edit3DView::handleEntriesChanged() @@ -311,6 +337,9 @@ void Edit3DView::modelAboutToBeDetached(Model *model) if (m_bakeLights) m_bakeLights->cancel(); + if (m_snapConfiguration) + m_snapConfiguration->cancel(); + // Hide the canvas when model is detached (i.e. changing documents) if (edit3DWidget() && edit3DWidget()->canvas()) { m_canvasCache.insert(model, edit3DWidget()->canvas()->renderImage()); @@ -420,7 +449,7 @@ QSize Edit3DView::canvasSize() const return {}; } -Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction) +void Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction) { QString description = QCoreApplication::translate("SelectBackgroundColorAction", "Select Background Color"); @@ -432,28 +461,29 @@ Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgro edit3DWidget(), DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, this, - View3DActionType::SelectBackgroundColor, + edit3dBgColorProperty, [this, syncBackgroundColorAction]() { if (syncBackgroundColorAction->isChecked()) { - Edit3DViewConfig::set(this, View3DActionType::SyncBackgroundColor, false); + emitView3DAction(View3DActionType::SyncBackgroundColor, false); syncBackgroundColorAction->setChecked(false); } }); }; - return new Edit3DAction(Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, - View3DActionType::SelectBackgroundColor, - description, - {}, - false, - false, - {}, - this, - operation, - tooltip); + m_selectBackgroundColorAction = std::make_unique( + Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, + View3DActionType::Empty, + description, + QKeySequence(), + false, + false, + QIcon(), + this, + operation, + tooltip); } -Edit3DAction *Edit3DView::createGridColorSelectionAction() +void Edit3DView::createGridColorSelectionAction() { QString description = QCoreApplication::translate("SelectGridColorAction", "Select Grid Color"); QString tooltip = QCoreApplication::translate("SelectGridColorAction", @@ -464,22 +494,23 @@ Edit3DAction *Edit3DView::createGridColorSelectionAction() edit3DWidget(), DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, this, - View3DActionType::SelectGridColor); + edit3dGridColorProperty); }; - return new Edit3DAction(Constants::EDIT3D_EDIT_SELECT_GRID_COLOR, - View3DActionType::SelectGridColor, - description, - {}, - false, - false, - {}, - this, - operation, - tooltip); + m_selectGridColorAction = std::make_unique( + Constants::EDIT3D_EDIT_SELECT_GRID_COLOR, + View3DActionType::Empty, + description, + QKeySequence(), + false, + false, + QIcon(), + this, + operation, + tooltip); } -Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) +void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) { QString description = QCoreApplication::translate("ResetEdit3DColorsAction", "Reset Colors"); QString tooltip = QCoreApplication::translate("ResetEdit3DColorsAction", @@ -488,32 +519,33 @@ Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAct auto operation = [this, syncBackgroundColorAction](const SelectionContext &) { QList bgColors = {QRgb(0x222222), QRgb(0x999999)}; - Edit3DViewConfig::setColors(this, View3DActionType::SelectBackgroundColor, bgColors); + Edit3DViewConfig::setColors(this, edit3dBgColorProperty, bgColors); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors); - QColor gridColor{0xaaaaaa}; - Edit3DViewConfig::setColors(this, View3DActionType::SelectGridColor, {gridColor}); + QColor gridColor{0xcccccc}; + Edit3DViewConfig::setColors(this, edit3dGridColorProperty, {gridColor}); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, {gridColor}); if (syncBackgroundColorAction->isChecked()) { - Edit3DViewConfig::set(this, View3DActionType::SyncBackgroundColor, false); + emitView3DAction(View3DActionType::SyncBackgroundColor, false); syncBackgroundColorAction->setChecked(false); } }; - return new Edit3DAction(QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, - View3DActionType::ResetBackgroundColor, - description, - {}, - false, - false, - {}, - this, - operation, - tooltip); + m_resetColorAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, + View3DActionType::Empty, + description, + QKeySequence(), + false, + false, + QIcon(), + this, + operation, + tooltip); } -Edit3DAction *Edit3DView::createSyncBackgroundColorAction() +void Edit3DView::createSyncBackgroundColorAction() { QString description = QCoreApplication::translate("SyncEdit3DColorAction", "Use Scene Environment Color"); @@ -521,112 +553,156 @@ Edit3DAction *Edit3DView::createSyncBackgroundColorAction() "Sets the 3D view to use the Scene Environment " "color as background color."); - return new Edit3DAction(QmlDesigner::Constants::EDIT3D_EDIT_SYNC_BACKGROUND_COLOR, - View3DActionType::SyncBackgroundColor, - description, - {}, - true, - false, - {}, - this, - {}, - tooltip); + m_syncBackgroundColorAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_SYNC_BACKGROUND_COLOR, + View3DActionType::SyncBackgroundColor, + description, + QKeySequence(), + true, + false, + QIcon(), + this, + nullptr, + tooltip); } -Edit3DAction *Edit3DView::createSeekerSliderAction() +void Edit3DView::createSeekerSliderAction() { - Edit3DParticleSeekerAction *seekerAction = new Edit3DParticleSeekerAction( - QmlDesigner::Constants::EDIT3D_PARTICLES_SEEKER, - View3DActionType::ParticlesSeek, - this); + m_seekerAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_PARTICLES_SEEKER, + View3DActionType::ParticlesSeek, + this); - seekerAction->action()->setEnabled(false); - seekerAction->action()->setToolTip(QLatin1String("Seek particle system time when paused.")); + m_seekerAction->action()->setEnabled(false); + m_seekerAction->action()->setToolTip(QLatin1String("Seek particle system time when paused.")); - connect(seekerAction->seekerAction(), + connect(m_seekerAction->seekerAction(), &SeekerSliderAction::valueChanged, this, [=] (int value) { this->emitView3DAction(View3DActionType::ParticlesSeek, value); - }); + }); +} - return seekerAction; +QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const +{ + QPoint pos; + const QList &objects = action->action()->associatedObjects(); + for (QObject *obj : objects) { + if (auto button = qobject_cast(obj)) { + // Add small negative modifier to Y coordinate, so highlighted toolbar buttons don't + // peek from under the popup + pos = button->mapToGlobal(QPoint(0, -2)); + break; + } + } + return pos; +} + +void Edit3DView::syncSnapAuxPropsToSettings() +{ + if (!model()) + return; + + bool snapToggle = m_snapToggleAction->action()->isChecked(); + rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, + snapToggle ? Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION) + : false); + rootModelNode().setAuxiliaryData(edit3dSnapRotProperty, + snapToggle ? Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION) + : false); + rootModelNode().setAuxiliaryData(edit3dSnapScaleProperty, + snapToggle ? Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE) + : false); + rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE)); + rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL)); + rootModelNode().setAuxiliaryData(edit3dSnapRotIntProperty, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL)); + rootModelNode().setAuxiliaryData(edit3dSnapScaleIntProperty, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL)); } void Edit3DView::createEdit3DActions() { - m_selectionModeAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_SELECTION_MODE, - View3DActionType::SelectionModeToggle, - QCoreApplication::translate("SelectionModeToggleAction", "Toggle Group/Single Selection Mode"), - QKeySequence(Qt::Key_Q), - true, - false, - toolbarIcon(Theme::selectOutline_medium, Theme::selectFill_medium), - this); + m_selectionModeAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SELECTION_MODE, + View3DActionType::SelectionModeToggle, + QCoreApplication::translate("SelectionModeToggleAction", + "Toggle Group/Single Selection Mode"), + QKeySequence(Qt::Key_Q), + true, + false, + toolbarIcon(DesignerIcons::ToggleGroupIcon), + this); - m_moveToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_MOVE_TOOL, - View3DActionType::MoveTool, - QCoreApplication::translate("MoveToolAction", - "Activate Move Tool"), - QKeySequence(Qt::Key_W), - true, - true, - toolbarIcon(Theme::move_medium), - this); + m_moveToolAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_MOVE_TOOL, + View3DActionType::MoveTool, + QCoreApplication::translate("MoveToolAction", + "Activate Move Tool"), + QKeySequence(Qt::Key_W), + true, + true, + toolbarIcon(DesignerIcons::MoveToolIcon), + this); - m_rotateToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ROTATE_TOOL, - View3DActionType::RotateTool, - QCoreApplication::translate("RotateToolAction", - "Activate Rotate Tool"), - QKeySequence(Qt::Key_E), - true, - false, - toolbarIcon(Theme::roatate_medium), - this); + m_rotateToolAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_ROTATE_TOOL, + View3DActionType::RotateTool, + QCoreApplication::translate("RotateToolAction", + "Activate Rotate Tool"), + QKeySequence(Qt::Key_E), + true, + false, + toolbarIcon(DesignerIcons::RotateToolIcon), + this); - m_scaleToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_SCALE_TOOL, - View3DActionType::ScaleTool, - QCoreApplication::translate("ScaleToolAction", - "Activate Scale Tool"), - QKeySequence(Qt::Key_R), - true, - false, - toolbarIcon(Theme::scale_medium), - this); + m_scaleToolAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SCALE_TOOL, + View3DActionType::ScaleTool, + QCoreApplication::translate("ScaleToolAction", + "Activate Scale Tool"), + QKeySequence(Qt::Key_R), + true, + false, + toolbarIcon(DesignerIcons::ScaleToolIcon), + this); - m_fitAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_FIT_SELECTED, - View3DActionType::FitToView, - QCoreApplication::translate("FitToViewAction", - "Fit Selected Object to View"), - QKeySequence(Qt::Key_F), - false, - false, - toolbarIcon(Theme::fitToView_medium), - this); + m_fitAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_FIT_SELECTED, + View3DActionType::FitToView, + QCoreApplication::translate("FitToViewAction", + "Fit Selected Object to View"), + QKeySequence(Qt::Key_F), + false, + false, + toolbarIcon(DesignerIcons::FitToViewIcon), + this); - m_alignCamerasAction = new Edit3DAction( + m_alignCamerasAction = std::make_unique( QmlDesigner::Constants::EDIT3D_ALIGN_CAMERAS, View3DActionType::AlignCamerasToView, - QCoreApplication::translate("AlignCamerasToViewAction", "Align Cameras to View"), + QCoreApplication::translate("AlignCamerasToViewAction", + "Align Cameras to View"), QKeySequence(), false, false, - toolbarIcon(Theme::alignToCam_medium), + toolbarIcon(DesignerIcons::AlignCameraToViewIcon), this); - m_alignCamerasAction->action()->setEnabled(false); - m_alignViewAction = new Edit3DAction( + m_alignViewAction = std::make_unique( QmlDesigner::Constants::EDIT3D_ALIGN_VIEW, View3DActionType::AlignViewToCamera, - QCoreApplication::translate("AlignCamerasToViewAction", "Align View to Camera"), + QCoreApplication::translate("AlignViewToCameraAction", + "Align View to Camera"), QKeySequence(), false, false, - toolbarIcon(Theme::alignToView_medium), + toolbarIcon(DesignerIcons::AlignViewToCameraIcon), this); - m_alignViewAction->action()->setEnabled(false); - m_cameraModeAction = new Edit3DAction( + m_cameraModeAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_CAMERA, View3DActionType::CameraToggle, QCoreApplication::translate("CameraToggleAction", @@ -634,77 +710,78 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_T), true, false, - toolbarIcon(Theme::orthCam_small, Theme::perspectiveCam_small), + toolbarIcon(DesignerIcons::CameraIcon), this); - m_orientationModeAction = new Edit3DAction( + m_orientationModeAction = std::make_unique( QmlDesigner::Constants::EDIT3D_ORIENTATION, View3DActionType::OrientationToggle, - QCoreApplication::translate("OrientationToggleAction", "Toggle Global/Local Orientation"), + QCoreApplication::translate("OrientationToggleAction", + "Toggle Global/Local Orientation"), QKeySequence(Qt::Key_Y), true, false, - toolbarIcon(Theme::localOrient_medium), + toolbarIcon(DesignerIcons::LocalOrientIcon), this); - m_editLightAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_EDIT_LIGHT, - View3DActionType::EditLightToggle, - QCoreApplication::translate("EditLightToggleAction", - "Toggle Edit Light On/Off"), - QKeySequence(Qt::Key_U), - true, - false, - toolbarIcon(Theme::editLightOff_medium, Theme::editLightOn_medium), - this); + m_editLightAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_LIGHT, + View3DActionType::EditLightToggle, + QCoreApplication::translate("EditLightToggleAction", + "Toggle Edit Light On/Off"), + QKeySequence(Qt::Key_U), + true, + false, + toolbarIcon(DesignerIcons::EditLightIcon), + this); - m_showGridAction = new Edit3DAction( + m_showGridAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_GRID, View3DActionType::ShowGrid, QCoreApplication::translate("ShowGridAction", "Show Grid"), QKeySequence(Qt::Key_G), true, true, - {}, + QIcon(), this, nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); - m_showSelectionBoxAction = new Edit3DAction( + m_showSelectionBoxAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionType::ShowSelectionBox, QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"), QKeySequence(Qt::Key_S), true, true, - {}, + QIcon(), this, nullptr, QCoreApplication::translate("ShowSelectionBoxAction", "Toggle the visibility of selection boxes.")); - m_showIconGizmoAction = new Edit3DAction( + m_showIconGizmoAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_ICON_GIZMO, View3DActionType::ShowIconGizmo, QCoreApplication::translate("ShowIconGizmoAction", "Show Icon Gizmos"), QKeySequence(Qt::Key_I), true, true, - {}, + QIcon(), this, nullptr, QCoreApplication::translate( "ShowIconGizmoAction", "Toggle the visibility of icon gizmos, such as light and camera icons.")); - m_showCameraFrustumAction = new Edit3DAction( + m_showCameraFrustumAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM, View3DActionType::ShowCameraFrustum, QCoreApplication::translate("ShowCameraFrustumAction", "Always Show Camera Frustums"), QKeySequence(Qt::Key_C), true, false, - {}, + QIcon(), this, nullptr, QCoreApplication::translate( @@ -712,7 +789,7 @@ void Edit3DView::createEdit3DActions() "Toggle between always showing the camera frustum visualization and only showing it " "when the camera is selected.")); - m_showParticleEmitterAction = new Edit3DAction( + m_showParticleEmitterAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_PARTICLE_EMITTER, View3DActionType::ShowParticleEmitter, QCoreApplication::translate("ShowParticleEmitterAction", @@ -720,7 +797,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_M), true, false, - {}, + QIcon(), this, nullptr, QCoreApplication::translate( @@ -764,75 +841,74 @@ void Edit3DView::createEdit3DActions() m_bakeLights->raiseDialog(); }; - m_particleViewModeAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_PARTICLE_MODE, - View3DActionType::Edit3DParticleModeToggle, - QCoreApplication::translate("ParticleViewModeAction", "Toggle particle animation On/Off"), - QKeySequence(Qt::Key_V), - true, - false, - toolbarIcon(Theme::particleAnimation_medium), - this, - particlesTrigger); + m_particleViewModeAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_PARTICLE_MODE, + View3DActionType::Edit3DParticleModeToggle, + QCoreApplication::translate("ParticleViewModeAction", + "Toggle particle animation On/Off"), + QKeySequence(Qt::Key_V), + true, + false, + toolbarIcon(DesignerIcons::ParticlesAnimationIcon), + this, + particlesTrigger); + particlemode = false; - m_particlesPlayAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_PARTICLES_PLAY, - View3DActionType::ParticlesPlay, - QCoreApplication::translate("ParticlesPlayAction", "Play Particles"), - QKeySequence(Qt::Key_Comma), - true, - true, - toolbarIcon(Theme::playOutline_medium, Theme::pause), // Icons::EDIT3D_PARTICLE_PLAY.icon(), Icons::EDIT3D_PARTICLE_PAUSE.icon(), - this, - particlesPlayTrigger); - m_particlesRestartAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_PARTICLES_RESTART, - View3DActionType::ParticlesRestart, - QCoreApplication::translate("ParticlesRestartAction", "Restart Particles"), - QKeySequence(Qt::Key_Slash), - false, - false, - toolbarIcon(Theme::restartParticles_medium), - this); + m_particlesPlayAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_PARTICLES_PLAY, + View3DActionType::ParticlesPlay, + QCoreApplication::translate("ParticlesPlayAction", + "Play Particles"), + QKeySequence(Qt::Key_Comma), + true, + true, + toolbarIcon(DesignerIcons::ParticlesPlayIcon), + this, + particlesPlayTrigger); + + m_particlesRestartAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_PARTICLES_RESTART, + View3DActionType::ParticlesRestart, + QCoreApplication::translate("ParticlesRestartAction", + "Restart Particles"), + QKeySequence(Qt::Key_Slash), + false, + false, + toolbarIcon(DesignerIcons::ParticlesRestartIcon), + this); + m_particlesPlayAction->action()->setEnabled(particlemode); m_particlesRestartAction->action()->setEnabled(particlemode); - m_resetAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_RESET_VIEW, - View3DActionType::Empty, - QCoreApplication::translate("ResetView", "Reset View"), - QKeySequence(Qt::Key_P), - false, - false, - toolbarIcon(Theme::reload_medium), - this, - resetTrigger); + m_resetAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_RESET_VIEW, + View3DActionType::Empty, + QCoreApplication::translate("ResetView", "Reset View"), + QKeySequence(Qt::Key_P), + false, + false, + toolbarIcon(DesignerIcons::ResetViewIcon), + this, + resetTrigger); SelectionContextOperation visibilityTogglesTrigger = [this](const SelectionContext &) { if (!edit3DWidget()->visibilityTogglesMenu()) return; - QPoint pos; - const auto &actionWidgets = m_visibilityTogglesAction->action()->associatedWidgets(); - for (auto actionWidget : actionWidgets) { - if (auto button = qobject_cast(actionWidget)) { - pos = button->mapToGlobal(QPoint(0, 0)); - break; - } - } - - edit3DWidget()->showVisibilityTogglesMenu(!edit3DWidget()->visibilityTogglesMenu()->isVisible(), - pos); + edit3DWidget()->showVisibilityTogglesMenu( + !edit3DWidget()->visibilityTogglesMenu()->isVisible(), + resolveToolbarPopupPos(m_visibilityTogglesAction.get())); }; - m_visibilityTogglesAction = new Edit3DAction( + m_visibilityTogglesAction = std::make_unique( QmlDesigner::Constants::EDIT3D_VISIBILITY_TOGGLES, View3DActionType::Empty, - QCoreApplication::translate("VisibilityTogglesAction", "Visibility Toggles"), + QCoreApplication::translate("VisibilityTogglesAction", + "Visibility Toggles"), QKeySequence(), false, false, - toolbarIcon(Theme::invisible_medium, Theme::visible_medium), + toolbarIcon(DesignerIcons::VisibilityIcon), this, visibilityTogglesTrigger); @@ -840,77 +916,110 @@ void Edit3DView::createEdit3DActions() if (!edit3DWidget()->backgroundColorMenu()) return; - QPoint pos; - const auto &actionWidgets = m_backgrondColorMenuAction->action()->associatedWidgets(); - for (auto actionWidget : actionWidgets) { - if (auto button = qobject_cast(actionWidget)) { - pos = button->mapToGlobal(QPoint(0, 0)); - break; - } - } - - edit3DWidget()->showBackgroundColorMenu(!edit3DWidget()->backgroundColorMenu()->isVisible(), - pos); + edit3DWidget()->showBackgroundColorMenu( + !edit3DWidget()->backgroundColorMenu()->isVisible(), + resolveToolbarPopupPos(m_backgroundColorMenuAction.get())); }; - m_backgrondColorMenuAction = new Edit3DAction( + m_backgroundColorMenuAction = std::make_unique( QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, View3DActionType::Empty, - QCoreApplication::translate("BackgroundColorMenuActions", "Background Color Actions"), + QCoreApplication::translate("BackgroundColorMenuActions", + "Background Color Actions"), QKeySequence(), false, false, - toolbarIcon(Theme::colorSelection_medium), + toolbarIcon(DesignerIcons::EditColorIcon), this, backgroundColorActionsTrigger); - m_seekerAction = createSeekerSliderAction(); + createSeekerSliderAction(); - m_bakeLightsAction = new Edit3DBakeLightsAction( - toolbarIcon(Theme::editLightOn_medium), //: TODO placeholder icon - this, - bakeLightsTrigger); + m_bakeLightsAction = std::make_unique( + toolbarIcon(DesignerIcons::EditLightIcon), //: TODO placeholder icon + this, + bakeLightsTrigger); - m_leftActions << m_selectionModeAction; + SelectionContextOperation snapToggleTrigger = [this](const SelectionContext &) { + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_ENABLED, + m_snapToggleAction->action()->isChecked()); + syncSnapAuxPropsToSettings(); + }; + + m_snapToggleAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SNAP_TOGGLE, + View3DActionType::Empty, + QCoreApplication::translate("SnapToggleAction", "Toggle snapping during node drag"), + QKeySequence(Qt::SHIFT | Qt::Key_Tab), + true, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ENABLED, false).toBool(), + toolbarIcon(DesignerIcons::SnappingIcon), + this, + snapToggleTrigger); + + SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { + if (!m_snapConfiguration) + m_snapConfiguration = new SnapConfiguration(this); + m_snapConfiguration->showConfigDialog(resolveToolbarPopupPos(m_snapConfigAction.get())); + }; + + m_snapConfigAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SNAP_CONFIG, + View3DActionType::Empty, + QCoreApplication::translate("SnapConfigAction", "Open snap configuration dialog"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::SnappingConfIcon), + this, + snapConfigTrigger); + + m_leftActions << m_selectionModeAction.get(); m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group - m_leftActions << m_moveToolAction; - m_leftActions << m_rotateToolAction; - m_leftActions << m_scaleToolAction; + m_leftActions << m_moveToolAction.get(); + m_leftActions << m_rotateToolAction.get(); + m_leftActions << m_scaleToolAction.get(); m_leftActions << nullptr; - m_leftActions << m_fitAction; + m_leftActions << m_fitAction.get(); m_leftActions << nullptr; - m_leftActions << m_cameraModeAction; - m_leftActions << m_orientationModeAction; - m_leftActions << m_editLightAction; + m_leftActions << m_cameraModeAction.get(); + m_leftActions << m_orientationModeAction.get(); + m_leftActions << m_editLightAction.get(); m_leftActions << nullptr; - m_leftActions << m_alignCamerasAction; - m_leftActions << m_alignViewAction; + m_leftActions << m_snapToggleAction.get(); + m_leftActions << m_snapConfigAction.get(); m_leftActions << nullptr; - m_leftActions << m_visibilityTogglesAction; + m_leftActions << m_alignCamerasAction.get(); + m_leftActions << m_alignViewAction.get(); m_leftActions << nullptr; - m_leftActions << m_backgrondColorMenuAction; + m_leftActions << m_visibilityTogglesAction.get(); + m_leftActions << m_backgroundColorMenuAction.get(); - m_rightActions << m_particleViewModeAction; - m_rightActions << m_particlesPlayAction; - m_rightActions << m_particlesRestartAction; + m_rightActions << m_particleViewModeAction.get(); + m_rightActions << m_particlesPlayAction.get(); + m_rightActions << m_particlesRestartAction.get(); m_rightActions << nullptr; - m_rightActions << m_seekerAction; + m_rightActions << m_seekerAction.get(); m_rightActions << nullptr; - m_rightActions << m_bakeLightsAction; - m_rightActions << m_resetAction; + m_rightActions << m_bakeLightsAction.get(); + m_rightActions << m_resetAction.get(); - m_visibilityToggleActions << m_showGridAction; - m_visibilityToggleActions << m_showSelectionBoxAction; - m_visibilityToggleActions << m_showIconGizmoAction; - m_visibilityToggleActions << m_showCameraFrustumAction; - m_visibilityToggleActions << m_showParticleEmitterAction; + m_visibilityToggleActions << m_showGridAction.get(); + m_visibilityToggleActions << m_showSelectionBoxAction.get(); + m_visibilityToggleActions << m_showIconGizmoAction.get(); + m_visibilityToggleActions << m_showCameraFrustumAction.get(); + m_visibilityToggleActions << m_showParticleEmitterAction.get(); - Edit3DAction *syncBackgroundColorAction = createSyncBackgroundColorAction(); - m_backgroundColorActions << createSelectBackgroundColorAction(syncBackgroundColorAction->action()); - m_backgroundColorActions << createGridColorSelectionAction(); - m_backgroundColorActions << syncBackgroundColorAction; - m_backgroundColorActions << createResetColorAction(syncBackgroundColorAction->action()); + createSyncBackgroundColorAction(); + createSelectBackgroundColorAction(m_syncBackgroundColorAction->action()); + createGridColorSelectionAction(); + createResetColorAction(m_syncBackgroundColorAction->action()); + + m_backgroundColorActions << m_selectBackgroundColorAction.get(); + m_backgroundColorActions << m_selectGridColorAction.get(); + m_backgroundColorActions << m_syncBackgroundColorAction.get(); + m_backgroundColorActions << m_resetColorAction.get(); } QVector Edit3DView::leftActions() const @@ -933,14 +1042,15 @@ QVector Edit3DView::backgroundColorActions() const return m_backgroundColorActions; } + Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const { - return m_edit3DActions.value(type, nullptr).data(); + return m_edit3DActions.value(type, nullptr); } Edit3DBakeLightsAction *Edit3DView::bakeLightsAction() const { - return m_bakeLightsAction; + return m_bakeLightsAction.get(); } void Edit3DView::addQuick3DImport() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 6e6b82948e5..2b2bd57d93f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once +#include "edit3dactions.h" #include "itemlibraryinfo.h" #include @@ -24,8 +25,7 @@ namespace QmlDesigner { class BakeLights; class Edit3DWidget; -class Edit3DAction; -class Edit3DBakeLightsAction; +class SnapConfiguration; class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView { @@ -76,6 +76,8 @@ public: bool isBakingLightsSupported() const; + void syncSnapAuxPropsToSettings(); + private slots: void onEntriesChanged(); @@ -99,11 +101,13 @@ private: void showMaterialPropertiesView(); void updateAlignActionStates(); - Edit3DAction *createSelectBackgroundColorAction(QAction *syncBackgroundColorAction); - Edit3DAction *createGridColorSelectionAction(); - Edit3DAction *createResetColorAction(QAction *syncBackgroundColorAction); - Edit3DAction *createSyncBackgroundColorAction(); - Edit3DAction *createSeekerSliderAction(); + void createSelectBackgroundColorAction(QAction *syncBackgroundColorAction); + void createGridColorSelectionAction(); + void createResetColorAction(QAction *syncBackgroundColorAction); + void createSyncBackgroundColorAction(); + void createSeekerSliderAction(); + + QPoint resolveToolbarPopupPos(Edit3DAction *action) const; QPointer m_edit3DWidget; QVector m_leftActions; @@ -111,30 +115,39 @@ private: QVector m_visibilityToggleActions; QVector m_backgroundColorActions; - QMap> m_edit3DActions; - Edit3DAction *m_selectionModeAction = nullptr; - Edit3DAction *m_moveToolAction = nullptr; - Edit3DAction *m_rotateToolAction = nullptr; - Edit3DAction *m_scaleToolAction = nullptr; - Edit3DAction *m_fitAction = nullptr; - Edit3DAction *m_alignCamerasAction = nullptr; - Edit3DAction *m_alignViewAction = nullptr; - Edit3DAction *m_cameraModeAction = nullptr; - Edit3DAction *m_orientationModeAction = nullptr; - Edit3DAction *m_editLightAction = nullptr; - Edit3DAction *m_showGridAction = nullptr; - Edit3DAction *m_showSelectionBoxAction = nullptr; - Edit3DAction *m_showIconGizmoAction = nullptr; - Edit3DAction *m_showCameraFrustumAction = nullptr; - Edit3DAction *m_showParticleEmitterAction = nullptr; - Edit3DAction *m_resetAction = nullptr; - Edit3DAction *m_particleViewModeAction = nullptr; - Edit3DAction *m_particlesPlayAction = nullptr; - Edit3DAction *m_particlesRestartAction = nullptr; - Edit3DAction *m_visibilityTogglesAction = nullptr; - Edit3DAction *m_backgrondColorMenuAction = nullptr; - Edit3DAction *m_seekerAction = nullptr; - Edit3DBakeLightsAction *m_bakeLightsAction = nullptr; + QMap m_edit3DActions; + std::unique_ptr m_selectionModeAction; + std::unique_ptr m_moveToolAction; + std::unique_ptr m_rotateToolAction; + std::unique_ptr m_scaleToolAction; + std::unique_ptr m_fitAction; + std::unique_ptr m_alignCamerasAction; + std::unique_ptr m_alignViewAction; + std::unique_ptr m_cameraModeAction; + std::unique_ptr m_orientationModeAction; + std::unique_ptr m_editLightAction; + std::unique_ptr m_showGridAction; + std::unique_ptr m_showSelectionBoxAction; + std::unique_ptr m_showIconGizmoAction; + std::unique_ptr m_showCameraFrustumAction; + std::unique_ptr m_showParticleEmitterAction; + std::unique_ptr m_particleViewModeAction; + std::unique_ptr m_particlesPlayAction; + std::unique_ptr m_particlesRestartAction; + std::unique_ptr m_seekerAction; + std::unique_ptr m_syncBackgroundColorAction; + std::unique_ptr m_selectBackgroundColorAction; + std::unique_ptr m_selectGridColorAction; + std::unique_ptr m_resetColorAction; + + // View3DActionType::Empty actions + std::unique_ptr m_resetAction; + std::unique_ptr m_visibilityTogglesAction; + std::unique_ptr m_backgroundColorMenuAction; + std::unique_ptr m_snapToggleAction; + std::unique_ptr m_snapConfigAction; + std::unique_ptr m_bakeLightsAction; + int particlemode; ModelCache m_canvasCache; ModelNode m_droppedModelNode; @@ -145,6 +158,7 @@ private: QTimer m_compressionTimer; QPointer m_bakeLights; bool m_isBakingLightsSupported = false; + QPointer m_snapConfiguration; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h index 06595e3d08b..33d40b332e7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include @@ -14,7 +14,17 @@ namespace QmlDesigner { class Edit3DViewConfig { public: - static QList loadColor(const char key[]) + static QColor loadColor(const char key[]) + { + QVariant var = QmlDesignerPlugin::settings().value(key); + + if (!var.isValid()) + return {}; + + return QColor{var.value()}; + } + + static QList loadColors(const char key[]) { QVariant var = QmlDesignerPlugin::settings().value(key); @@ -28,15 +38,30 @@ public: }); } - static void setColors(AbstractView *view, View3DActionType type, const QList &colorConfig) + static QVariant load(const QByteArray &key, const QVariant &defaultValue = {}) { - setVariant(view, type, QVariant::fromValue(colorConfig)); + return QmlDesignerPlugin::settings().value(key, defaultValue); + } + + static void save(const QByteArray &key, const QVariant &value) + { + QmlDesignerPlugin::settings().insert(key, value); + } + + static void setColors(AbstractView *view, const AuxiliaryDataKeyView &auxProp, const QList &colorConfig) + { + QVariant param; + if (auxProp.name == "edit3dGridColor") + param = colorConfig.isEmpty() ? QColor() : colorConfig[0]; + else + param = QVariant::fromValue(colorConfig); + setVariant(view, auxProp, param); } template - static void set(AbstractView *view, View3DActionType type, const T &value) + static void set(AbstractView *view, const AuxiliaryDataKeyView &auxProp, const T &value) { - setVariant(view, type, QVariant::fromValue(value)); + setVariant(view, auxProp, QVariant::fromValue(value)); } static void saveColors(const QByteArray &key, const QList &colorConfig) @@ -45,20 +70,15 @@ public: return color.name(); }); - saveVariant(key, QVariant::fromValue(colorNames)); + save(key, QVariant::fromValue(colorNames)); } static bool colorsValid(const QList &colorConfig) { return !colorConfig.isEmpty(); } private: - static void setVariant(AbstractView *view, View3DActionType type, const QVariant &colorConfig) + static void setVariant(AbstractView *view, const AuxiliaryDataKeyView &auxProp, const QVariant &value) { - view->emitView3DAction(type, colorConfig); - } - - static void saveVariant(const QByteArray &key, const QVariant &colorConfig) - { - QmlDesignerPlugin::settings().insert(key, colorConfig); + view->rootModelNode().setAuxiliaryData(auxProp, value); } }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 163cfc951ed..8c3d54bb4f2 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -6,8 +6,9 @@ #include "designericons.h" #include "edit3dactions.h" #include "edit3dcanvas.h" +#include "edit3dtoolbarmenu.h" #include "edit3dview.h" -#include "edit3dvisibilitytogglesmenu.h" +#include "edit3dviewconfig.h" #include "materialutils.h" #include "metainfo.h" #include "modelnodeoperations.h" @@ -74,7 +75,6 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) setAcceptDrops(true); QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); Core::Context context(Constants::C_QMLEDITOR3D); @@ -158,7 +158,7 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) handleActions(view->leftActions(), nullptr, true); handleActions(view->rightActions(), nullptr, false); - m_visibilityTogglesMenu = new Edit3DVisibilityTogglesMenu(this); + m_visibilityTogglesMenu = new Edit3DToolbarMenu(this); handleActions(view->visibilityToggleActions(), m_visibilityTogglesMenu, false); m_backgroundColorMenu = new QMenu(this); @@ -541,7 +541,8 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { - dragEnterEvent->acceptProposedAction(); + if (m_view->active3DSceneNode().isValid()) + dragEnterEvent->acceptProposedAction(); } else if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); if (!data.isEmpty()) { diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp new file mode 100644 index 00000000000..44fb7ef1322 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -0,0 +1,229 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "snapconfiguration.h" + +#include "designersettings.h" +#include "edit3dview.h" +#include "edit3dviewconfig.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +static QString qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/edit3dQmlSource"; +#endif + return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString(); +} + +SnapConfiguration::SnapConfiguration(Edit3DView *view) + : QObject(view) + , m_view(view) +{ +} + +SnapConfiguration::~SnapConfiguration() +{ + cleanup(); +} + +void SnapConfiguration::apply() +{ + if (m_changes) { + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, + m_positionEnabled); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION, + m_rotationEnabled); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE, + m_scaleEnabled); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, + m_absolute); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, + m_positionInterval); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, + m_rotationInterval); + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, + m_scaleInterval); + m_view->syncSnapAuxPropsToSettings(); + } + + cancel(); +} + +void SnapConfiguration::resetDefaults() +{ + setPosEnabled(true); + setRotEnabled(true); + setScaleEnabled(true); + setAbsolute(true); + setPosInt(defaultPosInt); + setRotInt(defaultRotInt); + setScaleInt(defaultScaleInt); +} + +void SnapConfiguration::showConfigDialog(const QPoint &pos) +{ + bool posEnabled = Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, true).toBool(); + bool rotEnabled = Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION, true).toBool(); + bool scaleEnabled = Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE, true).toBool(); + bool absolute = Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, true).toBool(); + double posInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, defaultPosInt).toDouble(); + double rotInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, defaultRotInt).toDouble(); + double scaleInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, defaultScaleInt).toDouble(); + + setPosEnabled(posEnabled); + setRotEnabled(rotEnabled); + setScaleEnabled(scaleEnabled); + setAbsolute(absolute); + setPosInt(posInt); + setRotInt(rotInt); + setScaleInt(scaleInt); + + m_changes = false; + + if (!m_configDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/SnapConfigurationDialog.qml"; + + m_configDialog = new QQuickView; + m_configDialog->setResizeMode(QQuickView::SizeViewToRootObject); + m_configDialog->setFlags(Qt::Dialog | Qt::FramelessWindowHint); + m_configDialog->setModality(Qt::NonModal); + m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_configDialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)} + }); + m_configDialog->setSource(QUrl::fromLocalFile(path)); + m_configDialog->installEventFilter(this); + + QPoint finalPos = pos; + finalPos.setX(pos.x() - m_configDialog->size().width() / 2); + finalPos.setY(pos.y()); + m_configDialog->setPosition(finalPos); + } + + m_configDialog->show(); +} + +void SnapConfiguration::setPosEnabled(bool enabled) +{ + if (enabled != m_positionEnabled) { + m_positionEnabled = enabled; + m_changes = true; + emit posEnabledChanged(); + } +} + +void SnapConfiguration::setRotEnabled(bool enabled) +{ + if (enabled != m_rotationEnabled) { + m_rotationEnabled = enabled; + m_changes = true; + emit rotEnabledChanged(); + } +} + +void SnapConfiguration::setScaleEnabled(bool enabled) +{ + if (enabled != m_scaleEnabled) { + m_scaleEnabled = enabled; + m_changes = true; + emit scaleEnabledChanged(); + } +} + +void SnapConfiguration::setAbsolute(bool enabled) +{ + if (enabled != m_absolute) { + m_absolute = enabled; + m_changes = true; + emit absoluteChanged(); + } +} + +void SnapConfiguration::setPosInt(double value) +{ + if (value != m_positionInterval) { + m_positionInterval = value; + m_changes = true; + emit posIntChanged(); + } +} + +void SnapConfiguration::setRotInt(double value) +{ + if (value != m_rotationInterval) { + m_rotationInterval = value; + m_changes = true; + emit rotIntChanged(); + } +} + +void SnapConfiguration::setScaleInt(double value) +{ + if (value != m_scaleInterval) { + m_scaleInterval = value; + m_changes = true; + emit scaleIntChanged(); + } +} + +void SnapConfiguration::cleanup() +{ + delete m_configDialog; +} + +void SnapConfiguration::cancel() +{ + if (!m_configDialog.isNull() && m_configDialog->isVisible()) + m_configDialog->close(); + + deleteLater(); +} + +bool SnapConfiguration::eventFilter(QObject *obj, QEvent *event) +{ + // Closing dialog always applies the changes + + if (obj == m_configDialog) { + if (event->type() == QEvent::FocusOut) { + apply(); + } else if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) + apply(); + } else if (event->type() == QEvent::Close) { + apply(); + } + } + + return QObject::eventFilter(obj, event); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h new file mode 100644 index 00000000000..ae1a4a0d93b --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QQuickView; +class QPoint; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Edit3DView; + +inline constexpr AuxiliaryDataKeyView edit3dSnapPosProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapPos3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapPosIntProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapPosInt3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapAbsProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapAbs3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapRotProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapRot3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapRotIntProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapRotInt3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapScaleProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapScale3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapScaleIntProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapScaleInt3d"}; + +class SnapConfiguration : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool posEnabled READ posEnabled WRITE setPosEnabled NOTIFY posEnabledChanged) + Q_PROPERTY(bool rotEnabled READ rotEnabled WRITE setRotEnabled NOTIFY rotEnabledChanged) + Q_PROPERTY(bool scaleEnabled READ scaleEnabled WRITE setScaleEnabled NOTIFY scaleEnabledChanged) + Q_PROPERTY(bool absolute READ absolute WRITE setAbsolute NOTIFY absoluteChanged) + Q_PROPERTY(double posInt READ posInt WRITE setPosInt NOTIFY posIntChanged) + Q_PROPERTY(double rotInt READ rotInt WRITE setRotInt NOTIFY rotIntChanged) + Q_PROPERTY(double scaleInt READ scaleInt WRITE setScaleInt NOTIFY scaleIntChanged) + +public: + SnapConfiguration(Edit3DView *view); + ~SnapConfiguration(); + + Q_INVOKABLE void resetDefaults(); + + void cancel(); + void apply(); + + void showConfigDialog(const QPoint &pos); + + void setPosEnabled(bool enabled); + bool posEnabled() const { return m_positionEnabled; } + void setRotEnabled(bool enabled); + bool rotEnabled() const { return m_rotationEnabled; } + void setScaleEnabled(bool enabled); + bool scaleEnabled() const { return m_scaleEnabled; } + void setAbsolute(bool enabled); + bool absolute() const { return m_absolute; } + + void setPosInt(double value); + double posInt() const { return m_positionInterval; } + void setRotInt(double value); + double rotInt() const { return m_rotationInterval; } + void setScaleInt(double value); + double scaleInt() const { return m_scaleInterval; } + + constexpr static double defaultPosInt = 50.; + constexpr static double defaultRotInt = 5.; + constexpr static double defaultScaleInt = 10.; + +signals: + void posEnabledChanged(); + void rotEnabledChanged(); + void scaleEnabledChanged(); + void absoluteChanged(); + void posIntChanged(); + void rotIntChanged(); + void scaleIntChanged(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void cleanup(); + + QPointer m_configDialog; + QPointer m_view; + bool m_positionEnabled = true; + bool m_rotationEnabled = true; + bool m_scaleEnabled = true; + bool m_absolute = true; + double m_positionInterval = 0.; + double m_rotationInterval = 0.; + double m_scaleInterval = 0.; + bool m_changes = false; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp new file mode 100644 index 00000000000..2e707636098 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -0,0 +1,122 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compositionnode.h" + +#include "effectutils.h" +#include "effectmakeruniformsmodel.h" +#include "uniform.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +CompositionNode::CompositionNode(const QString &qenPath) +{ + parse(qenPath); +} + +QString CompositionNode::fragmentCode() const +{ + return m_fragmentCode; +} + +QString CompositionNode::vertexCode() const +{ + return m_vertexCode; +} + +QString CompositionNode::description() const +{ + return m_description; +} + +QObject *CompositionNode::uniformsModel() +{ + return &m_unifomrsModel; +} + +QStringList CompositionNode::requiredNodes() const +{ + return m_requiredNodes; +} + +bool CompositionNode::isEnabled() const +{ + return m_isEnabled; +} + +void CompositionNode::setIsEnabled(bool newIsEnabled) +{ + if (newIsEnabled != m_isEnabled) { + m_isEnabled = newIsEnabled; + emit isEnabledChanged(); + } +} + +CompositionNode::NodeType CompositionNode::type() const +{ + return m_type; +} + +void CompositionNode::parse(const QString &qenPath) +{ + QFile qenFile(qenPath); + + if (!qenFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect file."); + return; + } + + QByteArray loadData = qenFile.readAll(); + QJsonParseError parseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); + if (parseError.error != QJsonParseError::NoError) { + QString error = QString("Error parsing the effect node: %1:").arg(qenPath); + QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); + qWarning() << qPrintable(error); + qWarning() << qPrintable(errorDetails); + return; + } + + QJsonObject json = jsonDoc.object().value("QEN").toObject(); + + int version = -1; + if (json.contains("version")) + version = json["version"].toInt(-1); + if (version != 1) { + QString error = QString("Error: Unknown effect version (%1)").arg(version); + qWarning() << qPrintable(error); + return; + } + + m_name = json.value("name").toString(); + m_description = json.value("description").toString(); + m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()); + m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); + + // parse properties + QJsonArray jsonProps = json.value("properties").toArray(); + for (const auto /*QJsonValueRef*/ &prop : jsonProps) + m_unifomrsModel.addUniform(new Uniform(prop.toObject())); + + // Seek through code to get tags + QStringList shaderCodeLines; + shaderCodeLines += m_vertexCode.split('\n'); + shaderCodeLines += m_fragmentCode.split('\n'); + for (const QString &codeLine : std::as_const(shaderCodeLines)) { + QString trimmedLine = codeLine.trimmed(); + if (trimmedLine.startsWith("@requires")) { + // Get the required node, remove "@requires" + QString l = trimmedLine.sliced(9).trimmed(); + QString nodeName = trimmedLine.sliced(10); + if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName)) + m_requiredNodes << nodeName; + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h new file mode 100644 index 00000000000..840abde96ea --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -0,0 +1,60 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "effectmakeruniformsmodel.h" + +#include + +namespace QmlDesigner { + +class CompositionNode : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) + Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) + +public: + enum NodeType { + SourceNode = 0, + DestinationNode, + CustomNode + }; + + CompositionNode(const QString &qenPath); + + QString fragmentCode() const; + QString vertexCode() const; + QString description() const; + + QObject *uniformsModel(); + + QStringList requiredNodes() const; + + NodeType type() const; + + bool isEnabled() const; + void setIsEnabled(bool newIsEnabled); + +signals: + void uniformsModelChanged(); + void isEnabledChanged(); + +private: + void parse(const QString &qenPath); + + QString m_name; + NodeType m_type = CustomNode; + QString m_fragmentCode; + QString m_vertexCode; + QString m_description; + QStringList m_requiredNodes; + bool m_isEnabled = true; + + EffectMakerUniformsModel m_unifomrsModel; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp new file mode 100644 index 00000000000..7ee8399c2cf --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakercontextobject.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +EffectMakerContextObject::EffectMakerContextObject(QQmlContext *context, QObject *parent) + : QObject(parent) + , m_qmlContext(context) +{ +} + +QString EffectMakerContextObject::convertColorToString(const QVariant &color) +{ + QString colorString; + QColor theColor; + if (color.canConvert(QVariant::Color)) { + theColor = color.value(); + } else if (color.canConvert(QVariant::Vector3D)) { + auto vec = color.value(); + theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z()); + } + + colorString = theColor.name(QColor::HexArgb); + + return colorString; +} + +// TODO: this method is used by the ColorEditor helper widget, check if at all needed? +QColor EffectMakerContextObject::colorFromString(const QString &colorString) +{ + return colorString; +} + +int EffectMakerContextObject::majorVersion() const +{ + return m_majorVersion; +} + +void EffectMakerContextObject::setMajorVersion(int majorVersion) +{ + if (m_majorVersion == majorVersion) + return; + + m_majorVersion = majorVersion; + + emit majorVersionChanged(); +} + +void EffectMakerContextObject::setStateName(const QString &newStateName) +{ + if (newStateName == m_stateName) + return; + + m_stateName = newStateName; + emit stateNameChanged(); +} + +void EffectMakerContextObject::setAllStateNames(const QStringList &allStates) +{ + if (allStates == m_allStateNames) + return; + + m_allStateNames = allStates; + emit allStateNamesChanged(); +} + +void EffectMakerContextObject::setIsBaseState(bool newIsBaseState) +{ + if (newIsBaseState == m_isBaseState) + return; + + m_isBaseState = newIsBaseState; + emit isBaseStateChanged(); +} + +void EffectMakerContextObject::setSelectionChanged(bool newSelectionChanged) +{ + if (newSelectionChanged == m_selectionChanged) + return; + + m_selectionChanged = newSelectionChanged; + emit selectionChangedChanged(); +} + +void EffectMakerContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) +{ + if (newBackendValues == m_backendValues) + return; + + m_backendValues = newBackendValues; + emit backendValuesChanged(); +} + +void EffectMakerContextObject::setModel(Model *model) +{ + m_model = model; +} + +void EffectMakerContextObject::triggerSelectionChanged() +{ + setSelectionChanged(!m_selectionChanged); +} + +void EffectMakerContextObject::hideCursor() +{ + if (QApplication::overrideCursor()) + return; + + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWidget *w = QApplication::activeWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void EffectMakerContextObject::restoreCursor() +{ + if (!QApplication::overrideCursor()) + return; + + QApplication::restoreOverrideCursor(); + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void EffectMakerContextObject::holdCursorInPlace() +{ + if (!QApplication::overrideCursor()) + return; + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int EffectMakerContextObject::devicePixelRatio() +{ + if (QWidget *w = QApplication::activeWindow()) + return w->devicePixelRatio(); + + return 1; +} + +QStringList EffectMakerContextObject::allStatesForId(const QString &id) +{ + if (m_model && m_model->rewriterView()) { + const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); + if (node.isValid()) + return node.allStateNames(); + } + + return {}; +} + +bool EffectMakerContextObject::isBlocked(const QString &) const +{ + return false; +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h new file mode 100644 index 00000000000..e27957f4ece --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h @@ -0,0 +1,101 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +class EffectMakerContextObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged) + Q_PROPERTY(QStringList allStateNames READ allStateNames WRITE setAllStateNames NOTIFY allStateNamesChanged) + Q_PROPERTY(int possibleTypeIndex READ possibleTypeIndex NOTIFY possibleTypeIndexChanged) + + Q_PROPERTY(bool isBaseState READ isBaseState WRITE setIsBaseState NOTIFY isBaseStateChanged) + Q_PROPERTY(bool selectionChanged READ selectionChanged WRITE setSelectionChanged NOTIFY selectionChangedChanged) + + Q_PROPERTY(int majorVersion READ majorVersion WRITE setMajorVersion NOTIFY majorVersionChanged) + + Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) + +public: + EffectMakerContextObject(QQmlContext *context, QObject *parent = nullptr); + + QString stateName() const { return m_stateName; } + QStringList allStateNames() const { return m_allStateNames; } + int possibleTypeIndex() const { return m_possibleTypeIndex; } + + bool isBaseState() const { return m_isBaseState; } + bool selectionChanged() const { return m_selectionChanged; } + + QQmlPropertyMap *backendValues() const { return m_backendValues; } + + Q_INVOKABLE QString convertColorToString(const QVariant &color); + Q_INVOKABLE QColor colorFromString(const QString &colorString); + + Q_INVOKABLE void hideCursor(); + Q_INVOKABLE void restoreCursor(); + Q_INVOKABLE void holdCursorInPlace(); + + Q_INVOKABLE int devicePixelRatio(); + + Q_INVOKABLE QStringList allStatesForId(const QString &id); + + Q_INVOKABLE bool isBlocked(const QString &propName) const; + + int majorVersion() const; + void setMajorVersion(int majorVersion); + + void setStateName(const QString &newStateName); + void setAllStateNames(const QStringList &allStates); + void setIsBaseState(bool newIsBaseState); + void setSelectionChanged(bool newSelectionChanged); + void setBackendValues(QQmlPropertyMap *newBackendValues); + void setModel(QmlDesigner::Model *model); + + void triggerSelectionChanged(); + +signals: + void stateNameChanged(); + void allStateNamesChanged(); + void possibleTypeIndexChanged(); + void isBaseStateChanged(); + void selectionChangedChanged(); + void backendValuesChanged(); + void majorVersionChanged(); + +private: + void updatePossibleTypeIndex(); + + QQmlContext *m_qmlContext = nullptr; + + QString m_stateName; + QStringList m_allStateNames; + int m_possibleTypeIndex = -1; + QString m_currentType; + + int m_majorVersion = 1; + + QQmlPropertyMap *m_backendValues = nullptr; + Model *m_model = nullptr; + + QPoint m_lastPos; + + bool m_isBaseState = false; + bool m_selectionChanged = false; +}; + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp new file mode 100644 index 00000000000..42f0977f7dc --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -0,0 +1,763 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakermodel.h" + +#include "compositionnode.h" +#include "syntaxhighlighterdata.h" +#include "uniform.h" + +#include +#include + +#include + +namespace QmlDesigner { + +EffectMakerModel::EffectMakerModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "nodeName"; + roles[EnabledRole] = "nodeEnabled"; + roles[UniformsRole] = "nodeUniformsModel"; + return roles; +} + +int EffectMakerModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_nodes.count(); +} + +QVariant EffectMakerModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_nodes.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + return m_nodes.at(index.row())->property(roleNames().value(role)); +} + +bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || !roleNames().contains(role)) + return false; + + if (role == EnabledRole) { + m_nodes.at(index.row())->setIsEnabled(value.toBool()); + emit dataChanged(index, index, {role}); + } + + return true; +} + +void EffectMakerModel::setIsEmpty(bool val) +{ + if (m_isEmpty != val) { + m_isEmpty = val; + emit isEmptyChanged(); + } +} + +void EffectMakerModel::addNode(const QString &nodeQenPath) +{ + beginInsertRows({}, m_nodes.size(), m_nodes.size()); + auto *node = new CompositionNode(nodeQenPath); + m_nodes.append(node); + endInsertRows(); + + setIsEmpty(false); +} + +void EffectMakerModel::moveNode(int fromIdx, int toIdx) +{ + if (fromIdx == toIdx) + return; + + int toIdxAdjusted = fromIdx < toIdx ? toIdx + 1 : toIdx; // otherwise beginMoveRows() crashes + beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted); + m_nodes.move(fromIdx, toIdx); + endMoveRows(); +} + +void EffectMakerModel::removeNode(int idx) +{ + beginRemoveRows({}, idx, idx); + CompositionNode *node = m_nodes.at(idx); + m_nodes.removeAt(idx); + delete node; + endRemoveRows(); + + if (m_nodes.isEmpty()) + setIsEmpty(true); +} + +QString EffectMakerModel::fragmentShader() const +{ + return m_fragmentShader; +} + +void EffectMakerModel::setFragmentShader(const QString &newFragmentShader) +{ + if (m_fragmentShader == newFragmentShader) + return; + + m_fragmentShader = newFragmentShader; +} + +QString EffectMakerModel::vertexShader() const +{ + return m_vertexShader; +} + +void EffectMakerModel::setVertexShader(const QString &newVertexShader) +{ + if (m_vertexShader == newVertexShader) + return; + + m_vertexShader = newVertexShader; +} + +const QList EffectMakerModel::allUniforms() +{ + QList uniforms = {}; + for (const auto &node : std::as_const(m_nodes)) + uniforms.append(static_cast(node->uniformsModel())->uniforms()); + return uniforms; +} + +const QString EffectMakerModel::getBufUniform() +{ + QList uniforms = allUniforms(); + QString s; + s += "layout(std140, binding = 0) uniform buf {\n"; + s += " mat4 qt_Matrix;\n"; + s += " float qt_Opacity;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) + s += " float iTime;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) + s += " int iFrame;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Resolution)) + s += " vec3 iResolution;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Mouse)) + s += " vec4 iMouse;\n"; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() != Uniform::Type::Sampler && uniform->type() != Uniform::Type::Define) { + QString type = Uniform::stringFromType(uniform->type()); + QString props = " " + type + " " + uniform->name() + ";\n"; + s += props; + } + } + s += "};\n"; + return s; +} + +const QString EffectMakerModel::getVSUniforms() +{ + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec4 qt_Vertex;\n"; + s += "layout(location = 1) in vec2 qt_MultiTexCoord0;\n"; + s += "layout(location = 0) out vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) out vec2 fragCoord;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + s += "out gl_PerVertex { vec4 gl_Position; };\n"; + s += '\n'; + return s; +} + +const QString EffectMakerModel::getFSUniforms() +{ + const QList uniforms = allUniforms(); + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) in vec2 fragCoord;\n"; + s += "layout(location = 0) out vec4 fragColor;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + + bool usesSource = m_shaderFeatures.enabled(ShaderFeatures::Source); + if (usesSource) + s += "layout(binding = 1) uniform sampler2D iSource;\n"; + + // Add sampler uniforms + int bindingIndex = usesSource ? 2 : 1; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() == Uniform::Type::Sampler) { + // Start index from 2, 1 is source item + QString props = QString("layout(binding = %1) uniform sampler2D %2") + .arg(bindingIndex).arg(uniform->name()); + s += props + ";\n"; + bindingIndex++; + } + } + s += '\n'; + if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + const int blurItems = 5; + for (int i = 1; i <= blurItems; i++) { + QString props = QString("layout(binding = %1) uniform sampler2D iSourceBlur%2") + .arg(bindingIndex).arg(QString::number(i)); + s += props + ";\n"; + bindingIndex++; + } + s += '\n'; + } + return s; +} + +// Detects common GLSL error messages and returns potential +// additional error information related to them. +QString EffectMakerModel::detectErrorMessage(const QString &errorMessage) +{ + static QHash nodeErrors { + { "'BLUR_HELPER_MAX_LEVEL' : undeclared identifier", "BlurHelper"}, + { "'iSourceBlur1' : undeclared identifier", "BlurHelper"}, + { "'hash23' : no matching overloaded function found", "NoiseHelper" }, + { "'HASH_BOX_SIZE' : undeclared identifier", "NoiseHelper" }, + { "'pseudo3dNoise' : no matching overloaded function found", "NoiseHelper" } + }; + + QString missingNodeError = QStringLiteral("Are you missing a %1 node?\n"); + QHash::const_iterator i = nodeErrors.constBegin(); + while (i != nodeErrors.constEnd()) { + if (errorMessage.contains(i.key())) + return missingNodeError.arg(i.value()); + ++i; + } + return QString(); +} + +// Return first error message (if any) +EffectError EffectMakerModel::effectError() const +{ + for (const EffectError &e : std::as_const(m_effectErrors)) { + if (!e.m_message.isEmpty()) + return e; + } + return {}; +} + +// Set the effect error message with optional type and lineNumber. +// Type comes from ErrorTypes, defaulting to common errors (-1). +// Note that type must match with UI editor tab index. +void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int lineNumber) +{ + EffectError error; + error.m_type = type; + if (type == 1 || type == 2) { + // For shaders, get the line number from baker output. + // Which is something like "ERROR: :15: message" + int glslErrorLineNumber = -1; + QStringList errorStringList = errorMessage.split(m_spaceReg, Qt::SkipEmptyParts); + if (errorStringList.size() >= 2) { + QString lineString = errorStringList.at(1).trimmed(); + if (lineString.size() >= 3) { + // String is ":[linenumber]:", get only the number. + glslErrorLineNumber = lineString.sliced(1, lineString.size() - 2).toInt(); + } + } + error.m_line = glslErrorLineNumber; + } else { + // For QML (and others) use given linenumber + error.m_line = lineNumber; + } + + QString additionalErrorInfo = detectErrorMessage(errorMessage); + error.m_message = additionalErrorInfo + errorMessage; + m_effectErrors.insert(type, error); + Q_EMIT effectErrorChanged(); +} + +void EffectMakerModel::resetEffectError(int type) +{ + if (m_effectErrors.contains(type)) { + m_effectErrors.remove(type); + Q_EMIT effectErrorChanged(); + } +} + +// Get value in QML format that used for exports +QString EffectMakerModel::valueAsString(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool) { + return uniform.value().toBool() ? QString("true") : QString("false"); + } else if (uniform.type() == Uniform::Type::Int) { + return QString::number(uniform.value().toInt()); + } else if (uniform.type() == Uniform::Type::Float) { + return QString::number(uniform.value().toDouble()); + } else if (uniform.type() == Uniform::Type::Vec2) { + QVector2D v2 = uniform.value().value(); + return QString("Qt.point(%1, %2)").arg(v2.x(), v2.y()); + } else if (uniform.type() == Uniform::Type::Vec3) { + QVector3D v3 = uniform.value().value(); + return QString("Qt.vector3d(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); + } else if (uniform.type() == Uniform::Type::Vec4) { + QVector4D v4 = uniform.value().value(); + return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); + } else if (uniform.type() == Uniform::Type::Color) { + QColor c = uniform.value().value(); + return QString("Qt.rgba(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else if (uniform.type() == Uniform::Type::Sampler) { + return getImageElementName(uniform); + } else if (uniform.type() == Uniform::Type::Define) { + return uniform.value().toString(); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Get value in QML binding that used for previews +QString EffectMakerModel::valueAsBinding(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool || uniform.type() == Uniform::Type::Int + || uniform.type() == Uniform::Type::Float || uniform.type() == Uniform::Type::Define) { + return "g_propertyData." + uniform.name(); + } else if (uniform.type() == Uniform::Type::Vec2) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + return QString("Qt.point(%1, %2)").arg(sx, sy); + } else if (uniform.type() == Uniform::Type::Vec3) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); + return QString("Qt.vector3d(%1, %2, %3)").arg(sx, sy, sz); + } else if (uniform.type() == Uniform::Type::Vec4) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); + QString sw = QString("g_propertyData.%1.w").arg(uniform.name()); + return QString("Qt.vector4d(%1, %2, %3, %4)").arg(sx, sy, sz, sw); + } else if (uniform.type() == Uniform::Type::Color) { + QString sr = QString("g_propertyData.%1.r").arg(uniform.name()); + QString sg = QString("g_propertyData.%1.g").arg(uniform.name()); + QString sb = QString("g_propertyData.%1.b").arg(uniform.name()); + QString sa = QString("g_propertyData.%1.a").arg(uniform.name()); + return QString("Qt.rgba(%1, %2, %3, %4)").arg(sr, sg, sb, sa); + } else if (uniform.type() == Uniform::Type::Sampler) { + return getImageElementName(uniform); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Get value in GLSL format that is used for non-exported const properties +QString EffectMakerModel::valueAsVariable(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool) { + return uniform.value().toBool() ? QString("true") : QString("false"); + } else if (uniform.type() == Uniform::Type::Int) { + return QString::number(uniform.value().toInt()); + } else if (uniform.type() == Uniform::Type::Float) { + return QString::number(uniform.value().toDouble()); + } else if (uniform.type() == Uniform::Type::Vec2) { + QVector2D v2 = uniform.value().value(); + return QString("vec2(%1, %2)").arg(v2.x(), v2.y()); + } else if (uniform.type() == Uniform::Type::Vec3) { + QVector3D v3 = uniform.value().value(); + return QString("vec3(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); + } else if (uniform.type() == Uniform::Type::Vec4) { + QVector4D v4 = uniform.value().value(); + return QString("vec4(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); + } else if (uniform.type() == Uniform::Type::Color) { + QColor c = uniform.value().value(); + return QString("vec4(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Return name for the image property Image element +QString EffectMakerModel::getImageElementName(const Uniform &uniform) +{ + // TODO + Q_UNUSED(uniform) + return {}; +} + +const QString EffectMakerModel::getConstVariables() +{ + const QList uniforms = allUniforms(); + QString s; + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + QString constValue = valueAsVariable(*uniform); + QString type = Uniform::stringFromType(uniform->type()); + s += QString("const %1 %2 = %3;\n").arg(type, uniform->name(), constValue); + } + if (!s.isEmpty()) + s += '\n'; + + return s; +} + +const QString EffectMakerModel::getDefineProperties() +{ + const QList uniforms = allUniforms(); + QString s; + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() == Uniform::Type::Define) { + QString defineValue = uniform->value().toString(); + s += QString("#define %1 %2\n").arg(uniform->name(), defineValue); + } + } + if (!s.isEmpty()) + s += '\n'; + + return s; +} + +int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) +{ + int index = -1; + int line = 0; + const QString tagString = QString("@%1").arg(tag); + for (const QString &s : code) { + auto st = s.trimmed(); + // Check if line or first non-space content of the line matches to tag + static auto spaceReg = QRegularExpression("\\s"); + auto firstSpace = st.indexOf(spaceReg); + QString firstWord = st; + if (firstSpace > 0) + firstWord = st.sliced(0, firstSpace); + if (firstWord == tagString) { + index = line; + break; + } + line++; + } + return index; +} + +QString EffectMakerModel::processVertexRootLine(const QString &line) +{ + QString output; + QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); + if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("out")) { + lineList.removeFirst(); + QString outLine = lineList.join(' '); + m_shaderVaryingVariables << outLine; + } else { + output = line + '\n'; + } + return output; +} + +QString EffectMakerModel::processFragmentRootLine(const QString &line) +{ + QString output; + QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); + // Just skip all "in" variables. It is enough to have "out" variable in vertex. + if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("in")) + return QString(); + output = line + '\n'; + return output; +} + +QStringList EffectMakerModel::getDefaultRootVertexShader() +{ + if (m_defaultRootVertexShader.isEmpty()) { + m_defaultRootVertexShader << "void main() {"; + m_defaultRootVertexShader << " texCoord = qt_MultiTexCoord0;"; + m_defaultRootVertexShader << " fragCoord = qt_Vertex.xy;"; + m_defaultRootVertexShader << " vec2 vertCoord = qt_Vertex.xy;"; + m_defaultRootVertexShader << " @nodes"; + m_defaultRootVertexShader << " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);"; + m_defaultRootVertexShader << "}"; + } + return m_defaultRootVertexShader; +} + +QStringList EffectMakerModel::getDefaultRootFragmentShader() +{ + if (m_defaultRootFragmentShader.isEmpty()) { + m_defaultRootFragmentShader << "void main() {"; + m_defaultRootFragmentShader << " fragColor = texture(iSource, texCoord);"; + m_defaultRootFragmentShader << " @nodes"; + m_defaultRootFragmentShader << " fragColor = fragColor * qt_Opacity;"; + m_defaultRootFragmentShader << "}"; + } + return m_defaultRootFragmentShader; +} + +// Remove all post-processing tags ("@tag") from the code. +// Except "@nodes" tag as that is handled later. +QStringList EffectMakerModel::removeTagsFromCode(const QStringList &codeLines) +{ + QStringList s; + for (const QString &line : codeLines) { + const auto trimmedLine = line.trimmed(); + if (!trimmedLine.startsWith('@') || trimmedLine.startsWith("@nodes")) { + s << line; + } else { + // Check if the tag is known + bool validTag = false; + const QList tags = SyntaxHighlighterData::reservedTagNames(); + QString firstWord = trimmedLine.split(m_spaceReg, Qt::SkipEmptyParts).first(); + for (const QByteArrayView &tag : tags) { + if (firstWord == QString::fromUtf8(tag)) { + validTag = true; + break; + } + } + if (!validTag) + setEffectError(QString("Unknown tag: %1").arg(trimmedLine), ErrorPreprocessor); + } + } + return s; +} + +QString EffectMakerModel::removeTagsFromCode(const QString &code) +{ + QStringList codeLines = removeTagsFromCode(code.split('\n')); + return codeLines.join('\n'); +} + +QString EffectMakerModel::getCustomShaderVaryings(bool outState) +{ + QString output; + QString direction = outState ? QStringLiteral("out") : QStringLiteral("in"); + int varLocation = m_shaderFeatures.enabled(ShaderFeatures::FragCoord) ? 2 : 1; + for (const QString &var : std::as_const(m_shaderVaryingVariables)) { + output += QString("layout(location = %1) %2 %3\n").arg(QString::number(varLocation), direction, var); + varLocation++; + } + return output; +} + +QString EffectMakerModel::generateVertexShader(bool includeUniforms) +{ + QString s; + + if (includeUniforms) + s += getVSUniforms(); + + // Remove tags when not generating for features check + const bool removeTags = includeUniforms; + + s += getDefineProperties(); + s += getConstVariables(); + + // When the node is complete, add shader code in correct nodes order + // split to root and main parts + QString s_root; + QString s_main; + QStringList s_sourceCode; + m_shaderVaryingVariables.clear(); + for (const CompositionNode *n : std::as_const(m_nodes)) { + if (!n->vertexCode().isEmpty() && n->isEnabled()) { + if (n->type() == CompositionNode::NodeType::SourceNode) { + s_sourceCode = n->vertexCode().split('\n'); + } else if (n->type() == CompositionNode::NodeType::CustomNode) { + const QStringList vertexCode = n->vertexCode().split('\n'); + int mainIndex = getTagIndex(vertexCode, QStringLiteral("main")); + int line = 0; + for (const QString &ss : vertexCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += QStringLiteral(" ") + ss + '\n'; + else if (line < mainIndex) + s_root += processVertexRootLine(ss); + line++; + } + } + } + } + + if (s_sourceCode.isEmpty()) { + // If source nodes doesn't contain any code, use default one + s_sourceCode << getDefaultRootVertexShader(); + } + + if (removeTags) { + s_sourceCode = removeTagsFromCode(s_sourceCode); + s_root = removeTagsFromCode(s_root); + s_main = removeTagsFromCode(s_main); + } + + s += getCustomShaderVaryings(true); + s += s_root + '\n'; + + int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); + int line = 0; + for (const QString &ss : std::as_const(s_sourceCode)) + s += (line++ == nodesIndex) ? s_main : ss + '\n'; + + return s; +} + +QString EffectMakerModel::generateFragmentShader(bool includeUniforms) +{ + QString s; + + if (includeUniforms) + s += getFSUniforms(); + + // Remove tags when not generating for features check + const bool removeTags = includeUniforms; + + s += getDefineProperties(); + s += getConstVariables(); + + // When the node is complete, add shader code in correct nodes order + // split to root and main parts + QString s_root; + QString s_main; + QStringList s_sourceCode; + for (const CompositionNode *n : std::as_const(m_nodes)) { + if (!n->fragmentCode().isEmpty() && n->isEnabled()) { + if (n->type() == CompositionNode::NodeType::SourceNode) { + s_sourceCode = n->fragmentCode().split('\n'); + } else if (n->type() == CompositionNode::NodeType::CustomNode) { + const QStringList fragmentCode = n->fragmentCode().split('\n'); + int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main")); + int line = 0; + for (const QString &ss : fragmentCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += QStringLiteral(" ") + ss + '\n'; + else if (line < mainIndex) + s_root += processFragmentRootLine(ss); + line++; + } + } + } + } + + if (s_sourceCode.isEmpty()) { + // If source nodes doesn't contain any code, use default one + s_sourceCode << getDefaultRootFragmentShader(); + } + + if (removeTags) { + s_sourceCode = removeTagsFromCode(s_sourceCode); + s_root = removeTagsFromCode(s_root); + s_main = removeTagsFromCode(s_main); + } + + s += getCustomShaderVaryings(false); + s += s_root + '\n'; + + int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); + int line = 0; + for (const QString &ss : std::as_const(s_sourceCode)) + s += (line++ == nodesIndex) ? s_main : ss + '\n'; + + return s; +} + +// Generates string of the custom properties (uniforms) into ShaderEffect component +// Also generates QML images elements for samplers. +void EffectMakerModel::updateCustomUniforms() +{ + QString exportedRootPropertiesString; + QString previewEffectPropertiesString; + QString exportedEffectPropertiesString; + + const QList uniforms = allUniforms(); + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + const bool isDefine = uniform->type() == Uniform::Type::Define; + QString type = Uniform::typeToProperty(uniform->type()); + QString value = valueAsString(*uniform); + QString bindedValue = valueAsBinding(*uniform); + // When user has set custom uniform value, use it as-is + if (uniform->useCustomValue()) { + value = uniform->customValue(); + bindedValue = value; + } + // Note: Define type properties appear also as QML properties (in preview) in case QML side + // needs to use them. This is used at least by BlurHelper BLUR_HELPER_MAX_LEVEL. + QString propertyName = isDefine ? uniform->name().toLower() : uniform->name(); + if (!uniform->useCustomValue() && !isDefine && !uniform->description().isEmpty()) { + // When exporting, add API documentation for properties + const QStringList descriptionLines = uniform->description().split('\n'); + for (const QString &line : descriptionLines) { + if (line.trimmed().isEmpty()) + exportedRootPropertiesString += QStringLiteral(" //\n"); + else + exportedRootPropertiesString += QStringLiteral(" // ") + line + '\n'; + } + } + QString valueString = value.isEmpty() ? QString() : QString(": %1").arg(value); + QString bindedValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); + // Custom values are not readonly, others inside the effect can be + QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); + previewEffectPropertiesString += " " + readOnly + "property " + type + " " + + propertyName + bindedValueString + '\n'; + // Define type properties are not added into exports + if (!isDefine) { + if (uniform->useCustomValue()) { + // Custom values are only inside the effect, with description comments + if (!uniform->description().isEmpty()) { + const QStringList descriptionLines = uniform->description().split('\n'); + for (const QString &line : descriptionLines) + exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; + } + exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + + "property " + type + " " + propertyName + + bindedValueString + '\n'; + } else { + // Custom values are not added into root + exportedRootPropertiesString += " property " + type + " " + propertyName + + valueString + '\n'; + exportedEffectPropertiesString += QStringLiteral(" ") + + readOnly + "property alias " + propertyName + + ": rootItem." + uniform->name() + '\n'; + } + } + } + + // See if any of the properties changed + // TODO +} + +void EffectMakerModel::bakeShaders() +{ + resetEffectError(ErrorPreprocessor); + if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { + setShadersUpToDate(true); + return; + } + + setShadersUpToDate(false); + + // First update the features based on shader content + // This will make sure that next calls to "generate" will produce correct uniforms. + m_shaderFeatures.update(generateVertexShader(false), generateFragmentShader(false), m_previewEffectPropertiesString); + + updateCustomUniforms(); + + // TODO: Shaders baking +} + +bool EffectMakerModel::shadersUpToDate() const +{ + return m_shadersUpToDate; +} + +void EffectMakerModel::setShadersUpToDate(bool UpToDate) +{ + if (m_shadersUpToDate == UpToDate) + return; + m_shadersUpToDate = UpToDate; + emit shadersUpToDateChanged(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h new file mode 100644 index 00000000000..58c6a93fd8f --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -0,0 +1,138 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "shaderfeatures.h" + +#include +#include +#include + +namespace QmlDesigner { + +class CompositionNode; +class Uniform; + +struct EffectError { + Q_GADGET + Q_PROPERTY(QString message MEMBER m_message) + Q_PROPERTY(int line MEMBER m_line) + Q_PROPERTY(int type MEMBER m_type) + +public: + QString m_message; + int m_line = -1; + int m_type = -1; +}; + +class EffectMakerModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) + +public: + EffectMakerModel(QObject *parent = nullptr); + + QHash roleNames() const override; + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + bool isEmpty() const { return m_isEmpty; } + void setIsEmpty(bool val); + + void addNode(const QString &nodeQenPath); + + Q_INVOKABLE void moveNode(int fromIdx, int toIdx); + Q_INVOKABLE void removeNode(int idx); + + bool shadersUpToDate() const; + void setShadersUpToDate(bool newShadersUpToDate); + + QString fragmentShader() const; + void setFragmentShader(const QString &newFragmentShader); + QString vertexShader() const; + void setVertexShader(const QString &newVertexShader); + +signals: + void isEmptyChanged(); + void selectedIndexChanged(int idx); + void effectErrorChanged(); + void shadersUpToDateChanged(); + +private: + enum Roles { + NameRole = Qt::UserRole + 1, + EnabledRole, + UniformsRole + }; + + enum ErrorTypes { + ErrorCommon = -1, + ErrorQMLParsing, + ErrorVert, + ErrorFrag, + ErrorQMLRuntime, + ErrorPreprocessor + }; + + bool isValidIndex(int idx) const; + + const QList allUniforms(); + + const QString getBufUniform(); + const QString getVSUniforms(); + const QString getFSUniforms(); + + QString detectErrorMessage(const QString &errorMessage); + EffectError effectError() const; + void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); + void resetEffectError(int type); + + QString valueAsString(const Uniform &uniform); + QString valueAsBinding(const Uniform &uniform); + QString valueAsVariable(const Uniform &uniform); + QString getImageElementName(const Uniform &uniform); + const QString getConstVariables(); + const QString getDefineProperties(); + int getTagIndex(const QStringList &code, const QString &tag); + QString processVertexRootLine(const QString &line); + QString processFragmentRootLine(const QString &line); + QStringList getDefaultRootVertexShader(); + QStringList getDefaultRootFragmentShader(); + QStringList removeTagsFromCode(const QStringList &codeLines); + QString removeTagsFromCode(const QString &code); + QString getCustomShaderVaryings(bool outState); + QString generateVertexShader(bool includeUniforms = true); + QString generateFragmentShader(bool includeUniforms = true); + void updateCustomUniforms(); + void bakeShaders(); + + QList m_nodes; + + int m_selectedIndex = -1; + bool m_isEmpty = true; + // True when shaders haven't changed since last baking + bool m_shadersUpToDate = true; + QMap m_effectErrors; + ShaderFeatures m_shaderFeatures; + QStringList m_shaderVaryingVariables; + QString m_fragmentShader; + QString m_vertexShader; + QStringList m_defaultRootVertexShader; + QStringList m_defaultRootFragmentShader; + // Used in exported QML, at root of the file + QString m_exportedRootPropertiesString; + // Used in exported QML, at ShaderEffect component of the file + QString m_exportedEffectPropertiesString; + // Used in preview QML, at ShaderEffect component of the file + QString m_previewEffectPropertiesString; + + const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp new file mode 100644 index 00000000000..521e3e7ce21 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -0,0 +1,107 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakernodesmodel.h" + +#include + +#include + +namespace QmlDesigner { + +EffectMakerNodesModel::EffectMakerNodesModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerNodesModel::roleNames() const +{ + QHash roles; + roles[CategoryNameRole] = "categoryName"; + roles[CategoryNodesRole] = "categoryNodes"; + + return roles; +} + +int EffectMakerNodesModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_categories.count(); +} + +QVariant EffectMakerNodesModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_categories.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + return m_categories.at(index.row())->property(roleNames().value(role)); +} + +void EffectMakerNodesModel::findNodesPath() +{ + if (m_nodesPath.exists() || m_probeNodesDir) + return; + + QDir nodesDir; + + if (!qEnvironmentVariable("EFFECT_MAKER_NODES_PATH").isEmpty()) + nodesDir.setPath(qEnvironmentVariable("EFFECT_MAKER_NODES_PATH")); + else if (Utils::HostOsInfo::isMacHost()) + nodesDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/effect_maker_nodes"); + + // search for nodesDir from exec dir and up + if (nodesDir.dirName() == ".") { + m_probeNodesDir = true; // probe only once + nodesDir.setPath(QCoreApplication::applicationDirPath()); + while (!nodesDir.cd("effect_maker_nodes") && nodesDir.cdUp()) + ; // do nothing + + if (nodesDir.dirName() != "effect_maker_nodes") // bundlePathDir not found + return; + } + + m_nodesPath = Utils::FilePath::fromString(nodesDir.path()); +} + +void EffectMakerNodesModel::loadModel() +{ + findNodesPath(); + + if (!m_nodesPath.exists()) { + qWarning() << __FUNCTION__ << "Effects not found."; + return; + } + + QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); + while (itCategories.hasNext()) { + itCategories.next(); + + if (itCategories.fileName() == "images" || itCategories.fileName() == "common") + continue; + + QString catName = itCategories.fileName(); + + QList effects = {}; + Utils::FilePath categoryPath = m_nodesPath.resolvePath(itCategories.fileName()); + QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files); + while (itEffects.hasNext()) { + itEffects.next(); + effects.push_back(new EffectNode(itEffects.filePath())); + } + + catName[0] = catName[0].toUpper(); // capitalize first letter + EffectNodesCategory *category = new EffectNodesCategory(catName, effects); + m_categories.push_back(category); + } + + resetModel(); +} + +void EffectMakerNodesModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h new file mode 100644 index 00000000000..5ed702f84be --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "effectnodescategory.h" + +#include + +#include + +namespace QmlDesigner { + +class EffectMakerNodesModel : public QAbstractListModel +{ + Q_OBJECT + + enum Roles { + CategoryNameRole = Qt::UserRole + 1, + CategoryNodesRole + }; + +public: + EffectMakerNodesModel(QObject *parent = nullptr); + + QHash roleNames() const override; + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + void loadModel(); + void resetModel(); + + QList categories() const { return m_categories; } + +private: + void findNodesPath(); + + QList m_categories; + Utils::FilePath m_nodesPath; + bool m_probeNodesDir = false; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp new file mode 100644 index 00000000000..dac01905b67 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakeruniformsmodel.h" + +#include "uniform.h" + +#include + +namespace QmlDesigner { + +EffectMakerUniformsModel::EffectMakerUniformsModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerUniformsModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "uniformName"; + roles[DescriptionRole] = "uniformDescription"; + roles[ValueRole] = "uniformValue"; + roles[BackendValueRole] = "uniformBackendValue"; + roles[DefaultValueRole] = "uniformDefaultValue"; + roles[MinValueRole] = "uniformMinValue"; + roles[MaxValueRole] = "uniformMaxValue"; + roles[TypeRole] = "uniformType"; + return roles; +} + +int EffectMakerUniformsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_uniforms.size(); +} + +QVariant EffectMakerUniformsModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_uniforms.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + return m_uniforms.at(index.row())->property(roleNames().value(role)); +} + +bool EffectMakerUniformsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || !roleNames().contains(role)) + return false; + + m_uniforms.at(index.row())->setValue(value); + emit dataChanged(index, index, {role}); + + return true; +} + +void EffectMakerUniformsModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void EffectMakerUniformsModel::addUniform(Uniform *uniform) +{ + beginInsertRows({}, m_uniforms.size(), m_uniforms.size()); + m_uniforms.append(uniform); + endInsertRows(); +} + +QList EffectMakerUniformsModel::uniforms() const +{ + return m_uniforms; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h new file mode 100644 index 00000000000..1d69d6d1b27 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +class Uniform; + +class EffectMakerUniformsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + EffectMakerUniformsModel(QObject *parent = nullptr); + + QHash roleNames() const override; + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + void resetModel(); + + void addUniform(Uniform *uniform); + + QList uniforms() const; + +private: + enum Roles { + NameRole = Qt::UserRole + 1, + DescriptionRole, + ValueRole, + BackendValueRole, + DefaultValueRole, + MaxValueRole, + MinValueRole, + TypeRole, + }; + + QList m_uniforms; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp new file mode 100644 index 00000000000..641b41a8ce2 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp @@ -0,0 +1,69 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakerview.h" + +#include "effectmakerwidget.h" +#include "effectmakernodesmodel.h" +#include "designmodecontext.h" +#include "nodeinstanceview.h" + +#include + +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +EffectMakerView::EffectMakerView(ExternalDependenciesInterface &externalDependencies) + : AbstractView{externalDependencies} +{ +} + +EffectMakerView::~EffectMakerView() +{} + +bool EffectMakerView::hasWidget() const +{ + return true; +} + +WidgetInfo EffectMakerView::widgetInfo() +{ + if (m_widget.isNull()) { + m_widget = new EffectMakerWidget{this}; + + auto context = new Internal::EffectMakerContext(m_widget.data()); + Core::ICore::addContextObject(context); + } + + return createWidgetInfo(m_widget.data(), "Effect Maker", WidgetInfo::LeftPane, 0, tr("Effect Maker")); +} + +void EffectMakerView::customNotification(const AbstractView * /*view*/, + const QString & /*identifier*/, + const QList & /*nodeList*/, + const QList & /*data*/) +{ + // TODO +} + +void EffectMakerView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + + // Add some dummy effects data + //m_widget->effectMakerModel()->setEffects({"Drop Shadow", "Colorize", "Fast Blue"}); // TODO + m_widget->effectMakerNodesModel()->loadModel(); + m_widget->initView(); +} + +void EffectMakerView::modelAboutToBeDetached(Model *model) +{ + AbstractView::modelAboutToBeDetached(model); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h new file mode 100644 index 00000000000..53e58acc67d --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "abstractview.h" + +#include + +namespace QmlDesigner { + +class EffectMakerWidget; + +class EffectMakerView : public AbstractView +{ +public: + EffectMakerView(ExternalDependenciesInterface &externalDependencies); + ~EffectMakerView() override; + + bool hasWidget() const override; + WidgetInfo widgetInfo() override; + + // AbstractView + void modelAttached(Model *model) override; + void modelAboutToBeDetached(Model *model) override; + +private: + void customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &data) override; + + QPointer m_widget; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp new file mode 100644 index 00000000000..f6f96bc886c --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -0,0 +1,145 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectmakerwidget.h" + +#include "effectmakercontextobject.h" +#include "effectmakermodel.h" +#include "effectmakernodesmodel.h" +#include "effectmakerview.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.h" +#include "qqmlcontext.h" +#include "theme.h" + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) + : m_effectMakerModel{new EffectMakerModel(this)} + , m_effectMakerNodesModel{new EffectMakerNodesModel(this)} + , m_effectMakerView(view) + , m_quickWidget{new StudioQuickWidget(this)} +{ + setWindowTitle(tr("Effect Maker", "Title of effect maker widget")); + setMinimumWidth(250); + + m_quickWidget->quickWidget()->installEventFilter(this); + + // create the inner widget + m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_EFFECT_MAKER); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + Theme::setupTheme(m_quickWidget->engine()); + m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_quickWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + + auto layout = new QHBoxLayout(this); + layout->setContentsMargins({}); + layout->setSpacing(0); + layout->addWidget(m_quickWidget.data()); + + setStyleSheet(Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + + QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_EFFECTMAKER_TIME); + + auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); + map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, + {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, + {"rootView", QVariant::fromValue(this)}}); +} + + +bool EffectMakerWidget::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_UNUSED(event) + + // TODO + + return false; +} + +void EffectMakerWidget::contextHelp(const Core::IContext::HelpCallback &callback) const +{ + Q_UNUSED(callback) +} + +StudioQuickWidget *EffectMakerWidget::quickWidget() const +{ + return m_quickWidget.data(); +} + +QPointer EffectMakerWidget::effectMakerModel() const +{ + return m_effectMakerModel; +} + +QPointer EffectMakerWidget::effectMakerNodesModel() const +{ + return m_effectMakerNodesModel; +} + +void EffectMakerWidget::addEffectNode(const QString &nodeQenPath) +{ + m_effectMakerModel->addNode(nodeQenPath); +} + +void EffectMakerWidget::focusSection(int section) +{ + Q_UNUSED(section) +} + +QSize EffectMakerWidget::sizeHint() const +{ + return {420, 420}; +} + +QString EffectMakerWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/effectMakerQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/effectMakerQmlSources").toString(); +} + +void EffectMakerWidget::initView() +{ + auto ctxObj = new EffectMakerContextObject(m_quickWidget->rootContext()); + m_quickWidget->rootContext()->setContextObject(ctxObj); + + m_backendModelNode.setup(m_effectMakerView->rootModelNode()); + m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +void EffectMakerWidget::reloadQmlSource() +{ + const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml"; + QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return); + m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath)); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h new file mode 100644 index 00000000000..d59318eb459 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -0,0 +1,59 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmlmodelnodeproxy.h" + +#include + +#include + +class StudioQuickWidget; + +namespace QmlDesigner { + +class EffectMakerView; +class EffectMakerModel; +class EffectMakerNodesModel; + +class EffectMakerWidget : public QFrame +{ + Q_OBJECT + +public: + EffectMakerWidget(EffectMakerView *view); + ~EffectMakerWidget() = default; + + void contextHelp(const Core::IContext::HelpCallback &callback) const; + + static QString qmlSourcesPath(); + void clearSearchFilter(); + + void delayedUpdateModel(); + void updateModel(); + void initView(); + + StudioQuickWidget *quickWidget() const; + QPointer effectMakerModel() const; + QPointer effectMakerNodesModel() const; + + Q_INVOKABLE void addEffectNode(const QString &nodeQenPath); + Q_INVOKABLE void focusSection(int section); + + QSize sizeHint() const override; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void reloadQmlSource(); + + QPointer m_effectMakerModel; + QPointer m_effectMakerNodesModel; + QPointer m_effectMakerView; + QPointer m_quickWidget; + QmlModelNodeProxy m_backendModelNode; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp new file mode 100644 index 00000000000..08d11925f56 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -0,0 +1,42 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectnode.h" + +#include +#include + +namespace QmlDesigner { + +EffectNode::EffectNode(const QString &qenPath) + : m_qenPath(qenPath) +{ + const QFileInfo fileInfo = QFileInfo(qenPath); + m_name = fileInfo.baseName(); + + QString iconPath = QStringLiteral("%1/icon/%2.svg").arg(fileInfo.absolutePath(), m_name); + if (!QFileInfo::exists(iconPath)) { + QDir parentDir = QDir(fileInfo.absolutePath()); + parentDir.cdUp(); + + iconPath = QStringLiteral("%1/%2").arg(parentDir.path(), "placeholder.svg"); + } + m_iconPath = QUrl::fromLocalFile(iconPath); +} + +QString EffectNode::name() const +{ + return m_name; +} + +QString EffectNode::description() const +{ + return m_description; +} + +QString EffectNode::qenPath() const +{ + return m_qenPath; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h new file mode 100644 index 00000000000..823fe092db0 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class EffectNode : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + Q_PROPERTY(QString nodeDescription MEMBER m_description CONSTANT) + Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT) + Q_PROPERTY(QString nodeQenPath MEMBER m_qenPath CONSTANT) + +public: + EffectNode(const QString &qenPath); + + QString name() const; + QString description() const; + QString qenPath() const; + +private: + QString m_name; + QString m_description; + QString m_qenPath; + QUrl m_iconPath; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp new file mode 100644 index 00000000000..36a8f0a0d06 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectnodescategory.h" + +namespace QmlDesigner { + +EffectNodesCategory::EffectNodesCategory(const QString &name, const QList &nodes) + : m_name(name), + m_categoryNodes(nodes) {} + +QString EffectNodesCategory::name() const +{ + return m_name; +} + +QList EffectNodesCategory::nodes() const +{ + return m_categoryNodes; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h new file mode 100644 index 00000000000..ba7d6868bc6 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "effectnode.h" + +#include + +namespace QmlDesigner { + +class EffectNodesCategory : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString categoryName MEMBER m_name CONSTANT) + Q_PROPERTY(QList categoryNodes MEMBER m_categoryNodes CONSTANT) + +public: + EffectNodesCategory(const QString &name, const QList &nodes); + + QString name() const; + QList nodes() const; + +private: + QString m_name; + QList m_categoryNodes; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp b/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp new file mode 100644 index 00000000000..8f45b9a1370 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp @@ -0,0 +1,23 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectutils.h" + +#include + +namespace QmlDesigner { + +QString EffectUtils::codeFromJsonArray(const QJsonArray &codeArray) +{ + if (codeArray.isEmpty()) + return {}; + + QString codeString; + for (const auto &element : codeArray) + codeString += element.toString() + '\n'; + + codeString.chop(1); // Remove last '\n' + return codeString; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.h b/src/plugins/qmldesigner/components/effectmaker/effectutils.h new file mode 100644 index 00000000000..0abe4d64e6b --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectutils.h @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +QT_FORWARD_DECLARE_CLASS(QJsonArray) + +namespace QmlDesigner { + +class EffectUtils +{ +public: + EffectUtils() = delete; + + static QString codeFromJsonArray(const QJsonArray &codeArray); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp new file mode 100644 index 00000000000..755b203d23c --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "shaderfeatures.h" +#include +#include + +namespace QmlDesigner { + +ShaderFeatures::ShaderFeatures() +{ +} + +// Browse the shaders and check which features are used in them. +void ShaderFeatures::update(const QString &vs, const QString &fs, const QString &qml) +{ + QStringList vsList = vs.split("\n"); + QStringList fsList = fs.split("\n"); + + const QStringList code = vsList + fsList; + Features newFeatures = {}; + m_gridMeshWidth = 1; + m_gridMeshHeight = 1; + for (const QString &line : code) + checkLine(line, newFeatures); + + // iTime may also be used in QML side, without being used in shaders. + // In this case enable the time helpers creation. + if (qml.contains("iTime")) + newFeatures.setFlag(Time, true); + + if (newFeatures != m_enabledFeatures) + m_enabledFeatures = newFeatures; +} + +bool ShaderFeatures::enabled(ShaderFeatures::Feature feature) const +{ + return m_enabledFeatures.testFlag(feature); +} + +void ShaderFeatures::checkLine(const QString &line, Features &features) +{ + if (line.contains("iTime")) + features.setFlag(Time, true); + + if (line.contains("iFrame")) + features.setFlag(Frame, true); + + if (line.contains("iResolution")) + features.setFlag(Resolution, true); + + if (line.contains("iSource")) + features.setFlag(Source, true); + + if (line.contains("iMouse")) + features.setFlag(Mouse, true); + + if (line.contains("fragCoord")) + features.setFlag(FragCoord, true); + + if (line.contains("@mesh")) { + // Get the mesh size, remove "@mesh" + QString l = line.trimmed().sliced(5); + QStringList list = l.split(QLatin1Char(',')); + if (list.size() >= 2) { + int w = list.at(0).trimmed().toInt(); + int h = list.at(1).trimmed().toInt(); + // Set size to max values + m_gridMeshWidth = std::max(m_gridMeshWidth, w); + m_gridMeshHeight = std::max(m_gridMeshHeight, h); + } + // If is bigger than default (1, 1), set the feature + if (m_gridMeshWidth > 1 || m_gridMeshHeight > 1) + features.setFlag(GridMesh, true); + } + if (line.contains("@blursources")) + features.setFlag(BlurSources, true); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h new file mode 100644 index 00000000000..35fb507066d --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class ShaderFeatures +{ +public: + enum Feature { + Time = 1 << 0, + Frame = 1 << 1, + Resolution = 1 << 2, + Source = 1 << 3, + Mouse = 1 << 4, + FragCoord = 1 << 5, + GridMesh = 1 << 6, + BlurSources = 1 << 7 + }; + Q_DECLARE_FLAGS(Features, Feature) + + ShaderFeatures(); + void update(const QString &vs, const QString &fs, const QString &qml); + + bool enabled(ShaderFeatures::Feature feature) const; + +private: + void checkLine(const QString &line, ShaderFeatures::Features &features); + ShaderFeatures::Features m_enabledFeatures; + int m_gridMeshWidth = 1; + int m_gridMeshHeight = 1; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ShaderFeatures::Features) +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp new file mode 100644 index 00000000000..47020ed0b0f --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp @@ -0,0 +1,190 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "syntaxhighlighterdata.h" + +namespace QmlDesigner { + +static constexpr QByteArrayView shader_arg_names[] { + { "gl_Position" }, + { "qt_MultiTexCoord0" }, + { "qt_Vertex" }, + { "qt_Matrix" }, + { "qt_Opacity" }, + { "vertCoord" }, + { "fragCoord" }, + { "texCoord" }, + { "fragColor" }, + { "iMouse" }, + { "iResolution" }, + { "iTime" }, + { "iFrame" }, + { "iSource" }, + { "iSourceBlur1" }, + { "iSourceBlur2" }, + { "iSourceBlur3" }, + { "iSourceBlur4" }, + { "iSourceBlur5" }, + { "iSourceBlur6" } +}; + +static constexpr QByteArrayView shader_tag_names[] { + { "@main" }, + { "@nodes" }, + { "@mesh" }, + { "@blursources" }, + { "@requires" } +}; + +// From https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.40.pdf +// Not including functions only available with compatibility profile +static constexpr QByteArrayView shader_function_names[] { + { "radians()" }, + { "degrees()" }, + { "sin()" }, + { "cos()" }, + { "tan()" }, + { "asin()" }, + { "acos()" }, + { "atan()" }, + { "sinh()" }, + { "cosh()" }, + { "tanh()" }, + { "asinh()" }, + { "acosh()" }, + { "atanh()" }, + { "pow()" }, + { "exp()" }, + { "log()" }, + { "exp2()" }, + { "log2()" }, + { "sqrt()" }, + { "inversesqrt()" }, + { "abs()" }, + { "sign()" }, + { "floor()" }, + { "trunc()" }, + { "round()" }, + { "roundEven()" }, + { "ceil()" }, + { "fract()" }, + { "mod()" }, + { "modf()" }, + { "min()" }, + { "max()" }, + { "clamp()" }, + { "mix()" }, + { "step()" }, + { "smoothstep()" }, + { "isnan()" }, + { "isinf()" }, + { "floatBitsToInt()" }, + { "intBitsToFloat()" }, + { "fma()" }, + { "frexp()" }, + { "ldexp()" }, + { "packUnorm2x16()" }, + { "packSnorm2x16()" }, + { "packUnorm4x8()" }, + { "packSnorm4x8()" }, + { "unpackUnorm2x16()" }, + { "unpackSnorm2x16()" }, + { "unpackUnorm4x8()" }, + { "unpackSnorm4x8()" }, + //{ "packDouble2x32()" }, // Not supported in HLSL + //{ "unpackDouble2x32()" }, + { "packHalf2x16()" }, + { "unpackHalf2x16()" }, + { "length()" }, + { "distance()" }, + { "dot()" }, + { "cross()" }, + { "normalize()" }, + { "faceforward()" }, + { "reflect()" }, + { "refract()" }, + { "matrixCompMult()" }, + { "outerProduct()" }, + { "transpose()" }, + { "determinant()" }, + { "inverse()" }, + { "lessThan()" }, + { "lessThanEqual()" }, + { "greaterThan()" }, + { "greaterThanEqual()" }, + { "equal()" }, + { "notEqual()" }, + { "any()" }, + { "all()" }, + { "not()" }, + //{ "uaddCarry()" }, // Extended arithmetic is only available from ESSL 310 + //{ "usubBorrow()" }, + //{ "umulExtended()" }, + //{ "imulExtended()" }, + { "bitfieldExtract()" }, + { "bitfieldInsert()" }, + { "bitfieldReverse()" }, + { "bitCount()" }, + { "findLSB()" }, + { "findMSB()" }, + { "textureSize()" }, + //{ "textureQueryLod()" }, // ImageQueryLod is only supported on MSL 2.2 and up. + //{ "textureQueryLevels()" }, // textureQueryLevels not supported in ES profile. + { "texture()" }, + { "textureProj()" }, + { "textureLod()" }, + { "textureOffset()" }, + { "texelFetch()" }, + { "texelFetchOffset()" }, + { "textureProjOffset()" }, + { "textureLodOffset()" }, + { "textureProjLod()" }, + { "textureProjLodOffset()" }, + { "textureGrad()" }, + { "textureGradOffset()" }, + { "textureProjGrad()" }, + { "textureProjGradOffset()" }, + //{ "textureGather()" }, // textureGather requires ESSL 310. + //{ "textureGatherOffset()" }, + //{ "textureGatherOffsets()" }, + //{ "atomicCounterIncrement()" }, // 'atomic counter types' : not allowed when using GLSL for Vulkan + //{ "atomicCounterDecrement()" }, + //{ "atomicCounter()" }, + //{ "atomicAdd()" }, // HLSL: interlocked targets must be groupshared or UAV elements + //{ "atomicMin()" }, + //{ "atomicMax()" }, + //{ "atomicAnd()" }, + //{ "atomicOr()" }, + //{ "atomicXor()" }, + //{ "atomicExchange()" }, + //{ "atomicCompSwap()" }, + { "dFdx()" }, + { "dFdy()" }, + { "fwidth()" } + //{ "interpolateAtCentroid()" }, // Pull-model interpolation requires MSL 2.3. + //{ "interpolateAtSample()" }, + //{ "interpolateAtOffset()" } +}; + +SyntaxHighlighterData::SyntaxHighlighterData() +{ +} + + +QList SyntaxHighlighterData::reservedArgumentNames() +{ + return { std::begin(shader_arg_names), std::end(shader_arg_names) }; +} + +QList SyntaxHighlighterData::reservedTagNames() +{ + return { std::begin(shader_tag_names), std::end(shader_tag_names) }; +} + +QList SyntaxHighlighterData::reservedFunctionNames() +{ + return { std::begin(shader_function_names), std::end(shader_function_names) }; +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h new file mode 100644 index 00000000000..6342ea094aa --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class SyntaxHighlighterData +{ +public: + SyntaxHighlighterData(); + + static QList reservedArgumentNames(); + static QList reservedTagNames(); + static QList reservedFunctionNames(); +}; + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp new file mode 100644 index 00000000000..8074c3cc95a --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -0,0 +1,324 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "uniform.h" + +#include "propertyeditorvalue.h" + +#include +#include +#include + +namespace QmlDesigner { + +Uniform::Uniform(const QJsonObject &propObj) +{ + QString value, defaultValue, minValue, maxValue; + + m_name = propObj.value("name").toString(); + m_description = propObj.value("description").toString(); + m_type = Uniform::typeFromString(propObj.value("type").toString()); + defaultValue = propObj.value("defaultValue").toString(); + + m_displayName = propObj.value("displayName").toString(); + if (m_displayName.isEmpty()) + m_displayName = m_name; + + if (m_type == Type::Sampler) { + if (!defaultValue.isEmpty()) + defaultValue = getResourcePath(defaultValue); + if (propObj.contains("enableMipmap")) + m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); + // Update the mipmap property + QString mipmapProperty = mipmapPropertyName(m_name); + } + if (propObj.contains("value")) { + value = propObj.value("value").toString(); + if (m_type == Type::Sampler && !value.isEmpty()) + value = getResourcePath(value); + } else { + // QEN files don't store the current value, so with those use default value + value = defaultValue; + } + minValue = propObj.value("minValue").toString(); + maxValue = propObj.value("maxValue").toString(); + + setValueData(value, defaultValue, minValue, maxValue); + + m_backendValue = new PropertyEditorValue(this); + m_backendValue->setValue(value); +} + +Uniform::Type Uniform::type() const +{ + return m_type; +} + +// String representation of the type for qml +QString Uniform::typeName() const +{ + return Uniform::stringFromType(m_type); +} + +QVariant Uniform::value() const +{ + return m_value; +} + +QVariant Uniform::backendValue() const +{ + return QVariant::fromValue(m_backendValue); +} + +void Uniform::setValue(const QVariant &newValue) +{ + if (m_value != newValue) { + m_value = newValue; + emit uniformValueChanged(); + } +} + +QVariant Uniform::defaultValue() const +{ + return m_defaultValue; +} + +QVariant Uniform::minValue() const +{ + return m_minValue; +} + +QVariant Uniform::maxValue() const +{ + return m_maxValue; +} + +QString Uniform::name() const +{ + return m_name; +} + +QString Uniform::description() const +{ + return m_description; +} + +QString Uniform::customValue() const +{ + return m_customValue; +} + +void Uniform::setCustomValue(const QString &newCustomValue) +{ + m_customValue = newCustomValue; +} + +bool Uniform::useCustomValue() const +{ + return m_useCustomValue; +} + +bool Uniform::enabled() const +{ + return m_enabled; +} + +void Uniform::setEnabled(bool newEnabled) +{ + m_enabled = newEnabled; +} + +bool Uniform::enableMipmap() const +{ + return m_enableMipmap; +} + +// Returns name for image mipmap property. +// e.g. "myImage" -> "myImageMipmap". +QString Uniform::mipmapPropertyName(const QString &name) const +{ + QString simplifiedName = name.simplified(); + simplifiedName = simplifiedName.remove(' '); + simplifiedName += "Mipmap"; + return simplifiedName; +} + +// Returns the boolean value of QJsonValue. It can be either boolean +// (true, false) or string ("true", "false"). Returns the defaultValue +// if QJsonValue is undefined, empty, or some other type. +bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue) +{ + if (jsonValue.isBool()) + return jsonValue.toBool(); + + if (jsonValue.isString()) + return jsonValue.toString().toLower() == "true"; + + return defaultValue; +} + +// Returns the path for a shader resource +// Used with sampler types +QString Uniform::getResourcePath(const QString &value) const +{ + Q_UNUSED(value) + //TODO + return {}; +} + +// Validation and setting values +void Uniform::setValueData(const QString &value, const QString &defaultValue, + const QString &minValue, const QString &maxValue) +{ + m_value = value.isEmpty() ? getInitializedVariant(false) : valueStringToVariant(value); + m_defaultValue = defaultValue.isEmpty() ? getInitializedVariant(false) + : valueStringToVariant(defaultValue); + m_minValue = minValue.isEmpty() ? getInitializedVariant(false) : valueStringToVariant(minValue); + m_maxValue = maxValue.isEmpty() ? getInitializedVariant(true) : valueStringToVariant(maxValue); +} + +// Initialize the value variant with correct type +QVariant Uniform::getInitializedVariant(bool maxValue) +{ + switch (m_type) { + case Uniform::Type::Bool: + return maxValue ? true : false; + case Uniform::Type::Int: + return maxValue ? 100 : 0; + case Uniform::Type::Float: + return maxValue ? 1.0 : 0.0; + case Uniform::Type::Vec2: + return maxValue ? QVector2D(1.0, 1.0) : QVector2D(0.0, 0.0); + case Uniform::Type::Vec3: + return maxValue ? QVector3D(1.0, 1.0, 1.0) : QVector3D(0.0, 0.0, 0.0); + case Uniform::Type::Vec4: + return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0); + case Uniform::Type::Color: + return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f); + default: + return QVariant(); + } +} + +QVariant Uniform::valueStringToVariant(const QString &value) +{ + QVariant variant; + switch (m_type) { + case Type::Bool: + variant = (value == "true"); + break; + case Type::Int: + case Type::Float: + variant = value; + break; + case Type::Vec2: { + QStringList list = value.split(QLatin1Char(',')); + if (list.size() >= 2) + variant = QVector2D(list.at(0).toDouble(), list.at(1).toDouble()); + } + break; + case Type::Vec3: { + QStringList list = value.split(QLatin1Char(',')); + if (list.size() >= 3) + variant = QVector3D(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble()); + } + break; + case Type::Vec4: { + QStringList list = value.split(QLatin1Char(',')); + if (list.size() >= 4) + variant = QVector4D(list.at(0).toDouble(), list.at(1).toDouble(), + list.at(2).toDouble(), list.at(3).toDouble()); + } + break; + case Type::Color: { + QStringList list = value.split(QLatin1Char(',')); + if (list.size() >= 4) + variant = QColor::fromRgbF(list.at(0).toDouble(), list.at(1).toDouble(), + list.at(2).toDouble(), list.at(3).toDouble()); + } + break; + case Type::Sampler: + variant = value; + break; + case Uniform::Type::Define: + variant = value; + break; + } + + return variant; +} + +QString Uniform::stringFromType(Uniform::Type type) +{ + if (type == Type::Bool) + return "bool"; + else if (type == Type::Int) + return "int"; + else if (type == Type::Float) + return "float"; + else if (type == Type::Vec2) + return "vec2"; + else if (type == Type::Vec3) + return "vec3"; + else if (type == Type::Vec4) + return "vec4"; + else if (type == Type::Color) + return "color"; + else if (type == Type::Sampler) + return "sampler2D"; + else if (type == Type::Define) + return "define"; + + qWarning() << QString("Unknown type"); + return "float"; +} + +Uniform::Type Uniform::typeFromString(const QString &typeString) +{ + if (typeString == "bool") + return Uniform::Type::Bool; + else if (typeString == "int") + return Uniform::Type::Int; + else if (typeString == "float") + return Uniform::Type::Float; + else if (typeString == "vec2") + return Uniform::Type::Vec2; + else if (typeString == "vec3") + return Uniform::Type::Vec3; + else if (typeString == "vec4") + return Uniform::Type::Vec4; + else if (typeString == "color") + return Uniform::Type::Color; + else if (typeString == "sampler2D") + return Uniform::Type::Sampler; + else if (typeString == "define") + return Uniform::Type::Define; + + qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1(); + return Uniform::Type::Float; +} + +QString Uniform::typeToProperty(Uniform::Type type) +{ + if (type == Uniform::Type::Bool) + return "bool"; + else if (type == Uniform::Type::Int) + return "int"; + else if (type == Uniform::Type::Float) + return "real"; + else if (type == Uniform::Type::Vec2) + return "point"; + else if (type == Uniform::Type::Vec3) + return "vector3d"; + else if (type == Uniform::Type::Vec4) + return "vector4d"; + else if (type == Uniform::Type::Color) + return "color"; + else if (type == Uniform::Type::Sampler || type == Uniform::Type::Define) + return "var"; + + qWarning() << QString("Unhandled const variable type: %1").arg(int(type)).toLatin1(); + return QString(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h new file mode 100644 index 00000000000..67699c5e53a --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -0,0 +1,108 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QColor) +QT_FORWARD_DECLARE_CLASS(QJsonObject) +QT_FORWARD_DECLARE_CLASS(QVector2D) + +namespace QmlDesigner { + +class PropertyEditorValue; + +class Uniform : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT) + Q_PROPERTY(QString uniformType READ typeName CONSTANT) + Q_PROPERTY(QString uniformDescription READ description CONSTANT) + Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) + Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged) + Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) + Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) + +public: + enum class Type + { + Bool, + Int, + Float, + Vec2, + Vec3, + Vec4, + Color, + Sampler, + Define + }; + + Uniform(const QJsonObject &props); + + Type type() const; + QString typeName() const; + + QVariant value() const; + void setValue(const QVariant &newValue); + + QVariant backendValue() const; + + QVariant defaultValue() const; + + QVariant minValue() const; + QVariant maxValue() const; + + QString name() const; + QString description() const; + + QString customValue() const; + void setCustomValue(const QString &newCustomValue); + bool useCustomValue() const; + + bool enabled() const; + void setEnabled(bool newEnabled); + + bool enableMipmap() const; + + static QString stringFromType(Uniform::Type type); + static Uniform::Type typeFromString(const QString &typeString); + static QString typeToProperty(Uniform::Type type); + +signals: + void uniformValueChanged(); + void uniformBackendValueChanged(); + +private: + QString mipmapPropertyName(const QString &name) const; + bool getBoolValue(const QJsonValue &jsonValue, bool defaultValue); + QString getResourcePath(const QString &value) const; + void setValueData(const QString &value, const QString &defaultValue, + const QString &minValue, const QString &maxValue); + + QVariant getInitializedVariant(bool maxValue); + QVariant valueStringToVariant(const QString &value); + + Type m_type; + QVariant m_value; + QVariant m_defaultValue; + QVariant m_minValue; + QVariant m_maxValue; + QString m_name; + QString m_displayName; + QString m_description; + QString m_customValue; + bool m_useCustomValue = false; + bool m_enabled = true; + bool m_enableMipmap = false; + PropertyEditorValue *m_backendValue = nullptr; + + bool operator==(const Uniform &rhs) const noexcept + { + return this->m_name == rhs.m_name; + } +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp index 18c16c28e7b..da3ba2981f2 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp @@ -186,12 +186,15 @@ void EventList::initialize(EventListPluginView *parent) if (!m_model) { QByteArray unqualifiedTypeName = "ListModel"; auto metaInfo = parent->model()->metaInfo(unqualifiedTypeName); - +#ifdef QDS_USE_PROJECTSTORAGE + m_model = Model::create(unqualifiedTypeName, -1, -1); +#else QByteArray fullTypeName = metaInfo.typeName(); int minorVersion = metaInfo.minorVersion(); int majorVersion = metaInfo.majorVersion(); m_model = Model::create(fullTypeName, majorVersion, minorVersion); +#endif m_model->setParent(parent); } diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp index 841e2c232c1..6d5753eb316 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp @@ -85,6 +85,9 @@ void EventListView::addEvent(const Event &event) executeInTransaction("EventListView::addEvent", [=]() { QByteArray unqualifiedTypeName = "ListElement"; +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode eventNode = createModelNode(unqualifiedTypeName, -1, -1); +#else auto metaInfo = model()->metaInfo(unqualifiedTypeName); QByteArray fullTypeName = metaInfo.typeName(); @@ -92,6 +95,7 @@ void EventListView::addEvent(const Event &event) int majorVersion = metaInfo.majorVersion(); ModelNode eventNode = createModelNode(fullTypeName, majorVersion, minorVersion); +#endif eventNode.variantProperty("eventId").setValue(event.eventId); if (!event.shortcut.isEmpty()) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index ef7ed1d52d4..2e7a5b2c887 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -291,7 +291,6 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view) fillLayout->addWidget(m_graphicsView.data()); QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); } @@ -549,6 +548,9 @@ void FormEditorWidget::exportAsImage(const QRectF &boundingRect) QImage FormEditorWidget::takeFormEditorScreenshot() { + if (!m_formEditorView->scene()->rootFormEditorItem()) + return {}; + const QRectF boundingRect = m_formEditorView->scene()->rootFormEditorItem()->boundingRect(); m_formEditorView->scene()->manipulatorLayerItem()->setVisible(false); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 600507823e9..638c57e0d90 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -63,23 +63,31 @@ namespace QmlDesigner { */ DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies) +#ifdef QDS_USE_PROJECTSTORAGE + : m_documentModel(Model::create(projectStorageDependencies, + "Item", + {Import::createLibraryImport("QtQuick")}, + {}, + std::make_unique())) +#else : m_documentModel( Model::create("QtQuick.Item", 1, 0, nullptr, std::make_unique())) +#endif , m_subComponentManager(new SubComponentManager(m_documentModel.get(), externalDependencies)) , m_rewriterView(new RewriterView(externalDependencies, RewriterView::Amend)) , m_documentLoaded(false) , m_currentTarget(nullptr) , m_projectStorageDependencies(projectStorageDependencies) , m_externalDependencies{externalDependencies} -{ -} +{} DesignDocument::~DesignDocument() = default; Model *DesignDocument::currentModel() const { - if (m_inFileComponentModel) + if (m_inFileComponentModel) { return m_inFileComponentModel.get(); + } return m_documentModel.get(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 99f07cb8a8a..0fb367cc3ea 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -166,23 +166,6 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo } } -void ItemLibraryAssetImporter::iconProcessFinished([[maybe_unused]] int exitCode, - [[maybe_unused]] QProcess::ExitStatus exitStatus) -{ - m_puppetProcess.reset(); - - int finishedCount = m_parseData.size() - m_puppetQueue.size(); - if (!m_puppetQueue.isEmpty()) - startNextIconProcess(); - - if (m_puppetQueue.isEmpty() && !m_puppetProcess) { - notifyProgress(100); - QTimer::singleShot(0, this, &ItemLibraryAssetImporter::finalizeQuick3DImport); - } else { - notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size())))); - } -} - void ItemLibraryAssetImporter::notifyFinished() { m_isImporting = false; @@ -277,18 +260,8 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa addWarning(tr("Skipped import of existing asset: \"%1\".").arg(pd.assetName)); return false; } else if (result == OverwriteResult::Update) { - // Add generated icons and existing source asset file, as those will always need - // to be overwritten + // Add existing source asset file, as that will always need to be overwritten QSet alwaysOverwrite; - QString iconPath = pd.targetDirPath + '/' + Constants::QUICK_3D_ASSET_ICON_DIR; - // Note: Despite the name, QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX is not a traditional file - // suffix. It's guaranteed to be in the generated icon filename, though. - QStringList filters {QStringLiteral("*%1*").arg(Constants::QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX)}; - QDirIterator iconIt(iconPath, filters, QDir::Files); - while (iconIt.hasNext()) { - iconIt.next(); - alwaysOverwrite.insert(iconIt.fileInfo().absoluteFilePath()); - } alwaysOverwrite.insert(sourceSceneTargetFilePath(pd)); alwaysOverwrite.insert(pd.targetDirPath + '/' + Constants::QUICK_3D_ASSET_IMPORT_DATA_NAME); @@ -353,7 +326,6 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) // subdirs assume they are used within the context of the toplevel qml files. QDirIterator qmlIt(outDir.path(), {QStringLiteral("*.qml")}, QDir::Files); if (qmlIt.hasNext()) { - outDir.mkdir(Constants::QUICK_3D_ASSET_ICON_DIR); if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QString qmlInfo; qmlInfo.append("module "); @@ -375,10 +347,6 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) QFile qmlFile(qmlIt.filePath()); if (qmlFile.open(QIODevice::ReadOnly)) { - QString iconFileName = outDir.path() + '/' - + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + fi.baseName() - + Constants::QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX; - QString iconFileName2x = iconFileName + "@2x"; QByteArray content = qmlFile.readAll(); int braceIdx = content.indexOf('{'); QString impVersionStr; @@ -421,16 +389,6 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) if (impVersionMajor > 0 && m_requiredImports.first() != "QtQuick3D") m_requiredImports.prepend("QtQuick3D"); } - if (impVersionMajor > 0 && impVersionMajor < 6) { - pd.iconFile = iconFileName; - pd.iconSource = qmlIt.filePath(); - m_puppetQueue.append(pd.importId); - // Since icon is generated by external process, the file won't be - // ready for asset gathering below, so assume its generation succeeds - // and add it now. - insertAsset(iconFileName); - insertAsset(iconFileName2x); - } } } } @@ -602,42 +560,6 @@ void ItemLibraryAssetImporter::startNextImportProcess() } } -void ItemLibraryAssetImporter::startNextIconProcess() -{ - if (m_puppetQueue.isEmpty()) - return; - - auto view = QmlDesignerPlugin::viewManager().view(); - auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); - Model *model = doc ? doc->currentModel() : nullptr; - - if (model && view) { - bool done = false; - while (!m_puppetQueue.isEmpty() && !done) { - const ParseData pd = m_parseData.value(m_puppetQueue.takeLast()); - QStringList puppetArgs; - puppetArgs << "--rendericon" << QString::number(24) << pd.iconFile << pd.iconSource; - m_puppetProcess = PuppetStarter::createPuppetProcess( - view->externalDependencies().puppetStartData(*model), - "custom", - {}, - [&] {}, - [&](int exitCode, QProcess::ExitStatus exitStatus) { - iconProcessFinished(exitCode, exitStatus); - }, - puppetArgs); - - if (m_puppetProcess->waitForStarted(10000)) { - done = true; - } else { - addError(tr("Failed to start icon generation process."), - pd.sourceInfo.absoluteFilePath()); - m_puppetProcess.reset(); - } - } - } -} - void ItemLibraryAssetImporter::postImport() { QTC_ASSERT(m_puppetQueue.isEmpty() && !m_puppetProcess, return); @@ -645,19 +567,10 @@ void ItemLibraryAssetImporter::postImport() if (!isCancelled()) { for (auto &pd : m_parseData) postParseQuick3DAsset(pd); - startNextIconProcess(); } - if (!isCancelled()) { - // Wait for icon generation processes to finish - if (m_puppetQueue.isEmpty() && !m_puppetProcess) { - finalizeQuick3DImport(); - } else { - const QString progressTitle = tr("Generating icons."); - addInfo(progressTitle); - notifyProgress(0, progressTitle); - } - } + if (!isCancelled()) + finalizeQuick3DImport(); } void ItemLibraryAssetImporter::finalizeQuick3DImport() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index a53f0c9de1b..8ad7b5a2dec 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -53,7 +53,6 @@ signals: private slots: void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); - void iconProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); private: struct ParseData { @@ -65,8 +64,6 @@ private: QString assetName; QString originalAssetName; int importId; - QString iconFile; - QString iconSource; }; void notifyFinished(); @@ -91,7 +88,6 @@ private: OverwriteResult confirmAssetOverwrite(const QString &assetName); void startNextImportProcess(); - void startNextIconProcess(); void postImport(); void finalizeQuick3DImport(); QString sourceSceneTargetFilePath(const ParseData &pd); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 98a1a5a5def..47a1e8d293e 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -405,6 +405,11 @@ void MaterialBrowserWidget::clearPreviewCache() m_previewImageProvider->clearPixmapCache(); } +QSize MaterialBrowserWidget::sizeHint() const +{ + return {420, 420}; +} + QPointer MaterialBrowserWidget::materialBrowserModel() const { return m_materialBrowserModel; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index ea0e5a212ab..bfe7ace34d6 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -65,6 +65,8 @@ public: void clearPreviewCache(); + QSize sizeHint() const override; + signals: void materialSectionFocusedChanged(); void isDraggingChanged(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index 4a4057d8ab3..552133ac887 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -233,11 +233,19 @@ void MaterialEditorContextObject::changeTypeName(const QString &typeName) } } +#ifdef QDS_USE_PROJECTSTORAGE + if (m_selectedMaterial.isRootNode()) + rewriterView->changeRootNodeType(typeName.toUtf8(), -1, -1); + else + m_selectedMaterial.changeType(typeName.toUtf8(), -1, -1); +#else if (m_selectedMaterial.isRootNode()) rewriterView->changeRootNodeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); else - m_selectedMaterial.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); - + m_selectedMaterial.changeType(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif for (const auto &key : copyKeys) { const CopyData ©Data = copyMap[key]; if (copyData.isBinding) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 7d3eb7efe6d..ac3a92c5d44 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -197,13 +197,13 @@ void MaterialEditorView::changeExpression(const QString &propertyName) } if (auto property = m_selectedMaterial.metaInfo().property(name)) { - auto propertyTypeName = property.propertyType().typeName(); - if (propertyTypeName == "QColor") { + auto propertyType = property.propertyType(); + if (propertyType.isColor()) { if (QColor(value->expression().remove('"')).isValid()) { qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"'))); return; } - } else if (propertyTypeName == "bool") { + } else if (propertyType.isBool()) { if (isTrueFalseLiteral(value->expression())) { if (value->expression().compare("true", Qt::CaseInsensitive) == 0) qmlObjectNode.setVariantProperty(name, true); @@ -211,21 +211,21 @@ void MaterialEditorView::changeExpression(const QString &propertyName) qmlObjectNode.setVariantProperty(name, false); return; } - } else if (propertyTypeName == "int") { + } else if (propertyType.isInteger()) { bool ok; int intValue = value->expression().toInt(&ok); if (ok) { qmlObjectNode.setVariantProperty(name, intValue); return; } - } else if (propertyTypeName == "qreal") { + } else if (propertyType.isFloat()) { bool ok; qreal realValue = value->expression().toDouble(&ok); if (ok) { qmlObjectNode.setVariantProperty(name, realValue); return; } - } else if (propertyTypeName == "QVariant") { + } else if (propertyType.isVariant()) { bool ok; qreal realValue = value->expression().toDouble(&ok); if (ok) { @@ -834,7 +834,7 @@ void MaterialEditorView::variantPropertiesChanged(const QList & ModelNode node(property.parentModelNode()); if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->variantPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (m_selectedMaterial.property(property.name()).isBindingProperty()) setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); else @@ -869,7 +869,7 @@ void MaterialEditorView::bindingPropertiesChanged(const QList & if (node == m_selectedMaterial || QmlObjectNode(m_selectedMaterial).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->bindingPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (QmlObjectNode(m_selectedMaterial).modelNode().property(property.name()).isBindingProperty()) setValue(m_selectedMaterial, property.name(), QmlObjectNode(m_selectedMaterial).instanceValue(property.name())); else @@ -897,12 +897,8 @@ void MaterialEditorView::auxiliaryDataChanged(const ModelNode &node, void MaterialEditorView::propertiesAboutToBeRemoved(const QList &propertyList) { - for (const auto &property : propertyList) { - if (property.isBindingProperty()) - m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty()); - else if (property.isVariantProperty()) - m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty()); - } + for (const auto &property : propertyList) + m_dynamicPropertiesModel->removeItem(property); } // request render image for the selected material node diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp index 1c83dff062a..6e1532155f5 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp @@ -10,6 +10,8 @@ #include "qproxystyle.h" #include "previewtooltip.h" +#include + #include #include @@ -35,7 +37,8 @@ namespace { class TableViewStyle : public QProxyStyle { public: - TableViewStyle(QObject *parent) : QProxyStyle(QStyleFactory::create("fusion")) + TableViewStyle(QObject *parent) + : QProxyStyle(new StudioStyle("fusion")) { setParent(parent); baseStyle()->setParent(parent); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp index 9c04438d710..91349e04f59 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp @@ -62,7 +62,6 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view) setWindowTitle(tr("Navigator", "Title of navigator view")); QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_NAVIGATORVIEW_TIME); diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 046f95ed738..45f89ae3392 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -27,6 +27,7 @@ #include "bindingproperty.h" #include "propertyeditorvalue.h" +#include "connectioneditorutils.h" #include @@ -96,7 +97,7 @@ QHash DynamicPropertiesProxyModel::roleNames() const QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) const { if (index.isValid() && index.row() < rowCount()) { - AbstractProperty property = m_model->abstractPropertyForRow(index.row()); + AbstractProperty property = m_model->propertyForRow(index.row()); QTC_ASSERT(property.isValid(), return QVariant()); @@ -142,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const { DynamicPropertiesModel *propsModel = dynamicPropertiesModel(); - return QString::fromUtf8(propsModel->unusedProperty(propsModel->singleSelectedNode())); + return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode())); } void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type) @@ -162,12 +163,12 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr return; } try { - if (DynamicPropertiesModel::isValueType(typeName)) { - QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); + if (isDynamicVariantPropertyType(typeName)) { + QVariant value = defaultValueForType(typeName); VariantProperty variantProp = modelNode.variantProperty(name.toUtf8()); variantProp.setDynamicTypeNameAndValue(typeName, value); } else { - QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); + QString expression = defaultExpressionForType(typeName); BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8()); bindingProp.setDynamicTypeNameAndExpression(typeName, expression); @@ -261,7 +262,7 @@ PropertyEditorValue *DynamicPropertyRow::backendValue() const void DynamicPropertyRow::remove() { - m_model->dynamicPropertiesModel()->deleteDynamicPropertyByRow(m_row); + m_model->dynamicPropertiesModel()->remove(m_row); } PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue() @@ -283,7 +284,7 @@ void DynamicPropertyRow::setupBackendValue() if (!m_model) return; - AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row); + AbstractProperty property = m_model->dynamicPropertiesModel()->propertyForRow(m_row); if (!property.isValid()) return; @@ -324,9 +325,9 @@ void DynamicPropertyRow::commitValue(const QVariant &value) return; auto propertiesModel = m_model->dynamicPropertiesModel(); - VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); - if (!DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName())) + if (!isDynamicVariantPropertyType(property.dynamicTypeName())) return; m_lock = true; @@ -335,16 +336,21 @@ void DynamicPropertyRow::commitValue(const QVariant &value) auto view = propertiesModel->view(); RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__); try { - QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); - if (view->currentState().isBaseState() - && !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) { - if (variantProperty.value() != value) - variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); - } else { - QTC_CHECK(objectNode.isValid()); - PropertyName name = variantProperty.name(); - if (objectNode.isValid() && objectNode.modelValue(name) != value) - objectNode.setVariantProperty(name, value); + if (property.isBindingProperty()) { + convertBindingToVariantProperty(property.toBindingProperty(), value); + } else if (property.isVariantProperty()) { + VariantProperty variantProperty = property.toVariantProperty(); + QmlObjectNode objectNode = variantProperty.parentQmlObjectNode(); + if (view->currentState().isBaseState() + && !(objectNode.timelineIsActive() && objectNode.currentTimeline().isRecording())) { + if (variantProperty.value() != value) + variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value); + } else { + QTC_CHECK(objectNode.isValid()); + PropertyName name = variantProperty.name(); + if (objectNode.isValid() && objectNode.modelValue(name) != value) + objectNode.setVariantProperty(name, value); + } } transaction.commit(); // committing in the try block } catch (Exception &e) { @@ -358,7 +364,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression) return; auto propertiesModel = m_model->dynamicPropertiesModel(); - AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); BindingProperty bindingProperty = property.parentModelNode().bindingProperty(property.name()); @@ -413,15 +419,15 @@ void DynamicPropertyRow::resetValue() auto propertiesModel = m_model->dynamicPropertiesModel(); auto view = propertiesModel->view(); - AbstractProperty property = propertiesModel->abstractPropertyForRow(m_row); + AbstractProperty property = propertiesModel->propertyForRow(m_row); TypeName typeName = property.dynamicTypeName(); if (view->currentState().isBaseState()) { - if (DynamicPropertiesModel::isValueType(typeName)) { - QVariant value = DynamicPropertiesModel::defaultValueForType(typeName); + if (isDynamicVariantPropertyType(typeName)) { + QVariant value = defaultValueForType(typeName); commitValue(value); } else { - QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName); + QString expression = defaultExpressionForType(typeName); commitExpression(expression); } } else { diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 570927a8638..eaf907e8f82 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -3,24 +3,192 @@ #include "gradientmodel.h" -#include "qmlanchorbindingproxy.h" -#include "propertyeditorview.h" -#include "gradientpresetitem.h" #include "gradientpresetcustomlistmodel.h" +#include "gradientpresetitem.h" +#include "propertyeditorview.h" +#include "qmlanchorbindingproxy.h" -#include -#include -#include -#include #include -#include #include +#include +#include +#include #include +#include #include -#include #include +#include + +namespace { +constexpr auto defaultValueLinearX1 = [](const QmlDesigner::QmlItemNode &) -> qreal { return 0.0; }; +constexpr auto defaultValueLinearY1 = [](const QmlDesigner::QmlItemNode &) -> qreal { return 0.0; }; +constexpr auto defaultValueLinearX2 = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return node.instanceValue("width").toReal(); +}; +constexpr auto defaultValueLinearY2 = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return node.instanceValue("height").toReal(); +}; +constexpr auto defaultValueRadialCenterRadius = [](const QmlDesigner::QmlItemNode &node) -> qreal { + const qreal width = node.instanceValue("width").toReal(); + const qreal height = node.instanceValue("height").toReal(); + return qMin(width, height) / 2.0; +}; +constexpr auto defaultValueRadialCenterX = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("width").toReal() / 2.0); +}; +constexpr auto defaultValueRadialCenterY = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("height").toReal() / 2.0); +}; +constexpr auto defaultValueRadialFocalRadius = [](const QmlDesigner::QmlItemNode &) -> qreal { + return 0.0; +}; +constexpr auto defaultValueRadialFocalX = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("width").toReal() / 2.0); +}; +constexpr auto defaultValueRadialFocalY = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("height").toReal() / 2.0); +}; +constexpr auto defaultValueConicalAngle = [](const QmlDesigner::QmlItemNode &) -> qreal { + return 0.0; +}; +constexpr auto defaultValueConicalCenterX = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("width").toReal() / 2.0); +}; +constexpr auto defaultValueConicalCenterY = [](const QmlDesigner::QmlItemNode &node) -> qreal { + return (node.instanceValue("height").toReal() / 2.0); +}; + +using DefaultValueFunctionVariant = std::variant; +} // namespace + +class ShapeGradientPropertyData +{ +public: + enum class UsePercents { No, Yes }; + + constexpr ShapeGradientPropertyData() = default; + + constexpr ShapeGradientPropertyData(QmlDesigner::PropertyNameView name, + QmlDesigner::PropertyNameView bindingProperty, + UsePercents canPercent, + DefaultValueFunctionVariant defVariant) + : name(name) + , bindingProperty(bindingProperty) + , canUsePercentage(canPercent) + , m_defaultValue(defVariant) + {} + + QmlDesigner::PropertyNameView name; + QmlDesigner::PropertyNameView bindingProperty; + UsePercents canUsePercentage = UsePercents::No; + +private: + DefaultValueFunctionVariant m_defaultValue; + +public: + constexpr qreal getDefaultValue(const QmlDesigner::QmlItemNode &itemNode) const + { + return std::visit( + [&](auto &defValue) -> qreal { + using Type = std::decay_t; + if constexpr (std::is_same_v) + return 0.0; + else + return defValue(itemNode); + }, + m_defaultValue); + } +}; + +constexpr QmlDesigner::PropertyNameView linearX1Str = u8"x1"; +constexpr QmlDesigner::PropertyNameView linearX2Str = u8"x2"; +constexpr QmlDesigner::PropertyNameView linearY1Str = u8"y1"; +constexpr QmlDesigner::PropertyNameView linearY2Str = u8"y2"; + +constexpr QmlDesigner::PropertyNameView radialCenterRadiusStr = u8"centerRadius"; +constexpr QmlDesigner::PropertyNameView radialCenterXStr = u8"centerX"; +constexpr QmlDesigner::PropertyNameView radialCenterYStr = u8"centerY"; +constexpr QmlDesigner::PropertyNameView radialFocalRadiusStr = u8"focalRadius"; +constexpr QmlDesigner::PropertyNameView radialFocalXStr = u8"focalX"; +constexpr QmlDesigner::PropertyNameView radialFocalYStr = u8"focalY"; + +constexpr QmlDesigner::PropertyNameView conicalAngleStr = u8"angle"; +constexpr QmlDesigner::PropertyNameView conicalCenterXStr = u8"centerX"; +constexpr QmlDesigner::PropertyNameView conicalCenterYStr = u8"centerY"; + +constexpr ShapeGradientPropertyData defaultLinearShapeGradients[] = { + {linearX1Str, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearX1}, + {linearX2Str, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearX2}, + {linearY1Str, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearY1}, + {linearY2Str, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueLinearY2}}; + +constexpr ShapeGradientPropertyData defaultRadialShapeGradients[] = { + {radialCenterRadiusStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueRadialCenterRadius}, + {radialCenterXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialCenterX}, + {radialCenterYStr, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialCenterY}, + {radialFocalRadiusStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueRadialFocalRadius}, + {radialFocalXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialFocalX}, + {radialFocalYStr, u8"height", ShapeGradientPropertyData::UsePercents::Yes, defaultValueRadialFocalY}}; + +constexpr ShapeGradientPropertyData defaultConicalShapeGradients[] = { + {conicalAngleStr, u8"", ShapeGradientPropertyData::UsePercents::No, defaultValueConicalAngle}, + {conicalCenterXStr, u8"width", ShapeGradientPropertyData::UsePercents::Yes, defaultValueConicalCenterX}, + {conicalCenterYStr, + u8"height", + ShapeGradientPropertyData::UsePercents::Yes, + defaultValueConicalCenterY}}; + +template +const ShapeGradientPropertyData *findGradientInArray(const GradientArrayType &array, + const QmlDesigner::PropertyNameView propName) +{ + const auto found = std::find_if(std::begin(array), std::end(array), [&](const auto &entry) { + return entry.name == propName; + }); + if (found != std::end(array)) + return std::addressof(*found); + + return nullptr; +} + +const ShapeGradientPropertyData *getDefaultGradientData(const QmlDesigner::PropertyNameView propName, + const QStringView &gradientType) +{ + if (gradientType == u"LinearGradient") { + return findGradientInArray(defaultLinearShapeGradients, propName); + } else if (gradientType == u"RadialGradient") { + return findGradientInArray(defaultRadialShapeGradients, propName); + } else if (gradientType == u"ConicalGradient") { + return findGradientInArray(defaultConicalShapeGradients, propName); + } + + return nullptr; +} + +template +void prepareGradient(const T &array, + const QmlDesigner::ModelNode &gradient, + const QmlDesigner::QmlItemNode &node) +{ + std::for_each(std::begin(array), std::end(array), [&](auto &a) { + gradient.variantProperty(a.name.toByteArray()).setValue(a.getDefaultValue(node)); + }); +} GradientModel::GradientModel(QObject *parent) : QAbstractListModel(parent) @@ -268,7 +436,7 @@ void GradientModel::unlock() void GradientModel::registerDeclarativeType() { - qmlRegisterType("HelperWidgets",2,0,"GradientModel"); + qmlRegisterType("HelperWidgets", 2, 0, "GradientModel"); } qreal GradientModel::readGradientProperty(const QString &propertyName) const @@ -276,10 +444,9 @@ qreal GradientModel::readGradientProperty(const QString &propertyName) const if (!m_itemNode.isValid()) return 0; - QmlDesigner::QmlObjectNode gradient; - - if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) - gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); if (!gradient.isValid()) return 0; @@ -287,15 +454,19 @@ qreal GradientModel::readGradientProperty(const QString &propertyName) const return gradient.modelValue(propertyName.toUtf8()).toReal(); } +qreal GradientModel::readGradientPropertyPercentage(const QString &propertyName) const +{ + return getPercentageGradientProperty(propertyName.toUtf8()); +} + QString GradientModel::readGradientOrientation() const { if (!m_itemNode.isValid()) return QString(); - QmlDesigner::QmlObjectNode gradient; - - if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) - gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); if (!gradient.isValid()) return QString(); @@ -303,6 +474,25 @@ QString GradientModel::readGradientOrientation() const return gradient.modelValue("orientation").value().nameToString(); } +GradientModel::GradientPropertyUnits GradientModel::readGradientPropertyUnits( + const QString &propertyName) const +{ + if (hasPercentageGradientProperty(propertyName)) + return GradientPropertyUnits::Percentage; + + return GradientPropertyUnits::Pixels; +} + +bool GradientModel::isPercentageSupportedByProperty(const QString &propertyName, + const QString &gradientTypeName) const +{ + const auto gradientPropertyData = getDefaultGradientPropertyData(propertyName.toUtf8(), gradientTypeName); + if (!gradientPropertyData.name.isEmpty()) + return gradientPropertyData.canUsePercentage == ShapeGradientPropertyData::UsePercents::Yes; + + return false; +} + void GradientModel::setupModel() { m_locked = true; @@ -400,34 +590,14 @@ void GradientModel::setupGradientProperties(const QmlDesigner::ModelNode &gradie QTC_ASSERT(gradient.isValid(), return); - if (m_gradientTypeName == "Gradient") { + if (m_gradientTypeName == u"Gradient") { gradient.variantProperty("orientation").setEnumeration("Gradient.Vertical"); - } else if (m_gradientTypeName == "LinearGradient") { - gradient.variantProperty("x1").setValue(0); - gradient.variantProperty("x2").setValue(m_itemNode.instanceValue("width")); - gradient.variantProperty("y1").setValue(0); - gradient.variantProperty("y2").setValue(m_itemNode.instanceValue("height")); - } else if (m_gradientTypeName == "RadialGradient") { - qreal width = m_itemNode.instanceValue("width").toReal(); - qreal height = m_itemNode.instanceValue("height").toReal(); - gradient.variantProperty("centerX").setValue(width / 2.0); - gradient.variantProperty("centerY").setValue(height / 2.0); - - gradient.variantProperty("focalX").setValue(width / 2.0); - gradient.variantProperty("focalY").setValue(height / 2.0); - - qreal radius = qMin(width, height) / 2; - - gradient.variantProperty("centerRadius").setValue(radius); - gradient.variantProperty("focalRadius").setValue(0); - - } else if (m_gradientTypeName == "ConicalGradient") { - qreal width = m_itemNode.instanceValue("width").toReal(); - qreal height = m_itemNode.instanceValue("height").toReal(); - gradient.variantProperty("centerX").setValue(width / 2.0); - gradient.variantProperty("centerY").setValue(height / 2.0); - - gradient.variantProperty("angle").setValue(0); + } else if (m_gradientTypeName == u"LinearGradient") { + prepareGradient(defaultLinearShapeGradients, gradient, m_itemNode); + } else if (m_gradientTypeName == u"RadialGradient") { + prepareGradient(defaultRadialShapeGradients, gradient, m_itemNode); + } else if (m_gradientTypeName == u"ConicalGradient") { + prepareGradient(defaultConicalShapeGradients, gradient, m_itemNode); } } @@ -450,7 +620,7 @@ void GradientModel::resetPuppet() QmlDesigner::ModelNode GradientModel::createGradientNode() { - QByteArray fullTypeName = m_gradientTypeName.toUtf8(); + QmlDesigner::TypeName fullTypeName = m_gradientTypeName.toUtf8(); if (m_gradientTypeName == "Gradient") { fullTypeName.prepend("QtQuick."); @@ -489,24 +659,115 @@ void GradientModel::deleteGradientNode(bool saveTransaction) if (modelNode.hasProperty(gradientPropertyName().toUtf8())) { QmlDesigner::RewriterTransaction transaction; if (saveTransaction) - transaction = view()->beginRewriterTransaction(QByteArrayLiteral("GradientModel::deleteGradient")); + transaction = view()->beginRewriterTransaction( + QByteArrayLiteral("GradientModel::deleteGradient")); - QmlDesigner::ModelNode gradientNode - = modelNode.nodeProperty(gradientPropertyName().toUtf8()).modelNode(); - if (QmlDesigner::QmlObjectNode(gradientNode).isValid()) - QmlDesigner::QmlObjectNode(gradientNode).destroy(); + QmlDesigner::QmlObjectNode gradientNode = modelNode + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); + if (gradientNode.isValid()) + gradientNode.destroy(); } } } +bool GradientModel::hasPercentageGradientProperty(const QString &propertyName) const +{ + bool result = false; + getPercentageGradientProperty(propertyName.toUtf8(), &result); + return result; +} + +qreal GradientModel::getPercentageGradientProperty(const QmlDesigner::PropertyNameView propertyName, + bool *ok) const +{ + if (ok != nullptr) + *ok = false; + + if (!m_itemNode.isValid()) + return 0.; + + //valid format is itemName1.property * 0.1 + //we are interested in parent. or parentName. items + //and in width and height properties as of now + //looking for something that starts with "itemName1.property" + //looking for something like "* 0.223" + const QmlDesigner::TypeName gradientPropertyTypeName = gradientPropertyName().toUtf8(); + + const QmlDesigner::ModelNode gradientModel = m_itemNode.modelNode() + .nodeProperty(gradientPropertyTypeName) + .modelNode(); + + if (!gradientModel) + return 0.; + + if (const auto bindingProperty = gradientModel.bindingProperty(propertyName.toByteArray())) { + const auto defaultGradient = getDefaultGradientPropertyData(propertyName, m_gradientTypeName); + const auto expectedParentProperty = defaultGradient.bindingProperty; + + const QString expression = bindingProperty.expression(); + const QStringList splitExpression = expression.split("*", Qt::SkipEmptyParts); + if (splitExpression.length() == 2 && !expectedParentProperty.isEmpty()) { + const QString parentStr = splitExpression.at(0).trimmed(); + const QString percentageStr = splitExpression.at(1).trimmed(); + + bool validStatement = false; + + if (!parentStr.isEmpty()) { + const QStringList splitParent = parentStr.split(".", Qt::SkipEmptyParts); + if (splitParent.length() == 2) { + const QString itemId = splitParent.at(0).trimmed(); + const QString itemProp = splitParent.at(1).trimmed(); + const QString realParentId = m_itemNode.modelNode().hasId() ? m_itemNode.id() : ""; + if (!itemId.isEmpty() && !itemProp.isEmpty() && (itemId == realParentId) + && (itemProp.toUtf8() == expectedParentProperty)) { + validStatement = true; + } + } + } + + if (!percentageStr.isEmpty() && validStatement) { + const qreal percentage = percentageStr.toFloat(ok); + return percentage; + } + } + } + + return 0.; +} + +QVariant GradientModel::getGradientPropertyVariant(const QString &propertyName) const +{ + if (!m_itemNode.isValid()) + return {}; + + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); + + if (!gradient.isValid()) + return {}; + + return gradient.modelValue(propertyName.toUtf8()); +} + +ShapeGradientPropertyData GradientModel::getDefaultGradientPropertyData( + const QmlDesigner::PropertyNameView propertyName, const QStringView &gradientType) const +{ + const auto *gradData = getDefaultGradientData(propertyName, gradientType); + if (gradData != nullptr) + return *gradData; + + return {}; +} + void GradientModel::setGradientProperty(const QString &propertyName, qreal value) { QTC_ASSERT(m_itemNode.isValid(), return); - QmlDesigner::QmlObjectNode gradient; - - if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) - gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); QTC_ASSERT(gradient.isValid(), return); @@ -517,14 +778,47 @@ void GradientModel::setGradientProperty(const QString &propertyName, qreal value } } +void GradientModel::setGradientPropertyPercentage(const QString &propertyName, qreal value) +{ + QTC_ASSERT(m_itemNode.isValid(), return); + + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); + + QTC_ASSERT(gradient.isValid(), return); + + const auto gradientDefaultData = getDefaultGradientPropertyData(propertyName.toUtf8(), + m_gradientTypeName); + QTC_ASSERT(gradientDefaultData.canUsePercentage == ShapeGradientPropertyData::UsePercents::Yes, + return); + + const QmlDesigner::PropertyNameView parentPropertyStr = gradientDefaultData.bindingProperty; + QTC_ASSERT(!parentPropertyStr.isEmpty(), return); + + if (parentPropertyStr.isEmpty() + || (gradientDefaultData.canUsePercentage == ShapeGradientPropertyData::UsePercents::No)) { + return; + } + + const QString parentId = m_itemNode.validId(); + const QString leftBinding = parentId + "." + parentPropertyStr; + const QString expression = leftBinding + " * " + QString::number(value); + + try { + gradient.setBindingProperty(propertyName.toUtf8(), expression); + } catch (const QmlDesigner::Exception &e) { + e.showException(); + } +} + void GradientModel::setGradientOrientation(Qt::Orientation value) { QTC_ASSERT(m_itemNode.isValid(), return); - QmlDesigner::QmlObjectNode gradient; - - if (m_itemNode.modelNode().hasProperty(gradientPropertyName().toUtf8())) - gradient = m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + QmlDesigner::QmlObjectNode gradient = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); QTC_ASSERT(gradient.isValid(), return); @@ -537,6 +831,56 @@ void GradientModel::setGradientOrientation(Qt::Orientation value) } } +void GradientModel::setGradientPropertyUnits(const QString &propertyName, + GradientModel::GradientPropertyUnits value) +{ + //translate from previous units to the new unit system + const bool toPixels = (value == GradientPropertyUnits::Pixels); + const bool toPercentage = (value == GradientPropertyUnits::Percentage); + const auto defaultGradientData = getDefaultGradientPropertyData(propertyName.toUtf8(), + m_gradientTypeName); + + const QmlDesigner::PropertyNameView parentPropertyName = defaultGradientData.bindingProperty; + if (parentPropertyName.isEmpty()) + return; + + const qreal parentPropertyValue = m_itemNode.instanceValue(parentPropertyName.toByteArray()).toReal(); + + if (toPixels) { + bool ok = false; + const qreal percent = getPercentageGradientProperty(propertyName.toUtf8(), &ok); + qreal pixelValue = 0.; + + if (!ok) + pixelValue = defaultGradientData.getDefaultValue(m_itemNode); + else + pixelValue = parentPropertyValue * percent; + + if (qIsNaN(pixelValue) || qIsInf(pixelValue)) + pixelValue = 0.; + + setGradientProperty(propertyName, qRound(pixelValue)); + } else if (toPercentage) { + const QVariant gradientProp = getGradientPropertyVariant(propertyName); + bool ok = false; + qreal pixels = gradientProp.toReal(&ok); + qreal percentValue = 0.; + + if (gradientProp.isNull() || !gradientProp.isValid() || !ok) + pixels = defaultGradientData.getDefaultValue(m_itemNode); + + if (qFuzzyIsNull(pixels) || qFuzzyIsNull(parentPropertyValue)) + percentValue = 0.; + else + percentValue = (pixels / parentPropertyValue); + + if (qIsNaN(percentValue) || qIsInf(percentValue)) + percentValue = 0.; + + setGradientPropertyPercentage(propertyName, percentValue); + } +} + void GradientModel::setPresetByID(int presetID) { const QGradient gradient(GradientPresetItem::createGradientFromPreset( diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h index d47bb8adf18..d92b97394eb 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.h @@ -10,6 +10,8 @@ #include #include +class ShapeGradientPropertyData; + class GradientModel : public QAbstractListModel { Q_OBJECT @@ -45,11 +47,21 @@ public: static void registerDeclarativeType(); - Q_INVOKABLE qreal readGradientProperty(const QString &property) const; + enum GradientPropertyUnits { Pixels = 0, Percentage = 1 }; + Q_ENUM(GradientPropertyUnits) + + Q_INVOKABLE qreal readGradientProperty(const QString &propertyName) const; + Q_INVOKABLE qreal readGradientPropertyPercentage(const QString &propertyName) const; Q_INVOKABLE QString readGradientOrientation() const; + Q_INVOKABLE GradientPropertyUnits readGradientPropertyUnits(const QString &propertyName) const; + Q_INVOKABLE bool isPercentageSupportedByProperty(const QString &propertyName, + const QString &gradientTypeName) const; Q_INVOKABLE void setGradientProperty(const QString &propertyName, qreal value); + Q_INVOKABLE void setGradientPropertyPercentage(const QString &propertyName, qreal value); Q_INVOKABLE void setGradientOrientation(Qt::Orientation value); + Q_INVOKABLE void setGradientPropertyUnits(const QString &propertyName, + GradientModel::GradientPropertyUnits value); Q_INVOKABLE void setPresetByID(int presetID); Q_INVOKABLE void setPresetByStops(const QList &stopsPositions, @@ -81,6 +93,15 @@ private: QmlDesigner::ModelNode createGradientStopNode(); void deleteGradientNode(bool saveTransaction); + bool hasPercentageGradientProperty(const QString &propertyName) const; + qreal getPercentageGradientProperty(const QmlDesigner::PropertyNameView propertyName, + bool *ok = nullptr) const; + + QVariant getGradientPropertyVariant(const QString &propertyName) const; + + ShapeGradientPropertyData getDefaultGradientPropertyData( + const QmlDesigner::PropertyNameView propertyName, const QStringView &gradientType) const; + private: QmlDesigner::QmlItemNode m_itemNode; QString m_gradientPropertyName; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index a8d7fb5a6fc..03199d274d1 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -272,11 +272,17 @@ void PropertyEditorContextObject::changeTypeName(const QString &typeName) selectedNode.removeProperty(p); } +#ifdef QDS_USE_PROJECTSTORAGE + if (selectedNode.isRootNode()) + rewriterView->changeRootNodeType(typeName.toUtf8(), -1, -1); + else + selectedNode.changeType(typeName.toUtf8(), -1, -1); +#else if (selectedNode.isRootNode()) rewriterView->changeRootNodeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); else selectedNode.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif transaction.commit(); } catch (const Exception &e) { e.showException(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index eaa5ef95738..2e49befd9e1 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -104,7 +104,7 @@ PropertyEditorQmlBackend::~PropertyEditorQmlBackend() = default; void PropertyEditorQmlBackend::setupPropertyEditorValue(const PropertyName &name, PropertyEditorView *propertyEditor, - const QString &type) + const NodeMetaInfo &type) { QmlDesigner::PropertyName propertyName(name); propertyName.replace('.', '_'); @@ -116,7 +116,7 @@ void PropertyEditorQmlBackend::setupPropertyEditorValue(const PropertyName &name backendValuesPropertyMap().insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject)); } valueObject->setName(propertyName); - if (type == QLatin1String("QColor")) + if (type.isColor()) valueObject->setValue(QVariant(QLatin1String("#000000"))); else valueObject->setValue(QVariant(1)); @@ -538,9 +538,7 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl NodeMetaInfo metaInfo = propertyEditor->model()->metaInfo(typeName); for (const auto &property : metaInfo.properties()) { - setupPropertyEditorValue(property.name(), - propertyEditor, - QString::fromUtf8(property.propertyType().typeName())); + setupPropertyEditorValue(property.name(), propertyEditor, property.propertyType()); } auto valueObject = qobject_cast(variantToQObject( @@ -955,19 +953,22 @@ void PropertyEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNod setValue(qmlObjectNode, propertyName, qmlObjectNode.modelNode().auxiliaryDataWithDefault(key)); } -QUrl PropertyEditorQmlBackend::getQmlUrlForMetaInfo(const NodeMetaInfo &metaInfo, TypeName &className) +std::tuple PropertyEditorQmlBackend::getQmlUrlForMetaInfo(const NodeMetaInfo &metaInfo) { + QString className; if (metaInfo.isValid()) { const NodeMetaInfos hierarchy = metaInfo.selfAndPrototypes(); for (const NodeMetaInfo &info : hierarchy) { QUrl fileUrl = fileToUrl(locateQmlFile(info, QString::fromUtf8(qmlFileName(info)))); if (fileUrl.isValid()) { - className = info.typeName(); - return fileUrl; + return {fileUrl, info}; } } } - return fileToUrl(QDir(propertyEditorResourcesPath()).filePath(QLatin1String("QtQuick/emptyPane.qml"))); + + return {fileToUrl( + QDir(propertyEditorResourcesPath()).filePath(QLatin1String("QtQuick/emptyPane.qml"))), + {}}; } QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const QString &relativePath) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 092d411dfac..b369a417e20 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -51,7 +51,7 @@ public: static QString templateGeneration(const NodeMetaInfo &type, const NodeMetaInfo &superType, const QmlObjectNode &node); static QUrl getQmlFileUrl(const TypeName &relativeTypeName, const NodeMetaInfo &info); - static QUrl getQmlUrlForMetaInfo(const NodeMetaInfo &modelNode, TypeName &className); + static std::tuple getQmlUrlForMetaInfo(const NodeMetaInfo &modelNode); static bool checkIfUrlExists(const QUrl &url); @@ -76,7 +76,9 @@ private: void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value, PropertyEditorView *propertyEditor); - void setupPropertyEditorValue(const PropertyName &name, PropertyEditorView *propertyEditor, const QString &type); + void setupPropertyEditorValue(const PropertyName &name, + PropertyEditorView *propertyEditor, + const NodeMetaInfo &type); static TypeName qmlFileName(const NodeMetaInfo &nodeInfo); static QUrl fileToUrl(const QString &filePath); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index ba6078a778a..bf35e2512eb 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -437,80 +437,130 @@ void PropertyEditorView::resetView() updateSize(); } +namespace { -void PropertyEditorView::setupQmlBackend() +std::tuple diffType(const NodeMetaInfo &commonAncestor, + const NodeMetaInfo &specificsClassMetaInfo) { - TypeName specificsClassName; - - const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode); - - const QUrl qmlFile(PropertyEditorQmlBackend::getQmlUrlForMetaInfo(commonAncestor, specificsClassName)); + NodeMetaInfo diffClassMetaInfo; QUrl qmlSpecificsFile; - TypeName diffClassName; if (commonAncestor.isValid()) { - diffClassName = commonAncestor.typeName(); + diffClassMetaInfo = commonAncestor; const NodeMetaInfos hierarchy = commonAncestor.selfAndPrototypes(); for (const NodeMetaInfo &metaInfo : hierarchy) { if (PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsFile)) break; - qmlSpecificsFile = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName() + "Specifics", metaInfo); - diffClassName = metaInfo.typeName(); + qmlSpecificsFile = PropertyEditorQmlBackend::getQmlFileUrl(metaInfo.typeName() + "Specifics", + metaInfo); + diffClassMetaInfo = metaInfo; } } if (!PropertyEditorQmlBackend::checkIfUrlExists(qmlSpecificsFile)) - diffClassName = specificsClassName; + diffClassMetaInfo = specificsClassMetaInfo; - QString specificQmlData; + return {diffClassMetaInfo, qmlSpecificsFile}; +} - if (commonAncestor.isValid() && m_selectedNode.metaInfo().isValid() && diffClassName != m_selectedNode.type()) - specificQmlData = PropertyEditorQmlBackend::templateGeneration(commonAncestor, model()->metaInfo(diffClassName), m_selectedNode); +QString getSpecificQmlData(const NodeMetaInfo &commonAncestor, + const ModelNode &selectedNode, + const NodeMetaInfo &diffClassMetaInfo) +{ + if (commonAncestor.isValid() && diffClassMetaInfo != selectedNode.metaInfo()) + return PropertyEditorQmlBackend::templateGeneration(commonAncestor, + diffClassMetaInfo, + selectedNode); - PropertyEditorQmlBackend *currentQmlBackend = m_qmlBackendHash.value(qmlFile.toString()); + return {}; +} - QString currentStateName = currentState().isBaseState() ? currentState().name() : QStringLiteral("invalid state"); +PropertyEditorQmlBackend *getQmlBackend(QHash &qmlBackendHash, + const QUrl &qmlFileUrl, + AsynchronousImageCache &imageCache, + PropertyEditorWidget *stackedWidget, + PropertyEditorView *propertyEditorView) +{ + auto qmlFileName = qmlFileUrl.toString(); + PropertyEditorQmlBackend *currentQmlBackend = qmlBackendHash.value(qmlFileName); if (!currentQmlBackend) { - currentQmlBackend = new PropertyEditorQmlBackend(this, m_imageCache); + currentQmlBackend = new PropertyEditorQmlBackend(propertyEditorView, imageCache); - m_stackedWidget->addWidget(currentQmlBackend->widget()); - m_qmlBackendHash.insert(qmlFile.toString(), currentQmlBackend); + stackedWidget->addWidget(currentQmlBackend->widget()); + qmlBackendHash.insert(qmlFileName, currentQmlBackend); - if (m_selectedNode.isValid()) { - QmlObjectNode qmlObjectNode{m_selectedNode}; - Q_ASSERT(qmlObjectNode.isValid()); - currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); - } - - if (specificQmlData.isEmpty()) - currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); - - currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); - currentQmlBackend->setSource(qmlFile); - } else { - QmlObjectNode qmlObjectNode{m_selectedNode}; - - if (specificQmlData.isEmpty()) - currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); - currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); - currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); + currentQmlBackend->setSource(qmlFileUrl); } - currentQmlBackend->widget()->installEventFilter(this); - m_stackedWidget->setCurrentWidget(currentQmlBackend->widget()); + return currentQmlBackend; +} +void setupCurrentQmlBackend(PropertyEditorQmlBackend *currentQmlBackend, + const ModelNode &selectedNode, + const QUrl &qmlSpecificsFile, + const QmlModelState ¤tState, + PropertyEditorView *propertyEditorView, + const QString &specificQmlData) +{ + QString currentStateName = currentState.isBaseState() ? currentState.name() + : QStringLiteral("invalid state"); + + QmlObjectNode qmlObjectNode{selectedNode}; + if (specificQmlData.isEmpty()) + currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); + currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, propertyEditorView); + currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); +} + +void setupInsight(const ModelNode &rootModelNode, PropertyEditorQmlBackend *currentQmlBackend) +{ + if (rootModelNode.hasAuxiliaryData(insightEnabledProperty)) { + currentQmlBackend->contextObject()->setInsightEnabled( + rootModelNode.auxiliaryData(insightEnabledProperty)->toBool()); + } + + if (rootModelNode.hasAuxiliaryData(insightCategoriesProperty)) { + currentQmlBackend->contextObject()->setInsightCategories( + rootModelNode.auxiliaryData(insightCategoriesProperty)->toStringList()); + } +} + +void setupWidget(PropertyEditorQmlBackend *currentQmlBackend, + PropertyEditorView *propertyEditorView, + QStackedWidget *stackedWidget) +{ + currentQmlBackend->widget()->installEventFilter(propertyEditorView); + stackedWidget->setCurrentWidget(currentQmlBackend->widget()); currentQmlBackend->contextObject()->triggerSelectionChanged(); +} +} // namespace + +void PropertyEditorView::setupQmlBackend() +{ + const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode); + + const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo( + commonAncestor); + + auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo); + + QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo); + + PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, + qmlFileUrl, + m_imageCache, + m_stackedWidget, + this); + + setupCurrentQmlBackend( + currentQmlBackend, m_selectedNode, qmlSpecificsFile, currentState(), this, specificQmlData); + + setupWidget(currentQmlBackend, this, m_stackedWidget); m_qmlBackEndForCurrentType = currentQmlBackend; - if (rootModelNode().hasAuxiliaryData(insightEnabledProperty)) - m_qmlBackEndForCurrentType->contextObject()->setInsightEnabled( - rootModelNode().auxiliaryData(insightEnabledProperty)->toBool()); - - if (rootModelNode().hasAuxiliaryData(insightCategoriesProperty)) - m_qmlBackEndForCurrentType->contextObject()->setInsightCategories( - rootModelNode().auxiliaryData(insightCategoriesProperty)->toStringList()); + setupInsight(rootModelNode(), currentQmlBackend); } void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 9f4bb59231e..122228f2086 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -70,11 +70,6 @@ void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtreditorWidget()->installEventFilter(this); - - static QString styleSheet = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); - m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 73c3c65f30e..42d909400c3 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -187,13 +187,13 @@ void TextureEditorView::changeExpression(const QString &propertyName) } if (auto property = m_selectedTexture.metaInfo().property(name)) { - auto propertyTypeName = property.propertyType().typeName(); - if (propertyTypeName == "QColor") { + auto propertyType = property.propertyType(); + if (propertyType.isColor()) { if (QColor(value->expression().remove('"')).isValid()) { qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"'))); return; } - } else if (propertyTypeName == "bool") { + } else if (propertyType.isBool()) { if (isTrueFalseLiteral(value->expression())) { if (value->expression().compare("true", Qt::CaseInsensitive) == 0) qmlObjectNode.setVariantProperty(name, true); @@ -201,21 +201,21 @@ void TextureEditorView::changeExpression(const QString &propertyName) qmlObjectNode.setVariantProperty(name, false); return; } - } else if (propertyTypeName == "int") { + } else if (propertyType.isInteger()) { bool ok; int intValue = value->expression().toInt(&ok); if (ok) { qmlObjectNode.setVariantProperty(name, intValue); return; } - } else if (propertyTypeName == "qreal") { + } else if (propertyType.isFloat()) { bool ok; qreal realValue = value->expression().toDouble(&ok); if (ok) { qmlObjectNode.setVariantProperty(name, realValue); return; } - } else if (propertyTypeName == "QVariant") { + } else if (propertyType.isVariant()) { bool ok; qreal realValue = value->expression().toDouble(&ok); if (ok) { @@ -588,7 +588,7 @@ void TextureEditorView::variantPropertiesChanged(const QList &p ModelNode node(property.parentModelNode()); if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->variantPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (m_selectedTexture.property(property.name()).isBindingProperty()) setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); else @@ -612,7 +612,7 @@ void TextureEditorView::bindingPropertiesChanged(const QList &p if (node == m_selectedTexture || QmlObjectNode(m_selectedTexture).propertyChangeForCurrentState() == node) { if (property.isDynamic()) - m_dynamicPropertiesModel->bindingPropertyChanged(property); + m_dynamicPropertiesModel->updateItem(property); if (QmlObjectNode(m_selectedTexture).modelNode().property(property.name()).isBindingProperty()) setValue(m_selectedTexture, property.name(), QmlObjectNode(m_selectedTexture).instanceValue(property.name())); else @@ -642,12 +642,8 @@ void TextureEditorView::auxiliaryDataChanged(const ModelNode &node, void TextureEditorView::propertiesAboutToBeRemoved(const QList &propertyList) { - for (const auto &property : propertyList) { - if (property.isBindingProperty()) - m_dynamicPropertiesModel->bindingRemoved(property.toBindingProperty()); - else if (property.isVariantProperty()) - m_dynamicPropertiesModel->variantRemoved(property.toVariantProperty()); - } + for (const auto &property : propertyList) + m_dynamicPropertiesModel->removeItem(property); } void TextureEditorView::nodeReparented(const ModelNode &node, diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp index b4ab0583c70..1d0c84a3b80 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp @@ -187,6 +187,15 @@ void TimelineToolBar::setPlayState(bool state) m_playing->setChecked(state); } +void TimelineToolBar::setIsMcu(bool isMcu) +{ + m_curvePicker->setDisabled(isMcu); + if (isMcu) + m_curvePicker->setText(tr("Not Supported for MCUs")); + else + m_curvePicker->setText(tr(m_curveEditorName)); +} + void TimelineToolBar::setActionEnabled(const QString &name, bool enabled) { for (auto *action : actions()) @@ -378,14 +387,14 @@ void TimelineToolBar::createCenterControls() addSpacing(10); - auto *curvePicker = createAction(TimelineConstants::C_CURVE_PICKER, + m_curvePicker = createAction(TimelineConstants::C_CURVE_PICKER, Theme::iconFromName(Theme::Icon::curveDesigner_medium), - tr("Easing Curve Editor"), + tr(m_curveEditorName), QKeySequence(Qt::Key_C)); - curvePicker->setObjectName("Easing Curve Editor"); - connect(curvePicker, &QAction::triggered, this, &TimelineToolBar::openEasingCurveEditor); - addAction(curvePicker); + m_curvePicker->setObjectName("Easing Curve Editor"); + connect(m_curvePicker, &QAction::triggered, this, &TimelineToolBar::openEasingCurveEditor); + addAction(m_curvePicker); addSpacing(10); @@ -394,7 +403,7 @@ void TimelineToolBar::createCenterControls() addSpacing(10); - auto *curveEditor = new QAction(TimelineIcons::CURVE_PICKER.icon(), tr("Curve Editor")); + auto *curveEditor = new QAction(TimelineIcons::CURVE_PICKER.icon(), tr(m_curveEditorName)); addAction(curveEditor); #endif } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h index ff1b567dfc3..1d5fb9eaebb 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.h @@ -60,6 +60,7 @@ public: void setEndFrame(qreal frame); void setScaleFactor(int factor); void setPlayState(bool state); + void setIsMcu(bool isMcu); void setActionEnabled(const QString &name, bool enabled); void removeTimeline(const QmlTimeline &timeline); @@ -86,7 +87,10 @@ private: QAction *m_playing = nullptr; QAction *m_recording = nullptr; + QAction *m_curvePicker = nullptr; bool m_blockReflection = false; + + static constexpr const char* m_curveEditorName = QT_TR_NOOP("Easing Curve Editor"); }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 64fce948489..b05083ff054 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -21,9 +21,11 @@ #include +#include #include #include #include +#include #include #include @@ -97,7 +99,7 @@ TimelineWidget::TimelineWidget(TimelineView *view) , m_toolbar(new TimelineToolBar(this)) , m_rulerView(new QGraphicsView(this)) , m_graphicsView(new QGraphicsView(this)) - , m_scrollbar(new QScrollBar(this)) + , m_scrollbar(new Utils::ScrollBar(this)) , m_statusBar(new QLabel(this)) , m_timelineView(view) , m_graphicsScene(new TimelineGraphicsScene(this, view->externalDependencies())) @@ -112,11 +114,6 @@ TimelineWidget::TimelineWidget(TimelineView *view) m_toolbar->setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - - const QString css = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - - m_scrollbar->setStyleSheet(css); m_scrollbar->setOrientation(Qt::Horizontal); QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -129,7 +126,6 @@ TimelineWidget::TimelineWidget(TimelineView *view) m_rulerView->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_rulerView->viewport()->installEventFilter(new Eventfilter(this)); m_rulerView->viewport()->setFocusPolicy(Qt::NoFocus); - m_rulerView->setStyleSheet(css); m_rulerView->setFrameShape(QFrame::NoFrame); m_rulerView->setFrameShadow(QFrame::Plain); m_rulerView->setLineWidth(0); @@ -137,12 +133,11 @@ TimelineWidget::TimelineWidget(TimelineView *view) m_rulerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_rulerView->setScene(graphicsScene()); - m_graphicsView->setStyleSheet(css); m_graphicsView->setObjectName("SceneView"); m_graphicsView->setFrameShape(QFrame::NoFrame); m_graphicsView->setFrameShadow(QFrame::Plain); m_graphicsView->setLineWidth(0); - m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setSizePolicy(sizePolicy1); @@ -286,6 +281,8 @@ TimelineWidget::TimelineWidget(TimelineView *view) auto onFinish = [this]() { graphicsScene()->setCurrentFrame(m_playbackAnimation->startValue().toInt()); }; connect(m_playbackAnimation, &QVariantAnimation::finished, onFinish); + + TimeLineNS::TimelineScrollAreaSupport::support(m_graphicsView, m_scrollbar); } void TimelineWidget::connectToolbar() @@ -482,6 +479,7 @@ void TimelineWidget::init(int zoom) // setScaleFactor uses QSignalBlocker. m_toolbar->setScaleFactor(zoom); + m_toolbar->setIsMcu(DesignerMcuManager::instance().isMCUProject()); m_graphicsScene->setZoom(zoom); } @@ -540,12 +538,17 @@ void TimelineWidget::invalidateTimelinePosition(const QmlTimeline &timeline) void TimelineWidget::setupScrollbar(int min, int max, int current) { - bool b = m_scrollbar->blockSignals(true); - m_scrollbar->setMinimum(min); - m_scrollbar->setMaximum(max); - m_scrollbar->setValue(current); - m_scrollbar->setSingleStep((max - min) / 10); - m_scrollbar->blockSignals(b); + int singleStep = (max - min) / 10; + + if (m_scrollbar->minimum() != min || m_scrollbar->maximum() != max + || m_scrollbar->value() != current || m_scrollbar->singleStep() != singleStep) { + bool b = m_scrollbar->blockSignals(true); + m_scrollbar->setRange(min, max); + m_scrollbar->setValue(current); + m_scrollbar->setSingleStep(singleStep); + m_scrollbar->blockSignals(b); + m_scrollbar->flash(); + } } void TimelineWidget::setTimelineId(const QString &id) @@ -577,6 +580,7 @@ void TimelineWidget::setTimelineActive(bool b) m_toolbar->setVisible(true); m_graphicsView->setVisible(true); m_rulerView->setVisible(true); + m_scrollbar->setEnabled(true); // Set the transient scrollbar enabled to be able to flash it. m_scrollbar->setVisible(true); m_addButton->setVisible(false); m_onboardingContainer->setVisible(false); @@ -586,6 +590,8 @@ void TimelineWidget::setTimelineActive(bool b) m_toolbar->setVisible(false); m_graphicsView->setVisible(false); m_rulerView->setVisible(false); + m_scrollbar->setEnabled( + false); // Set the transient scrollbar disabled to prevent it from being flashed. m_scrollbar->setVisible(false); m_statusBar->clear(); m_addButton->setVisible(true); @@ -634,4 +640,145 @@ TimelineView *TimelineWidget::timelineView() const return m_timelineView; } +namespace TimeLineNS { + +using ScrollBar = Utils::ScrollBar; +static constexpr char timelineScrollAreaSupportName[] = "timelinetransientScrollAreSupport"; +static constexpr char focusedPropertyName[] = "focused"; + +class TimelineScrollAreaPrivate +{ +public: + TimelineScrollAreaPrivate(QAbstractScrollArea *area, ScrollBar *scrollbar) + : area(area) + , scrollbar(scrollbar) + {} + + inline QRect scrollBarRect(ScrollBar *scrollBar) + { + QRect rect = viewPort ? viewPort->rect() : area->rect(); + if (scrollBar->orientation() == Qt::Vertical) { + int mDiff = rect.width() - scrollBar->sizeHint().width(); + return rect.adjusted(mDiff, 0, mDiff, 0); + } else { + int mDiff = rect.height() - scrollBar->sizeHint().height(); + return rect.adjusted(0, mDiff, 0, mDiff); + } + } + + inline bool checkToFlashScroll(QPointer scrollBar, const QPoint &pos) + { + if (scrollBar.isNull()) + return false; + + if (!scrollBar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, scrollBar)) + return false; + + if (scrollBarRect(scrollBar).contains(pos)) { + scrollBar->flash(); + return true; + } + return false; + } + + inline bool checkToFlashScroll(const QPoint &pos) + { + bool coversScroll = checkToFlashScroll(scrollbar, pos); + + return coversScroll; + } + + inline bool setFocus(const bool &focus) + { + if (scrollbar.isNull()) + return false; + + if (!scrollbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, scrollbar)) + return false; + + return scrollbar->setFocused(focus); + } + + inline void installViewPort(QObject *eventHandler) + { + QWidget *viewPort = area->viewport(); + if (viewPort && viewPort != this->viewPort + && viewPort->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, viewPort)) { + viewPort->installEventFilter(eventHandler); + this->viewPort = viewPort; + } + } + + inline void uninstallViewPort(QObject *eventHandler) + { + if (viewPort) { + viewPort->removeEventFilter(eventHandler); + this->viewPort = nullptr; + } + } + + QAbstractScrollArea *area = nullptr; + QPointer viewPort = nullptr; + QPointer scrollbar; +}; + +TimelineScrollAreaSupport::TimelineScrollAreaSupport(QAbstractScrollArea *scrollArea, + Utils::ScrollBar *scrollbar) + : QObject(scrollArea) + , d(new TimelineScrollAreaPrivate(scrollArea, scrollbar)) +{ + scrollArea->installEventFilter(this); +} + +void TimelineScrollAreaSupport::support(QAbstractScrollArea *scrollArea, Utils::ScrollBar *scrollbar) +{ + QObject *prevSupport = scrollArea->property(timelineScrollAreaSupportName).value(); + if (!prevSupport) + scrollArea->setProperty(timelineScrollAreaSupportName, + QVariant::fromValue( + new TimelineScrollAreaSupport(scrollArea, scrollbar))); +} + +TimelineScrollAreaSupport::~TimelineScrollAreaSupport() +{ + delete d; +} + +bool TimelineScrollAreaSupport::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::Enter: { + if (watched == d->area) + d->installViewPort(this); + } break; + case QEvent::Leave: { + if (watched == d->area) + d->uninstallViewPort(this); + } break; + case QEvent::MouseMove: { + if (watched == d->viewPort) { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent) { + if (d->checkToFlashScroll(mouseEvent->pos())) + return true; + } + } + } break; + case QEvent::DynamicPropertyChange: { + if (watched == d->area) { + auto *pEvent = static_cast(event); + if (!pEvent || pEvent->propertyName() != focusedPropertyName) + break; + + bool focused = d->area->property(focusedPropertyName).toBool(); + d->setFocus(focused); + } + } break; + default: + break; + } + return QObject::eventFilter(watched, event); +} + +} // namespace TimeLineNS } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h index d99e7041947..58d82159d9e 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h @@ -19,15 +19,44 @@ QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QVariantAnimation) QT_FORWARD_DECLARE_CLASS(QScrollBar) +QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea) + +namespace Utils { +QT_FORWARD_DECLARE_CLASS(ScrollBar) +} namespace QmlDesigner { class TimelineToolBar; class TimelineView; class TimelineGraphicsScene; +class TimelineWidget; class QmlTimeline; class Navigation2dScrollBar; +namespace TimeLineNS { + +class TimelineScrollAreaPrivate; +class ScrollBarPrivate; + +class TimelineScrollAreaSupport : public QObject +{ + Q_OBJECT +public: + static void support(QAbstractScrollArea *scrollArea, Utils::ScrollBar *scrollbar); + virtual ~TimelineScrollAreaSupport(); + +protected: + virtual bool eventFilter(QObject *watched, QEvent *event) override; + +private: + explicit TimelineScrollAreaSupport(QAbstractScrollArea *scrollArea, Utils::ScrollBar *scrollbar); + + TimelineScrollAreaPrivate *d = nullptr; +}; + +} // namespace TimeLineNS + class TimelineWidget : public QWidget { Q_OBJECT @@ -76,7 +105,7 @@ private: QGraphicsView *m_graphicsView = nullptr; - QScrollBar *m_scrollbar = nullptr; + Utils::ScrollBar *m_scrollbar = nullptr; QLabel *m_statusBar = nullptr; diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 99c46b43b33..8458cf8746f 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -548,6 +549,7 @@ void ToolBarBackend::updateDocumentModel() m_openDocuments.append(entry->displayName()); emit openDocumentsChanged(); + emit documentIndexChanged(); } int ToolBarBackend::documentIndex() const @@ -617,8 +619,13 @@ int ToolBarBackend::currentStyle() const QStringList ToolBarBackend::kits() const { - return Utils::transform(ProjectExplorer::KitManager::kits(), - [](ProjectExplorer::Kit *kit) { return kit->displayName(); }); + auto kits = Utils::filtered(ProjectExplorer::KitManager::kits(), [](ProjectExplorer::Kit *kit) { + const auto qtVersion = QtSupport::QtKitAspect::qtVersion(kit); + return kit->isValid() && !kit->isReplacementKit() && qtVersion && qtVersion->isValid() + /*&& kit->isAutoDetected() */; + }); + + return Utils::transform(kits, [](ProjectExplorer::Kit *kit) { return kit->displayName(); }); } int ToolBarBackend::currentKit() const diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp index adbe164201b..235722e4c8f 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp @@ -82,7 +82,7 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) , m_toolbar(new TransitionEditorToolBar(this)) , m_rulerView(new QGraphicsView(this)) , m_graphicsView(new QGraphicsView(this)) - , m_scrollbar(new QScrollBar(this)) + , m_scrollbar(new Utils::ScrollBar(this)) , m_statusBar(new QLabel(this)) , m_transitionEditorView(view) , m_graphicsScene(new TransitionEditorGraphicsScene(this)) @@ -94,11 +94,6 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) m_toolbar->setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - - const QString css = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - - m_scrollbar->setStyleSheet(css); m_scrollbar->setOrientation(Qt::Horizontal); QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -111,7 +106,6 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) m_rulerView->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_rulerView->viewport()->installEventFilter(new Eventfilter(this)); m_rulerView->viewport()->setFocusPolicy(Qt::NoFocus); - m_rulerView->setStyleSheet(css); m_rulerView->setFrameShape(QFrame::NoFrame); m_rulerView->setFrameShadow(QFrame::Plain); m_rulerView->setLineWidth(0); @@ -119,7 +113,6 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) m_rulerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_rulerView->setScene(graphicsScene()); - m_graphicsView->setStyleSheet(css); m_graphicsView->setObjectName("SceneView"); m_graphicsView->setFrameShape(QFrame::NoFrame); m_graphicsView->setFrameShadow(QFrame::Plain); @@ -238,6 +231,7 @@ void TransitionEditorWidget::setTransitionActive(bool b) m_toolbar->setVisible(true); m_graphicsView->setVisible(true); m_rulerView->setVisible(true); + m_scrollbar->setEnabled(true); // Set the transient scrollbar enabled to be able to flash it. m_scrollbar->setVisible(true); m_addButton->setVisible(false); m_onboardingContainer->setVisible(false); @@ -247,6 +241,8 @@ void TransitionEditorWidget::setTransitionActive(bool b) m_toolbar->setVisible(false); m_graphicsView->setVisible(false); m_rulerView->setVisible(false); + m_scrollbar->setEnabled( + false); // Set the transient scrollbar disabled to prevent it from being flashed. m_scrollbar->setVisible(false); m_addButton->setVisible(true); m_onboardingContainer->setVisible(true); @@ -385,12 +381,17 @@ TransitionEditorToolBar *TransitionEditorWidget::toolBar() const void TransitionEditorWidget::setupScrollbar(int min, int max, int current) { - bool b = m_scrollbar->blockSignals(true); - m_scrollbar->setMinimum(min); - m_scrollbar->setMaximum(max); - m_scrollbar->setValue(current); - m_scrollbar->setSingleStep((max - min) / 10); - m_scrollbar->blockSignals(b); + int singleStep = (max - min) / 10; + + if (m_scrollbar->minimum() != min || m_scrollbar->maximum() != max + || m_scrollbar->value() != current || m_scrollbar->singleStep() != singleStep) { + bool b = m_scrollbar->blockSignals(true); + m_scrollbar->setRange(min, max); + m_scrollbar->setValue(current); + m_scrollbar->setSingleStep(singleStep); + m_scrollbar->blockSignals(b); + m_scrollbar->flash(); + } } void TransitionEditorWidget::showEvent([[maybe_unused]] QShowEvent *event) diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h index e40f73ab59c..a8cb884be2e 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h @@ -7,6 +7,8 @@ #include +#include + #include #include @@ -75,7 +77,7 @@ private: QGraphicsView *m_graphicsView = nullptr; - QScrollBar *m_scrollbar = nullptr; + Utils::ScrollBar *m_scrollbar = nullptr; QLabel *m_statusBar = nullptr; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index 1c46722d4c4..70c0bced0e3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -12,26 +12,11 @@ namespace QmlDesigner { AsynchronousExplicitImageCache::AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage) : m_storage(storage) { - m_backgroundThread = std::thread{[this] { - while (isRunning()) { - if (auto entry = getEntry(); entry) { - request(entry->name, - entry->extraId, - entry->requestType, - std::move(entry->captureCallback), - std::move(entry->abortCallback), - m_storage); - } - waitForEntries(); - } - }}; } AsynchronousExplicitImageCache::~AsynchronousExplicitImageCache() { - clean(); - wait(); } void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, @@ -70,21 +55,16 @@ void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, } } -void AsynchronousExplicitImageCache::wait() -{ - stopThread(); - m_condition.notify_all(); - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); -} - void AsynchronousExplicitImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, Utils::SmallStringView extraId) { - addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::Image); - m_condition.notify_all(); + m_taskQueue.addTask(name, + extraId, + std::move(captureCallback), + std::move(abortCallback), + RequestType::Image); } void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::SmallStringView name, @@ -92,8 +72,11 @@ void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::SmallStringView ImageCache::AbortCallback abortCallback, Utils::SmallStringView extraId) { - addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::MidSizeImage); - m_condition.notify_all(); + m_taskQueue.addTask(name, + extraId, + std::move(captureCallback), + std::move(abortCallback), + RequestType::MidSizeImage); } void AsynchronousExplicitImageCache::requestSmallImage(Utils::SmallStringView name, @@ -101,68 +84,16 @@ void AsynchronousExplicitImageCache::requestSmallImage(Utils::SmallStringView na ImageCache::AbortCallback abortCallback, Utils::SmallStringView extraId) { - addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::SmallImage); - m_condition.notify_all(); + m_taskQueue.addTask(name, + extraId, + std::move(captureCallback), + std::move(abortCallback), + RequestType::SmallImage); } void AsynchronousExplicitImageCache::clean() { - clearEntries(); -} - -std::optional AsynchronousExplicitImageCache::getEntry() -{ - std::unique_lock lock{m_mutex}; - - if (m_requestEntries.empty()) - return {}; - - RequestEntry entry = m_requestEntries.front(); - m_requestEntries.pop_front(); - - return {entry}; -} - -void AsynchronousExplicitImageCache::addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - RequestType requestType) -{ - std::unique_lock lock{m_mutex}; - - m_requestEntries.emplace_back(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - requestType); -} - -void AsynchronousExplicitImageCache::clearEntries() -{ - std::unique_lock lock{m_mutex}; - for (RequestEntry &entry : m_requestEntries) - entry.abortCallback(ImageCache::AbortReason::Abort); - m_requestEntries.clear(); -} - -void AsynchronousExplicitImageCache::waitForEntries() -{ - std::unique_lock lock{m_mutex}; - if (m_requestEntries.empty()) - m_condition.wait(lock, [&] { return m_requestEntries.size() || m_finishing; }); -} - -void AsynchronousExplicitImageCache::stopThread() -{ - std::unique_lock lock{m_mutex}; - m_finishing = true; -} - -bool AsynchronousExplicitImageCache::isRunning() -{ - std::unique_lock lock{m_mutex}; - return !m_finishing || m_requestEntries.size(); + m_taskQueue.clean(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 00b3ab722ef..a18e051c364 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -18,29 +18,11 @@ AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &stora , m_generator(generator) , m_timeStampProvider(timeStampProvider) { - m_backgroundThread = std::thread{[this] { - while (isRunning()) { - if (auto entry = getEntry(); entry) { - request(entry->name, - entry->extraId, - entry->requestType, - std::move(entry->captureCallback), - std::move(entry->abortCallback), - std::move(entry->auxiliaryData), - m_storage, - m_generator, - m_timeStampProvider); - } - - waitForEntries(); - } - }}; } AsynchronousImageCache::~AsynchronousImageCache() { - clean(); - wait(); + m_generator.clean(); } void AsynchronousImageCache::request(Utils::SmallStringView name, @@ -110,27 +92,18 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, } } -void AsynchronousImageCache::wait() -{ - stopThread(); - m_condition.notify_all(); - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); -} - void AsynchronousImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - std::move(auxiliaryData), - RequestType::Image); - m_condition.notify_all(); + m_taskQueue.addTask(std::move(name), + std::move(extraId), + std::move(captureCallback), + std::move(abortCallback), + std::move(auxiliaryData), + RequestType::Image); } void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, @@ -139,13 +112,12 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - std::move(auxiliaryData), - RequestType::MidSizeImage); - m_condition.notify_all(); + m_taskQueue.addTask(std::move(name), + std::move(extraId), + std::move(captureCallback), + std::move(abortCallback), + std::move(auxiliaryData), + RequestType::MidSizeImage); } void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, @@ -154,76 +126,18 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - std::move(auxiliaryData), - RequestType::SmallImage); - m_condition.notify_all(); + m_taskQueue.addTask(std::move(name), + std::move(extraId), + std::move(captureCallback), + std::move(abortCallback), + std::move(auxiliaryData), + RequestType::SmallImage); } void AsynchronousImageCache::clean() { - clearEntries(); m_generator.clean(); -} - -std::optional AsynchronousImageCache::getEntry() -{ - std::unique_lock lock{m_mutex}; - - if (m_entries.empty()) - return {}; - - Entry entry = m_entries.front(); - m_entries.pop_front(); - - return {entry}; -} - -void AsynchronousImageCache::addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData, - RequestType requestType) -{ - std::unique_lock lock{m_mutex}; - - m_entries.emplace_back(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - std::move(auxiliaryData), - requestType); -} - -void AsynchronousImageCache::clearEntries() -{ - std::unique_lock lock{m_mutex}; - for (Entry &entry : m_entries) - entry.abortCallback(ImageCache::AbortReason::Abort); - m_entries.clear(); -} - -void AsynchronousImageCache::waitForEntries() -{ - std::unique_lock lock{m_mutex}; - if (m_entries.empty()) - m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; }); -} - -void AsynchronousImageCache::stopThread() -{ - std::unique_lock lock{m_mutex}; - m_finishing = true; -} - -bool AsynchronousImageCache::isRunning() -{ - std::unique_lock lock{m_mutex}; - return !m_finishing || m_entries.size(); + m_taskQueue.clean(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 5d6ced30601..a711f1ad7d8 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -17,70 +17,17 @@ AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &s , m_timeStampProvider(timeStampProvider) , m_collector(collector) { - m_backgroundThread = std::thread{[this] { - while (isRunning()) { - if (auto entry = getEntry(); entry) { - request(entry->name, - entry->extraId, - std::move(entry->auxiliaryData), - m_storage, - m_timeStampProvider, - m_collector); - } - waitForEntries(); - } - }}; -} - -AsynchronousImageFactory::~AsynchronousImageFactory() -{ - clean(); - wait(); } void AsynchronousImageFactory::generate(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(name, extraId, std::move(auxiliaryData)); - m_condition.notify_all(); + m_taskQueue.addTask(name, extraId, std::move(auxiliaryData)); } -void AsynchronousImageFactory::addEntry(Utils::SmallStringView name, - Utils::SmallStringView extraId, - ImageCache::AuxiliaryData &&auxiliaryData) -{ - std::unique_lock lock{m_mutex}; - - m_entries.emplace_back(std::move(name), std::move(extraId), std::move(auxiliaryData)); -} - -bool AsynchronousImageFactory::isRunning() -{ - std::unique_lock lock{m_mutex}; - return !m_finishing || m_entries.size(); -} - -void AsynchronousImageFactory::waitForEntries() -{ - std::unique_lock lock{m_mutex}; - if (m_entries.empty()) - m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; }); -} - -std::optional AsynchronousImageFactory::getEntry() -{ - std::unique_lock lock{m_mutex}; - - if (m_entries.empty()) - return {}; - - Entry entry = m_entries.front(); - m_entries.pop_front(); - - return {entry}; -} +AsynchronousImageFactory::~AsynchronousImageFactory() {} void AsynchronousImageFactory::request(Utils::SmallStringView name, Utils::SmallStringView extraId, @@ -99,8 +46,8 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, if (currentModifiedTime < (storageModifiedTime + pause)) return; - auto capture = [=](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { - m_storage.storeImage(id, currentModifiedTime, image, midSizeImage, smallImage); + auto capture = [&](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + storage.storeImage(id, currentModifiedTime, image, midSizeImage, smallImage); }; collector.start(name, @@ -112,27 +59,6 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, void AsynchronousImageFactory::clean() { - clearEntries(); + m_taskQueue.clean(); } - -void AsynchronousImageFactory::wait() -{ - stopThread(); - m_condition.notify_all(); - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); -} - -void AsynchronousImageFactory::clearEntries() -{ - std::unique_lock lock{m_mutex}; - m_entries.clear(); -} - -void AsynchronousImageFactory::stopThread() -{ - std::unique_lock lock{m_mutex}; - m_finishing = true; -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index 415646c73cd..f53ccc18ed7 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -5,6 +5,8 @@ #include "imagecacheauxiliarydata.h" +#include + #include #include @@ -50,31 +52,41 @@ private: ImageCache::AuxiliaryData auxiliaryData; }; - void addEntry(Utils::SmallStringView name, - Utils::SmallStringView extraId, - ImageCache::AuxiliaryData &&auxiliaryData); - bool isRunning(); - void waitForEntries(); - std::optional getEntry(); - void request(Utils::SmallStringView name, - Utils::SmallStringView extraId, - ImageCache::AuxiliaryData auxiliaryData, - ImageCacheStorageInterface &storage, - TimeStampProviderInterface &timeStampProvider, - ImageCacheCollectorInterface &collector); - void wait(); - void clearEntries(); - void stopThread(); + static void request(Utils::SmallStringView name, + Utils::SmallStringView extraId, + ImageCache::AuxiliaryData auxiliaryData, + ImageCacheStorageInterface &storage, + TimeStampProviderInterface &timeStampProvider, + ImageCacheCollectorInterface &collector); + + struct Dispatch + { + void operator()(Entry &entry) + { + request(entry.name, + entry.extraId, + std::move(entry.auxiliaryData), + storage, + timeStampProvider, + collector); + } + + ImageCacheStorageInterface &storage; + TimeStampProviderInterface &timeStampProvider; + ImageCacheCollectorInterface &collector; + }; + + struct Clean + { + void operator()(Entry &) {} + }; private: - std::deque m_entries; - std::mutex m_mutex; - std::condition_variable m_condition; - std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; TimeStampProviderInterface &m_timeStampProvider; ImageCacheCollectorInterface &m_collector; - bool m_finishing{false}; + TaskQueue m_taskQueue{Dispatch{m_storage, m_timeStampProvider, m_collector}, + Clean{}}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp index d3363e2d630..2e0c4813ec8 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp @@ -19,7 +19,7 @@ ImageCacheFontCollector::~ImageCacheFontCollector() = default; namespace { -QByteArray fileToByteArray(QString const &filename) +QByteArray fileToByteArray(const QString &filename) { QFile file(filename); QFileInfo fileInfo(file); @@ -98,16 +98,18 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name, { QFont font; if (resolveFont(QString(name), font) >= 0) { - auto &&auxiliaryData = std::get(auxiliaryDataValue); - QColor textColor = auxiliaryData.colorName; - QSize size = auxiliaryData.size; - QString text = font.family() + "\n" + auxiliaryData.text; + if (auto auxiliaryData = std::get_if( + &auxiliaryDataValue)) { + QColor textColor = auxiliaryData->colorName; + QSize size = auxiliaryData->size; + QString text = font.family() + "\n" + auxiliaryData->text; - QImage image = createFontImage(text, textColor, font, size); + QImage image = createFontImage(text, textColor, font, size); - if (!image.isNull()) { - captureCallback(std::move(image), {}, {}); - return; + if (!image.isNull()) { + captureCallback(std::move(image), {}, {}); + return; + } } } abortCallback(ImageCache::AbortReason::Failed); @@ -120,15 +122,17 @@ ImageCacheCollectorInterface::ImageTuple ImageCacheFontCollector::createImage( { QFont font; if (resolveFont(QString(name), font) >= 0) { - auto &&auxiliaryData = std::get(auxiliaryDataValue); - QColor textColor = auxiliaryData.colorName; - QSize size = auxiliaryData.size; - QString text = font.family() + "\n\n" + auxiliaryData.text; + if (auto auxiliaryData = std::get_if( + &auxiliaryDataValue)) { + QColor textColor = auxiliaryData->colorName; + QSize size = auxiliaryData->size; + QString text = font.family() + "\n\n" + auxiliaryData->text; - QImage image = createFontImage(text, textColor, font, size); + QImage image = createFontImage(text, textColor, font, size); - if (!image.isNull()) - return {image, {}, {}}; + if (!image.isNull()) + return {image, {}, {}}; + } } return {}; @@ -142,15 +146,17 @@ QIcon ImageCacheFontCollector::createIcon(Utils::SmallStringView name, QFont font; if (resolveFont(QString(name), font) >= 0) { - auto &&auxiliaryData = std::get(auxiliaryDataValue); - QColor textColor = auxiliaryData.colorName; - const auto sizes = auxiliaryData.sizes; - QString text = auxiliaryData.text; + if (auto auxiliaryData = std::get_if( + &auxiliaryDataValue)) { + QColor textColor = auxiliaryData->colorName; + const auto sizes = auxiliaryData->sizes; + QString text = auxiliaryData->text; - for (QSize size : sizes) { - QImage image = createFontImage(text, textColor, font, size); - if (!image.isNull()) - icon.addPixmap(QPixmap::fromImage(image)); + for (QSize size : sizes) { + QImage image = createFontImage(text, textColor, font, size); + if (!image.isNull()) + icon.addPixmap(QPixmap::fromImage(image)); + } } } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp index 42aef4fd201..c61327ee087 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp @@ -14,10 +14,7 @@ ImageCacheGenerator::ImageCacheGenerator(ImageCacheCollectorInterface &collector ImageCacheStorageInterface &storage) : m_collector{collector} , m_storage(storage) -{ - m_backgroundThread.reset(QThread::create([this]() { startGeneration(); })); - m_backgroundThread->start(); -} +{} ImageCacheGenerator::~ImageCacheGenerator() { @@ -25,6 +22,22 @@ ImageCacheGenerator::~ImageCacheGenerator() waitForFinished(); } +void ImageCacheGenerator::ensureThreadIsRunning() +{ + if (m_finishing) + return; + + if (m_sleeping) { + if (m_backgroundThread) + m_backgroundThread->wait(); + + m_sleeping = false; + + m_backgroundThread.reset(QThread::create([this]() { startGeneration(); })); + m_backgroundThread->start(); + } +} + void ImageCacheGenerator::generateImage(Utils::SmallStringView name, Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, @@ -35,6 +48,8 @@ void ImageCacheGenerator::generateImage(Utils::SmallStringView name, { std::lock_guard lock{m_mutex}; + ensureThreadIsRunning(); + auto found = std::find_if(m_tasks.begin(), m_tasks.end(), [&](const Task &task) { return task.filePath == name && task.extraId == extraId; }); @@ -91,18 +106,14 @@ void ImageCacheGenerator::waitForFinished() void ImageCacheGenerator::startGeneration() { - while (isRunning()) { - waitForEntries(); - + while (true) { Task task; { - std::lock_guard lock{m_mutex}; + auto [lock, abort] = waitForEntries(); - if (m_finishing && m_tasks.empty()) { - m_storage.walCheckpointFull(); + if (abort) return; - } task = std::move(m_tasks.front()); @@ -141,11 +152,23 @@ void ImageCacheGenerator::startGeneration() } } -void ImageCacheGenerator::waitForEntries() +std::tuple, bool> ImageCacheGenerator::waitForEntries() { + using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_tasks.empty()) - m_condition.wait(lock, [&] { return m_tasks.size() || m_finishing; }); + if (m_finishing) + return {std::move(lock), true}; + if (m_tasks.empty()) { + auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { + return m_tasks.size() || m_finishing; + }); + + if (timedOutWithoutEntriesOrFinishing || m_finishing) { + m_sleeping = true; + return {std::move(lock), true}; + } + } + return {std::move(lock), false}; } void ImageCacheGenerator::stopThread() @@ -154,10 +177,4 @@ void ImageCacheGenerator::stopThread() m_finishing = true; } -bool ImageCacheGenerator::isRunning() -{ - std::unique_lock lock{m_mutex}; - return !m_finishing || m_tasks.size(); -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h index 60d81959262..cfac1e0ef05 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h @@ -67,10 +67,9 @@ private: }; void startGeneration(); - - void waitForEntries(); + void ensureThreadIsRunning(); + [[nodiscard]] std::tuple, bool> waitForEntries(); void stopThread(); - bool isRunning(); private: private: @@ -81,6 +80,7 @@ private: ImageCacheCollectorInterface &m_collector; ImageCacheStorageInterface &m_storage; bool m_finishing{false}; + bool m_sleeping{true}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h new file mode 100644 index 00000000000..67b0904eed0 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -0,0 +1,141 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include +#include + +namespace QmlDesigner { + +template +class TaskQueue +{ + using Tasks = std::deque; + +public: + TaskQueue(DispatchCallback dispatchCallback, ClearCallback clearCallback) + : m_dispatchCallback(std::move(dispatchCallback)) + , m_clearCallback(std::move(clearCallback)) + {} + + ~TaskQueue() { destroy(); } + + template + void addTask(Arguments &&...arguments) + { + { + std::unique_lock lock{m_mutex}; + + ensureThreadIsRunning(); + + m_tasks.emplace_back(std::forward(arguments)...); + } + m_condition.notify_all(); + } + + void clean() + { + Tasks oldTasks; + { + std::unique_lock lock{m_mutex}; + std::swap(m_tasks, oldTasks); + } + clearTasks(oldTasks); + } + +private: + void destroy() + { + stopThread(); + joinThread(); + clearTasks(m_tasks); + } + + [[nodiscard]] std::tuple, bool> waitForTasks() + { + using namespace std::literals::chrono_literals; + std::unique_lock lock{m_mutex}; + if (m_finishing) + return {std::move(lock), true}; + if (m_tasks.empty()) { + auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { + return m_tasks.size() || m_finishing; + }); + + if (timedOutWithoutEntriesOrFinishing || m_finishing) { + m_sleeping = true; + return {std::move(lock), true}; + } + } + return {std::move(lock), false}; + } + + [[nodiscard]] std::optional getTask(std::unique_lock lock) + { + auto l = std::move(lock); + + if (m_tasks.empty()) + return {}; + + Task task = std::move(m_tasks.front()); + m_tasks.pop_front(); + + return {task}; + } + + void ensureThreadIsRunning() + { + if (m_finishing || !m_sleeping) + return; + + if (m_backgroundThread.joinable()) + return; + + m_sleeping = false; + + m_backgroundThread = std::thread{[this] { + while (true) { + auto [lock, abort] = waitForTasks(); + if (abort) + return; + if (auto task = getTask(std::move(lock)); task) + m_dispatchCallback(*task); + } + }}; + } + + void clearTasks(Tasks &tasks) + { + for (Task &task : tasks) + m_clearCallback(task); + } + + void stopThread() + { + { + std::unique_lock lock{m_mutex}; + m_finishing = true; + } + m_condition.notify_all(); + } + + void joinThread() + { + if (m_backgroundThread.joinable()) + m_backgroundThread.join(); + } + +private: + Tasks m_tasks; + std::mutex m_mutex; + std::condition_variable m_condition; + std::thread m_backgroundThread; + DispatchCallback m_dispatchCallback; + ClearCallback m_clearCallback; + bool m_finishing{false}; + bool m_sleeping{true}; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/abstractproperty.h b/src/plugins/qmldesigner/designercore/include/abstractproperty.h index 6074858265a..811be48870f 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/abstractproperty.h @@ -53,8 +53,12 @@ public: AbstractProperty &operator=(AbstractProperty &&) noexcept = default; ~AbstractProperty(); AbstractProperty(const AbstractProperty &property, AbstractView *view); + AbstractProperty(const PropertyName &propertyName, + const Internal::InternalNodePointer &internalNode, + Model *model, + AbstractView *view); - PropertyName name() const; + const PropertyName &name() const; bool isValid() const; explicit operator bool() const { return isValid(); } @@ -124,9 +128,14 @@ public: } protected: - AbstractProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); - AbstractProperty(const Internal::InternalPropertyPointer &property, Model* model, AbstractView *view); - Internal::InternalNodePointer internalNode() const { return m_internalNode; } + AbstractProperty(const Internal::InternalPropertyPointer &property, + Model *model, + AbstractView *view); + + Internal::InternalNode *internalNode() const { return m_internalNode.get(); } + + Internal::InternalNodePointer internalNodeSharedPointer() const { return m_internalNode; } + Internal::ModelPrivate *privateModel() const; private: diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index f9a053a9413..72d4746944e 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h @@ -5,6 +5,8 @@ #include "asynchronousimagecacheinterface.h" +#include + #include #include #include @@ -62,16 +64,6 @@ private: RequestType requestType = RequestType::Image; }; - std::optional getEntry(); - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - RequestType requestType); - void clearEntries(); - void waitForEntries(); - void stopThread(); - bool isRunning(); static void request(Utils::SmallStringView name, Utils::SmallStringView extraId, AsynchronousExplicitImageCache::RequestType requestType, @@ -79,8 +71,28 @@ private: ImageCache::AbortCallback abortCallback, ImageCacheStorageInterface &storage); -private: - void wait(); + struct Dispatch + { + void operator()(RequestEntry &entry) + { + request(entry.name, + entry.extraId, + entry.requestType, + std::move(entry.captureCallback), + std::move(entry.abortCallback), + storage); + } + + ImageCacheStorageInterface &storage; + }; + + struct Clean + { + void operator()(RequestEntry &entry) + { + entry.abortCallback(ImageCache::AbortReason::Abort); + } + }; private: std::deque m_requestEntries; @@ -88,7 +100,7 @@ private: std::condition_variable m_condition; std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; - bool m_finishing{false}; + TaskQueue m_taskQueue{Dispatch{m_storage}, Clean{}}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index 3257f9d75c1..3eed4a4487c 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -5,6 +5,8 @@ #include "asynchronousimagecacheinterface.h" +#include + #include #include #include @@ -73,17 +75,6 @@ private: RequestType requestType = RequestType::Image; }; - std::optional getEntry(); - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData, - RequestType requestType); - void clearEntries(); - void waitForEntries(); - void stopThread(); - bool isRunning(); static void request(Utils::SmallStringView name, Utils::SmallStringView extraId, AsynchronousImageCache::RequestType requestType, @@ -94,18 +85,37 @@ private: ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider); -private: - void wait(); + struct Dispatch + { + void operator()(Entry &entry) + { + request(entry.name, + entry.extraId, + entry.requestType, + std::move(entry.captureCallback), + std::move(entry.abortCallback), + std::move(entry.auxiliaryData), + storage, + generator, + timeStampProvider); + } + + ImageCacheStorageInterface &storage; + ImageCacheGeneratorInterface &generator; + TimeStampProviderInterface &timeStampProvider; + }; + + struct Clean + { + void operator()(Entry &entry) { entry.abortCallback(ImageCache::AbortReason::Abort); } + }; private: - std::deque m_entries; - mutable std::mutex m_mutex; - std::condition_variable m_condition; - std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; ImageCacheGeneratorInterface &m_generator; TimeStampProviderInterface &m_timeStampProvider; - bool m_finishing{false}; + TaskQueue m_taskQueue{Dispatch{m_storage, m_generator, m_timeStampProvider}, + Clean{}}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h index 3bbd9d74298..23b8dc30bda 100644 --- a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h @@ -25,7 +25,7 @@ public: TextEditor::TabSettings tabSettings() const override; bool renameId(const QString &oldId, const QString &newId) override; - bool moveToComponent(int nodeOffset) override; + bool moveToComponent(int nodeOffset, const QString &importData) override; QStringList autoComplete(QTextDocument *textDocument, int position, bool explicitComplete) override; private: diff --git a/src/plugins/qmldesigner/designercore/include/bindingproperty.h b/src/plugins/qmldesigner/designercore/include/bindingproperty.h index 3ddf554c2cf..022ae17cf08 100644 --- a/src/plugins/qmldesigner/designercore/include/bindingproperty.h +++ b/src/plugins/qmldesigner/designercore/include/bindingproperty.h @@ -39,7 +39,6 @@ public: static QVariant convertToLiteral(const TypeName &typeName, const QString &expression); -protected: BindingProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model *model, diff --git a/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h b/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h index 3210734e47f..a65e934c46a 100644 --- a/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/componenttextmodifier.h @@ -36,7 +36,7 @@ public: { return false; } QStringList autoComplete(QTextDocument * textDocument, int position, bool explicitComplete) override { return m_originalModifier->autoComplete(textDocument, position, explicitComplete); } - bool moveToComponent(int /* nodeOffset */) override + bool moveToComponent(int /* nodeOffset */, const QString & /* importData */) override { return false; } private: diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index d4a087f4c7b..eecbdd96b8f 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -29,8 +29,6 @@ public: virtual QString defaultPuppetToplevelBuildDirectory() const = 0; virtual QUrl projectUrl() const = 0; virtual QString currentProjectDirPath() const = 0; - virtual QList designerSettingsEdit3DViewBackgroundColor() const = 0; - virtual QColor designerSettingsEdit3DViewGridColor() const = 0; virtual QUrl currentResourcePath() const = 0; virtual void parseItemLibraryDescriptions() = 0; virtual const DesignerSettings &designerSettings() const = 0; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 1577fc24319..c404525a9c0 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -72,7 +72,8 @@ public: Model(ProjectStorageDependencies projectStorageDependencies, Utils::SmallStringView typeName, Imports imports, - const QUrl &fileUrl); + const QUrl &fileUrl, + std::unique_ptr resourceManagement = {}); Model(const TypeName &typeName, int major = 1, int minor = 1, @@ -91,12 +92,18 @@ public: new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))); } - static ModelPointer create(ProjectStorageDependencies projectStorageDependencies, - Utils::SmallStringView typeName, - Imports imports, - const QUrl &fileUrl) + static ModelPointer create( + ProjectStorageDependencies projectStorageDependencies, + Utils::SmallStringView typeName, + Imports imports, + const QUrl &fileUrl, + std::unique_ptr resourceManagement = {}) { - return ModelPointer(new Model(projectStorageDependencies, typeName, imports, fileUrl)); + return ModelPointer(new Model(projectStorageDependencies, + typeName, + imports, + fileUrl, + std::move(resourceManagement))); } static ModelPointer create(ProjectStorageDependencies m_projectStorageDependencies, const TypeName &typeName, diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 2483893d9a8..eedc46e3b56 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -7,10 +7,46 @@ #include "qmldesignercorelib_exports.h" #include +#include namespace QmlDesigner { using PropertyName = QByteArray; +using PropertyNameView = QByteArrayView; using PropertyNameList = QList; + +template +class ByteArrayViewLiteral : public QByteArrayView +{ +public: + constexpr ByteArrayViewLiteral() noexcept + : QByteArrayView{Text, Size - 1} + {} + + const QByteArray &toByteArray() const noexcept + { + static auto b = QByteArray::fromRawData(Text, Size - 1); + + return b; + } + + operator const QByteArray &() const noexcept { return toByteArray(); } +}; + +#define MakeCppPropertyViewLiteral(name, text) \ + constexpr char name##InternalString[] = text; \ + constexpr ByteArrayViewLiteral name{}; \ + /* */ + +#define MakeHeaderPropertyViewLiteral(name, text) \ + inline constexpr char name##InternalString[] = text; \ + inline constexpr ByteArrayViewLiteral name{}; \ + /* */ + +#define MakeFunctionPropertyViewLiteral(name, text) \ + static constexpr char name##InternalString[] = text; \ + static constexpr ByteArrayViewLiteral name{}; \ + /* */ + using TypeName = QByteArray; using PropertyTypeList = QList; using IdName = QByteArray; diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 9c4b8fb4efe..676a211cd61 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -122,7 +122,7 @@ public: NodeListProperty defaultNodeListProperty() const; NodeProperty defaultNodeProperty() const; - void removeProperty(const PropertyName &name) const; //### also implement in AbstractProperty + void removeProperty(PropertyNameView name) const; //### also implement in AbstractProperty QList properties() const; QList variantProperties() const; QList nodeAbstractProperties() const; @@ -133,17 +133,17 @@ public: QList dynamicProperties() const; PropertyNameList propertyNames() const; - bool hasProperties() const; - bool hasProperty(const PropertyName &name) const; - bool hasVariantProperty(const PropertyName &name) const; - bool hasBindingProperty(const PropertyName &name) const; - bool hasSignalHandlerProperty(const PropertyName &name) const; - bool hasNodeAbstractProperty(const PropertyName &name) const; + bool hasProperty(PropertyNameView name) const; + bool hasVariantProperty(PropertyNameView name) const; + bool hasBindingProperty(PropertyNameView name) const; + bool hasSignalHandlerProperty(PropertyNameView name) const; + bool hasNodeAbstractProperty(PropertyNameView name) const; bool hasDefaultNodeAbstractProperty() const; bool hasDefaultNodeListProperty() const; bool hasDefaultNodeProperty() const; - bool hasNodeProperty(const PropertyName &name) const; - bool hasNodeListProperty(const PropertyName &name) const; + bool hasNodeProperty(PropertyNameView name) const; + bool hasNodeListProperty(PropertyNameView name) const; + bool hasProperty(PropertyNameView name, PropertyType propertyType) const; void setScriptFunctions(const QStringList &scriptFunctionList); QStringList scriptFunctions() const; @@ -225,7 +225,6 @@ public: static bool isThisOrAncestorLocked(const ModelNode &node); static ModelNode lowestCommonAncestor(const QList &nodes); - static QList pruneChildren(const QList &nodes); qint32 internalId() const; @@ -270,6 +269,8 @@ public: private: // functions Internal::InternalNodePointer internalNode() const { return m_internalNode; } + template + QList properties(PropertyType... type) const; bool hasLocked() const; private: // variables diff --git a/src/plugins/qmldesigner/designercore/include/nodeabstractproperty.h b/src/plugins/qmldesigner/designercore/include/nodeabstractproperty.h index f4db7339872..a0ff96b0d58 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeabstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/nodeabstractproperty.h @@ -35,8 +35,12 @@ public: friend auto qHash(const NodeAbstractProperty &property) { qHash(AbstractProperty(property)); } + NodeAbstractProperty(const PropertyName &propertyName, + const Internal::InternalNodePointer &internalNode, + Model *model, + AbstractView *view); + protected: - NodeAbstractProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model *model, AbstractView *view); NodeAbstractProperty(const Internal::InternalNodeAbstractPropertyPointer &property, Model *model, AbstractView *view); void reparentHere(const ModelNode &modelNode, bool isNodeList, const TypeName &typeName = TypeName()); }; diff --git a/src/plugins/qmldesigner/designercore/include/nodelistproperty.h b/src/plugins/qmldesigner/designercore/include/nodelistproperty.h index 334a2a2758c..2f7a4423f09 100644 --- a/src/plugins/qmldesigner/designercore/include/nodelistproperty.h +++ b/src/plugins/qmldesigner/designercore/include/nodelistproperty.h @@ -166,7 +166,10 @@ public: using reference = ModelNode; NodeListProperty(); - NodeListProperty(const NodeListProperty &nodeListProperty, AbstractView *view); + NodeListProperty(const PropertyName &propertyName, + const Internal::InternalNodePointer &internalNode, + Model *model, + AbstractView *view); QList toModelNodeList() const; QList toQmlObjectNodeList() const; void slide(int, int) const; @@ -196,7 +199,6 @@ public: const_iterator end() const; protected: - NodeListProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); NodeListProperty(const Internal::InternalNodeListPropertyPointer &internalNodeListProperty, Model *model, AbstractView *view); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 0ee4e19fe37..b91b650e50c 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -29,6 +29,8 @@ class Model; class AbstractProperty; class NodeMetaInfoPrivate; +enum class MetaInfoType { None, Reference, Value, Sequence }; + class QMLDESIGNERCORE_EXPORT NodeMetaInfo { public: @@ -52,6 +54,8 @@ public: explicit operator bool() const { return isValid(); } TypeId id() const { return m_typeId; } + + MetaInfoType type() const; bool isFileComponent() const; bool isProjectComponent() const; bool isInProjectModule() const; @@ -139,6 +143,8 @@ public: bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; bool isQtQuick3DLight() const; + bool isQtQuickListElement() const; + bool isQtQuickListModel() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; @@ -194,6 +200,7 @@ public: bool isEnumeration() const; QString importDirectoryPath() const; + QString requiredImportString() const; friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second) { @@ -203,6 +210,23 @@ public: return first.m_privateData == second.m_privateData; } + friend bool operator!=(const NodeMetaInfo &first, const NodeMetaInfo &second) + { + return !(first == second); + } + + SourceId propertyEditorPathId() const; + + const ProjectStorageType &projectStorage() const { return *m_projectStorage; } + + void *key() const + { + if constexpr (!useProjectStorage()) + return m_privateData.get(); + + return nullptr; + } + private: const Storage::Info::Type &typeData() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; @@ -217,3 +241,17 @@ private: using NodeMetaInfos = std::vector; } //QmlDesigner + +namespace std { +template<> +struct hash +{ + auto operator()(const QmlDesigner::NodeMetaInfo &metaInfo) const + { + if constexpr (QmlDesigner::useProjectStorage()) + return std::hash{}(metaInfo.id()); + else + return std::hash{}(metaInfo.key()); + } +}; +} // namespace std diff --git a/src/plugins/qmldesigner/designercore/include/nodeproperty.h b/src/plugins/qmldesigner/designercore/include/nodeproperty.h index c2755cd1a05..c0c266c6f3d 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeproperty.h +++ b/src/plugins/qmldesigner/designercore/include/nodeproperty.h @@ -24,8 +24,10 @@ public: void setDynamicTypeNameAndsetModelNode(const TypeName &typeName, const ModelNode &modelNode); NodeProperty(); -protected: - NodeProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); + NodeProperty(const PropertyName &propertyName, + const Internal::InternalNodePointer &internalNode, + Model *model, + AbstractView *view); }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index 843af9f8af0..2c5b429c0aa 100644 --- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h @@ -50,7 +50,7 @@ public: QStringList autoComplete(QTextDocument * /*textDocument*/, int /*position*/, bool /*explicitComplete*/) override { return {}; } - bool moveToComponent(int /* nodeOffset */) override + bool moveToComponent(int /* nodeOffset */, const QString & /* importData */) override { return false; } private: diff --git a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h index 99a704a4066..42429d1480d 100644 --- a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h @@ -54,7 +54,9 @@ public: PropertyName name() const; NodeMetaInfo propertyType() const; + NodeMetaInfo type() const; bool isWritable() const; + bool isReadOnly() const; bool isListProperty() const; bool isEnumType() const; bool isPrivate() const; @@ -71,6 +73,8 @@ public: #endif } + const ProjectStorageType &projectStorage() const { return *m_projectStorage; } + private: const Storage::Info::PropertyDeclaration &propertyData() const; TypeName propertyTypeName() const; @@ -89,4 +93,35 @@ private: using PropertyMetaInfos = std::vector; +struct CompoundPropertyMetaInfo +{ + CompoundPropertyMetaInfo(PropertyMetaInfo &&property) + : property(std::move(property)) + {} + + CompoundPropertyMetaInfo(PropertyMetaInfo &&property, const PropertyMetaInfo &parent) + : property(std::move(property)) + , parent(parent) + {} + + PropertyName name() const + { + if (parent) + return parent.name() + '.' + property.name(); + + return property.name(); + } + + PropertyMetaInfo property; + PropertyMetaInfo parent; +}; + +using CompoundPropertyMetaInfos = std::vector; + +namespace MetaInfoUtils { + +CompoundPropertyMetaInfos inflateValueProperties(PropertyMetaInfos properties); +CompoundPropertyMetaInfos inflateValueAndReadOnlyProperties(PropertyMetaInfos properties); +} // namespace MetaInfoUtils + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/qmlvisualnode.h b/src/plugins/qmldesigner/designercore/include/qmlvisualnode.h index 69917dbc23b..a299d379bcd 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlvisualnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlvisualnode.h @@ -106,10 +106,10 @@ private: class QMLDESIGNERCORE_EXPORT QmlModelStateGroup { friend class QmlObjectNode; - friend class StatesEditorView; public: QmlModelStateGroup() = default; + QmlModelStateGroup(const ModelNode &modelNode) : m_modelNode(modelNode) {} explicit operator bool() const { return m_modelNode.isValid(); } @@ -120,9 +120,6 @@ public: QmlModelState addState(const QString &name); void removeState(const QString &name); -protected: - QmlModelStateGroup(const ModelNode &modelNode) : m_modelNode(modelNode) {} - private: ModelNode m_modelNode; }; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 95e2e51d8a1..23841accda9 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -176,8 +176,8 @@ signals: void modelInterfaceProjectUpdated(); protected: // functions - void importAdded(const Import &import); - void importRemoved(const Import &import); + void importsAdded(const Imports &imports); + void importsRemoved(const Imports &imports); Internal::ModelToTextMerger *modelToTextMerger() const; Internal::TextToModelMerger *textToModelMerger() const; diff --git a/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h b/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h index d1b12d87762..671e61c9823 100644 --- a/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h +++ b/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h @@ -24,7 +24,6 @@ public: static PropertyName prefixAdded(const PropertyName &propertyName); static PropertyName prefixRemoved(const PropertyName &propertyName); -protected: SignalHandlerProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); }; @@ -40,8 +39,6 @@ public: SignalDeclarationProperty(); SignalDeclarationProperty(const SignalDeclarationProperty &property, AbstractView *view); - -protected: SignalDeclarationProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); }; diff --git a/src/plugins/qmldesigner/designercore/include/stringutils.h b/src/plugins/qmldesigner/designercore/include/stringutils.h index 38c3c260a97..b93c439719f 100644 --- a/src/plugins/qmldesigner/designercore/include/stringutils.h +++ b/src/plugins/qmldesigner/designercore/include/stringutils.h @@ -5,6 +5,8 @@ #include +using namespace Qt::StringLiterals; + namespace QmlDesigner { inline QString escape(const QString &value) @@ -14,11 +16,11 @@ inline QString escape(const QString &value) if (value.length() == 6 && value.startsWith("\\u")) //Do not double escape unicode chars return value; - result.replace(QStringLiteral("\\"), QStringLiteral("\\\\")); - result.replace(QStringLiteral("\""), QStringLiteral("\\\"")); - result.replace(QStringLiteral("\t"), QStringLiteral("\\t")); - result.replace(QStringLiteral("\r"), QStringLiteral("\\r")); - result.replace(QStringLiteral("\n"), QStringLiteral("\\n")); + result.replace("\\"_L1, "\\\\"_L1); + result.replace("\""_L1, "\\\""_L1); + result.replace("\t"_L1, "\\t"_L1); + result.replace("\r"_L1, "\\r"_L1); + result.replace("\n"_L1, "\\n"_L1); return result; } @@ -30,11 +32,11 @@ inline QString deescape(const QString &value) if (value.length() == 6 && value.startsWith("\\u")) //Ignore unicode chars return value; - result.replace(QStringLiteral("\\\\"), QStringLiteral("\\")); - result.replace(QStringLiteral("\\\""), QStringLiteral("\"")); - result.replace(QStringLiteral("\\t"), QStringLiteral("\t")); - result.replace(QStringLiteral("\\r"), QStringLiteral("\r")); - result.replace(QStringLiteral("\\n"), QStringLiteral("\n")); + result.replace("\\\\"_L1, "\\"_L1); + result.replace("\\\""_L1, "\""_L1); + result.replace("\\t"_L1, "\t"_L1); + result.replace("\\r"_L1, "\r"_L1); + result.replace("\\n"_L1, "\n"_L1); return result; } diff --git a/src/plugins/qmldesigner/designercore/include/textmodifier.h b/src/plugins/qmldesigner/designercore/include/textmodifier.h index fb442a8aca7..e361d806a16 100644 --- a/src/plugins/qmldesigner/designercore/include/textmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/textmodifier.h @@ -68,7 +68,7 @@ public: virtual bool renameId(const QString &oldId, const QString &newId) = 0; virtual QStringList autoComplete(QTextDocument * /*textDocument*/, int /*position*/, bool explicitComplete = true) = 0; - virtual bool moveToComponent(int nodeOffset) = 0; + virtual bool moveToComponent(int nodeOffset, const QString &importData) = 0; signals: void textChanged(); diff --git a/src/plugins/qmldesigner/designercore/include/variantproperty.h b/src/plugins/qmldesigner/designercore/include/variantproperty.h index f8d944bae54..dfa6073ff01 100644 --- a/src/plugins/qmldesigner/designercore/include/variantproperty.h +++ b/src/plugins/qmldesigner/designercore/include/variantproperty.h @@ -37,7 +37,6 @@ public: VariantProperty(); VariantProperty(const VariantProperty &property, AbstractView *view); -protected: VariantProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); }; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index d430c957695..7e65f9ce341 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -60,7 +60,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 85cd634c605..99d34cd1353 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1182,9 +1182,6 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) stateInstanceId = stateNode.internalId(); - QColor gridColor = m_externalDependencies.designerSettingsEdit3DViewGridColor(); - QList backgroundColor = m_externalDependencies.designerSettingsEdit3DViewBackgroundColor(); - return CreateSceneCommand(instanceContainerList, reparentContainerList, idContainerList, @@ -1199,9 +1196,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, - stateInstanceId, - backgroundColor, - gridColor); + stateInstanceId); } ClearSceneCommand NodeInstanceView::createClearSceneCommand() const @@ -1740,7 +1735,9 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand } } } else if (command.type() == PuppetToCreatorCommand::Import3DSupport) { - const QVariantMap supportMap = qvariant_cast(command.data()); + QVariantMap supportMap; + if (externalDependencies().isQt6Project()) + supportMap = qvariant_cast(command.data()); emitImport3DSupportChanged(supportMap); } else if (command.type() == PuppetToCreatorCommand::NodeAtPos) { auto data = qvariant_cast(command.data()); @@ -2175,6 +2172,7 @@ void NodeInstanceView::handleShaderChanges() } QStringList args = baseArgs; + args.append("-o"); args.append(outPath.toString()); args.append(shader); auto qsbProcess = new Utils::Process(this); diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 004bad1674e..c43a9b2fb45 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -623,6 +623,7 @@ public: QString componentFileName() const; QString importDirectoryPath() const; + Import requiredImport() const; static std::shared_ptr create(Model *model, const TypeName &type, @@ -1228,6 +1229,43 @@ QString NodeMetaInfoPrivate::importDirectoryPath() const return QString(); } +Import NodeMetaInfoPrivate::requiredImport() const +{ + if (!isValid()) + return {}; + + const auto *imports = context()->imports(document()); + ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data()); + + if (importInfo.type() == ImportType::Directory) { + return Import::createFileImport(importInfo.name(), + importInfo.version().toString(), + importInfo.as()); + } else if (importInfo.type() == ImportType::Library) { + const QStringList importPaths = model()->importPaths(); + for (const QString &importPath : importPaths) { + const QDir importDir(importPath); + const QString targetPathVersion = importDir.filePath( + importInfo.path() + '.' + QString::number(importInfo.version().majorVersion())); + if (QDir(targetPathVersion).exists()) { + return Import::createLibraryImport(importInfo.name(), + importInfo.version().toString(), + importInfo.as(), + {targetPathVersion}); + } + + const QString targetPath = importDir.filePath(importInfo.path()); + if (QDir(targetPath).exists()) { + return Import::createLibraryImport(importInfo.name(), + importInfo.version().toString(), + importInfo.as(), + {targetPath}); + } + } + } + return {}; +} + QString NodeMetaInfoPrivate::lookupName() const { QString className = QString::fromUtf8(m_qualfiedTypeName); @@ -1418,6 +1456,26 @@ bool NodeMetaInfo::isValid() const return m_privateData && m_privateData->isValid(); } +MetaInfoType NodeMetaInfo::type() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) { + switch (typeData().traits) { + case Storage::TypeTraits::Reference: + return MetaInfoType::Reference; + case Storage::TypeTraits::Value: + return MetaInfoType::Value; + case Storage::TypeTraits::Sequence: + return MetaInfoType::Sequence; + default: + break; + } + } + } + + return MetaInfoType::None; +} + bool NodeMetaInfo::isFileComponent() const { if constexpr (useProjectStorage()) @@ -1444,10 +1502,41 @@ bool NodeMetaInfo::isInProjectModule() const return false; } +namespace { + +[[maybe_unused]] auto propertyId(const ProjectStorageType &projectStorage, + TypeId typeId, + Utils::SmallStringView propertyName) +{ + auto begin = propertyName.begin(); + const auto end = propertyName.end(); + + auto found = std::find(begin, end, '.'); + auto propertyId = projectStorage.propertyDeclarationId(typeId, {begin, found}); + + if (propertyId && found != end) { + auto propertyData = projectStorage.propertyDeclaration(propertyId); + if (auto propertyTypeId = propertyData->propertyTypeId) { + begin = std::next(found); + found = std::find(begin, end, '.'); + propertyId = projectStorage.propertyDeclarationId(propertyTypeId, {begin, found}); + + if (propertyId && found != end) { + begin = std::next(found); + return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + } + } + } + + return propertyId; +} + +} // namespace + bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { if constexpr (useProjectStorage()) - return isValid() && bool(m_projectStorage->propertyDeclarationId(m_typeId, propertyName)); + return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName)); else return isValid() && m_privateData->properties().contains(propertyName); } @@ -1506,10 +1595,8 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const PropertyMetaInfo NodeMetaInfo::property(const PropertyName &propertyName) const { if constexpr (useProjectStorage()) { - if (isValid()) { - return {m_projectStorage->propertyDeclarationId(m_typeId, propertyName), - m_projectStorage}; - } + if (isValid()) + return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; } else { if (hasProperty(propertyName)) { return PropertyMetaInfo{m_privateData, propertyName}; @@ -1747,6 +1834,28 @@ QString NodeMetaInfo::importDirectoryPath() const return {}; } +QString NodeMetaInfo::requiredImportString() const +{ + if (!isValid()) + return {}; + + Import imp = m_privateData->requiredImport(); + if (!imp.isEmpty()) + return imp.toImportString(); + + return {}; +} + +SourceId NodeMetaInfo::propertyEditorPathId() const +{ + if (useProjectStorage()) { + if (isValid()) + return m_projectStorage->propertyEditorPathId(m_typeId); + } + + return SourceId{}; +} + const Storage::Info::Type &NodeMetaInfo::typeData() const { if (!m_typeData) @@ -2407,6 +2516,26 @@ bool NodeMetaInfo::isQtQuick3DLight() const } } +bool NodeMetaInfo::isQtQuickListElement() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick3D.ListElement"); + } +} + +bool NodeMetaInfo::isQtQuickListModel() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick3D.ListModel"); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { @@ -2985,7 +3114,8 @@ PropertyMetaInfo::~PropertyMetaInfo() = default; NodeMetaInfo PropertyMetaInfo::propertyType() const { if constexpr (useProjectStorage()) { - return {propertyData().typeId, m_projectStorage}; + if (isValid()) + return {propertyData().propertyTypeId, m_projectStorage}; } else { if (isValid()) return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), @@ -2997,6 +3127,16 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const return {}; } +NodeMetaInfo PropertyMetaInfo::type() const +{ + if constexpr (useProjectStorage()) { + if (isValid()) + return NodeMetaInfo(propertyData().typeId, m_projectStorage); + } + + return {}; +} + PropertyName PropertyMetaInfo::name() const { if (isValid()) { @@ -3012,15 +3152,20 @@ PropertyName PropertyMetaInfo::name() const bool PropertyMetaInfo::isWritable() const { if constexpr (useProjectStorage()) - return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); + return isValid() && !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); else return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); } +bool PropertyMetaInfo::isReadOnly() const +{ + return !isWritable(); +} + bool PropertyMetaInfo::isListProperty() const { if constexpr (useProjectStorage()) - return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; + return isValid() && propertyData().traits & Storage::PropertyDeclarationTraits::IsList; else return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); } @@ -3036,7 +3181,7 @@ bool PropertyMetaInfo::isEnumType() const bool PropertyMetaInfo::isPrivate() const { if constexpr (useProjectStorage()) - return propertyData().name.startsWith("__"); + return isValid() && propertyData().name.startsWith("__"); else return isValid() && propertyName().startsWith("__"); } @@ -3044,15 +3189,23 @@ bool PropertyMetaInfo::isPrivate() const bool PropertyMetaInfo::isPointer() const { if constexpr (useProjectStorage()) - return propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer; + return isValid() && (propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer); else return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); } +namespace { +template +bool isType(const QMetaType &type, const QMetaTypes &...types) +{ + return ((type == types) || ...); +} +} // namespace + QVariant PropertyMetaInfo::castedValue(const QVariant &value) const { if (!isValid()) - return value; + return {}; if constexpr (!useProjectStorage()) { const QVariant variant = value; @@ -3064,7 +3217,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); - if (variant.typeId() == QVariant::UserType && variant.typeId() == ModelNode::variantTypeId()) { + if (variant.typeId() == ModelNode::variantTypeId()) { return variant; } else if (typeId == QVariant::UserType && typeName == "QVariant") { return variant; @@ -3093,18 +3246,25 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const } } else { - if (isEnumType() || value.canConvert()) + if (isEnumType() && value.canConvert()) return value; - const TypeId &typeId = propertyData().typeId; + const TypeId &typeId = propertyData().propertyTypeId; + + static constexpr auto boolType = QMetaType::fromType(); + static constexpr auto intType = QMetaType::fromType(); + static constexpr auto longType = QMetaType::fromType(); + static constexpr auto longLongType = QMetaType::fromType(); + static constexpr auto floatType = QMetaType::fromType(); + static constexpr auto doubleType = QMetaType::fromType(); + static constexpr auto qStringType = QMetaType::fromType(); + static constexpr auto qUrlType = QMetaType::fromType(); + static constexpr auto qColorType = QMetaType::fromType(); if (value.typeId() == QVariant::UserType && value.typeId() == ModelNode::variantTypeId()) { return value; } else if (typeId == m_projectStorage->builtinTypeId()) { return value; - } else if (value.typeId() == QVariant::List) { - // TODO: check the contents of the list - return value; } else if (typeId == m_projectStorage->builtinTypeId()) { return value.toDouble(); } else if (typeId == m_projectStorage->builtinTypeId()) { @@ -3112,32 +3272,35 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const } else if (typeId == m_projectStorage->builtinTypeId()) { return value.toInt(); } else if (typeId == m_projectStorage->builtinTypeId()) { - return value.toBool(); + return isType(value.metaType(), boolType, intType, longType, longLongType, floatType, doubleType) + && value.toBool(); } else if (typeId == m_projectStorage->builtinTypeId()) { - return value.toString(); + if (isType(value.metaType(), qStringType)) + return value; + else + return QString{}; } else if (typeId == m_projectStorage->builtinTypeId()) { return value.toDateTime(); } else if (typeId == m_projectStorage->builtinTypeId()) { - return value.toUrl(); + if (isType(value.metaType(), qUrlType)) + return value; + else + return QUrl{}; } else if (typeId == m_projectStorage->builtinTypeId()) { - return value.value(); + if (isType(value.metaType(), qColorType)) + return value; + else + return QColor{}; } else if (typeId == m_projectStorage->builtinTypeId()) { return value.value(); } else if (typeId == m_projectStorage->builtinTypeId()) { return value.value(); } else if (typeId == m_projectStorage->builtinTypeId()) { return value.value(); - } else { - const auto typeName = propertyTypeName(); - const auto metaType = QMetaType::fromName(typeName); - auto copy = value; - bool converted = copy.convert(metaType); - if (converted) - return copy; } } - return Internal::PropertyParser::variantFromString(value.toString()); + return {}; } const Storage::Info::PropertyDeclaration &PropertyMetaInfo::propertyData() const @@ -3199,4 +3362,78 @@ NodeMetaInfo NodeMetaInfo::commonBase(const NodeMetaInfo &metaInfo) const return {}; } +namespace { + +void addCompoundProperties(CompoundPropertyMetaInfos &inflatedProperties, + const PropertyMetaInfo &parentProperty, + PropertyMetaInfos properties) +{ + for (PropertyMetaInfo &property : properties) + inflatedProperties.emplace_back(std::move(property), parentProperty); +} + +bool maybeCanHaveProperties(const NodeMetaInfo &type) +{ + if (!type) + return false; + + using namespace Storage::Info; + const auto &cache = type.projectStorage().commonTypeCache(); + auto typeId = type.id(); + const auto &typeIdsWithoutProperties = cache.typeIdsWithoutProperties(); + const auto begin = typeIdsWithoutProperties.begin(); + const auto end = typeIdsWithoutProperties.end(); + + return std::find(begin, end, typeId) == end; +} + +void addSubProperties(CompoundPropertyMetaInfos &inflatedProperties, + PropertyMetaInfo &propertyMetaInfo, + const NodeMetaInfo &propertyType) +{ + if (maybeCanHaveProperties(propertyType)) { + auto subProperties = propertyType.properties(); + if (!subProperties.empty()) { + addCompoundProperties(inflatedProperties, propertyMetaInfo, subProperties); + return; + } + } + + inflatedProperties.emplace_back(std::move(propertyMetaInfo)); +} + +} // namespace + +CompoundPropertyMetaInfos MetaInfoUtils::inflateValueProperties(PropertyMetaInfos properties) +{ + CompoundPropertyMetaInfos inflatedProperties; + inflatedProperties.reserve(properties.size() * 2); + + for (auto &property : properties) { + if (auto propertyType = property.propertyType(); propertyType.type() == MetaInfoType::Value) + addSubProperties(inflatedProperties, property, propertyType); + else + inflatedProperties.emplace_back(std::move(property)); + } + + return inflatedProperties; +} + +CompoundPropertyMetaInfos MetaInfoUtils::inflateValueAndReadOnlyProperties(PropertyMetaInfos properties) +{ + CompoundPropertyMetaInfos inflatedProperties; + inflatedProperties.reserve(properties.size() * 2); + + for (auto &property : properties) { + if (auto propertyType = property.propertyType(); + propertyType.type() == MetaInfoType::Value || property.isReadOnly()) { + addSubProperties(inflatedProperties, property, propertyType); + } else { + inflatedProperties.emplace_back(std::move(property)); + } + } + + return inflatedProperties; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index b793cf03c32..936c23fa57a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -434,11 +434,6 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components")); itemLibraryEntry.setCustomComponentSource(qmlIt.fileInfo().absoluteFilePath()); itemLibraryEntry.setRequiredImport(importUrl); - QString iconPath = qmlIt.fileInfo().absolutePath() + '/' - + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name - + Constants::QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX; - if (QFileInfo::exists(iconPath)) - itemLibraryEntry.setLibraryEntryIconPath(iconPath); itemLibraryEntry.setTypeIcon(QIcon(defaultIconPath)); // load hints file if exists diff --git a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp index e2e5800cab3..d8db3ac269d 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp @@ -71,7 +71,7 @@ AbstractView *AbstractProperty::view() const The QVariant is null if the property does not exist. */ -PropertyName AbstractProperty::name() const +const PropertyName &AbstractProperty::name() const { return m_propertyName; } @@ -126,7 +126,7 @@ VariantProperty AbstractProperty::toVariantProperty() const if (!isValid()) return {}; - VariantProperty propertyVariant(name(), internalNode(), model(), view()); + VariantProperty propertyVariant(name(), internalNodeSharedPointer(), model(), view()); if (propertyVariant.isVariantProperty()) return propertyVariant; @@ -139,7 +139,7 @@ NodeProperty AbstractProperty::toNodeProperty() const if (!isValid()) return {}; - NodeProperty propertyNode(name(), internalNode(), model(), view()); + NodeProperty propertyNode(name(), internalNodeSharedPointer(), model(), view()); if (propertyNode.isNodeProperty()) return propertyNode; @@ -152,7 +152,7 @@ SignalHandlerProperty AbstractProperty::toSignalHandlerProperty() const if (!isValid()) return {}; - SignalHandlerProperty propertyNode(name(), internalNode(), model(), view()); + SignalHandlerProperty propertyNode(name(), internalNodeSharedPointer(), model(), view()); if (propertyNode.isSignalHandlerProperty()) return propertyNode; @@ -165,7 +165,7 @@ SignalDeclarationProperty AbstractProperty::toSignalDeclarationProperty() const if (!isValid()) return {}; - SignalDeclarationProperty propertyNode(name(), internalNode(), model(), view()); + SignalDeclarationProperty propertyNode(name(), internalNodeSharedPointer(), model(), view()); if (propertyNode.isSignalDeclarationProperty()) return propertyNode; @@ -178,7 +178,7 @@ NodeListProperty AbstractProperty::toNodeListProperty() const if (!isValid()) return {}; - NodeListProperty propertyNodeList(name(), internalNode(), model(), view()); + NodeListProperty propertyNodeList(name(), internalNodeSharedPointer(), model(), view()); if (propertyNodeList.isNodeListProperty()) return propertyNodeList; @@ -191,7 +191,7 @@ NodeAbstractProperty AbstractProperty::toNodeAbstractProperty() const if (!isValid()) return {}; - NodeAbstractProperty propertyNodeAbstract(name(), internalNode(), model(), view()); + NodeAbstractProperty propertyNodeAbstract(name(), internalNodeSharedPointer(), model(), view()); if (propertyNodeAbstract.isNodeAbstractProperty()) return propertyNodeAbstract; @@ -204,7 +204,7 @@ BindingProperty AbstractProperty::toBindingProperty() const if (!isValid()) return {}; - BindingProperty propertyBinding(name(), internalNode(), model(), view()); + BindingProperty propertyBinding(name(), internalNodeSharedPointer(), model(), view()); if (propertyBinding.isBindingProperty()) return propertyBinding; @@ -217,10 +217,8 @@ bool AbstractProperty::isVariantProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isVariantProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isVariantProperty(); return false; } @@ -230,10 +228,8 @@ bool AbstractProperty::isNodeAbstractProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isNodeAbstractProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isNodeAbstractProperty(); return false; } @@ -243,10 +239,8 @@ bool AbstractProperty::isNodeListProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isNodeListProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isNodeListProperty(); return false; } @@ -256,10 +250,8 @@ bool AbstractProperty::isNodeProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isNodeProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isNodeProperty(); return false; } @@ -269,10 +261,8 @@ bool AbstractProperty::isSignalHandlerProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isSignalHandlerProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isSignalHandlerProperty(); return false; } @@ -282,10 +272,8 @@ bool AbstractProperty::isSignalDeclarationProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isSignalDeclarationProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isSignalDeclarationProperty(); return false; } @@ -295,8 +283,8 @@ PropertyType AbstractProperty::type() const if (!isValid()) return PropertyType::None; - if (internalNode()->hasProperty(name())) - return internalNode()->property(name())->propertyType(); + if (auto property = internalNode()->property(name())) + return property->propertyType(); return PropertyType::None; } @@ -306,10 +294,8 @@ bool AbstractProperty::isBindingProperty() const if (!isValid()) return false; - if (internalNode()->hasProperty(name())) { - Q_ASSERT(internalNode()->property(name())); - return internalNode()->property(name())->isBindingProperty(); - } + if (auto property = internalNode()->property(name())) + return property->isBindingProperty(); return false; } @@ -324,8 +310,8 @@ TypeName AbstractProperty::dynamicTypeName() const if (!isValid()) return {}; - if (internalNode()->hasProperty(name())) - return internalNode()->property(name())->dynamicTypeName(); + if (auto property = internalNode()->property(name())) + return property->dynamicTypeName(); return TypeName(); } diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 64912f5b73b..17cdffb6f7e 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -787,12 +787,16 @@ void AbstractView::ensureMaterialLibraryNode() } executeInTransaction(__FUNCTION__, [&] { - // Create material library node + // Create material library node +#ifdef QDS_USE_PROJECTSTORAGE + TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; + matLib = createModelNode(nodeTypeName, -1, -1); +#else auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() ? model()->qtQuick3DNodeMetaInfo() : model()->qtQuickItemMetaInfo(); matLib = createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion()); - +#endif matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); rootModelNode().defaultNodeListProperty().reparentHere(matLib); }); @@ -928,10 +932,8 @@ static int getMajorVersionFromNode(const ModelNode &modelNode) { if (modelNode.metaInfo().isValid()) { for (const NodeMetaInfo &info : modelNode.metaInfo().selfAndPrototypes()) { - if (info.typeName() == "QtQml.QtObject" || info.typeName() == "QtQuick.QtObject" - || info.typeName() == "QtQuick.Item") { + if (info.isQtObject() || info.isQtQuickItem()) return info.majorVersion(); - } } } @@ -943,7 +945,7 @@ static int getMinorVersionFromNode(const ModelNode &modelNode) if (modelNode.metaInfo().isValid()) { const NodeMetaInfos infos = modelNode.metaInfo().selfAndPrototypes(); for (const NodeMetaInfo &info : infos) { - if (info.typeName() == "QtQuick.QtObject" || info.typeName() == "QtQuick.Item") + if (info.isQtObject() || info.isQtQuickItem()) return info.minorVersion(); } } diff --git a/src/plugins/qmldesigner/designercore/model/annotation.cpp b/src/plugins/qmldesigner/designercore/model/annotation.cpp index 096cc1f931c..cbd6af83d65 100644 --- a/src/plugins/qmldesigner/designercore/model/annotation.cpp +++ b/src/plugins/qmldesigner/designercore/model/annotation.cpp @@ -7,6 +7,8 @@ #include #include +using namespace Qt::StringLiterals; + namespace QmlDesigner { static const QString s_sep = " //;;// "; //separator @@ -59,13 +61,13 @@ QString Comment::deescapedText() const { QString result = m_text; - result.replace(QStringLiteral("*\\/"), QStringLiteral("*/")); - result.replace(QStringLiteral("\\n"), QStringLiteral("\n")); - result.replace(QStringLiteral("\\r"), QStringLiteral("\r")); - result.replace(QStringLiteral("\\t"), QStringLiteral("\t")); - result.replace(QStringLiteral("\\\""), QStringLiteral("\"")); - result.replace(QStringLiteral("\\\'"), QStringLiteral("\'")); - result.replace(QStringLiteral("\\\\"), QStringLiteral("\\")); + result.replace("*\\/"_L1, "*/"_L1); + result.replace("\\n"_L1, "\n"_L1); + result.replace("\\r"_L1, "\r"_L1); + result.replace("\\t"_L1, "\t"_L1); + result.replace("\\\""_L1, "\""_L1); + result.replace("\\\'"_L1, "\'"_L1); + result.replace("\\\\"_L1, "\\"_L1); return result; } @@ -135,7 +137,7 @@ QJsonValue Comment::toJsonValue() const {{"title", m_title}, {"author", m_author}, {"text", m_text}, {"timestamp", m_timestamp}}}; }; -bool Comment::fromJsonValue(QJsonValue const &v) +bool Comment::fromJsonValue(const QJsonValue &v) { if (!v.isObject()) return false; diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp index 2f4a27e09fd..d0d8953415a 100644 --- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp @@ -100,7 +100,7 @@ static QmlJS::AST::UiObjectDefinition *getObjectDefinition(const QList( @@ -115,7 +115,8 @@ bool BaseTextEditModifier::moveToComponent(int nodeOffset) QmlJSEditor::performComponentFromObjectDef(qobject_cast( m_textEdit), document->filePath().toString(), - object); + object, + importData); return true; } } diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index bf1dc59e155..7c57e8ac355 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -8,6 +8,8 @@ #include "model.h" #include "model_p.h" +using namespace Qt::StringLiterals; + namespace QmlDesigner { bool compareBindingProperties(const QmlDesigner::BindingProperty &bindingProperty01, const QmlDesigner::BindingProperty &bindingProperty02) @@ -22,7 +24,7 @@ bool compareBindingProperties(const QmlDesigner::BindingProperty &bindingPropert BindingProperty::BindingProperty() = default; BindingProperty::BindingProperty(const BindingProperty &property, AbstractView *view) - : AbstractProperty(property.name(), property.internalNode(), property.model(), view) + : AbstractProperty(property.name(), property.internalNodeSharedPointer(), property.model(), view) { } @@ -58,14 +60,15 @@ void BindingProperty::setExpression(const QString &expression) privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setBindingProperty(internalNode(), name(), expression); + privateModel()->setBindingProperty(internalNodeSharedPointer(), name(), expression); } QString BindingProperty::expression() const { - if (isValid() && internalNode()->hasProperty(name()) - && internalNode()->property(name())->isBindingProperty()) - return internalNode()->bindingProperty(name())->expression(); + if (isValid()) { + if (auto property = internalNode()->bindingProperty(name())) + return property->expression(); + } return QString(); } @@ -119,7 +122,7 @@ ModelNode BindingProperty::resolveToModelNode() const inline static QStringList commaSeparatedSimplifiedStringList(const QString &string) { - const QStringList stringList = string.split(QStringLiteral(",")); + const QStringList stringList = string.split(","_L1); QStringList simpleList; for (const QString &simpleString : stringList) simpleList.append(simpleString.simplified()); @@ -351,7 +354,7 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName, privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression); + privateModel()->setDynamicBindingProperty(internalNodeSharedPointer(), name(), typeName, expression); } QDebug operator<<(QDebug debug, const BindingProperty &property) diff --git a/src/plugins/qmldesigner/designercore/model/documentmessage.cpp b/src/plugins/qmldesigner/designercore/model/documentmessage.cpp index 9167ca1fdfc..a92c0d4b19e 100644 --- a/src/plugins/qmldesigner/designercore/model/documentmessage.cpp +++ b/src/plugins/qmldesigner/designercore/model/documentmessage.cpp @@ -73,7 +73,7 @@ QString DocumentMessage::toString() const } if (!str.isEmpty()) - QStringLiteral(": "); + str += QStringLiteral(": "); str += description(); return str; diff --git a/src/plugins/qmldesigner/designercore/model/internalnode.cpp b/src/plugins/qmldesigner/designercore/model/internalnode.cpp index 0c81d2ab28f..bf59383747c 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnode.cpp @@ -10,6 +10,8 @@ #include "internalsignalhandlerproperty.h" #include "internalvariantproperty.h" +#include + #include #include @@ -18,10 +20,6 @@ namespace QmlDesigner { namespace Internal { -InternalNodeAbstractProperty::Pointer InternalNode::parentProperty() const -{ - return m_parentProperty.lock(); -} void InternalNode::setParentProperty(const InternalNodeAbstractProperty::Pointer &parent) { InternalNodeAbstractProperty::Pointer parentProperty = m_parentProperty.lock(); @@ -114,64 +112,79 @@ AuxiliaryDatasForType InternalNode::auxiliaryData(AuxiliaryDataType type) const return data; } -void InternalNode::removeProperty(const PropertyName &name) -{ - InternalProperty::Pointer property = m_namePropertyHash.take(name); - Q_ASSERT(property); -} - PropertyNameList InternalNode::propertyNameList() const { - return m_namePropertyHash.keys(); -} - -bool InternalNode::hasProperties() const -{ - return !m_namePropertyHash.isEmpty(); -} - -bool InternalNode::hasProperty(const PropertyName &name) const -{ - return m_namePropertyHash.contains(name); -} - -QList InternalNode::propertyList() const -{ - return m_namePropertyHash.values(); -} - -QList InternalNode::nodeAbstractPropertyList() const -{ - QList abstractPropertyList; - const QList properties = propertyList(); - for (const InternalProperty::Pointer &property : properties) { - if (property->isNodeAbstractProperty()) - abstractPropertyList.append(property->toProperty()); - } - - return abstractPropertyList; + return Utils::transform(m_nameProperties, + [](const auto &entry) { return entry.first; }); } QList InternalNode::allSubNodes() const { - QList nodeList; - const QList properties = nodeAbstractPropertyList(); - for (const InternalNodeAbstractProperty::Pointer &property : properties) { - nodeList.append(property->allSubNodes()); - } + QList nodes; + nodes.reserve(1024); - return nodeList; + addSubNodes(nodes); + + return nodes; +} + +void InternalNode::addSubNodes(QList &nodes, const InternalProperty *property) +{ + switch (property->type()) { + case PropertyType::NodeList: + property->to()->addSubNodes(nodes); + break; + case PropertyType::Node: + property->to()->addSubNodes(nodes); + break; + case PropertyType::Binding: + case PropertyType::None: + case PropertyType::SignalDeclaration: + case PropertyType::SignalHandler: + case PropertyType::Variant: + break; + } +} + +void InternalNode::addSubNodes(QList &nodes) const +{ + for (const auto &entry : m_nameProperties) + addSubNodes(nodes, entry.second.get()); +} + +void InternalNode::addDirectSubNodes(QList &nodes) const +{ + for (const auto &entry : m_nameProperties) + addDirectSubNodes(nodes, entry.second.get()); +} + +void InternalNode::addDirectSubNodes(QList &nodes, + const InternalProperty *property) +{ + switch (property->type()) { + case PropertyType::NodeList: + nodes.append(property->to()->nodes()); + break; + case PropertyType::Node: + nodes.append(property->to()->node()); + break; + case PropertyType::Binding: + case PropertyType::None: + case PropertyType::SignalDeclaration: + case PropertyType::SignalHandler: + case PropertyType::Variant: + break; + } } QList InternalNode::allDirectSubNodes() const { - QList nodeList; - const QList properties = nodeAbstractPropertyList(); - for (const InternalNodeAbstractProperty::Pointer &property : properties) { - nodeList.append(property->directSubNodes()); - } + QList nodes; + nodes.reserve(96); - return nodeList; + addDirectSubNodes(nodes); + + return nodes; } } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index bf8523653cf..af5b43eec28 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -52,7 +53,7 @@ public: , internalId(internalId) {} - InternalNodeAbstractProperty::Pointer parentProperty() const; + InternalNodeAbstractProperty::Pointer parentProperty() const { return m_parentProperty.lock(); } // Reparent within model void setParentProperty(const InternalNodeAbstractProperty::Pointer &parent); @@ -66,110 +67,119 @@ public: AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); } template - typename Type::Pointer property(const PropertyName &name) const + Type *property(PropertyNameView name) const { - auto property = m_namePropertyHash.value(name); - if (property && property->propertyType() == Type::type) - return std::static_pointer_cast(property); + auto propertyIter = m_nameProperties.find(name); + + if (propertyIter != m_nameProperties.end()) { + if (auto property = propertyIter->second.get(); + property && property->propertyType() == Type::type) + return static_cast(property); + } return {}; } - InternalProperty::Pointer property(const PropertyName &name) const + InternalProperty *property(PropertyNameView name) const { - return m_namePropertyHash.value(name); + auto propertyIter = m_nameProperties.find(name); + if (propertyIter != m_nameProperties.end()) + return propertyIter->second.get(); + + return nullptr; } - auto bindingProperty(const PropertyName &name) const + auto bindingProperty(PropertyNameView name) const { return property(name); } - auto signalHandlerProperty(const PropertyName &name) const + auto signalHandlerProperty(PropertyNameView name) const { return property(name); } - auto signalDeclarationProperty(const PropertyName &name) const + auto signalDeclarationProperty(PropertyNameView name) const { return property(name); } - auto variantProperty(const PropertyName &name) const + auto variantProperty(PropertyNameView name) const { return property(name); } - auto nodeListProperty(const PropertyName &name) const + auto nodeListProperty(PropertyNameView name) const { return property(name); } - InternalNodeAbstractProperty::Pointer nodeAbstractProperty(const PropertyName &name) const + InternalNodeAbstractProperty::Pointer nodeAbstractProperty(PropertyNameView name) const { - auto property = m_namePropertyHash.value(name); - if (property - && (property->propertyType() == PropertyType::NodeList - || property->propertyType() == PropertyType::Node)) { - return std::static_pointer_cast(property); + auto found = m_nameProperties.find(name); + if (found != m_nameProperties.end()) { + auto property = found->second; + auto propertyType = property->propertyType(); + if (propertyType == PropertyType::NodeList || propertyType == PropertyType::Node) { + return std::static_pointer_cast(property); + } } return {}; } - InternalNodeProperty::Pointer nodeProperty(const PropertyName &name) const - { - return property(name); - } + auto nodeProperty(PropertyNameView name) const { return property(name); } template - auto &addProperty(const PropertyName &name) + Type *addProperty(const PropertyName &name) { auto newProperty = std::make_shared(name, shared_from_this()); - auto inserted = m_namePropertyHash.insert(name, std::move(newProperty)); + auto pointer = newProperty.get(); + m_nameProperties.try_emplace(name, std::move(newProperty)); - return *inserted->get(); + return pointer; } - void addBindingProperty(const PropertyName &name) + auto addBindingProperty(const PropertyName &name) { - addProperty(name); + return addProperty(name); } - void addSignalHandlerProperty(const PropertyName &name) + auto addSignalHandlerProperty(const PropertyName &name) { - addProperty(name); + return addProperty(name); } - void addSignalDeclarationProperty(const PropertyName &name) + auto addSignalDeclarationProperty(const PropertyName &name) { - addProperty(name); + return addProperty(name); } - void addNodeListProperty(const PropertyName &name) + auto addNodeListProperty(const PropertyName &name) { - addProperty(name); + return addProperty(name); } - void addVariantProperty(const PropertyName &name) + auto addVariantProperty(const PropertyName &name) { - addProperty(name); + return addProperty(name); } - void addNodeProperty(const PropertyName &name, const TypeName &dynamicTypeName) + auto addNodeProperty(const PropertyName &name, const TypeName &dynamicTypeName) { - auto &property = addProperty(name); - property.setDynamicTypeName(dynamicTypeName); + auto property = addProperty(name); + property->setDynamicTypeName(dynamicTypeName); + + return property; } PropertyNameList propertyNameList() const; - bool hasProperties() const; - bool hasProperty(const PropertyName &name) const; - - QList propertyList() const; - QList nodeAbstractPropertyList() const; QList allSubNodes() const; QList allDirectSubNodes() const; + void addSubNodes(QList &nodes) const; + void addDirectSubNodes(QList &nodes) const; + static void addSubNodes(QList &nodes, const InternalProperty *property); + static void addDirectSubNodes(QList &nodes, const InternalProperty *property); friend bool operator<(const InternalNode::Pointer &firstNode, const InternalNode::Pointer &secondNode) @@ -185,8 +195,17 @@ public: friend size_t qHash(const InternalNodePointer &node) { return ::qHash(node.get()); } -protected: - void removeProperty(const PropertyName &name); + void removeProperty(PropertyNameView name) + { + auto found = m_nameProperties.find(name); + m_nameProperties.erase(found); // C++ 23 -> m_nameProperties.erase(name) + } + + using PropertyDict = std::map>; + + PropertyDict::const_iterator begin() const { return m_nameProperties.begin(); } + + PropertyDict::const_iterator end() const { return m_nameProperties.end(); } public: TypeName typeName; @@ -206,7 +225,7 @@ public: private: AuxiliaryDatas m_auxiliaryDatas; InternalNodeAbstractProperty::WeakPointer m_parentProperty; - QHash m_namePropertyHash; + PropertyDict m_nameProperties; }; } // Internal diff --git a/src/plugins/qmldesigner/designercore/model/internalnodeabstractproperty.h b/src/plugins/qmldesigner/designercore/model/internalnodeabstractproperty.h index 72e01d019b2..7205fe7377d 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodeabstractproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalnodeabstractproperty.h @@ -17,17 +17,12 @@ public: using Pointer = std::shared_ptr; using WeakPointer = std::weak_ptr; - virtual QList allSubNodes() const = 0; - virtual QList directSubNodes() const = 0; - virtual bool isEmpty() const = 0; virtual int count() const = 0; virtual int indexOf(const InternalNodePointer &node) const = 0; bool isValid() const override; - using InternalProperty::remove; // keep the virtual remove(...) function around - protected: InternalNodeAbstractProperty(const PropertyName &name, const InternalNodePointer &propertyOwner, diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp index f5c46627856..c5aaa8fa94a 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp @@ -60,20 +60,22 @@ void InternalNodeListProperty::slide(int from, int to) m_nodeList.insert(to, internalNode); } -QList InternalNodeListProperty::allSubNodes() const +void InternalNodeListProperty::addSubNodes(QList &container) const { - QList nodeList; - for (const InternalNode::Pointer &childNode : std::as_const(m_nodeList)) { - nodeList.append(childNode->allSubNodes()); - nodeList.append(childNode); + for (const auto &node : std::as_const(m_nodeList)) { + container.push_back(node); + node->addSubNodes(container); } - - return nodeList; } -QList InternalNodeListProperty::directSubNodes() const +QList InternalNodeListProperty::allSubNodes() const { - return nodeList(); + QList nodes; + nodes.reserve(1024); + + addSubNodes(nodes); + + return nodes; } } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h index 2e15fdf8aef..01b508b80ab 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h @@ -44,14 +44,18 @@ public: return *found; } - QList allSubNodes() const override; - QList directSubNodes() const override; + QList allSubNodes() const; const QList &nodeList() const; void slide(int from, int to); + void addSubNodes(QList &container) const; + QList::iterator begin() { return m_nodeList.begin(); } + QList::iterator end() { return m_nodeList.end(); } + const QList &nodes() const { return m_nodeList; } + protected: void add(const InternalNodePointer &node) override; void remove(const InternalNodePointer &node) override; diff --git a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp index 0dd286e765e..df9f5593725 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp @@ -39,11 +39,6 @@ bool InternalNodeProperty::isValid() const return InternalProperty::isValid() && isNodeProperty(); } -InternalNode::Pointer InternalNodeProperty::node() const -{ - return m_node; -} - void InternalNodeProperty::remove([[maybe_unused]] const InternalNode::Pointer &node) { Q_ASSERT(m_node == node); @@ -59,24 +54,18 @@ void InternalNodeProperty::add(const InternalNode::Pointer &node) QList InternalNodeProperty::allSubNodes() const { - QList nodeList; + QList nodes; + nodes.reserve(1024); - if (node()) { - nodeList.append(node()); - nodeList.append(node()->allSubNodes()); - } + addSubNodes(nodes); - return nodeList; + return nodes; } -QList InternalNodeProperty::directSubNodes() const +void InternalNodeProperty::addSubNodes(QList &container) const { - QList nodeList; - - if (node()) - nodeList.append(node()); - - return nodeList; + container.push_back(m_node); + m_node->addSubNodes(container); } } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.h b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.h index 65b9b895e8a..bd4b66304e7 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.h @@ -21,10 +21,10 @@ public: int count() const override; int indexOf(const InternalNodePointer &node) const override; - QList allSubNodes() const override; - QList directSubNodes() const override; + QList allSubNodes() const; + void addSubNodes(QList &container) const; - InternalNodePointer node() const; + const InternalNodePointer &node() const { return m_node; } protected: void add(const InternalNodePointer &node) override; diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp index 076c14fa9b6..a5735425bdb 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp @@ -37,17 +37,6 @@ PropertyName InternalProperty::name() const return m_name; } -InternalNode::Pointer InternalProperty::propertyOwner() const -{ - return m_propertyOwner.lock(); -} - -void InternalProperty::remove() -{ - propertyOwner()->removeProperty(name()); - m_propertyOwner.reset(); -} - TypeName InternalProperty::dynamicTypeName() const { return m_dynamicType; diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h index 1f24b825093..66d0bcaa881 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h @@ -8,6 +8,7 @@ #include #include +#include namespace QmlDesigner { @@ -24,7 +25,7 @@ class InternalNode; using InternalNodePointer = std::shared_ptr; -template +template struct TypeLookup {}; @@ -68,6 +69,21 @@ struct TypeLookup using Type = InternalVariantProperty; }; +template<> +struct TypeLookup +{ + using Type = InternalNodeAbstractProperty; +}; + +template<> +struct TypeLookup +{ + using Type = InternalNodeAbstractProperty; +}; + +template +using type_lookup_t = typename TypeLookup::Type; + class QMLDESIGNERCORE_EXPORT InternalProperty : public std::enable_shared_from_this { public: @@ -103,24 +119,60 @@ public: return std::static_pointer_cast(shared_from_this()); } - template - auto to() + template + auto toShared() { - if (propertyType == m_propertyType) - return std::static_pointer_cast::Type>( - shared_from_this()); + using Type = type_lookup_t; - return std::shared_ptr::Type>{}; + if (((propertyType == m_propertyType) || ...)) + return std::static_pointer_cast(shared_from_this()); + + return std::shared_ptr{}; } - InternalNodePointer propertyOwner() const; + template + auto toShared() const + { + using Type = const type_lookup_t; - virtual void remove(); + if (((propertyType == m_propertyType) || ...)) + return std::static_pointer_cast(shared_from_this()); + + return std::shared_ptr{}; + } + + template + auto *to() + { + using Type = type_lookup_t; + + if (((propertyType == m_propertyType) || ...)) + return static_cast(this); + + return static_cast(nullptr); + } + + template + const auto *to() const + { + using Type = const type_lookup_t; + + if (((propertyType == m_propertyType) || ...)) + return static_cast(this); + + return static_cast(nullptr); + } + + const InternalNodePointer propertyOwner() const { return m_propertyOwner.lock(); } + + InternalNodePointer propertyOwner() { return m_propertyOwner.lock(); } TypeName dynamicTypeName() const; void resetDynamicTypeName(); + PropertyType type() const { return m_propertyType; } + protected: // functions InternalProperty(const PropertyName &name, const InternalNodePointer &propertyOwner, diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 653ffea077a..d511ae5d64d 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -88,10 +88,12 @@ ModelPrivate::ModelPrivate(Model *model, ProjectStorageDependencies projectStorageDependencies, Utils::SmallStringView typeName, Imports imports, - const QUrl &fileUrl) + const QUrl &fileUrl, + std::unique_ptr resourceManagement) : projectStorage{&projectStorageDependencies.storage} , pathCache{&projectStorageDependencies.cache} , m_model{model} + , m_resourceManagement{std::move(resourceManagement)} { setFileUrl(fileUrl); changeImports(std::move(imports), {}); @@ -413,7 +415,7 @@ void ModelPrivate::removeNode(const InternalNodePointer &node) notifyNodeAboutToBeRemoved(node); - InternalNodeAbstractPropertyPointer oldParentProperty(node->parentProperty()); + auto oldParentProperty = node->parentProperty(); removeAllSubNodes(node); removeNodeFromModel(node); @@ -426,7 +428,7 @@ void ModelPrivate::removeNode(const InternalNodePointer &node) } if (oldParentProperty && oldParentProperty->isEmpty()) { - removePropertyWithoutNotification(oldParentProperty); + removePropertyWithoutNotification(oldParentProperty.get()); propertyChangeFlags |= AbstractView::EmptyPropertiesRemoved; } @@ -465,7 +467,7 @@ void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString & } } -bool ModelPrivate::propertyNameIsValid(const PropertyName &propertyName) const +bool ModelPrivate::propertyNameIsValid(PropertyNameView propertyName) { if (propertyName.isEmpty()) return false; @@ -753,8 +755,7 @@ void ModelPrivate::notifyPropertiesRemoved(const QList &propertyPa }); } -void ModelPrivate::notifyPropertiesAboutToBeRemoved( - const QList &internalPropertyList) +void ModelPrivate::notifyPropertiesAboutToBeRemoved(const QList &internalPropertyList) { bool resetModel = false; QString description; @@ -762,9 +763,12 @@ void ModelPrivate::notifyPropertiesAboutToBeRemoved( try { if (rewriterView()) { QList propertyList; - for (const InternalPropertyPointer &property : internalPropertyList) { - AbstractProperty newProperty(property->name(), property->propertyOwner(), m_model, rewriterView()); - propertyList.append(newProperty); + for (InternalProperty *property : internalPropertyList) { + AbstractProperty newProperty(property->name(), + property->propertyOwner(), + m_model, + rewriterView()); + propertyList.append(newProperty); } rewriterView()->propertiesAboutToBeRemoved(propertyList); @@ -777,7 +781,7 @@ void ModelPrivate::notifyPropertiesAboutToBeRemoved( for (const QPointer &view : enabledViews()) { QList propertyList; Q_ASSERT(view != nullptr); - for (const InternalPropertyPointer &property : internalPropertyList) { + for (auto property : internalPropertyList) { AbstractProperty newProperty(property->name(), property->propertyOwner(), m_model, view.data()); propertyList.append(newProperty); } @@ -792,7 +796,7 @@ void ModelPrivate::notifyPropertiesAboutToBeRemoved( if (nodeInstanceView()) { QList propertyList; - for (const InternalPropertyPointer &property : internalPropertyList) { + for (auto property : internalPropertyList) { AbstractProperty newProperty(property->name(), property->propertyOwner(), m_model, nodeInstanceView()); propertyList.append(newProperty); } @@ -900,11 +904,11 @@ void ModelPrivate::notifyNodeIdChanged(const InternalNodePointer &node, } void ModelPrivate::notifyBindingPropertiesAboutToBeChanged( - const QList &internalPropertyList) + const QList &internalPropertyList) { notifyNodeInstanceViewLast([&](AbstractView *view) { QList propertyList; - for (const InternalBindingPropertyPointer &bindingProperty : internalPropertyList) { + for (auto bindingProperty : internalPropertyList) { propertyList.append(BindingProperty(bindingProperty->name(), bindingProperty->propertyOwner(), m_model, @@ -914,13 +918,12 @@ void ModelPrivate::notifyBindingPropertiesAboutToBeChanged( }); } -void ModelPrivate::notifyBindingPropertiesChanged( - const QList &internalPropertyList, - AbstractView::PropertyChangeFlags propertyChange) +void ModelPrivate::notifyBindingPropertiesChanged(const QList &internalPropertyList, + AbstractView::PropertyChangeFlags propertyChange) { notifyNodeInstanceViewLast([&](AbstractView *view) { QList propertyList; - for (const InternalBindingPropertyPointer &bindingProperty : internalPropertyList) { + for (auto bindingProperty : internalPropertyList) { propertyList.append(BindingProperty(bindingProperty->name(), bindingProperty->propertyOwner(), m_model, @@ -931,12 +934,12 @@ void ModelPrivate::notifyBindingPropertiesChanged( } void ModelPrivate::notifySignalHandlerPropertiesChanged( - const QVector &internalPropertyList, + const QVector &internalPropertyList, AbstractView::PropertyChangeFlags propertyChange) { notifyNodeInstanceViewLast([&](AbstractView *view) { QVector propertyList; - for (const InternalSignalHandlerPropertyPointer &signalHandlerProperty : internalPropertyList) { + for (auto signalHandlerProperty : internalPropertyList) { propertyList.append(SignalHandlerProperty(signalHandlerProperty->name(), signalHandlerProperty->propertyOwner(), m_model, @@ -947,12 +950,12 @@ void ModelPrivate::notifySignalHandlerPropertiesChanged( } void ModelPrivate::notifySignalDeclarationPropertiesChanged( - const QVector &internalPropertyList, + const QVector &internalPropertyList, AbstractView::PropertyChangeFlags propertyChange) { notifyNodeInstanceViewLast([&](AbstractView *view) { QVector propertyList; - for (const InternalSignalDeclarationPropertyPointer &signalHandlerProperty : internalPropertyList) { + for (auto signalHandlerProperty : internalPropertyList) { propertyList.append(SignalDeclarationProperty(signalHandlerProperty->name(), signalHandlerProperty->propertyOwner(), m_model, @@ -986,7 +989,8 @@ void ModelPrivate::notifyVariantPropertiesChanged(const InternalNodePointer &nod } void ModelPrivate::notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodePointer &newParent, + const PropertyName &newPropertyName, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange) @@ -995,11 +999,15 @@ void ModelPrivate::notifyNodeAboutToBeReparent(const InternalNodePointer &node, NodeAbstractProperty newProperty; NodeAbstractProperty oldProperty; - if (!oldPropertyName.isEmpty() && oldParent->isValid) + if (!oldPropertyName.isEmpty() && oldParent && oldParent->isValid) oldProperty = NodeAbstractProperty(oldPropertyName, oldParent, m_model, view); - if (newPropertyParent) - newProperty = NodeAbstractProperty(newPropertyParent, m_model, view); + if (!newPropertyName.isEmpty() && newParent && newParent->isValid) { + newProperty = NodeAbstractProperty(newPropertyName, + newParent, + m_model, + view); + } ModelNode modelNode(node, m_model, view); view->nodeAboutToBeReparented(modelNode, newProperty, oldProperty, propertyChange); @@ -1007,7 +1015,7 @@ void ModelPrivate::notifyNodeAboutToBeReparent(const InternalNodePointer &node, } void ModelPrivate::notifyNodeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodeAbstractProperty *newPropertyParent, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange) @@ -1019,28 +1027,39 @@ void ModelPrivate::notifyNodeReparent(const InternalNodePointer &node, if (!oldPropertyName.isEmpty() && oldParent->isValid) oldProperty = NodeAbstractProperty(oldPropertyName, oldParent, m_model, view); - if (newPropertyParent) - newProperty = NodeAbstractProperty(newPropertyParent, m_model, view); + if (newPropertyParent) { + newProperty = NodeAbstractProperty(newPropertyParent->name(), + newPropertyParent->propertyOwner(), + m_model, + view); + } + ModelNode modelNode(node, m_model, view); view->nodeReparented(modelNode, newProperty, oldProperty, propertyChange); }); } -void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty, +void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListProperty *internalListProperty, const InternalNodePointer &node, int oldIndex) { notifyNodeInstanceViewLast([&](AbstractView *view) { - NodeListProperty nodeListProperty(internalListProperty, m_model, view); + NodeListProperty nodeListProperty(internalListProperty->name(), + internalListProperty->propertyOwner(), + m_model, + view); view->nodeOrderChanged(nodeListProperty, ModelNode(node, m_model, view), oldIndex); }); } -void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty) +void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListProperty *internalListProperty) { notifyNodeInstanceViewLast([&](AbstractView *view) { - NodeListProperty nodeListProperty(internalListProperty, m_model, view); + NodeListProperty nodeListProperty(internalListProperty->name(), + internalListProperty->propertyOwner(), + m_model, + view); view->nodeOrderChanged(nodeListProperty); }); } @@ -1118,9 +1137,9 @@ QVector ModelPrivate::toInternalNodeVector(const QVector ModelPrivate::toInternalProperties(const AbstractProperties &properties) +QList ModelPrivate::toInternalProperties(const AbstractProperties &properties) { - QList internalProperties; + QList internalProperties; internalProperties.reserve(properties.size()); for (const auto &property : properties) { @@ -1133,10 +1152,10 @@ QList ModelPrivate::toInternalProperties(const Abstract return internalProperties; } -QList> ModelPrivate::toInternalBindingProperties( +QList> ModelPrivate::toInternalBindingProperties( const ModelResourceSet::SetExpressions &setExpressions) { - QList> internalProperties; + QList> internalProperties; internalProperties.reserve(setExpressions.size()); for (const auto &setExpression : setExpressions) { @@ -1195,45 +1214,49 @@ void ModelPrivate::deselectNode(const InternalNodePointer &node) setSelectedNodes(selectedNodeList); } -void ModelPrivate::removePropertyWithoutNotification(const InternalPropertyPointer &property) +void ModelPrivate::removePropertyWithoutNotification(InternalProperty *property) { - if (property->isNodeAbstractProperty()) { - const auto &&allSubNodes = property->toProperty()->allSubNodes(); + if (auto nodeListProperty = property->to()) { + const auto allSubNodes = nodeListProperty->allSubNodes(); for (const InternalNodePointer &node : allSubNodes) removeNodeFromModel(node); + } else if (auto nodeProperty = property->to()) { + if (auto node = nodeProperty->node()) + removeNodeFromModel(node); } - property->remove(); + auto propertyOwner = property->propertyOwner(); + propertyOwner->removeProperty(property->name()); } -static QList toPropertyPairList(const QList &propertyList) +static QList toPropertyPairList(const QList &propertyList) { QList propertyPairList; propertyPairList.reserve(propertyList.size()); - for (const InternalPropertyPointer &property : propertyList) + for (const InternalProperty *property : propertyList) propertyPairList.append({property->propertyOwner(), property->name()}); return propertyPairList; } -void ModelPrivate::removePropertyAndRelatedResources(const InternalPropertyPointer &property) +void ModelPrivate::removePropertyAndRelatedResources(InternalProperty *property) { if (m_resourceManagement) { - handleResourceSet( - m_resourceManagement->removeProperties({AbstractProperty{property, m_model, nullptr}}, - m_model)); + handleResourceSet(m_resourceManagement->removeProperties( + {AbstractProperty{property->name(), property->propertyOwner(), m_model, nullptr}}, + m_model)); } else { removeProperty(property); } } -void ModelPrivate::removeProperty(const InternalPropertyPointer &property) +void ModelPrivate::removeProperty(InternalProperty *property) { removeProperties({property}); } -void ModelPrivate::removeProperties(const QList &properties) +void ModelPrivate::removeProperties(const QList &properties) { if (properties.isEmpty()) return; @@ -1242,7 +1265,7 @@ void ModelPrivate::removeProperties(const QList &proper const QList propertyPairList = toPropertyPairList(properties); - for (const auto &property : properties) + for (auto property : properties) removePropertyWithoutNotification(property); notifyPropertiesRemoved(propertyPairList); @@ -1253,12 +1276,14 @@ void ModelPrivate::setBindingProperty(const InternalNodePointer &node, const QString &expression) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addBindingProperty(name); + InternalBindingProperty *bindingProperty = nullptr; + if (auto property = node->property(name)) { + bindingProperty = property->to(); + } else { + bindingProperty = node->addBindingProperty(name); propertyChange = AbstractView::PropertiesAdded; } - InternalBindingPropertyPointer bindingProperty = node->bindingProperty(name); notifyBindingPropertiesAboutToBeChanged({bindingProperty}); bindingProperty->setExpression(expression); notifyBindingPropertiesChanged({bindingProperty}, propertyChange); @@ -1283,12 +1308,14 @@ void ModelPrivate::setBindingProperties(const ModelResourceSet::SetExpressions & void ModelPrivate::setSignalHandlerProperty(const InternalNodePointer &node, const PropertyName &name, const QString &source) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addSignalHandlerProperty(name); + InternalSignalHandlerProperty *signalHandlerProperty = nullptr; + if (auto property = node->property(name)) { + signalHandlerProperty = property->to(); + } else { + signalHandlerProperty = node->addSignalHandlerProperty(name); propertyChange = AbstractView::PropertiesAdded; } - InternalSignalHandlerPropertyPointer signalHandlerProperty = node->signalHandlerProperty(name); signalHandlerProperty->setSource(source); notifySignalHandlerPropertiesChanged({signalHandlerProperty}, propertyChange); } @@ -1296,12 +1323,14 @@ void ModelPrivate::setSignalHandlerProperty(const InternalNodePointer &node, con void ModelPrivate::setSignalDeclarationProperty(const InternalNodePointer &node, const PropertyName &name, const QString &signature) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addSignalDeclarationProperty(name); + InternalSignalDeclarationProperty *signalDeclarationProperty = nullptr; + if (auto property = node->property(name)) { + signalDeclarationProperty = property->to(); + } else { + signalDeclarationProperty = node->addSignalDeclarationProperty(name); propertyChange = AbstractView::PropertiesAdded; } - InternalSignalDeclarationPropertyPointer signalDeclarationProperty = node->signalDeclarationProperty(name); signalDeclarationProperty->setSignature(signature); notifySignalDeclarationPropertiesChanged({signalDeclarationProperty}, propertyChange); } @@ -1309,13 +1338,16 @@ void ModelPrivate::setSignalDeclarationProperty(const InternalNodePointer &node, void ModelPrivate::setVariantProperty(const InternalNodePointer &node, const PropertyName &name, const QVariant &value) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addVariantProperty(name); + InternalVariantProperty *variantProperty = nullptr; + if (auto property = node->property(name)) { + variantProperty = property->to(); + } else { + variantProperty = node->addVariantProperty(name); propertyChange = AbstractView::PropertiesAdded; } - node->variantProperty(name)->setValue(value); - node->variantProperty(name)->resetDynamicTypeName(); + variantProperty->setValue(value); + variantProperty->resetDynamicTypeName(); notifyVariantPropertiesChanged(node, PropertyNameList({name}), propertyChange); } @@ -1325,12 +1357,15 @@ void ModelPrivate::setDynamicVariantProperty(const InternalNodePointer &node, const QVariant &value) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addVariantProperty(name); + InternalVariantProperty *variantProperty = nullptr; + if (auto property = node->property(name)) { + variantProperty = property->to(); + } else { + variantProperty = node->addVariantProperty(name); propertyChange = AbstractView::PropertiesAdded; } - node->variantProperty(name)->setDynamicValue(dynamicPropertyType, value); + variantProperty->setDynamicValue(dynamicPropertyType, value); notifyVariantPropertiesChanged(node, PropertyNameList({name}), propertyChange); } @@ -1340,12 +1375,14 @@ void ModelPrivate::setDynamicBindingProperty(const InternalNodePointer &node, const QString &expression) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!node->hasProperty(name)) { - node->addBindingProperty(name); + InternalBindingProperty *bindingProperty = nullptr; + if (auto property = node->property(name)) { + bindingProperty = property->to(); + } else { + bindingProperty = node->addBindingProperty(name); propertyChange = AbstractView::PropertiesAdded; } - InternalBindingPropertyPointer bindingProperty = node->bindingProperty(name); notifyBindingPropertiesAboutToBeChanged({bindingProperty}); bindingProperty->setDynamicExpression(dynamicPropertyType, expression); notifyBindingPropertiesChanged({bindingProperty}, propertyChange); @@ -1358,13 +1395,6 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, const TypeName &dynamicTypeName) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!parentNode->hasProperty(name)) { - if (list) - parentNode->addNodeListProperty(name); - else - parentNode->addNodeProperty(name, dynamicTypeName); - propertyChange |= AbstractView::PropertiesAdded; - } InternalNodeAbstractPropertyPointer oldParentProperty(childNode->parentProperty()); InternalNodePointer oldParentNode; @@ -1374,16 +1404,34 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, oldParentPropertyName = childNode->parentProperty()->name(); } - InternalNodeAbstractPropertyPointer newParentProperty(parentNode->nodeAbstractProperty(name)); + notifyNodeAboutToBeReparent(childNode, + parentNode, + name, + oldParentNode, + oldParentPropertyName, + propertyChange); + + InternalNodeAbstractProperty *newParentProperty = nullptr; + if (auto property = parentNode->property(name)) { + newParentProperty = property->to(); + } else { + if (list) + newParentProperty = parentNode->addNodeListProperty(name); + else + newParentProperty = parentNode->addNodeProperty(name, dynamicTypeName); + + propertyChange |= AbstractView::PropertiesAdded; + } + Q_ASSERT(newParentProperty); - notifyNodeAboutToBeReparent(childNode, newParentProperty, oldParentNode, oldParentPropertyName, propertyChange); - - if (newParentProperty) - childNode->setParentProperty(newParentProperty); + if (newParentProperty) { + childNode->setParentProperty( + newParentProperty->toShared()); + } if (oldParentProperty && oldParentProperty->isValid() && oldParentProperty->isEmpty()) { - removePropertyWithoutNotification(oldParentProperty); + removePropertyWithoutNotification(oldParentProperty.get()); propertyChange |= AbstractView::EmptyPropertiesRemoved; } @@ -1402,7 +1450,11 @@ void ModelPrivate::clearParent(const InternalNodePointer &node) } node->resetParentProperty(); - notifyNodeReparent(node, InternalNodeAbstractPropertyPointer(), oldParentNode, oldParentPropertyName, AbstractView::NoAdditionalChanges); + notifyNodeReparent(node, + nullptr, + oldParentNode, + oldParentPropertyName, + AbstractView::NoAdditionalChanges); } void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion) @@ -1431,7 +1483,7 @@ void ModelPrivate::setNodeSource(const InternalNodePointer &node, const QString void ModelPrivate::changeNodeOrder(const InternalNodePointer &parentNode, const PropertyName &listPropertyName, int from, int to) { - InternalNodeListPropertyPointer nodeList(parentNode->nodeListProperty(listPropertyName)); + auto nodeList = parentNode->nodeListProperty(listPropertyName); Q_ASSERT(nodeList); nodeList->slide(from, to); @@ -1597,9 +1649,14 @@ Model::Model(ProjectStorageDependencies projectStorageDependencies, Model::Model(ProjectStorageDependencies projectStorageDependencies, Utils::SmallStringView typeName, Imports imports, - const QUrl &fileUrl) - : d(std::make_unique( - this, projectStorageDependencies, typeName, std::move(imports), fileUrl)) + const QUrl &fileUrl, + std::unique_ptr resourceManagement) + : d(std::make_unique(this, + projectStorageDependencies, + typeName, + std::move(imports), + fileUrl, + std::move(resourceManagement))) {} Model::Model(const TypeName &typeName, @@ -1725,7 +1782,7 @@ QString Model::generateNewId(const QString &prefixName, int counter = 0; - QString newBaseId = QString(QStringLiteral("%1")).arg(firstCharToLower(prefixName)); + QString newBaseId = QStringView(u"%1").arg(firstCharToLower(prefixName)); newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); if (!newBaseId.isEmpty()) { @@ -1742,9 +1799,9 @@ QString Model::generateNewId(const QString &prefixName, isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId) - || d->rootNode()->hasProperty(newId.toUtf8())) { + || d->rootNode()->property(newId.toUtf8())) { ++counter; - newId = QString(QStringLiteral("%1%2")).arg(firstCharToLower(newBaseId)).arg(counter); + newId = QStringView(u"%1%2").arg(firstCharToLower(newBaseId)).arg(counter); } return newId; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 324df37ddb2..8ba7c69b595 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -43,14 +43,6 @@ class InternalVariantProperty; class InternalNodeAbstractProperty; class InternalNodeListProperty; -using InternalNodePointer = std::shared_ptr; -using InternalPropertyPointer = std::shared_ptr; -using InternalBindingPropertyPointer = std::shared_ptr; -using InternalSignalHandlerPropertyPointer = std::shared_ptr; -using InternalSignalDeclarationPropertyPointer = std::shared_ptr; -using InternalVariantPropertyPointer = std::shared_ptr; -using InternalNodeAbstractPropertyPointer = std::shared_ptr; -using InternalNodeListPropertyPointer = std::shared_ptr; using PropertyPair = QPair; class ModelPrivate; @@ -108,7 +100,8 @@ public: ProjectStorageDependencies m_projectStorageDependencies, Utils::SmallStringView typeName, Imports imports, - const QUrl &filePath); + const QUrl &filePath, + std::unique_ptr resourceManagement); ModelPrivate(Model *model, const TypeName &type, int major, @@ -156,12 +149,13 @@ public: void notifyNodeCreated(const InternalNodePointer &newNode); void notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodePointer &newParent, + const PropertyName &newPropertyName, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange); void notifyNodeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodeAbstractProperty *newPropertyParent, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange); @@ -174,19 +168,25 @@ public: void notifyNodeTypeChanged(const InternalNodePointer &node, const TypeName &type, int majorVersion, int minorVersion); void notifyPropertiesRemoved(const QList &propertyList); - void notifyPropertiesAboutToBeRemoved(const QList &internalPropertyList); + void notifyPropertiesAboutToBeRemoved(const QList &internalPropertyList); void notifyBindingPropertiesAboutToBeChanged( - const QList &internalPropertyList); - void notifyBindingPropertiesChanged(const QList &internalPropertyList, AbstractView::PropertyChangeFlags propertyChange); - void notifySignalHandlerPropertiesChanged(const QVector &propertyList, AbstractView::PropertyChangeFlags propertyChange); - void notifySignalDeclarationPropertiesChanged(const QVector &propertyList, AbstractView::PropertyChangeFlags propertyChange); + const QList &internalPropertyList); + void notifyBindingPropertiesChanged( + const QList &internalPropertyList, + AbstractView::PropertyChangeFlags propertyChange); + void notifySignalHandlerPropertiesChanged( + const QVector &propertyList, + AbstractView::PropertyChangeFlags propertyChange); + void notifySignalDeclarationPropertiesChanged( + const QVector &propertyList, + AbstractView::PropertyChangeFlags propertyChange); void notifyVariantPropertiesChanged(const InternalNodePointer &node, const PropertyNameList &propertyNameList, AbstractView::PropertyChangeFlags propertyChange); void notifyScriptFunctionsChanged(const InternalNodePointer &node, const QStringList &scriptFunctionList); - void notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty, + void notifyNodeOrderChanged(const QmlDesigner::Internal::InternalNodeListProperty *internalListProperty, const InternalNodePointer &node, int oldIndex); - void notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty); + void notifyNodeOrderChanged(const InternalNodeListProperty *internalListProperty); void notifyAuxiliaryDataChanged(const InternalNodePointer &node, AuxiliaryDataKeyView key, const QVariant &data); @@ -248,9 +248,9 @@ public: //node state property manipulation void addProperty(const InternalNodePointer &node, const PropertyName &name); void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value); - void removePropertyAndRelatedResources(const InternalPropertyPointer &property); - void removeProperty(const InternalPropertyPointer &property); - void removeProperties(const QList &properties); + void removePropertyAndRelatedResources(InternalProperty *property); + void removeProperty(InternalProperty *property); + void removeProperties(const QList &properties); void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, @@ -264,7 +264,7 @@ public: void reparentNode(const InternalNodePointer &parentNode, const PropertyName &name, const InternalNodePointer &childNode, bool list = true, const TypeName &dynamicTypeName = TypeName()); void changeNodeOrder(const InternalNodePointer &parentNode, const PropertyName &listPropertyName, int from, int to); - bool propertyNameIsValid(const PropertyName &propertyName) const; + static bool propertyNameIsValid(PropertyNameView propertyName); void clearParent(const InternalNodePointer &node); void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); void setScriptFunctions(const InternalNodePointer &node, const QStringList &scriptFunctionList); @@ -300,16 +300,16 @@ public: } private: - void removePropertyWithoutNotification(const InternalPropertyPointer &property); + void removePropertyWithoutNotification(InternalProperty *property); void removeAllSubNodes(const InternalNodePointer &node); void removeNodeFromModel(const InternalNodePointer &node); QList toInternalNodeList(const QList &modelNodeList) const; QList toModelNodeList(const QList &nodeList, AbstractView *view) const; QVector toModelNodeVector(const QVector &nodeVector, AbstractView *view) const; QVector toInternalNodeVector(const QVector &modelNodeVector) const; - static QList toInternalProperties(const AbstractProperties &properties); - static QList> toInternalBindingProperties( - const ModelResourceSet::SetExpressions &setExpressions); + static QList toInternalProperties(const AbstractProperties &properties); + static QList> + toInternalBindingProperties(const ModelResourceSet::SetExpressions &setExpressions); EnabledViewRange enabledViews() const; ImportedTypeNameId importedTypeNameId(Utils::SmallStringView typeName); void setTypeId(InternalNode *node, Utils::SmallStringView typeName); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index cb862b03431..c285161e833 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -509,46 +508,22 @@ The list of all properties containing just an atomic value. */ QList ModelNode::variantProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &abstractProperty : abstractProperties) - if (abstractProperty.isVariantProperty()) - propertyList.append(abstractProperty.toVariantProperty()); - return propertyList; + return properties(PropertyType::Variant); } QList ModelNode::nodeAbstractProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &nodeAbstractProperty : abstractProperties) - if (nodeAbstractProperty.isNodeAbstractProperty()) - propertyList.append(nodeAbstractProperty.toNodeAbstractProperty()); - return propertyList; + return properties(PropertyType::Node, PropertyType::NodeList); } QList ModelNode::nodeProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &nodeProperty : abstractProperties) - if (nodeProperty.isNodeProperty()) - propertyList.append(nodeProperty.toNodeProperty()); - return propertyList; + return properties(PropertyType::Node); } QList ModelNode::nodeListProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &nodeListProperty : abstractProperties) - if (nodeListProperty.isNodeListProperty()) - propertyList.append(nodeListProperty.toNodeListProperty()); - return propertyList; + return properties(PropertyType::NodeList); } /*! \brief returns a list of all BindingProperties @@ -559,36 +534,29 @@ The list of all properties containing an expression. */ QList ModelNode::bindingProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &bindingProperty : abstractProperties) - if (bindingProperty.isBindingProperty()) - propertyList.append(bindingProperty.toBindingProperty()); - return propertyList; + return properties(PropertyType::Binding); } QList ModelNode::signalProperties() const { - QList propertyList; - - const QList abstractProperties = properties(); - for (const AbstractProperty &property : abstractProperties) - if (property.isSignalHandlerProperty()) - propertyList.append(property.toSignalHandlerProperty()); - return propertyList; + return properties(PropertyType::SignalHandler); } QList ModelNode::dynamicProperties() const { - QList propertyList; + if (!isValid()) + return {}; - const QList abstractProperties = properties(); - for (const AbstractProperty &abstractProperty : abstractProperties) { - if (abstractProperty.isDynamic()) - propertyList.append(abstractProperty); + QList properties; + + for (const auto &propertyEntry : *m_internalNode.get()) { + auto propertyName = propertyEntry.first; + auto property = propertyEntry.second; + if (property->dynamicTypeName().size()) + properties.emplace_back(propertyName, m_internalNode, model(), view()); } - return propertyList; + + return properties; } /*! @@ -599,7 +567,7 @@ Does nothing if the node state does not set this property. \see addProperty property properties hasProperties */ -void ModelNode::removeProperty(const PropertyName &name) const +void ModelNode::removeProperty(PropertyNameView name) const { if (!isValid()) return; @@ -607,8 +575,8 @@ void ModelNode::removeProperty(const PropertyName &name) const if (!model()->d->propertyNameIsValid(name)) return; - if (m_internalNode->hasProperty(name)) - model()->d->removePropertyAndRelatedResources(m_internalNode->property(name)); + if (auto property = m_internalNode->property(name)) + model()->d->removePropertyAndRelatedResources(property); } /*! \brief removes this node from the node tree @@ -795,63 +763,99 @@ PropertyNameList ModelNode::propertyNames() const return m_internalNode->propertyNameList(); } +template +QList ModelNode::properties(PropertyType... type) const +{ + if (!isValid()) + return {}; + + QList properties; + + for (const auto &propertyEntry : *m_internalNode.get()) { + auto propertyName = propertyEntry.first; + auto property = propertyEntry.second; + auto propertyType = property->type(); + QT_WARNING_PUSH + QT_WARNING_DISABLE_CLANG("-Wparentheses-equality") + if (((propertyType == type) || ...)) + properties.emplace_back(propertyName, m_internalNode, model(), view()); + QT_WARNING_POP + } + + return properties; +} + /*! \brief test a if a property is set for this node \return true if property a property ins this or a ancestor state exists */ -bool ModelNode::hasProperty(const PropertyName &name) const +bool ModelNode::hasProperty(PropertyNameView name) const { - return isValid() && m_internalNode->hasProperty(name); + return isValid() && m_internalNode->property(name); } -bool ModelNode::hasVariantProperty(const PropertyName &name) const +bool ModelNode::hasVariantProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isVariantProperty(); + return hasProperty(name, PropertyType::Variant); } -bool ModelNode::hasBindingProperty(const PropertyName &name) const +bool ModelNode::hasBindingProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isBindingProperty(); + return hasProperty(name, PropertyType::Binding); } -bool ModelNode::hasSignalHandlerProperty(const PropertyName &name) const +bool ModelNode::hasSignalHandlerProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isSignalHandlerProperty(); + return hasProperty(name, PropertyType::SignalHandler); } -bool ModelNode::hasNodeAbstractProperty(const PropertyName &name) const +bool ModelNode::hasNodeAbstractProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isNodeAbstractProperty(); + if (!isValid()) + return false; + + if (auto property = m_internalNode->property(name)) + return property->isNodeAbstractProperty(); + + return false; } bool ModelNode::hasDefaultNodeAbstractProperty() const { auto defaultPropertyName = metaInfo().defaultPropertyName(); - return hasProperty(defaultPropertyName) - && m_internalNode->property(defaultPropertyName)->isNodeAbstractProperty(); + return hasNodeAbstractProperty(defaultPropertyName); } bool ModelNode::hasDefaultNodeListProperty() const { auto defaultPropertyName = metaInfo().defaultPropertyName(); - return hasProperty(defaultPropertyName) - && m_internalNode->property(defaultPropertyName)->isNodeListProperty(); + return hasNodeListProperty(defaultPropertyName); } bool ModelNode::hasDefaultNodeProperty() const { auto defaultPropertyName = metaInfo().defaultPropertyName(); - return hasProperty(defaultPropertyName) - && m_internalNode->property(defaultPropertyName)->isNodeProperty(); + return hasNodeProperty(defaultPropertyName); } -bool ModelNode::hasNodeProperty(const PropertyName &name) const +bool ModelNode::hasNodeProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isNodeProperty(); + return hasProperty(name, PropertyType::Node); } -bool ModelNode::hasNodeListProperty(const PropertyName &name) const +bool ModelNode::hasNodeListProperty(PropertyNameView name) const { - return hasProperty(name) && m_internalNode->property(name)->isNodeListProperty(); + return hasProperty(name, PropertyType::NodeList); +} + +bool ModelNode::hasProperty(PropertyNameView name, PropertyType propertyType) const +{ + if (!isValid()) + return false; + + if (auto property = m_internalNode->property(name)) + return property->type() == propertyType; + + return false; } static bool recursiveAncestor(const ModelNode &possibleAncestor, const ModelNode &node) @@ -1328,30 +1332,6 @@ ModelNode ModelNode::lowestCommonAncestor(const QList &nodes) return accumulatedNode; } -QList ModelNode::pruneChildren(const QList &nodes) -{ - QList forwardNodes; - QList backNodes; - - auto pushIfIsNotChild = [](QList &container, const ModelNode &node) { - bool hasAncestor = Utils::anyOf(container, [node](const ModelNode &testNode) -> bool { - return testNode.isAncestorOf(node); - }); - if (!hasAncestor) - container.append(node); - }; - - for (const ModelNode &node : nodes | Utils::views::reverse) { - if (node) - pushIfIsNotChild(forwardNodes, node); - } - - for (const ModelNode &node : forwardNodes | Utils::views::reverse) - pushIfIsNotChild(backNodes, node); - - return backNodes; -} - void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { if (!isValid()) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index 1cbace80adf..c57d947f69c 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -25,6 +25,7 @@ namespace { using namespace QmlJS; using namespace QmlDesigner; using namespace QmlDesigner::Internal; +using namespace Qt::StringLiterals; ModelToTextMerger::ModelToTextMerger(RewriterView *reWriterView): m_rewriterView(reWriterView) @@ -112,16 +113,20 @@ void ModelToTextMerger::nodeTypeChanged(const ModelNode &node,const QString &/*t schedule(new ChangeTypeRewriteAction(node)); } -void ModelToTextMerger::addImport(const Import &import) +void ModelToTextMerger::addImports(const Imports &imports) { - if (!import.isEmpty()) - schedule(new AddImportRewriteAction(import)); + for (const Import &import : imports) { + if (!import.isEmpty()) + schedule(new AddImportRewriteAction(import)); + } } -void ModelToTextMerger::removeImport(const Import &import) +void ModelToTextMerger::removeImports(const Imports &imports) { - if (!import.isEmpty()) - schedule(new RemoveImportRewriteAction(import)); + for (const Import &import : imports) { + if (!import.isEmpty()) + schedule(new RemoveImportRewriteAction(import)); + } } void ModelToTextMerger::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) @@ -199,10 +204,10 @@ void ModelToTextMerger::applyChanges() if (m_rewriteActions.isEmpty()) return; - dumpRewriteActions(QStringLiteral("Before compression")); + dumpRewriteActions(u"Before compression"); RewriteActionCompressor compress(propertyOrder(), m_rewriterView->positionStorage()); compress(m_rewriteActions, m_rewriterView->textModifier()->tabSettings()); - dumpRewriteActions(QStringLiteral("After compression")); + dumpRewriteActions(u"After compression"); if (m_rewriteActions.isEmpty()) return; @@ -216,7 +221,7 @@ void ModelToTextMerger::applyChanges() qDebug() << "*** Possible problem: QML file wasn't parsed correctly."; qDebug() << "*** QML text:" << m_rewriterView->textModifier()->text(); - QString errorMessage = QStringLiteral("Error while rewriting"); + QString errorMessage = "Error while rewriting"_L1; if (!tmpDocument->diagnosticMessages().isEmpty()) errorMessage = tmpDocument->diagnosticMessages().constFirst().message; @@ -391,15 +396,15 @@ bool ModelToTextMerger::isInHierarchy(const AbstractProperty &property) { return property.isValid() && property.parentModelNode().isInHierarchy(); } -void ModelToTextMerger::dumpRewriteActions(const QString &msg) +void ModelToTextMerger::dumpRewriteActions(QStringView msg) { if (DebugRewriteActions) { - qDebug() << "---->" << qPrintable(msg); + qDebug() << "---->" << msg; for (RewriteAction *action : std::as_const(m_rewriteActions)) { qDebug() << "-----" << qPrintable(action->info()); } - qDebug() << "<----" << qPrintable(msg); + qDebug() << "<----" << msg; } } diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h index efd199a2846..75d7178039e 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h @@ -38,8 +38,8 @@ public: void nodeSlidAround(const ModelNode &movingNode, const ModelNode &inFrontOfNode); void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion); - void addImport(const Import &import); - void removeImport(const Import &import); + void addImports(const Imports &import); + void removeImports(const Imports &imports); protected: RewriterView *view(); @@ -56,7 +56,7 @@ protected: static bool isInHierarchy(const AbstractProperty &property); - void dumpRewriteActions(const QString &msg); + void dumpRewriteActions(QStringView msg); private: RewriterView *m_rewriterView; diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index efbe8f09783..e1fe3f3cadc 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -3,11 +3,13 @@ #include "modelutils.h" +#include #include #include #include #include +#include #include @@ -129,4 +131,35 @@ QString componentFilePath(const ModelNode &node) return {}; } +QList pruneChildren(const QList &nodes) +{ + QList forwardNodes; + QList backNodes; + + auto pushIfIsNotChild = [](QList &container, const ModelNode &node) { + bool hasAncestor = Utils::anyOf(container, [node](const ModelNode &testNode) -> bool { + return testNode.isAncestorOf(node); + }); + if (!hasAncestor) + container.append(node); + }; + + for (const ModelNode &node : nodes | Utils::views::reverse) { + if (node) + pushIfIsNotChild(forwardNodes, node); + } + + for (const ModelNode &node : forwardNodes | Utils::views::reverse) + pushIfIsNotChild(backNodes, node); + + return backNodes; +} + +QList allModelNodesWithId(AbstractView *view) +{ + QTC_ASSERT(view->isAttached(), return {}); + return Utils::filtered(view->allModelNodes(), + [&](const ModelNode &node) { return node.hasId(); }); +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index 946882c2df7..ee6d3e41303 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -33,4 +33,9 @@ QMLDESIGNERCORE_EXPORT QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo); QMLDESIGNERCORE_EXPORT QString componentFilePath(const ModelNode &node); + +QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &nodes); + +QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp index 0688ff1c256..f131137ae87 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp @@ -18,7 +18,7 @@ namespace QmlDesigner { NodeAbstractProperty::NodeAbstractProperty() = default; NodeAbstractProperty::NodeAbstractProperty(const NodeAbstractProperty &property, AbstractView *view) - : AbstractProperty(property.name(), property.internalNode(), property.model(), view) + : AbstractProperty(property.name(), property.internalNodeSharedPointer(), property.model(), view) { } @@ -36,14 +36,9 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode) if (!isValid() || !modelNode.isValid()) return; - if (internalNode()->hasProperty(name()) - && !internalNode()->property(name())->isNodeAbstractProperty()) { - reparentHere(modelNode, isNodeListProperty()); - } else { - reparentHere(modelNode, - parentModelNode().metaInfo().property(name()).isListProperty() - || isDefaultProperty()); //we could use the metasystem instead? - } + reparentHere(modelNode, + parentModelNode().metaInfo().property(name()).isListProperty() + || isDefaultProperty()); //we could use the metasystem instead? } void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNodeList, const TypeName &dynamicTypeName) @@ -70,19 +65,28 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNode if (modelNode.hasParentProperty() && modelNode.parentProperty().isDynamic()) return; - auto internalProperty = internalNode()->property(name()); - if (internalProperty && !internalProperty->isNodeAbstractProperty()) + if (auto internalProperty = internalNode()->property(name()); + internalProperty && !internalProperty->isNodeAbstractProperty()) { privateModel()->removePropertyAndRelatedResources(internalProperty); + } if (modelNode.hasParentProperty()) { Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty(); - privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), isNodeList, dynamicTypeName); + privateModel()->reparentNode(internalNodeSharedPointer(), + name(), + modelNode.internalNode(), + isNodeList, + dynamicTypeName); Q_ASSERT(oldParentProperty); } else { - privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), isNodeList, dynamicTypeName); + privateModel()->reparentNode(internalNodeSharedPointer(), + name(), + modelNode.internalNode(), + isNodeList, + dynamicTypeName); } } @@ -136,22 +140,62 @@ int NodeAbstractProperty::count() const QList NodeAbstractProperty::allSubNodes() { - if (!internalNode() || !internalNode()->isValid || !internalNode()->hasProperty(name()) - || !internalNode()->property(name())->isNodeAbstractProperty()) + if (!internalNode() || !internalNode()->isValid) return {}; - Internal::InternalNodeAbstractProperty::Pointer property = internalNode()->nodeAbstractProperty(name()); - return QmlDesigner::toModelNodeList(property->allSubNodes(), model(), view()); + auto property = internalNode()->property(name()); + + if (!property) + return {}; + + switch (property->type()) { + case PropertyType::Node: + return QmlDesigner::toModelNodeList({property->to()->allSubNodes()}, + model(), + view()); + case PropertyType::NodeList: + return QmlDesigner::toModelNodeList({property->to()->allSubNodes()}, + model(), + view()); + case PropertyType::Binding: + case PropertyType::None: + case PropertyType::SignalDeclaration: + case PropertyType::SignalHandler: + case PropertyType::Variant: + break; + } + + return {}; } QList NodeAbstractProperty::directSubNodes() const { - if (!internalNode() || !internalNode()->isValid || !internalNode()->hasProperty(name()) - || !internalNode()->property(name())->isNodeAbstractProperty()) + if (!internalNode() || !internalNode()->isValid) return {}; - Internal::InternalNodeAbstractProperty::Pointer property = internalNode()->nodeAbstractProperty(name()); - return QmlDesigner::toModelNodeList(property->directSubNodes(), model(), view()); + auto property = internalNode()->property(name()); + + if (!property) + return {}; + + switch (property->type()) { + case PropertyType::Node: + return QmlDesigner::toModelNodeList({property->to()->node()}, + model(), + view()); + case PropertyType::NodeList: + return QmlDesigner::toModelNodeList({property->to()->nodes()}, + model(), + view()); + case PropertyType::Binding: + case PropertyType::None: + case PropertyType::SignalDeclaration: + case PropertyType::SignalHandler: + case PropertyType::Variant: + break; + } + + return {}; } /*! diff --git a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp index 4f8e84e8a44..51ca5eb7050 100644 --- a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp @@ -23,14 +23,11 @@ Internal::NodeListPropertyIterator::value_type Internal::NodeListPropertyIterato NodeListProperty::NodeListProperty() = default; -NodeListProperty::NodeListProperty(const NodeListProperty &property, AbstractView *view) - : NodeAbstractProperty(property.name(), property.internalNode(), property.model(), view) -{ - -} - -NodeListProperty::NodeListProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) : - NodeAbstractProperty(propertyName, internalNode, model, view) +NodeListProperty::NodeListProperty(const PropertyName &propertyName, + const Internal::InternalNodePointer &internalNode, + Model *model, + AbstractView *view) + : NodeAbstractProperty(propertyName, internalNode, model, view) { } @@ -44,10 +41,11 @@ Internal::InternalNodeListPropertyPointer &NodeListProperty::internalNodeListPro if (m_internalNodeListProperty) return m_internalNodeListProperty; - auto internalProperty = internalNode()->nodeListProperty(name()); - if (internalProperty) - m_internalNodeListProperty = internalProperty; - + auto property = internalNode()->property(name()); + if (property) { + if (auto nodeListProperty = property->toShared()) + m_internalNodeListProperty = nodeListProperty; + } return m_internalNodeListProperty; } @@ -94,7 +92,7 @@ void NodeListProperty::slide(int from, int to) const if (to < 0 || to > count() - 1 || from < 0 || from > count() - 1) return; - privateModel()->changeNodeOrder(internalNode(), name(), from, to); + privateModel()->changeNodeOrder(internalNodeSharedPointer(), name(), from, to); } void NodeListProperty::swap(int from, int to) const @@ -159,7 +157,7 @@ NodeListProperty::iterator NodeListProperty::rotate(NodeListProperty::iterator f std::next(begin, newFirst.m_currentIndex), std::next(begin, last.m_currentIndex)); - privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty); + privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty.get()); return {iter - begin, internalNodeListProperty().get(), model(), view()}; } @@ -176,7 +174,7 @@ void NodeListProperty::reverse(NodeListProperty::iterator first, NodeListPropert std::reverse(std::next(begin, first.m_currentIndex), std::next(begin, last.m_currentIndex)); - privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty); + privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty.get()); } void NodeListProperty::reverseModelNodes(const QList &nodes) diff --git a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp index a628c96b7d2..330b7e75e93 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp @@ -24,16 +24,19 @@ void NodeProperty::setModelNode(const ModelNode &modelNode) if (!modelNode.isValid()) return; - auto internalProperty = internalNode()->nodeProperty(name()); - if (internalProperty - && internalProperty->node() == modelNode.internalNode()) { //check if oldValue != value - return; + if (auto property = internalNode()->property(name()); property) { + auto nodeProperty = property->to(); + if (nodeProperty && nodeProperty->node() == modelNode.internalNode()) + return; + + if (!nodeProperty) + privateModel()->removePropertyAndRelatedResources(property); } - if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) - privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); - - privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list + privateModel()->reparentNode(internalNodeSharedPointer(), + name(), + modelNode.internalNode(), + false); //### we have to add a flag that this is not a list } ModelNode NodeProperty::modelNode() const diff --git a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp index 6f9cabfed42..107cc41b07a 100644 --- a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp +++ b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp @@ -12,6 +12,8 @@ #include #include +using namespace Qt::StringLiterals; + namespace { uchar fromHex(const uchar c, const uchar c2) @@ -202,7 +204,7 @@ QVariant read(const QString &typeStr, const QString &str) { int type = QMetaType::type(typeStr.toUtf8().constData()); if (type == 0) { - if (typeStr != QStringLiteral("binding") && typeStr != QStringLiteral("enum")) { + if (typeStr != "binding"_L1 && typeStr != "enum"_L1) { qWarning() << "Type " << typeStr << " is unknown to QMetaType system. Cannot create properly typed QVariant for value " << str; diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index b2694b9951d..55e56512e63 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -541,13 +541,16 @@ QString QmlObjectNode::generateTranslatableText([[maybe_unused]] const QString & if (settings.value(DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt()) switch (settings.value(DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt()) { - case 0: return QString(QStringLiteral("qsTr(\"%1\")")).arg(escapedText); - case 1: return QString(QStringLiteral("qsTrId(\"%1\")")).arg(escapedText); - case 2: return QString(QStringLiteral("qsTranslate(\"%1\", \"context\")")).arg(escapedText); + case 0: + return QStringView(u"qsTr(\"%1\")").arg(escapedText); + case 1: + return QStringView(u"qsTrId(\"%1\")").arg(escapedText); + case 2: + return QStringView(u"qsTranslate(\"%1\", \"context\")").arg(escapedText); default: break; } - return QString(QStringLiteral("qsTr(\"%1\")")).arg(escapedText); + return QStringView(u"qsTr(\"%1\")").arg(escapedText); } QString QmlObjectNode::stripedTranslatableTextFunction(const QString &text) diff --git a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp index e5721d21c32..921f838ff90 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp @@ -20,8 +20,9 @@ using namespace QmlDesigner; using namespace QmlDesigner::Internal; +using namespace Qt::StringLiterals; -inline static QString properColorName(const QColor &color) +static QString properColorName(const QColor &color) { if (color.alpha() == 255) return QString::asprintf("#%02x%02x%02x", color.red(), color.green(), color.blue()); @@ -29,20 +30,26 @@ inline static QString properColorName(const QColor &color) return QString::asprintf("#%02x%02x%02x%02x", color.alpha(), color.red(), color.green(), color.blue()); } -inline static QString doubleToString(const PropertyName &propertyName, double d) +static bool isLowPrecisionProperties(PropertyNameView property) +{ + static constexpr PropertyNameView properties[] = { + "width", "height", "x", "y", "rotation", "scale", "opacity"}; + + return std::find(std::begin(properties), std::end(properties), property) != std::end(properties); +} + +static QString doubleToString(const PropertyName &propertyName, double d) { - static QVector lowPrecisionProperties - = {"width", "height", "x", "y", "rotation", "scale", "opacity"}; int precision = 5; if (propertyName.contains("anchors") || propertyName.contains("font") - || lowPrecisionProperties.contains(propertyName)) + || isLowPrecisionProperties(propertyName)) precision = 3; QString string = QString::number(d, 'f', precision); - if (string.contains(QLatin1Char('.'))) { - while (string.at(string.length()- 1) == QLatin1Char('0')) + if (string.contains('.'_L1)) { + while (string.at(string.length() - 1) == '0'_L1) string.chop(1); - if (string.at(string.length()- 1) == QLatin1Char('.')) + if (string.at(string.length() - 1) == '.'_L1) string.chop(1); } return string; @@ -90,23 +97,23 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept QString result; for (int i = 0; i < nodes.length(); ++i) { if (i > 0) - result += QStringLiteral("\n\n"); + result += "\n\n"_L1; result += m_tabSettings.indentationString(0, indentDepth, 0); result += toQml(nodes.at(i), indentDepth); } return result; } else { - QString result = QStringLiteral("["); + QString result = "["_L1; const int arrayContentDepth = indentDepth + m_tabSettings.m_indentSize; - const QString arrayContentIndentation(arrayContentDepth, QLatin1Char(' ')); + const QString arrayContentIndentation(arrayContentDepth, ' '_L1); for (int i = 0; i < nodes.length(); ++i) { if (i > 0) - result += QLatin1Char(','); - result += QLatin1Char('\n'); + result += ','_L1; + result += '\n'_L1; result += arrayContentIndentation; result += toQml(nodes.at(i), arrayContentDepth); } - return result + QLatin1Char(']'); + return result + ']'_L1; } } else if (property.isVariantProperty()) { const VariantProperty variantProperty = property.toVariantProperty(); @@ -126,7 +133,7 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept return QStringLiteral("false"); case QMetaType::QColor: - return QStringLiteral("\"%1\"").arg(properColorName(value.value())); + return QStringView(u"\"%1\"").arg(properColorName(value.value())); case QMetaType::Float: case QMetaType::Double: @@ -138,25 +145,22 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept return stringValue; case QMetaType::QString: case QMetaType::QChar: - return QStringLiteral("\"%1\"").arg(escape(unicodeEscape(stringValue))); + return QStringView(u"\"%1\"").arg(escape(unicodeEscape(stringValue))); case QMetaType::QVector2D: { auto vec = value.value(); - return QStringLiteral("Qt.vector2d(%1, %2)").arg(vec.x()).arg(vec.y()); + return QStringLiteral("Qt.vector2d(%1, %2)").arg(vec.x(), vec.y()); } case QMetaType::QVector3D: { auto vec = value.value(); - return QStringLiteral("Qt.vector3d(%1, %2, %3)").arg(vec.x()).arg(vec.y()).arg(vec.z()); + return QStringLiteral("Qt.vector3d(%1, %2, %3)").arg(vec.x(), vec.y(), vec.z()); } case QMetaType::QVector4D: { auto vec = value.value(); return QStringLiteral("Qt.vector4d(%1, %2, %3, %4)") - .arg(vec.x()) - .arg(vec.y()) - .arg(vec.z()) - .arg(vec.w()); + .arg(vec.x(), vec.y(), vec.z(), vec.w()); } default: - return QStringLiteral("\"%1\"").arg(escape(stringValue)); + return QStringView(u"\"%1\"").arg(escape(stringValue)); } } } else { @@ -205,8 +209,7 @@ QString QmlTextGenerator::toQml(const ModelNode &node, int indentDepth) const const QString properties = propertiesToQml(node, propertyIndentDepth); - return result + properties + m_tabSettings.indentationString(0, indentDepth, 0) - + QLatin1Char('}'); + return result + properties + m_tabSettings.indentationString(0, indentDepth, 0) + '}'_L1; } QString QmlTextGenerator::propertiesToQml(const ModelNode &node, int indentDepth) const @@ -224,7 +227,7 @@ QString QmlTextGenerator::propertiesToQml(const ModelNode &node, int indentDepth QString idLine = m_tabSettings.indentationString(0, indentDepth, 0); idLine += QStringLiteral("id: "); idLine += node.id(); - idLine += QLatin1Char('\n'); + idLine += '\n'_L1; if (addToTop) topPart.append(idLine); @@ -258,26 +261,19 @@ QString QmlTextGenerator::propertyToQml(const AbstractProperty &property, int in result = toQml(property, indentDepth); } else { if (property.isDynamic()) { - result = m_tabSettings.indentationString(0, indentDepth, 0) - + QStringLiteral("property ") - + QString::fromUtf8(property.dynamicTypeName()) - + QStringLiteral(" ") - + QString::fromUtf8(property.name()) - + QStringLiteral(": ") - + toQml(property, indentDepth); - } - if (property.isSignalDeclarationProperty()) { + result = m_tabSettings.indentationString(0, indentDepth, 0) + "property "_L1 + + QString::fromUtf8(property.dynamicTypeName()) + " "_L1 + + QString::fromUtf8(property.name()) + ": "_L1 + toQml(property, indentDepth); + } else if (property.isSignalDeclarationProperty()) { result = m_tabSettings.indentationString(0, indentDepth, 0) + "signal" + " " - + QString::fromUtf8(property.name()) + " " + toQml(property, indentDepth); + + QString::fromUtf8(property.name()) + " "_L1 + toQml(property, indentDepth); } else { result = m_tabSettings.indentationString(0, indentDepth, 0) - + QString::fromUtf8(property.name()) - + QStringLiteral(": ") - + toQml(property, indentDepth); + + QString::fromUtf8(property.name()) + ": "_L1 + toQml(property, indentDepth); } } - result += QLatin1Char('\n'); + result += '\n'_L1; return result; } diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 2bfbe860314..6ccab365bd1 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -427,7 +427,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, Utils::FilePath targetFile = qmlFilePath.pathAppended(sourceFile.fileName()); // We don't want to overwrite existing default files if (!targetFile.exists() && !sourceFile.copyFile(targetFile)) - qWarning() << QStringLiteral("Copying extra file '%1' failed.").arg(copyFileStr); + qWarning() << QStringView(u"Copying extra file '%1' failed.").arg(copyFileStr); } } diff --git a/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp b/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp index e8b1654a50c..3d301488612 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp @@ -11,6 +11,7 @@ using namespace QmlDesigner; using namespace QmlDesigner::Internal; using namespace QmlDesigner; +using namespace Qt::StringLiterals; namespace { // anonymous @@ -21,35 +22,37 @@ QString toInfo(const Import &import) if (import.isEmpty()) { return QStringLiteral("empty import"); } else if (import.isFileImport()) { - txt = QStringLiteral("import file \"%1\""); - txt = txt.arg(import.url()); + txt = QStringView(u"import file \"%1\"").arg(import.url()); } else if (import.isLibraryImport()) { - txt = QStringLiteral("import library \"%1\""); - txt = txt.arg(import.file()); + txt = QStringView(u"import library \"%1\"").arg(import.file()); } else { return QStringLiteral("unknown type of import"); } if (import.hasVersion()) - txt += QStringLiteral("with version \"%1\"").arg(import.version()); + txt += QStringView(u"with version \"%1\"").arg(import.version()); else - txt += QStringLiteral("without version"); + txt += QStringView(u"without version"); if (import.hasAlias()) - txt += QStringLiteral("aliassed as \"%1\"").arg(import.alias()); + txt += QStringView(u"aliassed as \"%1\"").arg(import.alias()); else - txt += QStringLiteral("unaliassed"); + txt += QStringView(u"unaliassed"); return txt; } -QString toString(QmlRefactoring::PropertyType type) +QStringView toString(QmlRefactoring::PropertyType type) { switch (type) { - case QmlRefactoring::ArrayBinding: return QStringLiteral("array binding"); - case QmlRefactoring::ObjectBinding: return QStringLiteral("object binding"); - case QmlRefactoring::ScriptBinding: return QStringLiteral("script binding"); - default: return QStringLiteral("UNKNOWN"); + case QmlRefactoring::ArrayBinding: + return QStringView(u"array binding"); + case QmlRefactoring::ObjectBinding: + return QStringView(u"object binding"); + case QmlRefactoring::ScriptBinding: + return QStringView(u"script binding"); + default: + return QStringView(u"UNKNOWN"); } } @@ -85,11 +88,8 @@ bool AddPropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePos if (!result) { qDebug() << "*** AddPropertyRewriteAction::execute failed in addProperty(" - << nodeLocation << ',' - << m_property.name() << ',' - << m_valueText << "," - << qPrintable(toString(m_propertyType)) << ") **" - << info(); + << nodeLocation << ',' << m_property.name() << ',' << m_valueText << "," + << toString(m_propertyType) << ") **" << info(); } } @@ -101,12 +101,15 @@ bool AddPropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePos QString AddPropertyRewriteAction::info() const { - return QString(QStringLiteral("AddPropertyRewriteAction for property \"%1\" (type: %2) of node \"%3\" with value >>%4<< and contained object \"%5\"")) - .arg(QString::fromUtf8(m_property.name()), - toString(m_propertyType), - (m_property.parentModelNode().isValid() ? m_property.parentModelNode().id() : QLatin1String("(invalid)")), - QString(m_valueText).replace(QLatin1Char('\n'), QLatin1String("\\n")), - (m_containedModelNode.isValid() ? m_containedModelNode.id() : QString(QStringLiteral("(none)")))); + return QStringView(u"AddPropertyRewriteAction for property \"%1\" (type: %2) of node \"%3\" " + u"with value >>%4<< and contained object \"%5\"") + .arg(QString::fromUtf8(m_property.name()), + toString(m_propertyType), + (m_property.parentModelNode().isValid() ? m_property.parentModelNode().id() + : QLatin1String("(invalid)")), + QString(m_valueText).replace(QLatin1Char('\n'), QLatin1String("\\n")), + (m_containedModelNode.isValid() ? m_containedModelNode.id() + : QString(QStringLiteral("(none)")))); } bool ChangeIdRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) @@ -152,7 +155,7 @@ bool ChangeIdRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositi QString ChangeIdRewriteAction::info() const { - return QString(QStringLiteral("ChangeIdRewriteAction from \"%1\" to \"%2\"")).arg(m_oldId, m_newId); + return QStringView(u"ChangeIdRewriteAction from \"%1\" to \"%2\"").arg(m_oldId, m_newId); } bool ChangePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) @@ -189,11 +192,8 @@ bool ChangePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNode if (!result) { qDebug() << "*** ChangePropertyRewriteAction::execute failed in changeProperty(" - << nodeLocation << ',' - << m_property.name() << ',' - << m_valueText << ',' - << qPrintable(toString(m_propertyType)) << ") **" - << info(); + << nodeLocation << ',' << m_property.name() << ',' << m_valueText << ',' + << toString(m_propertyType) << ") **" << info(); } } @@ -205,12 +205,15 @@ bool ChangePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNode QString ChangePropertyRewriteAction::info() const { - return QString(QStringLiteral("ChangePropertyRewriteAction for property \"%1\" (type: %2) of node \"%3\" with value >>%4<< and contained object \"%5\"")) - .arg(QString::fromUtf8(m_property.name()), - toString(m_propertyType), - (m_property.parentModelNode().isValid() ? m_property.parentModelNode().id() : QLatin1String("(invalid)")), - QString(m_valueText).replace(QLatin1Char('\n'), QLatin1String("\\n")), - (m_containedModelNode.isValid() ? m_containedModelNode.id() : QString(QStringLiteral("(none)")))); + return QStringView(u"ChangePropertyRewriteAction for property \"%1\" (type: %2) of node \"%3\" " + u"with value >>%4<< and contained object \"%5\"") + .arg(QString::fromUtf8(m_property.name()), + toString(m_propertyType), + (m_property.parentModelNode().isValid() ? m_property.parentModelNode().id() + : QLatin1String("(invalid)")), + QString(m_valueText).replace(QLatin1Char('\n'), QLatin1String("\\n")), + (m_containedModelNode.isValid() ? m_containedModelNode.id() + : QString(QStringLiteral("(none)")))); } bool ChangeTypeRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) @@ -219,7 +222,7 @@ bool ChangeTypeRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePosi bool result = false; QString newNodeType = m_node.convertTypeToImportAlias(); - const int slashIdx = newNodeType.lastIndexOf('.'); + const int slashIdx = newNodeType.lastIndexOf(u'.'); if (slashIdx != -1) newNodeType = newNodeType.mid(slashIdx + 1); @@ -273,7 +276,8 @@ bool RemovePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNode QString RemovePropertyRewriteAction::info() const { - return QStringLiteral("RemovePropertyRewriteAction for property \"%1\"").arg(QString::fromUtf8(m_property.name())); + return QStringView(u"RemovePropertyRewriteAction for property \"%1\"") + .arg(QString::fromUtf8(m_property.name())); } bool ReparentNodeRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) @@ -303,12 +307,13 @@ bool ReparentNodeRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePo QString ReparentNodeRewriteAction::info() const { if (m_node.isValid()) - return QString(QStringLiteral("ReparentNodeRewriteAction for node \"%1\" into property \"%2\" of node \"%3\"")) - .arg(m_node.id(), - QString::fromUtf8(m_targetProperty.name()), - m_targetProperty.parentModelNode().id()); + return QStringView( + u"ReparentNodeRewriteAction for node \"%1\" into property \"%2\" of node \"%3\"") + .arg(m_node.id(), + QString::fromUtf8(m_targetProperty.name()), + m_targetProperty.parentModelNode().id()); else - return QLatin1String("ReparentNodeRewriteAction for an invalid node"); + return "ReparentNodeRewriteAction for an invalid node"_L1; } bool MoveNodeRewriteAction::execute(QmlRefactoring &refactoring, @@ -335,11 +340,14 @@ QString MoveNodeRewriteAction::info() const { if (m_movingNode.isValid()) { if (m_newTrailingNode.isValid()) - return QString(QStringLiteral("MoveNodeRewriteAction for node \"%1\" before node \"%2\"")).arg(m_movingNode.id(), m_newTrailingNode.id()); + return QStringView(u"MoveNodeRewriteAction for node \"%1\" before node \"%2\"") + .arg(m_movingNode.id(), m_newTrailingNode.id()); else - return QString(QStringLiteral("MoveNodeRewriteAction for node \"%1\" to the end of its containing property")).arg(m_movingNode.id()); + return QStringView(u"MoveNodeRewriteAction for node \"%1\" to the end of its " + u"containing property") + .arg(m_movingNode.id()); } else { - return QString(QStringLiteral("MoveNodeRewriteAction for an invalid node")); + return "MoveNodeRewriteAction for an invalid node"_L1; } } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 60067a6bc91..ea344d3e62b 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -32,12 +32,14 @@ #include #include +#include #include #include #include using namespace QmlDesigner::Internal; +using namespace Qt::StringLiterals; namespace QmlDesigner { @@ -260,40 +262,29 @@ void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractPrope void RewriterView::importsChanged(const Imports &addedImports, const Imports &removedImports) { - for (const Import &import : addedImports) - importAdded(import); - - for (const Import &import : removedImports) - importRemoved(import); - + importsAdded(addedImports); + importsRemoved(removedImports); } -void RewriterView::importAdded(const Import &import) +void RewriterView::importsAdded(const Imports &imports) { Q_ASSERT(textModifier()); if (textToModelMerger()->isActive()) return; - if (import.url() == QLatin1String("Qt")) { - for (const Import &import : model()->imports()) { - if (import.url() == QLatin1String("QtQuick")) - return; //QtQuick magic we do not have to add an import for Qt - } - } - - modelToTextMerger()->addImport(import); + modelToTextMerger()->addImports(imports); if (!isModificationGroupActive()) applyChanges(); } -void RewriterView::importRemoved(const Import &import) +void RewriterView::importsRemoved(const Imports &imports) { Q_ASSERT(textModifier()); if (textToModelMerger()->isActive()) return; - modelToTextMerger()->removeImport(import); + modelToTextMerger()->removeImports(imports); if (!isModificationGroupActive()) applyChanges(); @@ -606,13 +597,12 @@ QString RewriterView::auxiliaryDataAsQML() const if (metaType == QMetaType::QString || metaType == QMetaType::QColor) { - - strValue.replace(QStringLiteral("\\"), QStringLiteral("\\\\")); - strValue.replace(QStringLiteral("\""), QStringLiteral("\\\"")); - strValue.replace(QStringLiteral("\t"), QStringLiteral("\\t")); - strValue.replace(QStringLiteral("\r"), QStringLiteral("\\r")); - strValue.replace(QStringLiteral("\n"), QStringLiteral("\\n")); - strValue.replace(QStringLiteral("*/"), QStringLiteral("*\\/")); + strValue.replace("\\"_L1, "\\\\"_L1); + strValue.replace("\""_L1, "\\\""_L1); + strValue.replace("\t"_L1, "\\t"_L1); + strValue.replace("\r"_L1, "\\r"_L1); + strValue.replace("\n"_L1, "\\n"_L1); + strValue.replace("*/"_L1, "*\\/"_L1); strValue = "\"" + strValue + "\""; } @@ -1031,11 +1021,25 @@ QSet > RewriterView::qrcMapping() const void RewriterView::moveToComponent(const ModelNode &modelNode) { + if (!modelNode.isValid()) + return; + int offset = nodeOffset(modelNode); + const QList nodes = modelNode.allSubModelNodesAndThisNode(); + QSet directPaths; - textModifier()->moveToComponent(offset); + for (const ModelNode &partialNode : nodes) { + QString importStr = partialNode.metaInfo().requiredImportString(); + if (importStr.size()) + directPaths << importStr; + } + QString importData = Utils::sorted(directPaths.values()).join(QChar::LineFeed); + if (importData.size()) + importData.append(QString(2, QChar::LineFeed)); + + textModifier()->moveToComponent(offset, importData); } QStringList RewriterView::autoComplete(const QString &text, int pos, bool explicitComplete) diff --git a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp index a260a2a0ef9..fcae441e529 100644 --- a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp @@ -11,7 +11,7 @@ namespace QmlDesigner { SignalHandlerProperty::SignalHandlerProperty() = default; SignalHandlerProperty::SignalHandlerProperty(const SignalHandlerProperty &property, AbstractView *view) - : AbstractProperty(property.name(), property.internalNode(), property.model(), view) + : AbstractProperty(property.name(), property.internalNodeSharedPointer(), property.model(), view) { } @@ -42,7 +42,7 @@ void SignalHandlerProperty::setSource(const QString &source) privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setSignalHandlerProperty(internalNode(), name(), source); + privateModel()->setSignalHandlerProperty(internalNodeSharedPointer(), name(), source); } QString SignalHandlerProperty::source() const @@ -50,9 +50,8 @@ QString SignalHandlerProperty::source() const if (!isValid()) return {}; - if (internalNode()->hasProperty(name()) - && internalNode()->property(name())->isSignalHandlerProperty()) - return internalNode()->signalHandlerProperty(name())->source(); + if (auto property = internalNode()->signalHandlerProperty(name())) + return property->source(); return QString(); } @@ -87,7 +86,7 @@ SignalDeclarationProperty::SignalDeclarationProperty() = default; SignalDeclarationProperty::SignalDeclarationProperty(const SignalDeclarationProperty &property, AbstractView *view) - : AbstractProperty(property.name(), property.internalNode(), property.model(), view) + : AbstractProperty(property.name(), property.internalNodeSharedPointer(), property.model(), view) {} SignalDeclarationProperty::SignalDeclarationProperty( @@ -121,7 +120,7 @@ void SignalDeclarationProperty::setSignature(const QString &signature) privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature); + privateModel()->setSignalDeclarationProperty(internalNodeSharedPointer(), name(), signature); } QString SignalDeclarationProperty::signature() const @@ -129,9 +128,8 @@ QString SignalDeclarationProperty::signature() const if (!isValid()) return {}; - if (internalNode()->hasProperty(name()) - && internalNode()->property(name())->isSignalDeclarationProperty()) - return internalNode()->signalDeclarationProperty(name())->signature(); + if (auto property = internalNode()->signalDeclarationProperty(name())) + return property->signature(); return QString(); } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index ccfffbc867b..dd4fe3b2555 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -51,6 +51,7 @@ using namespace LanguageUtils; using namespace QmlJS; +using namespace Qt::StringLiterals; static Q_LOGGING_CATEGORY(rewriterBenchmark, "qtc.rewriter.load", QtWarningMsg) static Q_LOGGING_CATEGORY(texttomodelMergerDebug, "qtc.texttomodelmerger.debug", QtDebugMsg) @@ -74,41 +75,45 @@ bool isSupportedVersion(QmlDesigner::Version version) return false; } -QStringList globalQtEnums() +bool isGlobalQtEnums(QStringView value) { - static const QStringList list = { - "Horizontal", "Vertical", "AlignVCenter", "AlignLeft", "LeftToRight", "RightToLeft", - "AlignHCenter", "AlignRight", "AlignBottom", "AlignBaseline", "AlignTop", "BottomLeft", - "LeftEdge", "RightEdge", "BottomEdge", "TopEdge", "TabFocus", "ClickFocus", "StrongFocus", - "WheelFocus", "NoFocus", "ArrowCursor", "UpArrowCursor", "CrossCursor", "WaitCursor", - "IBeamCursor", "SizeVerCursor", "SizeHorCursor", "SizeBDiagCursor", "SizeFDiagCursor", - "SizeAllCursor", "BlankCursor", "SplitVCursor", "SplitHCursor", "PointingHandCursor", - "ForbiddenCursor", "WhatsThisCursor", "BusyCursor", "OpenHandCursor", "ClosedHandCursor", - "DragCopyCursor", "DragMoveCursor", "DragLinkCursor", "TopToBottom", - "LeftButton", "RightButton", "MiddleButton", "BackButton", "ForwardButton", "AllButtons" - }; + static constexpr QLatin1StringView list[] = { + "Horizontal"_L1, "Vertical"_L1, "AlignVCenter"_L1, "AlignLeft"_L1, + "LeftToRight"_L1, "RightToLeft"_L1, "AlignHCenter"_L1, "AlignRight"_L1, + "AlignBottom"_L1, "AlignBaseline"_L1, "AlignTop"_L1, "BottomLeft"_L1, + "LeftEdge"_L1, "RightEdge"_L1, "BottomEdge"_L1, "TopEdge"_L1, + "TabFocus"_L1, "ClickFocus"_L1, "StrongFocus"_L1, "WheelFocus"_L1, + "NoFocus"_L1, "ArrowCursor"_L1, "UpArrowCursor"_L1, "CrossCursor"_L1, + "WaitCursor"_L1, "IBeamCursor"_L1, "SizeVerCursor"_L1, "SizeHorCursor"_L1, + "SizeBDiagCursor"_L1, "SizeFDiagCursor"_L1, "SizeAllCursor"_L1, "BlankCursor"_L1, + "SplitVCursor"_L1, "SplitHCursor"_L1, "PointingHandCursor"_L1, "ForbiddenCursor"_L1, + "WhatsThisCursor"_L1, "BusyCursor"_L1, "OpenHandCursor"_L1, "ClosedHandCursor"_L1, + "DragCopyCursor"_L1, "DragMoveCursor"_L1, "DragLinkCursor"_L1, "TopToBottom"_L1, + "LeftButton"_L1, "RightButton"_L1, "MiddleButton"_L1, "BackButton"_L1, + "ForwardButton"_L1, "AllButtons"_L1}; - return list; + return std::find(std::begin(list), std::end(list), value) != std::end(list); } -QStringList knownEnumScopes() +bool isKnownEnumScopes(QStringView value) { - static const QStringList list = {"TextInput", - "TextEdit", - "Material", - "Universal", - "Font", - "Shape", - "ShapePath", - "AbstractButton", - "Text", - "ShaderEffectSource", - "Grid", - "ItemLayer", - "ImageLayer", - "SpriteLayer", - "Light"}; - return list; + static constexpr QLatin1StringView list[] = {"TextInput"_L1, + "TextEdit"_L1, + "Material"_L1, + "Universal"_L1, + "Font"_L1, + "Shape"_L1, + "ShapePath"_L1, + "AbstractButton"_L1, + "Text"_L1, + "ShaderEffectSource"_L1, + "Grid"_L1, + "ItemLayer"_L1, + "ImageLayer"_L1, + "SpriteLayer"_L1, + "Light"_L1}; + + return std::find(std::begin(list), std::end(list), value) != std::end(list); } bool supportedQtQuickVersion(const QmlDesigner::Import &import) @@ -130,11 +135,11 @@ QString deEscape(const QString &value) { QString result = value; - result.replace(QStringLiteral("\\\\"), QStringLiteral("\\")); - result.replace(QStringLiteral("\\\""), QStringLiteral("\"")); - result.replace(QStringLiteral("\\t"), QStringLiteral("\t")); - result.replace(QStringLiteral("\\r"), QStringLiteral("\r")); - result.replace(QStringLiteral("\\n"), QStringLiteral("\n")); + result.replace("\\\\"_L1, "\\"_L1); + result.replace("\\\""_L1, "\""_L1); + result.replace("\\t"_L1, "\t"_L1); + result.replace("\\r"_L1, "\r"_L1); + result.replace("\\n"_L1, "\n"_L1); return result; } @@ -185,8 +190,8 @@ bool isSignalPropertyName(const QString &signalName) QStringList list = signalName.split(QLatin1String(".")); const QString &pureSignalName = list.constLast(); - return pureSignalName.length() >= 3 && pureSignalName.startsWith(QStringLiteral("on")) && - pureSignalName.at(2).isLetter(); + return pureSignalName.length() >= 3 && pureSignalName.startsWith(u"on") + && pureSignalName.at(2).isLetter(); } QVariant cleverConvert(const QString &value) @@ -242,23 +247,23 @@ bool isLiteralValue(AST::UiScriptBinding *script) int propertyType(const QString &typeName) { - if (typeName == QStringLiteral("bool")) + if (typeName == u"bool") return QMetaType::type("bool"); - else if (typeName == QStringLiteral("color")) + else if (typeName == u"color") return QMetaType::type("QColor"); - else if (typeName == QStringLiteral("date")) + else if (typeName == u"date") return QMetaType::type("QDate"); - else if (typeName == QStringLiteral("int")) + else if (typeName == u"int") return QMetaType::type("int"); - else if (typeName == QStringLiteral("real")) + else if (typeName == u"real") return QMetaType::type("double"); - else if (typeName == QStringLiteral("double")) + else if (typeName == u"double") return QMetaType::type("double"); - else if (typeName == QStringLiteral("string")) + else if (typeName == u"string") return QMetaType::type("QString"); - else if (typeName == QStringLiteral("url")) + else if (typeName == u"url") return QMetaType::type("QUrl"); - else if (typeName == QStringLiteral("var") || typeName == QStringLiteral("variant")) + else if (typeName == u"var" || typeName == u"variant") return QMetaType::type("QVariant"); else return -1; @@ -473,7 +478,7 @@ public: const QString propertyName = propertyPrefix.isEmpty() ? propertyId->name.toString() : propertyPrefix; - if (propertyName == QStringLiteral("id") && !propertyId->next) + if (propertyName == u"id" && !propertyId->next) return false; // ### should probably be a special value //compare to lookupProperty(propertyPrefix, propertyId); @@ -509,7 +514,7 @@ public: if (name) *name = propertyName; - if (propertyName == QStringLiteral("id") && ! id->next) + if (propertyName == u"id" && !id->next) return false; // ### should probably be a special value // attached properties @@ -603,15 +608,18 @@ public: const PropertyMetaInfo propertyMetaInfo = node.metaInfo().property(propertyName.toUtf8()); - const bool hasQuotes = astValue.trimmed().left(1) == QStringLiteral("\"") - && astValue.trimmed().right(1) == QStringLiteral("\""); + const bool hasQuotes = astValue.trimmed().left(1) == u"\"" + && astValue.trimmed().right(1) == u"\""; const QString cleanedValue = fixEscapedUnicodeChar(deEscape(stripQuotes(astValue.trimmed()))); if (!propertyMetaInfo.isValid()) { - qCInfo(texttomodelMergerDebug) - << Q_FUNC_INFO << "Unknown property" - << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" - << propertyId->identifierToken.startLine << "column" - << propertyId->identifierToken.startColumn; + // Only list elements might have unknown properties. + if (!node.metaInfo().isQtQuickListElement()) { + qCInfo(texttomodelMergerDebug) + << Q_FUNC_INFO << "Unknown property" + << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" + << propertyId->identifierToken.startLine << "column" + << propertyId->identifierToken.startColumn; + } return hasQuotes ? QVariant(cleanedValue) : cleverConvert(cleanedValue); } @@ -649,7 +657,8 @@ public: QVariant convertToVariant(const QString &astValue, const QString &propertyPrefix, AST::UiQualifiedId *propertyId) { - const bool hasQuotes = astValue.trimmed().left(1) == QStringLiteral("\"") && astValue.trimmed().right(1) == QStringLiteral("\""); + const bool hasQuotes = astValue.trimmed().left(1) == u"\"" + && astValue.trimmed().right(1) == u"\""; const QString cleanedValue = fixEscapedUnicodeChar(deEscape(stripQuotes(astValue.trimmed()))); const Value *property = nullptr; const ObjectValue *containingObject = nullptr; @@ -706,16 +715,15 @@ public: AST::UiQualifiedId *propertyId, const QString &astValue) { - QStringList astValueList = astValue.split(QStringLiteral(".")); + QStringList astValueList = astValue.split(u'.'); if (astValueList.size() == 2) { //Check for global Qt enums - if (astValueList.constFirst() == QStringLiteral("Qt") - && globalQtEnums().contains(astValueList.constLast())) + if (astValueList.constFirst() == u"Qt" && isGlobalQtEnums(astValueList.constLast())) return QVariant::fromValue(Enumeration(astValue)); //Check for known enum scopes used globally - if (knownEnumScopes().contains(astValueList.constFirst())) + if (isKnownEnumScopes(astValueList.constFirst())) return QVariant::fromValue(Enumeration(astValue)); } @@ -1221,7 +1229,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (defaultPropertyName.isEmpty()) //fallback and use the meta system of the model defaultPropertyName = modelNode.metaInfo().defaultPropertyName(); - if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && isComponentType(typeName)) { + if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && info.isQmlComponent()) { for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) { if (auto def = AST::cast(iter->member)) { syncNode(modelNode, def, context, differenceHandler); @@ -1478,7 +1486,7 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN astValue = astValue.trimmed(); } - if (astPropertyName == QStringLiteral("id")) { + if (astPropertyName == u"id") { syncNodeId(modelNode, astValue, differenceHandler); return astPropertyName.toUtf8(); } @@ -1803,7 +1811,7 @@ void ModelValidator::modelMissesImport([[maybe_unused]] const QmlDesigner::Impor void ModelValidator::importAbsentInQMl([[maybe_unused]] const QmlDesigner::Import &import) { - Q_ASSERT(! m_merger->view()->model()->imports().contains(import)); + QTC_ASSERT(!m_merger->view()->model()->imports().contains(import), return); } void ModelValidator::bindingExpressionsDiffer([[maybe_unused]] BindingProperty &modelProperty, @@ -2225,7 +2233,7 @@ void TextToModelMerger::collectImportErrors(QList *errors) bool hasQtQuick = false; for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) { - if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { + if (import.isLibraryImport() && import.url() == u"QtQuick") { if (supportedQtQuickVersion(import)) { hasQtQuick = true; diff --git a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp index 859bb0691ec..37d52ceb19f 100644 --- a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp @@ -14,7 +14,7 @@ namespace QmlDesigner { VariantProperty::VariantProperty() = default; VariantProperty::VariantProperty(const VariantProperty &property, AbstractView *view) - : AbstractProperty(property.name(), property.internalNode(), property.model(), view) + : AbstractProperty(property.name(), property.internalNodeSharedPointer(), property.model(), view) { } @@ -49,7 +49,7 @@ void VariantProperty::setValue(const QVariant &value) privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setVariantProperty(internalNode(), name(), value); + privateModel()->setVariantProperty(internalNodeSharedPointer(), name(), value); } QVariant VariantProperty::value() const @@ -99,7 +99,7 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa privateModel()->removePropertyAndRelatedResources(internalProperty); } - privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value); + privateModel()->setDynamicVariantProperty(internalNodeSharedPointer(), name(), type, value); } void VariantProperty::setDynamicTypeNameAndEnumeration(const TypeName &type, const EnumerationName &enumerationName) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index fb4fb660660..e7b41c559e3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -5,6 +5,9 @@ #include "projectstoragetypes.h" +#include + +#include #include #include @@ -243,23 +246,20 @@ class CommonTypeCache public: CommonTypeCache(const ProjectStorage &projectStorage) : m_projectStorage{projectStorage} - {} + { + m_typesWithoutProperties.fill(TypeId{}); + } + + CommonTypeCache(const CommonTypeCache &) = delete; + CommonTypeCache &operator=(const CommonTypeCache &) = delete; + CommonTypeCache(CommonTypeCache &&) = default; + CommonTypeCache &operator=(CommonTypeCache &&) = default; void resetTypeIds() { std::apply([](auto &...type) { ((type.typeId = QmlDesigner::TypeId{}), ...); }, m_types); - } - TypeId refreshTypedId(BaseCacheType &type, - ::Utils::SmallStringView moduleName, - ::Utils::SmallStringView typeName) const - { - if (!type.moduleId) - type.moduleId = m_projectStorage.moduleId(moduleName); - - type.typeId = m_projectStorage.typeId(type.moduleId, typeName, QmlDesigner::Storage::Version{}); - - return type.typeId; + updateTypeIdsWithoutProperties(); } template @@ -312,9 +312,70 @@ public: return TypeId{}; } + const TypeIdsWithoutProperties &typeIdsWithoutProperties() const + { + return m_typesWithoutProperties; + } + +private: + TypeId refreshTypedId(BaseCacheType &type, + ::Utils::SmallStringView moduleName, + ::Utils::SmallStringView typeName) const + { + if (!type.moduleId) + type.moduleId = m_projectStorage.moduleId(moduleName); + + type.typeId = m_projectStorage.typeId(type.moduleId, typeName, Storage::Version{}); + + return type.typeId; + } + + TypeId refreshTypedIdWithoutTransaction(BaseCacheType &type, + ::Utils::SmallStringView moduleName, + ::Utils::SmallStringView typeName) const + { + if (!type.moduleId) + type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName); + + type.typeId = m_projectStorage.fetchTypeIdByModuleIdAndExportedName(type.moduleId, typeName); + + return type.typeId; + } + + template + void setupTypeIdsWithoutProperties(const TypeId (&typeIds)[size]) + { + static_assert(size == std::tuple_size_v, + "array size must match type id count!"); + std::copy(std::begin(typeIds), std::end(typeIds), std::begin(m_typesWithoutProperties)); + } + + template + TypeId typeIdWithoutTransaction() const + { + auto &type = std::get>(m_types); + if (type.typeId) + return type.typeId; + + return refreshTypedIdWithoutTransaction(type, moduleName, typeName); + } + + void updateTypeIdsWithoutProperties() + { + setupTypeIdsWithoutProperties({typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction()}); + } + private: const ProjectStorage &m_projectStorage; mutable CommonTypes m_types; + TypeIdsWithoutProperties m_typesWithoutProperties; }; } // namespace QmlDesigner::Storage::Info diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp index 19cbbbac56b..1376b2c3d9d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp @@ -32,7 +32,7 @@ SourceIds FileSystem::directoryEntries(const QString &directoryPath) const QStringList FileSystem::qmlFileNames(const QString &directoryPath) const { - return QDir{directoryPath}.entryList({".qml"}); + return QDir{directoryPath}.entryList({"*.qml"}, QDir::Files); } long long FileSystem::lastModified(SourceId sourceId) const diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 78064fedab0..1158572e5e0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -28,6 +28,8 @@ namespace QmlDesigner { template class ProjectStorage final : public ProjectStorageInterface { + friend Storage::Info::CommonTypeCache; + public: template using ReadStatement = typename Database::template ReadStatement; @@ -83,6 +85,8 @@ public: relinkablePrototypes, relinkableExtensions, package.updatedSourceIds); + synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, + package.updatedPropertyEditorQmlPathSourceIds); deleteNotUpdatedTypes(updatedTypeIds, package.updatedSourceIds, @@ -156,14 +160,14 @@ public: Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override { return selectExportedTypesByTypeIdStatement - .template valuesWithTransaction(4, typeId); + .template valuesWithTransaction(typeId); } Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override { return selectExportedTypesByTypeIdAndSourceIdStatement - .template valuesWithTransaction(4, typeId, sourceId); + .template valuesWithTransaction(typeId, sourceId); } ImportId importId(const Storage::Import &import) const override @@ -193,16 +197,16 @@ public: }); } - PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const override + QVarLengthArray propertyDeclarationIds(TypeId typeId) const override { return selectPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction(32, typeId); + .template valuesWithTransaction>(typeId); } - PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const override + QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override { return selectLocalPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction(16, typeId); + .template valuesWithTransaction>(typeId); } PropertyDeclarationId propertyDeclarationId(TypeId typeId, @@ -236,13 +240,13 @@ public: std::vector signalDeclarationNames(TypeId typeId) const override { return selectSignalDeclarationNamesForTypeStatement - .template valuesWithTransaction(32, typeId); + .template valuesWithTransaction(typeId); } std::vector functionDeclarationNames(TypeId typeId) const override { return selectFuncionDeclarationNamesForTypeStatement - .template valuesWithTransaction(32, typeId); + .template valuesWithTransaction(typeId); } std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override @@ -276,14 +280,14 @@ public: TypeIds prototypeIds(TypeId type) const override { - return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction(16, - type); + return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction( + type); } TypeIds prototypeAndSelfIds(TypeId type) const override { return selectPrototypeAndSelfIdsForTypeIdInOrderStatement - .template valuesWithTransaction(16, type); + .template valuesWithTransaction(type); } template @@ -291,7 +295,10 @@ public: { static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); - auto range = selectPrototypeAndSelfIdsStatement.template rangeWithTransaction(typeId); + if (((typeId == baseTypeIds) || ...)) + return true; + + auto range = selectPrototypeIdsStatement.template rangeWithTransaction(typeId); for ([[maybe_unused]] TypeId currentTypeId : range) { if (((currentTypeId == baseTypeIds) || ...)) @@ -379,7 +386,7 @@ public: Storage::Synchronization::Types fetchTypes() { return Sqlite::withDeferredTransaction(database, [&] { - auto types = selectTypesStatement.template values(64); + auto types = selectTypesStatement.template values(); for (Storage::Synchronization::Type &type : types) { type.exportedTypes = fetchExportedTypes(type.typeId); @@ -400,7 +407,7 @@ public: auto fetchPrototypes(TypeId type) { - return selectPrototypeIdsStatement.template rangeWithTransaction(type); + return selectPrototypeIdsInOrderStatement.template rangeWithTransaction(type); } SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) @@ -437,8 +444,8 @@ public: auto fetchAllSourceContexts() const { - return selectAllSourceContextsStatement.template valuesWithTransaction( - 128); + return selectAllSourceContextsStatement + .template valuesWithTransaction(); } SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) @@ -480,7 +487,7 @@ public: auto fetchAllSources() const { - return selectAllSourcesStatement.template valuesWithTransaction(1024); + return selectAllSourcesStatement.template valuesWithTransaction(); } SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) @@ -513,14 +520,29 @@ public: Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { return selectProjectDatasForSourceIdStatement - .template valuesWithTransaction(64, - projectSourceId); + .template valuesWithTransaction( + projectSourceId); } Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { - return selectProjectDatasForSourceIdsStatement.template valuesWithTransaction< - Storage::Synchronization::ProjectData>(64, toIntegers(projectSourceIds)); + return selectProjectDatasForSourceIdsStatement + .template valuesWithTransaction( + toIntegers(projectSourceIds)); + } + + void setPropertyEditorPathId(TypeId typeId, SourceId pathId) + { + Sqlite::ImmediateSessionTransaction transaction{database}; + + upsertPropertyEditorPathIdStatement.write(typeId, pathId); + + transaction.commit(); + } + + SourceId propertyEditorPathId(TypeId typeId) const override + { + return selectPropertyEditorPathIdStatement.template valueWithTransaction(typeId); } private: @@ -577,7 +599,7 @@ private: auto fetchAllModules() const { - return selectAllModulesStatement.template valuesWithTransaction(128); + return selectAllModulesStatement.template valuesWithTransaction(); } class AliasPropertyDeclaration @@ -710,7 +732,7 @@ private: TypeIds fetchTypeIds(const SourceIds &sourceIds) { - return selectTypeIdsForSourceIdsStatement.template values(128, toIntegers(sourceIds)); + return selectTypeIdsForSourceIdsStatement.template values(toIntegers(sourceIds)); } void unique(SourceIds &sourceIds) @@ -739,7 +761,7 @@ private: SourceIds exportedSourceIds; exportedSourceIds.reserve(types.size()); - for (auto &&type : types) { + for (auto &type : types) { if (!type.sourceId) throw TypeHasInvalidSourceId{}; @@ -806,8 +828,6 @@ private: throw ProjectDataHasInvalidProjectSourceId{}; if (!projectData.sourceId) throw ProjectDataHasInvalidSourceId{}; - if (!projectData.moduleId) - throw ProjectDataHasInvalidModuleId{}; insertProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId, @@ -817,11 +837,8 @@ private: auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, const Storage::Synchronization::ProjectData &projectData) { - if (!projectData.moduleId) - throw ProjectDataHasInvalidModuleId{}; - if (projectDataFromDatabase.fileType != projectData.fileType - || projectDataFromDatabase.moduleId != projectData.moduleId) { + || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { updateProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId, projectData.moduleId, @@ -950,7 +967,7 @@ private: Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); } - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override { auto moduleId = selectModuleIdByNameStatement.template value(name); @@ -1056,7 +1073,7 @@ private: auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); if (!typeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( typeId, alias.aliasPropertyName); @@ -1084,7 +1101,7 @@ private: TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, propertyTypeId); @@ -1108,7 +1125,7 @@ private: TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); if (!prototypeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)}; updateStatement(prototype.typeId, prototypeId); checkForPrototypeChainCycle(prototype.typeId); @@ -1178,8 +1195,10 @@ private: for (const auto &aliasDeclaration : aliasDeclarations) { auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); - if (!aliasTypeId) - throw TypeNameDoesNotExists{}; + if (!aliasTypeId) { + throw TypeNameDoesNotExists{ + fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; + } auto aliasId = fetchAliasId(aliasTypeId, aliasDeclaration.aliasPropertyName, @@ -1288,7 +1307,7 @@ private: type.typeId); } } catch (const Sqlite::ConstraintPreventsModification &) { - throw QmlDesigner::ExportedTypeCannotBeInserted{}; + throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; } }; @@ -1344,7 +1363,7 @@ private: auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; auto propertyDeclarationId = insertPropertyDeclarationStatement.template value( typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); @@ -1386,7 +1405,7 @@ private: auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; if (view.traits == value.traits && propertyTypeId == view.typeId && propertyImportedTypeNameId == view.typeNameId) @@ -1663,6 +1682,77 @@ private: return json; } + TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, + Utils::SmallStringView name) const override + { + return selectTypeIdByModuleIdAndExportedNameStatement.template value(moduleId, name); + } + + void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths) + { + for (auto &path : paths) + path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); + } + + class PropertyEditorQmlPathView + { + public: + PropertyEditorQmlPathView(TypeId typeId, SourceId pathId, SourceId directoryId) + : typeId{typeId} + , pathId{pathId} + , directoryId{directoryId} + {} + + public: + TypeId typeId; + SourceId pathId; + SourceId directoryId; + }; + + void synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) + { + using Storage::Synchronization::PropertyEditorQmlPath; + std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = selectPropertyEditorPathsForForSourceIdsStatement + .template range( + toIntegers(updatedPropertyEditorQmlPathsSourceIds)); + + auto compareKey = [](const PropertyEditorQmlPathView &view, + const PropertyEditorQmlPath &value) -> long long { + return view.typeId - value.typeId; + }; + + auto insert = [&](const PropertyEditorQmlPath &path) { + if (path.typeId) + insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); + }; + + auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { + if (value.pathId != view.pathId || value.directoryId != view.directoryId) { + updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); + return Sqlite::UpdateChange::Update; + } + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const PropertyEditorQmlPathView &view) { + deletePropertyEditorPathStatement.write(view.typeId); + }; + + Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); + } + + void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) + { + addTypeIdToPropertyEditorQmlPaths(paths); + synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); + } + void synchronizeFunctionDeclarations( TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) { @@ -2038,7 +2128,7 @@ private: typeId = fetchTypeId(typeNameId); if (!typeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)}; } return {typeId, typeNameId}; @@ -2146,6 +2236,11 @@ private: return fetchTypeId(typeNameId, kind); } + Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const + { + return selectNameFromImportedTypeNamesStatement.template value(typeNameId); + } + TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const { if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) { @@ -2233,13 +2328,13 @@ private: auto fetchExportedTypes(TypeId typeId) { return selectExportedTypesByTypeIdStatement - .template values(12, typeId); + .template values(typeId); } auto fetchPropertyDeclarations(TypeId typeId) { return selectPropertyDeclarationsByTypeIdStatement - .template values(24, typeId); + .template values(typeId); } auto fetchFunctionDeclarations(TypeId typeId) @@ -2250,8 +2345,9 @@ private: Utils::SmallStringView returnType, FunctionDeclarationId functionDeclarationId) { auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); - functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement.template values< - Storage::Synchronization::ParameterDeclaration>(8, functionDeclarationId); + functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement + .template values(functionDeclarationId); }; selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); @@ -2265,8 +2361,9 @@ private: auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { auto &signalDeclaration = signalDeclarations.emplace_back(name); - signalDeclaration.parameters = selectSignalParameterDeclarationsStatement.template values< - Storage::Synchronization::ParameterDeclaration>(8, signalDeclarationId); + signalDeclaration.parameters = selectSignalParameterDeclarationsStatement + .template values(signalDeclarationId); }; selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); @@ -2282,8 +2379,9 @@ private: EnumerationDeclarationId enumerationDeclarationId) { enumerationDeclarations.emplace_back( name, - selectEnumeratorDeclarationStatement.template values< - Storage::Synchronization::EnumeratorDeclaration>(8, enumerationDeclarationId)); + selectEnumeratorDeclarationStatement + .template values( + enumerationDeclarationId)); }; selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement @@ -2311,6 +2409,7 @@ private: createDocumentImportsTable(database, moduleIdColumn); createFileStatusesTable(database); createProjectDatasTable(database); + createPropertyEditorPathsTable(database); } database.setIsInitialized(true); } @@ -2644,6 +2743,22 @@ private: table.initialize(database); } + + void createPropertyEditorPathsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("propertyEditorPaths"); + table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer); + auto &directoryIdColumn = table.addColumn("directoryId", + Sqlite::StrictColumnType::Integer); + + table.addIndex({directoryIdColumn}); + + table.initialize(database); + } }; public: @@ -2724,7 +2839,7 @@ public: " FROM propertyDeclarations JOIN typeSelection USING(typeId) " " WHERE name=?2 ORDER BY level LIMIT 1", database}; - mutable ReadStatement<1, 1> selectPrototypeIdsStatement{ + mutable ReadStatement<1, 1> selectPrototypeIdsInOrderStatement{ "WITH RECURSIVE " " all_prototype_and_extension(typeId, prototypeId) AS (" " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" @@ -3109,6 +3224,8 @@ public: database}; mutable ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{ "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; + mutable ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{ + "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database}; mutable ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND " @@ -3314,19 +3431,41 @@ public: " typeChain AS tc USING(typeId)) " "SELECT typeId FROM typeChain ORDER BY level", database}; - mutable ReadStatement<1, 1> selectPrototypeAndSelfIdsStatement{ + mutable ReadStatement<1, 1> selectPrototypeIdsStatement{ "WITH RECURSIVE " " all_prototype_and_extension(typeId, prototypeId) AS (" " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" " UNION ALL " " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," " typeSelection(typeId) AS (" - " VALUES(?1) " + " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?1 " " UNION ALL " " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection " " USING(typeId))" "SELECT typeId FROM typeSelection", database}; + WriteStatement<2> upsertPropertyEditorPathIdStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " + "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " + "excluded.pathSourceId", + database}; + mutable ReadStatement<1, 1> selectPropertyEditorPathIdStatement{ + "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database}; + mutable ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{ + "SELECT typeId, pathSourceId, directoryId " + "FROM propertyEditorPaths " + "WHERE directoryId IN carray(?1) " + "ORDER BY typeId", + database}; + WriteStatement<3> insertPropertyEditorPathStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)", + database}; + WriteStatement<3> updatePropertyEditorPathsStatement{"UPDATE propertyEditorPaths " + "SET pathSourceId=?2, directoryId=?3 " + "WHERE typeId=?1", + database}; + WriteStatement<1> deletePropertyEditorPathStatement{ + "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; }; extern template class ProjectStorage; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index 07e925df689..9f7e72784b8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -40,15 +40,9 @@ const char *ModuleAlreadyExists::what() const noexcept return "The module does already exist!"; } -const char *ExportedTypeCannotBeInserted::what() const noexcept -{ - return "The exported type cannot be inserted!"; -} - -const char *TypeNameDoesNotExists::what() const noexcept -{ - return "The type name does not exist!"; -} +TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view errorMessage) + : ProjectStorageErrorWithMessage{"TypeNameDoesNotExists"sv, errorMessage} +{} const char *PropertyNameDoesNotExists::what() const noexcept { @@ -100,4 +94,22 @@ const char *ProjectStorageError::what() const noexcept return "Project storage error!"; } +ProjectStorageErrorWithMessage::ProjectStorageErrorWithMessage(std::string_view error, + std::string_view message) +{ + errorMessage += error; + errorMessage += "{"sv; + errorMessage += message; + errorMessage += "}"sv; +} + +const char *ProjectStorageErrorWithMessage::what() const noexcept +{ + return errorMessage.c_str(); +} + +ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage) + : ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage} +{} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index e5e50e76092..a9a9a5c8856 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -9,115 +9,128 @@ namespace QmlDesigner { -class QMLDESIGNERCORE_EXPORT ProjectStorageError : std::exception +using namespace std::literals::string_view_literals; + +class QMLDESIGNERCORE_EXPORT ProjectStorageError : public std::exception { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : ProjectStorageError +class ProjectStorageErrorWithMessage : public ProjectStorageError +{ +public: + ProjectStorageErrorWithMessage(std::string_view error, std::string_view errorMessage); + + const char *what() const noexcept override; + +public: + std::string errorMessage; +}; + +class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : ProjectStorageError +class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : ProjectStorageError +class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : public ProjectStorageErrorWithMessage +{ +public: + ExportedTypeCannotBeInserted(std::string_view errorMessage); +}; + +class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : public ProjectStorageErrorWithMessage +{ +public: + TypeNameDoesNotExists(std::string_view errorMessage); +}; + +class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : ProjectStorageError +class QMLDESIGNERCORE_EXPORT AliasChainCycle : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : ProjectStorageError +class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT AliasChainCycle : ProjectStorageError +class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : ProjectStorageError +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : ProjectStorageError -{ -public: - const char *what() const noexcept override; -}; - -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : ProjectStorageError -{ -public: - const char *what() const noexcept override; -}; - -class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : ProjectStorageError +class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : public ProjectStorageError { public: const char *what() const noexcept override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 1f16e762713..0d290c22f8c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -205,4 +206,6 @@ public: TypeTraits traits; }; +using TypeIdsWithoutProperties = std::array; + } // namespace QmlDesigner::Storage::Info diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index adbbfcac4c3..49ae0637936 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -10,11 +10,20 @@ #include +#include + namespace QmlDesigner { class ProjectStorageInterface { + friend Storage::Info::CommonTypeCache; + public: + ProjectStorageInterface(const ProjectStorageInterface &) = delete; + ProjectStorageInterface &operator=(const ProjectStorageInterface &) = delete; + ProjectStorageInterface(ProjectStorageInterface &&) = default; + ProjectStorageInterface &operator=(ProjectStorageInterface &&) = default; + virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; virtual void synchronizeDocumentImports(const Storage::Imports imports, SourceId sourceId) = 0; @@ -35,8 +44,8 @@ public: = 0; virtual ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) = 0; - virtual PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const = 0; - virtual PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const = 0; + virtual QVarLengthArray propertyDeclarationIds(TypeId typeId) const = 0; + virtual QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const = 0; virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, ::Utils::SmallStringView propertyName) const = 0; @@ -59,6 +68,7 @@ public: virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; virtual std::optional fetchProjectData(SourceId sourceId) const = 0; + virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; template @@ -80,7 +90,11 @@ public: } protected: + ProjectStorageInterface() = default; ~ProjectStorageInterface() = default; + + virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const = 0; + virtual TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index e2e4c1bb4b1..7c7cab80e27 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -722,6 +722,26 @@ public: using Types = std::vector; +class PropertyEditorQmlPath +{ +public: + PropertyEditorQmlPath(ModuleId moduleId, TypeNameString typeName, SourceId pathId, SourceId directoryId) + : typeName{typeName} + , pathId{pathId} + , directoryId{directoryId} + , moduleId{moduleId} + {} + +public: + TypeNameString typeName; + TypeId typeId; + SourceId pathId; + SourceId directoryId; + ModuleId moduleId; +}; + +using PropertyEditorQmlPaths = std::vector; + class ProjectData { public: @@ -735,7 +755,8 @@ public: friend bool operator==(const ProjectData &first, const ProjectData &second) { return first.projectSourceId == second.projectSourceId && first.sourceId == second.sourceId - && first.moduleId == second.moduleId && first.fileType == second.fileType; + && first.moduleId.internalId() == second.moduleId.internalId() + && first.fileType == second.fileType; } public: @@ -799,6 +820,8 @@ public: SourceIds updatedModuleDependencySourceIds; ModuleExportedImports moduleExportedImports; ModuleIds updatedModuleIds; + PropertyEditorQmlPaths propertyEditorQmlPaths; + SourceIds updatedPropertyEditorQmlPathSourceIds; }; } // namespace Synchronization diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 2b8a9c58456..624b4c47c9c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -14,6 +14,9 @@ #include +#include +#include + #include #include @@ -177,7 +180,9 @@ std::vector createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds wa } // namespace -void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths) +void ProjectStorageUpdater::update(QStringList directories, + QStringList qmlTypesPaths, + const QString &propertyEditorResourcesPath) { Storage::Synchronization::SynchronizationPackage package; WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; @@ -185,6 +190,7 @@ void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypes updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds); updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds); + updatePropertyEditorPaths(propertyEditorResourcesPath, package, notUpdatedSourceIds); package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), std::move(notUpdatedSourceIds.sourceIds)); @@ -341,6 +347,63 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa } } +void ProjectStorageUpdater::updatePropertyEditorPaths( + const QString &propertyEditorResourcesPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + if (propertyEditorResourcesPath.isEmpty()) + return; + + QDirIterator dirIterator{QDir::cleanPath(propertyEditorResourcesPath), + QDir::Dirs | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories}; + + while (dirIterator.hasNext()) { + auto pathInfo = dirIterator.nextFileInfo(); + + SourceId directorySourceId = m_pathCache.sourceId(SourcePath{pathInfo.filePath() + "/."}); + + auto state = fileState(directorySourceId, package, notUpdatedSourceIds); + + if (state == FileState::Changed) + updatePropertyEditorPath(pathInfo.filePath(), package, directorySourceId); + } +} + +void ProjectStorageUpdater::updatePropertyEditorPath( + const QString &directoryPath, + Storage::Synchronization::SynchronizationPackage &package, + SourceId directorySourceId) +{ + package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId); + auto dir = QDir{directoryPath}; + const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); + for (const auto &fileInfo : fileInfos) + updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId); +} + +void ProjectStorageUpdater::updatePropertyEditorFilePath( + const QString &path, + Storage::Synchronization::SynchronizationPackage &package, + SourceId directorySourceId) +{ + QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; + auto match = regex.match(path); + QString oldModuleName; + ModuleId moduleId; + if (match.hasMatch()) { + auto moduleName = match.capturedView(1); + if (oldModuleName != moduleName) { + oldModuleName = moduleName.toString(); + moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}); + } + Storage::TypeNameString typeName{match.capturedView(2)}; + SourceId pathId = m_pathCache.sourceId(SourcePath{path}); + package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, pathId, directorySourceId); + } +} + namespace { SourceContextIds filterUniqueSourceContextIds(const SourceIds &sourceIds, ProjectStorageUpdater::PathCache &pathCache) @@ -505,7 +568,7 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); } - }; + } } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 519d11a920a..6a77f353e20 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -55,7 +55,9 @@ public: , m_projectPartId{projectPartId} {} - void update(QStringList directories, QStringList qmlTypesPaths); + void update(QStringList directories, + QStringList qmlTypesPaths, + const QString &propertyEditorResourcesPath); void pathsWithIdsChanged(const std::vector &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -140,6 +142,15 @@ private: NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds); + void updatePropertyEditorPath(const QString &path, + Storage::Synchronization::SynchronizationPackage &package, + SourceId directorySourceId); + void updatePropertyEditorFilePath(const QString &filePath, + Storage::Synchronization::SynchronizationPackage &package, + SourceId directorySourceId); void parseTypeInfos(const QStringList &typeInfos, const QList &qmldirDependencies, const QList &qmldirImports, diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index ca1b927c3a6..13a03bc10b1 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,19 +3,20 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" +#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" +#include "effectmakerwidget.h" #include "formeditorwidget.h" #include "materialbrowserwidget.h" #include "navigatorwidget.h" #include "qmldesignerconstants.h" #include "texteditorwidget.h" -namespace QmlDesigner { -namespace Internal { +namespace QmlDesigner::Internal { DesignModeContext::DesignModeContext(QWidget *widget) - : IContext(widget) + : IContext(widget) { setWidget(widget); setContext(Core::Context(Constants::C_QMLDESIGNER, Constants::C_QT_QUICK_TOOLS_MENU)); @@ -98,6 +99,27 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -} +EffectMakerContext::EffectMakerContext(QWidget *widget) + : IContext(widget) +{ + setWidget(widget); + setContext(Core::Context(Constants::C_QMLEFFECTMAKER, Constants::C_QT_QUICK_TOOLS_MENU)); } +void EffectMakerContext::contextHelp(const HelpCallback &callback) const +{ + qobject_cast(m_widget)->contextHelp(callback); +} + +CollectionEditorContext::CollectionEditorContext(QWidget *widget) + : IContext(widget) +{ + setWidget(widget); + setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU)); +} + +void CollectionEditorContext::contextHelp(const HelpCallback &callback) const +{ + qobject_cast(m_widget)->contextHelp(callback); +} +} // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index fab7fe0ea30..72c0a195239 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -74,5 +74,22 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; -} -} +class EffectMakerContext : public Core::IContext +{ + Q_OBJECT + +public: + EffectMakerContext(QWidget *widget); + void contextHelp(const Core::IContext::HelpCallback &callback) const override; +}; + +class CollectionEditorContext : public Core::IContext +{ + Q_OBJECT + +public: + CollectionEditorContext(QWidget *widget); + void contextHelp(const Core::IContext::HelpCallback &callback) const override; +}; +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index e1468ca86e4..822b23fc9a7 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -125,7 +126,6 @@ QWidget *DesignModeWidget::createProjectExplorerWidget(QWidget *parent) if (navigationView.widget) { QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); sheet += "QLabel { background-color: #4f4f4f; }"; navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); navigationView.widget->setParent(parent); @@ -190,7 +190,6 @@ void DesignModeWidget::setup() Core::ICore::resourcePath("qmldesigner/workspacePresets/").toString()); QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); - sheet += QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); // Setup icons @@ -300,7 +299,6 @@ void DesignModeWidget::setup() // Apply stylesheet to QWidget QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); sheet += "QLabel { background-color: creatorTheme.DSsectionHeadBackground; }"; navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); @@ -428,6 +426,8 @@ void DesignModeWidget::setup() setupNavigatorHistory(currentDesignDocument()->textEditor()); m_dockManager->initialize(); + if (style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) + Utils::GlobalTransientSupport::support(m_dockManager); // Hide all floating widgets if the initial mode isn't design mode if (Core::ModeManager::instance()->currentModeId() != Core::Constants::MODE_DESIGN) { diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 6e8ecb7e17a..7e876816c97 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -50,6 +50,7 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const addImportPaths(); addCustomFileSelectors(); addDisableDeferredProperties(); + addResolveUrlsOnAssignment(); qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); @@ -81,10 +82,26 @@ bool PuppetEnvironmentBuilder::usesVirtualKeyboard() const QString PuppetEnvironmentBuilder::getStyleConfigFileName() const { if (m_target) { - for (const Utils::FilePath &fileName : - m_target->project()->files(ProjectExplorer::Project::SourceFiles)) { - if (fileName.fileName() == "qtquickcontrols2.conf") - return fileName.toString(); + const auto *qmlBuild = qobject_cast( + m_target->buildSystem()); + if (qmlBuild) { + const auto &environment = qmlBuild->environment(); + const auto &envVar = std::find_if( + std::begin(environment), std::end(environment), [](const auto &envVar) { + return (envVar.name == u"QT_QUICK_CONTROLS_CONF" + && envVar.operation != Utils::EnvironmentItem::SetDisabled); + }); + if (envVar != std::end(environment)) { + const auto &sourceFiles = m_target->project()->files( + ProjectExplorer::Project::SourceFiles); + const auto &foundFile = std::find_if(std::begin(sourceFiles), + std::end(sourceFiles), + [&](const auto &fileName) { + return fileName.fileName() == envVar->value; + }); + if (foundFile != std::end(sourceFiles)) + return foundFile->toString(); + } } } @@ -232,6 +249,11 @@ void PuppetEnvironmentBuilder::addDisableDeferredProperties() const m_environment.set("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "true"); } +void PuppetEnvironmentBuilder::addResolveUrlsOnAssignment() const +{ + m_environment.set("QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT", "true"); +} + PuppetType PuppetEnvironmentBuilder::determinePuppetType() const { if (m_target && m_target->kit() && m_target->kit()->isValid()) { diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h index 3c2ffba271e..8179a36b90d 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.h +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h @@ -50,6 +50,7 @@ private: void addImportPaths() const; void addCustomFileSelectors() const; void addDisableDeferredProperties() const; + void addResolveUrlsOnAssignment() const; private: ProjectExplorer::Target *m_target = nullptr; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 1e16848c6b5..60e1ded8474 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -11,13 +11,15 @@ const char C_DELETE[] = "QmlDesigner.Delete"; const char C_DUPLICATE[] = "QmlDesigner.Duplicate"; // Context -const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; -const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; -const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; -const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; -const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; -const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; -const char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; +const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; +const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; +const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; +const char C_QMLEFFECTMAKER[] = "QmlDesigner::EffectMaker"; +const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; +const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; +const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; +const char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; +const char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; // Special context for preview menu, shared b/w designer and text editor const char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; @@ -63,18 +65,20 @@ const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; +const char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle"; +const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; const char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; const char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; -const char QUICK_3D_ASSET_ICON_DIR[] = "_icons"; const char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; const char MATERIAL_LIB_ID[] = "__materialLibrary__"; +const char COLLECTION_LIB_ID[] = "__collectionLibrary__"; const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo"; const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; @@ -122,6 +126,7 @@ const char EVENT_TEXTEDITOR_TIME[] = "textEditor"; const char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor"; const char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor"; const char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary"; +const char EVENT_EFFECTMAKER_TIME[] = "effectMaker"; const char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary"; const char EVENT_TRANSLATIONVIEW_TIME[] = "translationView"; const char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView"; @@ -152,9 +157,11 @@ const char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary"; const char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary"; const char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator"; const char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary"; +const char OBJECT_NAME_EFFECT_MAKER[] = "QQuickWidgetEffectMaker"; const char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; const char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; const char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; +const char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; const char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; const char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; const char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; @@ -163,6 +170,7 @@ const char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback"; const char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog"; const char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen"; const char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage"; +const char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor"; const char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS"; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index a8ab2cff1d4..f2c96de6736 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -61,20 +61,6 @@ QString ExternalDependencies::currentProjectDirPath() const return QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString(); } -QList ExternalDependencies::designerSettingsEdit3DViewBackgroundColor() const -{ - return Edit3DViewConfig::loadColor(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); -} - -QColor ExternalDependencies::designerSettingsEdit3DViewGridColor() const -{ - QList gridColorList = Edit3DViewConfig::loadColor(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR); - if (!gridColorList.isEmpty()) - return gridColorList.front(); - - return {}; -} - QUrl ExternalDependencies::currentResourcePath() const { return QUrl::fromLocalFile( @@ -235,9 +221,7 @@ QStringList ExternalDependencies::modulePaths() const if (auto path = qmlPath(target); !path.isEmpty()) modulePaths.push_back(path); - for (const QString &modulePath : qmlBuildSystem->customImportPaths()) - modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); - + modulePaths.append(qmlBuildSystem->absoluteImportPaths()); return modulePaths; } @@ -249,10 +233,7 @@ QStringList ExternalDependencies::projectModulePaths() const auto [project, target, qmlBuildSystem] = activeProjectEntries(); if (project && target && qmlBuildSystem) { - QStringList modulePaths; - - for (const QString &modulePath : qmlBuildSystem->customImportPaths()) - modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + return qmlBuildSystem->absoluteImportPaths(); } return {}; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index 0f6acf2c106..13317075027 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -22,8 +22,6 @@ public: QString defaultPuppetToplevelBuildDirectory() const override; QUrl projectUrl() const override; QString currentProjectDirPath() const override; - QList designerSettingsEdit3DViewBackgroundColor() const override; - QColor designerSettingsEdit3DViewGridColor() const override; QUrl currentResourcePath() const override; void parseItemLibraryDescriptions() override; const DesignerSettings &designerSettings() const override; diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index a92b890da55..3cae3ecab47 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -337,6 +337,53 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) return {}; } +template +bool skipDirectoriesWith(const QStringView directoryPath, const Path &...paths) +{ + return (directoryPath.contains(paths) || ...); +} + +template +bool skipDirectoriesEndsWith(const QStringView directoryPath, const Path &...paths) +{ + return (directoryPath.endsWith(paths) || ...); +} + +bool skipPath(const QString &directoryPath) +{ + return skipDirectoriesWith(directoryPath, + u"QtApplicationManager", + u"QtInterfaceFramework", + u"QtOpcUa", + u"Qt3D", + u"Scene2D", + u"Scene3D", + u"QtWayland", + u"Qt5Compat", + u"QtCharts", + u"QtLocation", + u"QtPositioning", + u"MaterialEditor", + u"QtTextToSpeech", + u"QtWebEngine", + u"Qt/labs", + u"QtDataVisualization") + || skipDirectoriesEndsWith(directoryPath, u"designer"); +} + +void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) +{ + QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; + + while (dirIterator.hasNext()) { + auto directoryPath = dirIterator.next(); + + QString qmldirPath = directoryPath + "/qmldir"; + if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) + qmldirPaths.push_back(directoryPath); + } +} + void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); @@ -346,53 +393,16 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa const QDir projectDirectory(projectDirectoryPath.toString()); for (const QString &importPath : importPaths) - qmldirPaths.push_back(QDir::cleanPath(projectDirectory.absoluteFilePath(importPath)) - + "/qmldir"); + collectQmldirPaths(importPath, qmldirPaths); } -#ifdef QDS_HAS_QMLPRIVATE -QStringView currentDirectoryName(const QString &path) +void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { - auto found = std::find(path.rbegin(), path.rend(), u'/'); - - if (found == path.rend()) - return {}; - - return QStringView{found.base(), path.end()}; -} -bool skipPath(const QString &path) -{ - auto directory = currentDirectoryName(path); - - bool skip = directory == u"QtApplicationManager" || directory == u"QtInterfaceFramework" - || directory == u"QtOpcUa" || directory == u"Qt3D" || directory == u"Qt3D" - || directory == u"Scene2D" || directory == u"Scene3D" || directory == u"QtWayland" - || directory == u"Qt5Compat"; - - return skip; -} -#endif - -void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target, - [[maybe_unused]] QStringList &qmldirPaths) -{ -#ifdef QDS_HAS_QMLPRIVATE - if (useProjectStorage()) { - const QString installDirectory = qmlPath(target).toString(); - QDirIterator dirIterator{installDirectory, QDir::Dirs, QDirIterator::Subdirectories}; - - while (dirIterator.hasNext()) { - auto directoryPath = dirIterator.next(); - - QString qmldirPath = directoryPath + "/qmldir"; - if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) - qmldirPaths.push_back(directoryPath); - } - } -#endif + if constexpr (useProjectStorage()) + collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); } -QStringList qmlDirs(::ProjectExplorer::Target *target) +QStringList directories(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -428,6 +438,16 @@ QStringList qmlTypes(::ProjectExplorer::Target *target) return qmldirPaths; } +QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) { + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; + } +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + } // namespace void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) @@ -561,8 +581,9 @@ void QmlDesignerProjectManager::update() if (!m_projectData || !m_projectData->projectStorageData) return; - m_projectData->projectStorageData->updater.update(qmlDirs(m_projectData->activeTarget), - qmlTypes(m_projectData->activeTarget)); + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), + qmlTypes(m_projectData->activeTarget), + propertyEditorResourcesPath()); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp index d6cf9f115b2..d5036c939cd 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp @@ -65,8 +65,8 @@ QmlPreviewWidgetPlugin::QmlPreviewWidgetPlugin() m_previewToggleAction = previewAction->action(); Core::Context globalContext; - auto registerCommand = [&globalContext](ActionInterface *action){ - const QString id = QStringLiteral("QmlPreview.%1").arg(QString::fromLatin1(action->menuId())); + auto registerCommand = [&globalContext](ActionInterface *action) { + const QString id = QStringView(u"QmlPreview.%1").arg(QString::fromLatin1(action->menuId())); Core::Command *cmd = Core::ActionManager::registerAction(action->action(), id.toLatin1().constData(), globalContext); diff --git a/src/plugins/qmldesigner/utils/designeralgorithm.h b/src/plugins/qmldesigner/utils/designeralgorithm.h new file mode 100644 index 00000000000..94d84c8b0be --- /dev/null +++ b/src/plugins/qmldesigner/utils/designeralgorithm.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner::CoreUtils { + +template +#ifdef Q_CC_MSVC +__forceinline +#else +[[gnu::always_inline]] +#endif + constexpr bool + contains(Element element, Elements... elements) +{ + return ((element == elements) || ...); +} + +} // namespace QmlDesigner::CoreUtils diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt index a191b6d43ca..5f5f1ef258b 100644 --- a/src/plugins/qmldesignerbase/CMakeLists.txt +++ b/src/plugins/qmldesignerbase/CMakeLists.txt @@ -36,6 +36,7 @@ extend_qtc_plugin(QmlDesignerBase SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/studio SOURCES studiostyle.cpp studiostyle.h + studiostyle_p.cpp studiostyle_p.h studioquickwidget.cpp studioquickwidget.h studiosettingspage.cpp studiosettingspage.h ) diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h index 61c2ff8922f..165db562e21 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.h +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.h @@ -147,6 +147,7 @@ public: class QMLDESIGNERBASE_EXPORT StudioQuickWidget : public QWidget { Q_OBJECT + public: explicit StudioQuickWidget(QWidget *parent = nullptr); @@ -168,6 +169,9 @@ public: StudioPropertyMap *registerPropertyMap(const QByteArray &name); QQuickWidget *quickWidget() const; +signals: + void adsFocusChanged(); + private: QQuickWidget *m_quickWidget = nullptr; }; diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index 74dd5bfe495..59f19e9f5c7 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -1,7 +1,10 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "studiostyle.h" +#include "studiostyle_p.h" +#include +#include #include #include @@ -9,7 +12,9 @@ #include #include +#define ANIMATE_SCROLLBARS QT_CONFIG(animation) using namespace Utils; +using namespace QmlDesigner; namespace { @@ -65,6 +70,17 @@ inline QColor studioButtonOutlineColor(bool enabled, return creatorTheme()->color(themePenColorId); } +inline bool anyParentsFocused(const QWidget *widget) +{ + const QWidget *p = widget; + while (p) { + if (p->property("focused").toBool()) + return true; + p = p->parentWidget(); + } + return false; +} + bool styleEnabled(const QWidget *widget) { const QWidget *p = widget; @@ -93,221 +109,43 @@ bool isQmlEditorMenu(const QWidget *widget) return false; } -QPixmap getPixmapFromIcon(const QIcon &icon, const QSize &size, bool enabled, bool active, bool checked) +inline QPixmap getPixmapFromIcon( + const QIcon &icon, const QSize &size, bool enabled, bool active, bool checked) { QIcon::Mode mode = enabled ? ((active) ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; QIcon::State state = (checked) ? QIcon::On : QIcon::Off; return icon.pixmap(size, mode, state); } -struct StudioShortcut { - StudioShortcut(const QStyleOptionMenuItem *option, - const QString &shortcutText) - : shortcutText(shortcutText) - , enabled(option->state & QStyle::State_Enabled) - , active(option->state & QStyle::State_Selected) - , font(option->font) - , fm(font) - , defaultHeight(fm.height()) - , spaceConst(fm.boundingRect(".").width()) - { - reset(); - - if (backspaceMatch(shortcutText).hasMatch()) - backspaceIcon = option->styleObject->property("backspaceIcon").value(); - } - - QSize getSize() - { - if (isFirstParticle) - calcResult(); - return _size; - } - - QPixmap getPixmap() - { - if (!isFirstParticle && !_pixmap.isNull()) - return _pixmap; - - _pixmap = QPixmap(getSize()); - _pixmap.fill(Qt::transparent); - QPainter painter(&_pixmap); - painter.setFont(font); - QPen pPen = painter.pen(); - pPen.setColor(studioTextColor(enabled, active, false)); - painter.setPen(pPen); - calcResult(&painter); - painter.end(); - - return _pixmap; - } - -private: - void applySize(const QSize &itemSize) { - width += itemSize.width(); - height = std::max(height, itemSize.height()); - if (isFirstParticle) - isFirstParticle = false; - else - width += spaceConst; - }; - - void addText(const QString &txt, QPainter *painter = nullptr) - { - if (txt.size()) { - int textWidth = fm.horizontalAdvance(txt); - QSize itemSize = {textWidth, defaultHeight}; - if (painter) { - static const QTextOption textOption(Qt::AlignLeft | Qt::AlignVCenter); - QRect placeRect({width, 0}, itemSize); - painter->drawText(placeRect, txt, textOption); - } - applySize(itemSize); - } - }; - - void addPixmap(const QPixmap &pixmap, QPainter *painter = nullptr) - { - if (painter) - painter->drawPixmap(QRect({width, 0}, pixmap.size()), pixmap); - - applySize(pixmap.size()); - }; - - void calcResult(QPainter *painter = nullptr) - { - reset(); -#ifndef QT_NO_SHORTCUT - if (!shortcutText.isEmpty()) { - int fwdIndex = 0; - - QRegularExpressionMatch mMatch = backspaceMatch(shortcutText); - int matchCount = mMatch.lastCapturedIndex(); - - for (int i = 0; i <= matchCount; ++i) { - QString mStr = mMatch.captured(i); - QSize iconSize(defaultHeight * 3, defaultHeight); - const QList iconSizes = backspaceIcon.availableSizes(); - if (iconSizes.size()) - iconSize = iconSizes.last(); - double aspectRatio = (defaultHeight + .0) / iconSize.height(); - int newWidth = iconSize.width() * aspectRatio; - - QPixmap pixmap = getPixmapFromIcon(backspaceIcon, - {newWidth, defaultHeight}, - enabled, active, false); - - int lIndex = shortcutText.indexOf(mStr, fwdIndex); - int diffChars = lIndex - fwdIndex; - addText(shortcutText.mid(fwdIndex, diffChars), painter); - addPixmap(pixmap, painter); - fwdIndex = lIndex + mStr.size(); - } - addText(shortcutText.mid(fwdIndex), painter); - } -#endif - _size = {width, height}; - } - - void reset() - { - isFirstParticle = true; - width = 0; - height = 0; - } - - inline QRegularExpressionMatch backspaceMatch(const QString &str) const - { - static const QRegularExpression backspaceDetect( - "\\+*backspace\\+*", - QRegularExpression::CaseInsensitiveOption); - return backspaceDetect.match(str); - } - - const QString shortcutText; - const bool enabled; - const bool active; - const QFont font; - const QFontMetrics fm; - const int defaultHeight; - const int spaceConst; - QIcon backspaceIcon; - bool isFirstParticle = true; - - int width = 0; - int height = 0; - QSize _size; - QPixmap _pixmap; -}; - -} // blank namespace - -class StudioStylePrivate +inline QRect expandScrollRect(const QRect &ref, + const qreal &factor, + const Qt::Orientation &orientation) { -public: - explicit StudioStylePrivate(); + if (qFuzzyCompare(factor, 1)) + return ref; -public: - QPalette stdPalette; -}; - -StudioStylePrivate::StudioStylePrivate() -{ - auto color = [] (Theme::Color c) { - return creatorTheme()->color(c); - }; - - { - stdPalette.setColorGroup( - QPalette::Disabled, // group - color(Theme::DStextColorDisabled), // windowText - color(Theme::DScontrolBackgroundDisabled), // button - color(Theme::DScontrolOutlineDisabled), // light - color(Theme::DStextSelectedTextColor), // dark - color(Theme::DSstatusbarBackground), // mid - color(Theme::DStextColorDisabled), // text - color(Theme::DStextColorDisabled), // brightText - color(Theme::DStoolbarIcon_blocked), // base - color(Theme::DStoolbarIcon_blocked) // window - ); - - stdPalette.setColorGroup( - QPalette::Inactive, // group - color(Theme::DStextColor), // windowText - color(Theme::DScontrolBackground), // button - color(Theme::DStoolbarBackground), // light - color(Theme::DSstatusbarBackground), // dark - color(Theme::DScontrolBackground), // mid - color(Theme::DStextColor), // text - color(Theme::DStextColor), // brightText - color(Theme::DStoolbarBackground), // base - color(Theme::DStoolbarBackground) // window - ); - - stdPalette.setColorGroup( - QPalette::Active, // group - color(Theme::DStextSelectedTextColor), // windowText - color(Theme::DSnavigatorItemBackgroundHover), // button - color(Theme::DSstateBackgroundColor_hover), // light - color(Theme::DSpanelBackground), // dark - color(Theme::DSnavigatorItemBackgroundHover), // mid - color(Theme::DStextSelectedTextColor), // text - color(Theme::DStextSelectedTextColor), // brightText - color(Theme::DStoolbarBackground), // base - color(Theme::DStoolbarBackground) // window - ); + if (orientation == Qt::Horizontal) { + qreal newExp = ref.height() * factor; + qreal newDiff = ref.height() - newExp; + return ref.adjusted(0, newDiff, 0, 0); + } else { + qreal newExp = ref.width() * factor; + qreal newDiff = ref.width() - newExp; + return ref.adjusted(newDiff, 0, 0, 0); } } +} // namespace + StudioStyle::StudioStyle(QStyle *style) : QProxyStyle(style) - , d(new StudioStylePrivate) + , d(new StudioStylePrivate(this)) { } StudioStyle::StudioStyle(const QString &key) : QProxyStyle(key) - , d(new StudioStylePrivate) + , d(new StudioStylePrivate(this)) { } @@ -358,8 +196,7 @@ void StudioStyle::drawPrimitive( if (!isQmlEditorMenu(widget)) Super::drawPrimitive(element, option, painter, widget); break; - case PE_FrameDefaultButton: - { + case PE_FrameDefaultButton: { if (const auto button = qstyleoption_cast(option)) { bool enabled = button->state & QStyle::State_Enabled; bool hovered = enabled && button->state & QStyle::State_MouseOver; @@ -385,30 +222,29 @@ void StudioStyle::drawPrimitive( painter->restore(); } - } - break; + } break; - case PE_IndicatorToolBarSeparator: - { + case PE_IndicatorToolBarSeparator: { bool horizontal = option->state & State_Horizontal; int thickness = pixelMetric(PM_ToolBarSeparatorExtent, option, widget); QRect colorRect; if (horizontal) { - colorRect = {option->rect.center().x() - thickness / 2 , option->rect.top() + 2, - thickness , option->rect.height() - 4}; + colorRect = {option->rect.center().x() - thickness / 2, + option->rect.top() + 2, + thickness, + option->rect.height() - 4}; } else { - colorRect = {option->rect.left() + 2, option->rect.center().y() - thickness / 2, - option->rect.width() - 4, thickness}; + colorRect = {option->rect.left() + 2, + option->rect.center().y() - thickness / 2, + option->rect.width() - 4, + thickness}; } // The separator color is currently the same as toolbar bg - painter->fillRect(colorRect, - creatorTheme()->color(Theme::DStoolbarBackground)); - } - break; + painter->fillRect(colorRect, creatorTheme()->color(Theme::DStoolbarBackground)); + } break; - default: - { + default: { Super::drawPrimitive(element, option, painter, widget); break; } @@ -452,7 +288,7 @@ void StudioStyle::drawControl( if (item.menuItemType == QStyleOptionMenuItem::Separator) { int commonHeight = item.rect.center().y(); - int additionalMargin = forwardX /*hmargin*/; + int additionalMargin = forwardX; QLineF separatorLine (item.rect.left() + additionalMargin, commonHeight, item.rect.right() - additionalMargin, @@ -764,6 +600,296 @@ void StudioStyle::drawComplexControl( } Super::drawComplexControl(control, option, painter, widget); } break; + +#if QT_CONFIG(slider) + case CC_ScrollBar: { + painter->save(); + if (const QStyleOptionSlider *scrollBar = qstyleoption_cast( + option)) { + bool wasActive = false; + bool wasAdjacent = false; + bool isFocused = anyParentsFocused(widget); + bool isAdjacent = false; + qreal scaleCoFactor = 1.0; + QObject *styleObject = option->styleObject; + bool hasTransientStyle = proxy()->styleHint(SH_ScrollBar_Transient, option, widget); + if (styleObject && hasTransientStyle) { +#if ANIMATE_SCROLLBARS + qreal opacity = 0.0; + bool shouldExpand = false; + const qreal minExpandScale = 0.7; + const qreal maxExpandScale = 1.0; +#endif + isAdjacent = styleObject->property("adjacentScroll").toBool(); + int oldPos = styleObject->property("_qdss_stylepos").toInt(); + int oldMin = styleObject->property("_qdss_stylemin").toInt(); + int oldMax = styleObject->property("_qdss_stylemax").toInt(); + QRect oldRect = styleObject->property("_qdss_stylerect").toRect(); + QStyle::State oldState = static_cast( + qvariant_cast(styleObject->property("_qdss_stylestate"))); + uint oldActiveControls = styleObject->property("_qdss_stylecontrols").toUInt(); + bool oldFocus = styleObject->property("_qdss_focused").toBool(); + bool oldAdjacent = styleObject->property("_qdss_adjacentScroll").toBool(); + // a scrollbar is transient when the scrollbar itself and + // its sibling are both inactive (ie. not pressed/hovered/moved) + bool transient = !option->activeSubControls && !(option->state & State_On); + if (!transient || oldPos != scrollBar->sliderPosition + || oldMin != scrollBar->minimum || oldMax != scrollBar->maximum + || oldRect != scrollBar->rect || oldState != scrollBar->state + || oldActiveControls != scrollBar->activeSubControls || oldFocus != isFocused + || oldAdjacent != isAdjacent) { + styleObject->setProperty("_qdss_stylepos", scrollBar->sliderPosition); + styleObject->setProperty("_qdss_stylemin", scrollBar->minimum); + styleObject->setProperty("_qdss_stylemax", scrollBar->maximum); + styleObject->setProperty("_qdss_stylerect", scrollBar->rect); + styleObject->setProperty("_qdss_stylestate", + static_cast(scrollBar->state)); + styleObject->setProperty("_qdss_stylecontrols", + static_cast(scrollBar->activeSubControls)); + styleObject->setProperty("_qdss_focused", isFocused); + styleObject->setProperty("_qdss_adjacentScroll", isAdjacent); +#if ANIMATE_SCROLLBARS + // if the scrollbar is transient or its attributes, geometry or + // state has changed, the opacity is reset back to 100% opaque + opacity = 1.0; + QScrollbarStyleAnimation *anim = qobject_cast( + d->animation(styleObject)); + if (transient) { + if (anim && anim->mode() != QScrollbarStyleAnimation::Deactivating) { + d->stopAnimation(styleObject); + anim = nullptr; + } + if (!anim) { + anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Deactivating, + styleObject); + d->startAnimation(anim); + } else if (anim->mode() == QScrollbarStyleAnimation::Deactivating) { + // the scrollbar was already fading out while the + // state changed -> restart the fade out animation + anim->setCurrentTime(0); + } + } else if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) { + d->stopAnimation(styleObject); + } +#endif // animation + } +#if ANIMATE_SCROLLBARS + QScrollbarStyleAnimation *anim = qobject_cast( + d->animation(styleObject)); + if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) { + // once a scrollbar was active (hovered/pressed), it retains + // the active look even if it's no longer active while fading out + if (oldActiveControls) + anim->setActive(true); + + if (oldAdjacent) + anim->setAdjacent(true); + + wasActive = anim->wasActive(); + wasAdjacent = anim->wasAdjacent(); + opacity = anim->currentValue(); + } + shouldExpand = (option->activeSubControls || wasActive); + if (shouldExpand) { + if (!anim && !oldActiveControls) { + // Start expand animation only once and when entering + anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Activating, + styleObject); + d->startAnimation(anim); + } + if (anim && anim->mode() == QScrollbarStyleAnimation::Activating) { + scaleCoFactor = (1.0 - anim->currentValue()) * minExpandScale + + anim->currentValue() * maxExpandScale; + } else { + // Keep expanded state after the animation ends, and when fading out + scaleCoFactor = maxExpandScale; + } + } + painter->setOpacity(opacity); +#endif // animation + } + bool horizontal = scrollBar->orientation == Qt::Horizontal; + bool sunken = scrollBar->state & State_Sunken; + QRect scrollBarSubLine = proxy()->subControlRect(control, + scrollBar, + SC_ScrollBarSubLine, + widget); + QRect scrollBarAddLine = proxy()->subControlRect(control, + scrollBar, + SC_ScrollBarAddLine, + widget); + QRect scrollBarSlider = proxy()->subControlRect(control, + scrollBar, + SC_ScrollBarSlider, + widget); + QRect scrollBarGroove = proxy()->subControlRect(control, + scrollBar, + SC_ScrollBarGroove, + widget); + QRect rect = option->rect; + + QColor alphaOutline = StyleHelper::borderColor(); + alphaOutline.setAlpha(180); + QColor arrowColor = option->palette.windowText().color(); + arrowColor.setAlpha(160); + + bool enabled = scrollBar->state & QStyle::State_Enabled; + bool hovered = enabled && scrollBar->state & QStyle::State_MouseOver; + + QColor buttonColor = creatorTheme()->color(hovered ? Theme::DSscrollBarHandle + : Theme::DSscrollBarHandle_idle); + QColor gradientStartColor = buttonColor.lighter(118); + QColor gradientStopColor = buttonColor; + if (hasTransientStyle) { + rect = expandScrollRect(rect, scaleCoFactor, scrollBar->orientation); + scrollBarSlider = expandScrollRect(scrollBarSlider, + scaleCoFactor, + scrollBar->orientation); + scrollBarGroove = expandScrollRect(scrollBarGroove, + scaleCoFactor, + scrollBar->orientation); + } + // Paint groove + if ((!hasTransientStyle || scrollBar->activeSubControls || wasActive || isAdjacent + || wasAdjacent) + && scrollBar->subControls & SC_ScrollBarGroove) { + painter->save(); + painter->setPen(Qt::NoPen); + if (hasTransientStyle) { + QColor brushColor(creatorTheme()->color(Theme::DSscrollBarTrack)); + brushColor.setAlpha(0.3 * 255); + painter->setBrush(QBrush(brushColor)); + painter->drawRoundedRect(scrollBarGroove, 4, 4); + } else { + painter->setBrush(QBrush(creatorTheme()->color(Theme::DSscrollBarTrack))); + painter->drawRect(rect); + } + painter->restore(); + } + QRect pixmapRect = scrollBarSlider; + QLinearGradient gradient(pixmapRect.center().x(), + pixmapRect.top(), + pixmapRect.center().x(), + pixmapRect.bottom()); + if (!horizontal) + gradient = QLinearGradient(pixmapRect.left(), + pixmapRect.center().y(), + pixmapRect.right(), + pixmapRect.center().y()); + QLinearGradient highlightedGradient = gradient; + QColor midColor2 = StyleHelper::mergedColors(gradientStartColor, gradientStopColor, 40); + gradient.setColorAt(0, buttonColor.lighter(108)); + gradient.setColorAt(1, buttonColor); + QColor innerContrastLine = StyleHelper::highlightColor(); + highlightedGradient.setColorAt(0, gradientStartColor.darker(102)); + highlightedGradient.setColorAt(1, gradientStopColor.lighter(102)); + // Paint slider + if (scrollBar->subControls & SC_ScrollBarSlider) { + if (hasTransientStyle) { + QRect rect = scrollBarSlider; + painter->setPen(Qt::NoPen); + painter->setBrush(highlightedGradient); + int r = qMin(rect.width(), rect.height()) / 2; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->drawRoundedRect(rect, r, r); + painter->restore(); + } else { + QRect pixmapRect = scrollBarSlider; + painter->setPen(QPen(alphaOutline)); + if (option->state & State_Sunken + && scrollBar->activeSubControls & SC_ScrollBarSlider) + painter->setBrush(midColor2); + else if (option->state & State_MouseOver + && scrollBar->activeSubControls & SC_ScrollBarSlider) + painter->setBrush(highlightedGradient); + else + painter->setBrush(gradient); + painter->drawRect(pixmapRect.adjusted(horizontal ? -1 : 0, + horizontal ? 0 : -1, + horizontal ? 0 : 1, + horizontal ? 1 : 0)); + painter->setPen(innerContrastLine); + painter->drawRect( + scrollBarSlider.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, -1, -1)); + } + } + // The SubLine (up/left) buttons + if (!hasTransientStyle && scrollBar->subControls & SC_ScrollBarSubLine) { + if ((scrollBar->activeSubControls & SC_ScrollBarSubLine) && sunken) + painter->setBrush(gradientStopColor); + else if ((scrollBar->activeSubControls & SC_ScrollBarSubLine)) + painter->setBrush(highlightedGradient); + else + painter->setBrush(gradient); + painter->setPen(Qt::NoPen); + painter->drawRect( + scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0)); + painter->setPen(QPen(alphaOutline)); + if (option->state & State_Horizontal) { + if (option->direction == Qt::RightToLeft) { + pixmapRect.setLeft(scrollBarSubLine.left()); + painter->drawLine(pixmapRect.topLeft(), pixmapRect.bottomLeft()); + } else { + pixmapRect.setRight(scrollBarSubLine.right()); + painter->drawLine(pixmapRect.topRight(), pixmapRect.bottomRight()); + } + } else { + pixmapRect.setBottom(scrollBarSubLine.bottom()); + painter->drawLine(pixmapRect.bottomLeft(), pixmapRect.bottomRight()); + } + QRect upRect = scrollBarSubLine.adjusted(horizontal ? 0 : 1, + horizontal ? 1 : 0, + horizontal ? -2 : -1, + horizontal ? -1 : -2); + painter->setBrush(Qt::NoBrush); + painter->setPen(innerContrastLine); + painter->drawRect(upRect); + // Arrows + PrimitiveElement arrowType = PE_IndicatorArrowUp; + if (option->state & State_Horizontal) + arrowType = option->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft + : PE_IndicatorArrowRight; + StyleHelper::drawArrow(arrowType, painter, option); + } + // The AddLine (down/right) button + if (!hasTransientStyle && scrollBar->subControls & SC_ScrollBarAddLine) { + if ((scrollBar->activeSubControls & SC_ScrollBarAddLine) && sunken) + painter->setBrush(gradientStopColor); + else if ((scrollBar->activeSubControls & SC_ScrollBarAddLine)) + painter->setBrush(midColor2); + else + painter->setBrush(gradient); + painter->setPen(Qt::NoPen); + painter->drawRect( + scrollBarAddLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0)); + painter->setPen(QPen(alphaOutline, 1)); + if (option->state & State_Horizontal) { + if (option->direction == Qt::LeftToRight) { + pixmapRect.setLeft(scrollBarAddLine.left()); + painter->drawLine(pixmapRect.topLeft(), pixmapRect.bottomLeft()); + } else { + pixmapRect.setRight(scrollBarAddLine.right()); + painter->drawLine(pixmapRect.topRight(), pixmapRect.bottomRight()); + } + } else { + pixmapRect.setTop(scrollBarAddLine.top()); + painter->drawLine(pixmapRect.topLeft(), pixmapRect.topRight()); + } + QRect downRect = scrollBarAddLine.adjusted(1, 1, -1, -1); + painter->setPen(innerContrastLine); + painter->setBrush(Qt::NoBrush); + painter->drawRect(downRect); + PrimitiveElement arrowType = PE_IndicatorArrowDown; + if (option->state & State_Horizontal) + arrowType = option->direction == Qt::LeftToRight ? PE_IndicatorArrowRight + : PE_IndicatorArrowLeft; + StyleHelper::drawArrow(arrowType, painter, option); + } + } + painter->restore(); + } break; +#endif // QT_CONFIG(slider) default: Super::drawComplexControl(control, option, painter, widget); break; @@ -779,7 +905,7 @@ QSize StudioStyle::sizeFromContents( QSize newSize; switch (type) { - case CT_MenuItem: + case CT_MenuItem: { if (const auto mbi = qstyleoption_cast(option)) { if (!isQmlEditorMenu(widget)) { newSize = Super::sizeFromContents(type, option, size, widget); @@ -835,8 +961,7 @@ QSize StudioStyle::sizeFromContents( break; } } - break; - + } break; default: newSize = Super::sizeFromContents(type, option, size, widget); break; @@ -860,26 +985,56 @@ QRect StudioStyle::subControlRect( } #endif - switch (control) - { - case CC_Slider: + switch (control) { + case CC_Slider: { if (const auto slider = qstyleoption_cast(option)) { switch (subControl) { case SubControl::SC_SliderGroove: return slider->rect; - case SubControl::SC_SliderHandle: - { + case SubControl::SC_SliderHandle: { QRect retval = Super::subControlRect(control, option, subControl, widget); - return (slider->orientation == Qt::Horizontal) - ? retval.adjusted(0, 1, 0, 0) - : retval.adjusted(1, 0, 0, 0); - } - break; + return (slider->orientation == Qt::Horizontal) ? retval.adjusted(0, 1, 0, 0) + : retval.adjusted(1, 0, 0, 0); + } break; default: break; } } - break; + } break; + case CC_ScrollBar: { + if (!styleHint(SH_ScrollBar_Transient, option, widget)) + break; + + if (const auto scrollbar = qstyleoption_cast(option)) { + QRect newRect = Super::subControlRect(control, option, subControl, widget); + if (Utils::HostOsInfo::isMacHost()) { + if (scrollbar->orientation == Qt::Horizontal) { + const int halfThickness = newRect.height() / 2; + newRect.adjust(0, halfThickness, 0, 0); + } else { + const int halfThickness = newRect.width() / 2; + newRect.adjust(halfThickness, 0, 0, 0); + } + } + if (subControl == SC_ScrollBarSlider) { + bool hasGroove + = (scrollbar->activeSubControls.testFlag(SC_SliderGroove) + || (scrollbar->styleObject + && scrollbar->styleObject->property("adjacentScrollBar").toBool())) + && scrollbar->subControls.testFlag(SC_ScrollBarGroove); + bool interacted = scrollbar->activeSubControls.testFlag(SC_ScrollBarSlider); + + if (hasGroove || interacted) + return newRect; + + if (scrollbar->orientation == Qt::Horizontal) + newRect.adjust(0, 1, 0, -1); + else + newRect.adjust(1, 0, -1, 0); + } + return newRect; + } + } break; default: break; } @@ -893,6 +1048,12 @@ int StudioStyle::styleHint( const QWidget *widget, QStyleHintReturn *returnData) const { + switch (hint) { + case SH_ScrollBar_Transient: + return true; + default: + break; + } return Super::styleHint(hint, option, widget, returnData); } @@ -946,19 +1107,22 @@ int StudioStyle::pixelMetric( return 4; case PM_ToolBarExtensionExtent: return 29; - case PM_ScrollBarExtent: - return 20; + case PM_ScrollBarExtent: { + if (styleHint(SH_ScrollBar_Transient, option, widget)) + return 10; + return 14; + } break; case PM_ScrollBarSliderMin: return 30; case PM_SliderLength: return 5; - case PM_SliderThickness: + case PM_SliderThickness: { if (const auto *slider = qstyleoption_cast(option)) { return (slider->orientation == Qt::Horizontal ? slider->rect.height() : slider->rect.width()) - 1; } - break; + } break; case PM_SliderControlThickness: return 2; default: diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.h b/src/plugins/qmldesignerbase/studio/studiostyle.h index c55797354b3..63250a007de 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.h +++ b/src/plugins/qmldesignerbase/studio/studiostyle.h @@ -7,6 +7,7 @@ #include +namespace QmlDesigner { class StudioStylePrivate; class QMLDESIGNERBASE_EXPORT StudioStyle : public QProxyStyle @@ -20,57 +21,51 @@ public: virtual ~StudioStyle() override; // Drawing Methods - void drawPrimitive( - PrimitiveElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget = nullptr) const override; + void drawPrimitive(PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; - void drawControl( - ControlElement element, - const QStyleOption *option, - QPainter *painter, - const QWidget *widget = nullptr) const override; + void drawControl(ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; - void drawComplexControl( - ComplexControl control, - const QStyleOptionComplex *option, - QPainter *painter, - const QWidget *widget = nullptr) const override; + void drawComplexControl(ComplexControl control, + const QStyleOptionComplex *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; // Topology - QSize sizeFromContents( - ContentsType type, - const QStyleOption *option, - const QSize &size, - const QWidget *widget) const override; + QSize sizeFromContents(ContentsType type, + const QStyleOption *option, + const QSize &size, + const QWidget *widget) const override; - QRect subControlRect( - ComplexControl control, - const QStyleOptionComplex *option, - SubControl subControl, - const QWidget *widget) const override; + QRect subControlRect(ComplexControl control, + const QStyleOptionComplex *option, + SubControl subControl, + const QWidget *widget) const override; - int styleHint( - StyleHint hint, - const QStyleOption *option, - const QWidget *widget, - QStyleHintReturn *returnData) const override; + int styleHint(StyleHint hint, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, + QStyleHintReturn *returnData = nullptr) const override; - int pixelMetric( - PixelMetric metric, - const QStyleOption *option = nullptr, - const QWidget *widget = nullptr) const override; + int pixelMetric(PixelMetric metric, + const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; QPalette standardPalette() const override; private: - void drawQmlEditorIcon( - PrimitiveElement element, - const QStyleOption *option, - const char *propertyName, - QPainter *painter, - const QWidget *widget = nullptr) const; + void drawQmlEditorIcon(PrimitiveElement element, + const QStyleOption *option, + const char *propertyName, + QPainter *painter, + const QWidget *widget = nullptr) const; StudioStylePrivate *d = nullptr; }; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp new file mode 100644 index 00000000000..c1e1dece05f --- /dev/null +++ b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp @@ -0,0 +1,246 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "studiostyle_p.h" + +#include "studiostyle.h" + +#include +#include + +#include +#include +#include + +using namespace Utils; +using namespace QmlDesigner; + +namespace { +inline QPixmap getPixmapFromIcon( + const QIcon &icon, const QSize &size, bool enabled, bool active, bool checked) +{ + QIcon::Mode mode = enabled ? ((active) ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; + QIcon::State state = (checked) ? QIcon::On : QIcon::Off; + return icon.pixmap(size, mode, state); +} + +inline QColor studioTextColor(bool enabled, bool active, bool checked) +{ + Theme::Color themePenColorId = enabled ? (active ? (checked ? Theme::DSsubPanelBackground + : Theme::DSpanelBackground) + : Theme::DStextColor) + : Theme::DStextColorDisabled; + + return creatorTheme()->color(themePenColorId); +} +}; // namespace + +StudioStylePrivate::StudioStylePrivate(StudioStyle *q) + : QObject(q) + , q(q) +{ + auto color = [](Theme::Color c) { return creatorTheme()->color(c); }; + + { + stdPalette.setColorGroup(QPalette::Disabled, // group + color(Theme::DStextColorDisabled), // windowText + color(Theme::DScontrolBackgroundDisabled), // button + color(Theme::DScontrolOutlineDisabled), // light + color(Theme::DStextSelectedTextColor), // dark + color(Theme::DSstatusbarBackground), // mid + color(Theme::DStextColorDisabled), // text + color(Theme::DStextColorDisabled), // brightText + color(Theme::DStoolbarIcon_blocked), // base + color(Theme::DStoolbarIcon_blocked) // window + ); + + stdPalette.setColorGroup(QPalette::Inactive, // group + color(Theme::DStextColor), // windowText + color(Theme::DScontrolBackground), // button + color(Theme::DStoolbarBackground), // light + color(Theme::DSstatusbarBackground), // dark + color(Theme::DScontrolBackground), // mid + color(Theme::DStextColor), // text + color(Theme::DStextColor), // brightText + color(Theme::DStoolbarBackground), // base + color(Theme::DStoolbarBackground) // window + ); + + stdPalette.setColorGroup(QPalette::Active, // group + color(Theme::DStextSelectedTextColor), // windowText + color(Theme::DSnavigatorItemBackgroundHover), // button + color(Theme::DSstateBackgroundColor_hover), // light + color(Theme::DSpanelBackground), // dark + color(Theme::DSnavigatorItemBackgroundHover), // mid + color(Theme::DStextSelectedTextColor), // text + color(Theme::DStextSelectedTextColor), // brightText + color(Theme::DStoolbarBackground), // base + color(Theme::DStoolbarBackground) // window + ); + + stdPalette.setBrush(QPalette::ColorRole::ToolTipBase, color(Theme::DStoolbarBackground)); + stdPalette.setColor(QPalette::ColorRole::ToolTipText, color(Theme::DStextColor)); + } + + QToolTip::setPalette(stdPalette); +} + +QList StudioStylePrivate::animationTargets() const +{ + return m_animations.keys(); +} + +QStyleAnimation *StudioStylePrivate::animation(const QObject *target) const +{ + return m_animations.value(target, nullptr); +} + +void StudioStylePrivate::startAnimation(QStyleAnimation *animation) const +{ + stopAnimation(animation->target()); + QObject::connect(animation, + &QObject::destroyed, + this, + &StudioStylePrivate::removeAnimation, + Qt::UniqueConnection); + m_animations.insert(animation->target(), animation); + animation->start(); +} + +void StudioStylePrivate::stopAnimation(const QObject *target) const +{ + QStyleAnimation *animation = m_animations.take(target); + if (animation) { + animation->stop(); + delete animation; + } +} + +void StudioStylePrivate::removeAnimation(const QObject *animationObject) +{ + if (animationObject) + m_animations.remove(animationObject->parent()); +} + +StudioShortcut::StudioShortcut(const QStyleOptionMenuItem *option, const QString &shortcutText) + : m_shortcutText(shortcutText) + , m_enabled(option->state & QStyle::State_Enabled) + , m_active(option->state & QStyle::State_Selected) + , m_font(option->font) + , m_fontMetrics(m_font) + , m_defaultHeight(m_fontMetrics.height()) + , m_spaceConst(m_fontMetrics.boundingRect(".").width()) +{ + reset(); + + if (backspaceMatch(shortcutText).hasMatch() && option->styleObject) + m_backspaceIcon = option->styleObject->property("backspaceIcon").value(); +} + +QSize StudioShortcut::getSize() +{ + if (m_isFirstParticle) + calcResult(); + return m_size; +} + +QPixmap StudioShortcut::getPixmap() +{ + if (!m_isFirstParticle && !m_pixmap.isNull()) + return m_pixmap; + + m_pixmap = QPixmap(getSize()); + m_pixmap.fill(Qt::transparent); + QPainter painter(&m_pixmap); + painter.setFont(m_font); + QPen pPen = painter.pen(); + pPen.setColor(studioTextColor(m_enabled, m_active, false)); + painter.setPen(pPen); + calcResult(&painter); + painter.end(); + + return m_pixmap; +} + +void StudioShortcut::applySize(const QSize &itemSize) +{ + m_width += itemSize.width(); + m_height = std::max(m_height, itemSize.height()); + if (m_isFirstParticle) + m_isFirstParticle = false; + else + m_width += m_spaceConst; +} + +void StudioShortcut::addText(const QString &txt, QPainter *painter) +{ + if (txt.size()) { + int textWidth = m_fontMetrics.horizontalAdvance(txt); + QSize itemSize = {textWidth, m_defaultHeight}; + if (painter) { + static const QTextOption textOption(Qt::AlignLeft | Qt::AlignVCenter); + QRect placeRect({m_width, 0}, itemSize); + painter->drawText(placeRect, txt, textOption); + } + applySize(itemSize); + } +} + +void StudioShortcut::addPixmap(const QPixmap &pixmap, QPainter *painter) +{ + if (painter) + painter->drawPixmap(QRect({m_width, 0}, pixmap.size()), pixmap); + + applySize(pixmap.size()); +} + +void StudioShortcut::calcResult(QPainter *painter) +{ + reset(); +#ifndef QT_NO_SHORTCUT + if (!m_shortcutText.isEmpty()) { + int fwdIndex = 0; + + QRegularExpressionMatch mMatch = backspaceMatch(m_shortcutText); + int matchCount = mMatch.lastCapturedIndex(); + + for (int i = 0; i <= matchCount; ++i) { + QString mStr = mMatch.captured(i); + QSize iconSize(m_defaultHeight * 3, m_defaultHeight); + const QList iconSizes = m_backspaceIcon.availableSizes(); + if (iconSizes.size()) + iconSize = iconSizes.last(); + double aspectRatio = (m_defaultHeight + .0) / iconSize.height(); + int newWidth = iconSize.width() * aspectRatio; + + QPixmap pixmap = getPixmapFromIcon(m_backspaceIcon, + {newWidth, m_defaultHeight}, + m_enabled, + m_active, + false); + + int lIndex = m_shortcutText.indexOf(mStr, fwdIndex); + int diffChars = lIndex - fwdIndex; + addText(m_shortcutText.mid(fwdIndex, diffChars), painter); + addPixmap(pixmap, painter); + fwdIndex = lIndex + mStr.size(); + } + addText(m_shortcutText.mid(fwdIndex), painter); + } +#endif + m_size = {m_width, m_height}; +} + +void StudioShortcut::reset() +{ + m_isFirstParticle = true; + m_width = 0; + m_height = 0; +} + +QRegularExpressionMatch StudioShortcut::backspaceMatch(const QString &str) const +{ + static const QRegularExpression backspaceDetect("\\+*backspace\\+*", + QRegularExpression::CaseInsensitiveOption); + return backspaceDetect.match(str); +} diff --git a/src/plugins/qmldesignerbase/studio/studiostyle_p.h b/src/plugins/qmldesignerbase/studio/studiostyle_p.h new file mode 100644 index 00000000000..ce556e61af2 --- /dev/null +++ b/src/plugins/qmldesignerbase/studio/studiostyle_p.h @@ -0,0 +1,84 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QStyleOptionMenuItem; +class QPainter; + +namespace Utils { +class StyleAnimation; +class QStyleAnimation; +} // namespace Utils + +namespace QmlDesigner { +class StudioStyle; + +class StudioStylePrivate : public QObject +{ + Q_OBJECT + using StyleAnimation = Utils::StyleAnimation; + using QStyleAnimation = Utils::QStyleAnimation; + +public: + explicit StudioStylePrivate(StudioStyle *q); + + QList animationTargets() const; + QStyleAnimation *animation(const QObject *target) const; + void startAnimation(QStyleAnimation *animation) const; + void stopAnimation(const QObject *target) const; + + QPalette stdPalette; + +private: + StudioStyle *q = nullptr; + mutable QHash m_animations; + +private slots: + void removeAnimation(const QObject *animationObject); +}; + +struct StudioShortcut +{ + StudioShortcut(const QStyleOptionMenuItem *option, const QString &shortcutText); + QSize getSize(); + QPixmap getPixmap(); + +private: + void applySize(const QSize &itemSize); + void addText(const QString &txt, QPainter *painter = nullptr); + void addPixmap(const QPixmap &pixmap, QPainter *painter = nullptr); + void calcResult(QPainter *painter = nullptr); + void reset(); + QRegularExpressionMatch backspaceMatch(const QString &str) const; + + const QString m_shortcutText; + const bool m_enabled; + const bool m_active; + const QFont m_font; + const QFontMetrics m_fontMetrics; + const int m_defaultHeight; + const int m_spaceConst; + + QIcon m_backspaceIcon; + bool m_isFirstParticle = true; + + int m_width = 0; + int m_height = 0; + QSize m_size; + QPixmap m_pixmap; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index 7071cfffbd1..8157712437b 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -82,7 +82,15 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, true); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QStringList{"#222222", "#999999"}); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa"); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#cccccc"); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ENABLED, false); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, 50.); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, 5.); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, 10.); restoreValue(settings, DesignerSettingsKey::SMOOTH_RENDERING, false); restoreValue(settings, DesignerSettingsKey::SHOW_DEBUG_SETTINGS, false); restoreValue(settings, DesignerSettingsKey::EDITOR_ZOOM_FACTOR, 1.0); diff --git a/src/plugins/qmldesignerbase/utils/designersettings.h b/src/plugins/qmldesignerbase/utils/designersettings.h index 2e2e90c83f1..850e2a43a06 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -32,6 +32,14 @@ inline constexpr char SHOW_DEBUGVIEW[] = "ShowQtQuickDesignerDebugView"; inline constexpr char ENABLE_DEBUGVIEW[] = "EnableQtQuickDesignerDebugView"; inline constexpr char EDIT3DVIEW_BACKGROUND_COLOR[] = "Edit3DViewBackgroundColor"; inline constexpr char EDIT3DVIEW_GRID_COLOR[] = "Edit3DViewGridLineColor"; +inline constexpr char EDIT3DVIEW_SNAP_ABSOLUTE[] = "Edit3DViewSnapAbsolute"; +inline constexpr char EDIT3DVIEW_SNAP_ENABLED[] = "Edit3DViewSnapEnabled"; +inline constexpr char EDIT3DVIEW_SNAP_POSITION[] = "Edit3DViewSnapPosition"; +inline constexpr char EDIT3DVIEW_SNAP_POSITION_INTERVAL[] = "Edit3DViewSnapPositionInterval"; +inline constexpr char EDIT3DVIEW_SNAP_ROTATION[] = "Edit3DViewSnapRotation"; +inline constexpr char EDIT3DVIEW_SNAP_ROTATION_INTERVAL[] = "Edit3DViewSnapRotationInterval"; +inline constexpr char EDIT3DVIEW_SNAP_SCALE[] = "Edit3DViewSnapScale"; +inline constexpr char EDIT3DVIEW_SNAP_SCALE_INTERVAL[] = "Edit3DViewSnapScaleInterval"; inline constexpr char ALWAYS_SAVE_IN_CRUMBLEBAR[] = "AlwaysSaveInCrumbleBar"; inline constexpr char USE_DEFAULT_PUPPET[] = "UseDefaultQml2Puppet"; inline constexpr char PUPPET_TOPLEVEL_BUILD_DIRECTORY[] = "PuppetToplevelBuildDirectory"; diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp index b29d1627db5..4f040d12dfc 100644 --- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp +++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp @@ -79,7 +79,8 @@ public: } void performChanges(QmlJSRefactoringFilePtr currentFile, - const QmlJSRefactoringChanges &refactoring) override + const QmlJSRefactoringChanges &refactoring, + const QString &imports = QString()) override { QString componentName = m_componentName; @@ -128,18 +129,12 @@ public: const Utils::FilePath newFileName = path.pathAppended(componentName + QLatin1String(".") + suffix); - QString imports; - UiProgram *prog = currentFile->qmljsDocument()->qmlProgram(); - if (prog && prog->headers) { - const unsigned int start = currentFile->startOf(prog->headers->firstSourceLocation()); - const unsigned int end = currentFile->startOf(prog->members->member->firstSourceLocation()); - imports = currentFile->textOf(start, end); - } + QString qmlImports = imports.size() ? imports : currentFile->qmlImports(); const unsigned int start = currentFile->startOf(m_firstSourceLocation); const unsigned int end = currentFile->startOf(m_lastSourceLocation); - QString newComponentSource = imports + currentFile->textOf(start, end) - + QLatin1String("}\n"); + QString newComponentSource = qmlImports + currentFile->textOf(start, end) + + QLatin1String("}\n"); //Remove properties from resulting code... @@ -248,7 +243,8 @@ void matchComponentFromObjectDefQuickFix(const QmlJSQuickFixAssistInterface *int void performComponentFromObjectDef(QmlJSEditorWidget *editor, const QString &fileName, - QmlJS::AST::UiObjectDefinition *objDef) + QmlJS::AST::UiObjectDefinition *objDef, + const QString &importData) { QmlJSRefactoringChanges refactoring(QmlJS::ModelManagerInterface::instance(), QmlJS::ModelManagerInterface::instance()->snapshot()); @@ -257,7 +253,7 @@ void performComponentFromObjectDef(QmlJSEditorWidget *editor, QmlJSQuickFixAssistInterface interface(editor, TextEditor::AssistReason::ExplicitlyInvoked); Operation operation(&interface, objDef); - operation.performChanges(current, refactoring); + operation.performChanges(current, refactoring, importData); } } //namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h index 72a0f50e20e..1f723074556 100644 --- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h +++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h @@ -15,6 +15,7 @@ QMLJSEDITOR_EXPORT void matchComponentFromObjectDefQuickFix( QMLJSEDITOR_EXPORT void performComponentFromObjectDef(QmlJSEditorWidget *editor, const QString &fileName, - QmlJS::AST::UiObjectDefinition *objDef); + QmlJS::AST::UiObjectDefinition *objDef, + const QString &importData); } // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljsquickfix.h b/src/plugins/qmljseditor/qmljsquickfix.h index b050ba4d1f1..a787c0ed6d2 100644 --- a/src/plugins/qmljseditor/qmljsquickfix.h +++ b/src/plugins/qmljseditor/qmljsquickfix.h @@ -40,7 +40,9 @@ protected: using Range = Utils::ChangeSet::Range; virtual void performChanges(QmlJSTools::QmlJSRefactoringFilePtr currentFile, - const QmlJSTools::QmlJSRefactoringChanges &refactoring) = 0; + const QmlJSTools::QmlJSRefactoringChanges &refactoring, + const QString &imports = QString()) + = 0; const QmlJSTools::SemanticInfo &semanticInfo() const; diff --git a/src/plugins/qmljseditor/qmljsquickfixes.cpp b/src/plugins/qmljseditor/qmljsquickfixes.cpp index f6baa01509d..44ee1bd5226 100644 --- a/src/plugins/qmljseditor/qmljsquickfixes.cpp +++ b/src/plugins/qmljseditor/qmljsquickfixes.cpp @@ -50,7 +50,8 @@ public: } void performChanges(QmlJSRefactoringFilePtr currentFile, - const QmlJSRefactoringChanges &) override + const QmlJSRefactoringChanges &, + const QString &) override { Q_ASSERT(_objectInitializer); @@ -115,7 +116,8 @@ public: } void performChanges(QmlJSRefactoringFilePtr currentFile, - const QmlJSRefactoringChanges &) override + const QmlJSRefactoringChanges &, + const QString &) override { Utils::ChangeSet changes; const int insertLoc = _message.location.begin() - _message.location.startColumn + 1; diff --git a/src/plugins/qmljseditor/qmljswrapinloader.cpp b/src/plugins/qmljseditor/qmljswrapinloader.cpp index 91a762191aa..70dc321f419 100644 --- a/src/plugins/qmljseditor/qmljswrapinloader.cpp +++ b/src/plugins/qmljseditor/qmljswrapinloader.cpp @@ -90,7 +90,8 @@ public: } void performChanges(QmlJSRefactoringFilePtr currentFile, - const QmlJSRefactoringChanges &) override + const QmlJSRefactoringChanges &, + const QString &) override { UiScriptBinding *idBinding; const QString id = idOfObject(m_objDef, &idBinding); diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp index 635be5a51f6..e431b6ef718 100644 --- a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp +++ b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp @@ -136,6 +136,18 @@ Document::Ptr QmlJSRefactoringFile::qmljsDocument() const return m_qmljsDocument; } +QString QmlJSRefactoringFile::qmlImports() const +{ + QString imports; + QmlJS::AST::UiProgram *prog = qmljsDocument()->qmlProgram(); + if (prog && prog->headers) { + const unsigned int start = startOf(prog->headers->firstSourceLocation()); + const unsigned int end = startOf(prog->members->member->firstSourceLocation()); + imports = textOf(start, end); + } + return imports; +} + unsigned QmlJSRefactoringFile::startOf(const SourceLocation &loc) const { return position(loc.startLine, loc.startColumn); diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.h b/src/plugins/qmljstools/qmljsrefactoringchanges.h index 33545e2bfc5..b95da2076cf 100644 --- a/src/plugins/qmljstools/qmljsrefactoringchanges.h +++ b/src/plugins/qmljstools/qmljsrefactoringchanges.h @@ -22,6 +22,7 @@ class QMLJSTOOLS_EXPORT QmlJSRefactoringFile: public TextEditor::RefactoringFile { public: QmlJS::Document::Ptr qmljsDocument() const; + QString qmlImports() const; /*! Returns the offset in the document for the start position of the given diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt index 40114a7e47c..4c1889143aa 100644 --- a/src/plugins/qmlpreview/CMakeLists.txt +++ b/src/plugins/qmlpreview/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(QmlPreview CONDITION TARGET QmlProjectManager PUBLIC_DEPENDS QmlDebug - DEPENDS QmlJS + DEPENDS QmlJS Qt::QmlPrivate PLUGIN_DEPENDS Core ProjectExplorer QmlJSTools QtSupport ResourceEditor QmlProjectManager @@ -22,19 +22,3 @@ extend_qtc_plugin(QmlPreview tests/qmlpreviewclient_test.cpp tests/qmlpreviewclient_test.h tests/qmlpreviewplugin_test.cpp tests/qmlpreviewplugin_test.h ) - -if(TARGET Qt6::QmlPrivate) - get_target_property(qmldebugprivate_include_directories - Qt6::QmlPrivate - INTERFACE_INCLUDE_DIRECTORIES - ) - find_file(have_qml_debug_translation_protocol - NAMES private/qqmldebugtranslationprotocol_p.h - PATHS ${qmldebugprivate_include_directories} - ) - extend_qtc_plugin(QmlPreview - CONDITION have_qml_debug_translation_protocol - PUBLIC_DEPENDS Qt::QmlPrivate - PUBLIC_DEFINES "FOUND_QML_DEBUG_TRANSLATION_PROTOCOL" - ) -endif() diff --git a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp index d0cfc2d96ed..fc3018018eb 100644 --- a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp +++ b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp @@ -6,9 +6,7 @@ #include -#ifdef FOUND_QML_DEBUG_TRANSLATION_PROTOCOL #include -#endif namespace QmlPreview { @@ -20,14 +18,9 @@ QmlDebugTranslationClient::QmlDebugTranslationClient(QmlDebug::QmlDebugConnectio void QmlDebugTranslationClient::changeLanguage(const QUrl &url, const QString &localeIsoCode) { QmlDebug::QPacket packet(dataStreamVersion()); -#ifdef FOUND_QML_DEBUG_TRANSLATION_PROTOCOL - sendMessage(QQmlDebugTranslation::createChangeLanguageRequest(packet, url, localeIsoCode)); -#else const int request_change_language = 1; packet << request_change_language << url << localeIsoCode; sendMessage(packet.data()); -#endif - } void QmlDebugTranslationClient::stateChanged(QmlDebug::QmlDebugClient::State state) diff --git a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp index 358991f2ba0..9b270478597 100644 --- a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp +++ b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp @@ -12,7 +12,6 @@ #include namespace QmlPreview { -namespace Internal { QmlPreviewConnectionManager::QmlPreviewConnectionManager(QObject *parent) : QmlDebug::QmlDebugConnectionManager(parent) @@ -44,7 +43,7 @@ void QmlPreviewConnectionManager::setFpsHandler(QmlPreviewFpsHandler fpsHandler) m_fpsHandler = fpsHandler; } -void QmlPreviewConnectionManager::setQmlDebugTranslationClientCreator(QmlDebugTranslationClientCreator creator) +void QmlPreviewConnectionManager::setQmlDebugTranslationClientCreator(QmlDebugTranslationClientFactoryFunction creator) { m_createDebugTranslationClientMethod = creator; } @@ -249,5 +248,4 @@ void QmlPreviewConnectionManager::destroyClients() m_fileSystemWatcher.clear(); } -} // namespace Internal } // namespace QmlPreview diff --git a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h index d965d4216a2..6ce92f99ff3 100644 --- a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h +++ b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h @@ -17,7 +17,6 @@ class Target; } namespace QmlPreview { -namespace Internal { class QmlPreviewConnectionManager : public QmlDebug::QmlDebugConnectionManager { @@ -30,7 +29,7 @@ public: void setFileLoader(QmlPreviewFileLoader fileLoader); void setFileClassifier(QmlPreviewFileClassifier fileClassifier); void setFpsHandler(QmlPreviewFpsHandler fpsHandler); - void setQmlDebugTranslationClientCreator(QmlDebugTranslationClientCreator creator); + void setQmlDebugTranslationClientCreator(QmlDebugTranslationClientFactoryFunction creator); signals: void loadFile(const QString &filename, const QString &changedFile, const QByteArray &contents); @@ -58,8 +57,7 @@ private: QmlPreviewFileLoader m_fileLoader = nullptr; QmlPreviewFileClassifier m_fileClassifier = nullptr; QmlPreviewFpsHandler m_fpsHandler = nullptr; - QmlDebugTranslationClientCreator m_createDebugTranslationClientMethod; + QmlDebugTranslationClientFactoryFunction m_createDebugTranslationClientMethod; }; -} // namespace Internal } // namespace QmlPreview diff --git a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp index d1d9b4fced1..146195dc276 100644 --- a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp +++ b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp @@ -14,7 +14,6 @@ #include namespace QmlPreview { -namespace Internal { void QmlPreviewFileOnTargetFinder::setTarget(ProjectExplorer::Target *target) { @@ -96,5 +95,4 @@ QUrl QmlPreviewFileOnTargetFinder::findUrl(const QString &filePath, bool *succes } } -} // namespace Internal } // namespace QmlPreview diff --git a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.h b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.h index 4ab4a5a4a82..c0873ebe714 100644 --- a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.h +++ b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.h @@ -12,7 +12,6 @@ class Target; } namespace QmlPreview { -namespace Internal { class QmlPreviewFileOnTargetFinder { @@ -27,5 +26,4 @@ private: QPointer m_target; }; -} // namespace Internal } // namespace QmlPreview diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index 550af3a6a90..9d05d0ee398 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -44,6 +44,7 @@ #include #include +#include #include using namespace ProjectExplorer; @@ -95,6 +96,9 @@ static std::unique_ptr defaultCreateDebugTranslationC return client; } +static void defaultRefreshTranslationFunction() +{} + class QmlPreviewPluginPrivate : public QObject { public: @@ -127,7 +131,7 @@ public: QmlPreviewPlugin *q = nullptr; QThread m_parseThread; QString m_previewedFile; - Core::IEditor *m_lastEditor = nullptr; + QPointer m_lastEditor; QmlPreviewRunControlList m_runningPreviews; bool m_dirty = false; QString m_localeIsoCode; @@ -145,6 +149,7 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent) m_settings.fileClassifier = &defaultFileClassifier; m_settings.fpsHandler = &defaultFpsHandler; m_settings.createDebugTranslationClientMethod = &defaultCreateDebugTranslationClientMethod; + m_settings.refreshTranslationsFunction = &defaultRefreshTranslationFunction; Core::ActionContainer *menu = Core::ActionManager::actionContainer( Constants::M_BUILDPROJECT); @@ -236,6 +241,12 @@ QmlPreviewRunControlList QmlPreviewPlugin::runningPreviews() const return d->m_runningPreviews; } +void QmlPreviewPlugin::stopAllPreviews() +{ + for (auto &runningPreview : d->m_runningPreviews) + runningPreview->initiateStop(); +} + QmlPreviewFileLoader QmlPreviewPlugin::fileLoader() const { return d->m_settings.fileLoader; @@ -299,11 +310,16 @@ void QmlPreviewPlugin::setLocaleIsoCode(const QString &localeIsoCode) emit localeIsoCodeChanged(d->m_localeIsoCode); } -void QmlPreviewPlugin::setQmlDebugTranslationClientCreator(QmlDebugTranslationClientCreator creator) +void QmlPreviewPlugin::setQmlDebugTranslationClientCreator(QmlDebugTranslationClientFactoryFunction creator) { d->m_settings.createDebugTranslationClientMethod = creator; } +void QmlPreviewPlugin::setRefreshTranslationsFunction(QmlPreviewRefreshTranslationFunction refreshTranslationsFunction) +{ + d->m_settings.refreshTranslationsFunction = refreshTranslationsFunction; +} + void QmlPreviewPlugin::setFileLoader(QmlPreviewFileLoader fileLoader) { if (d->m_settings.fileLoader == fileLoader) diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.h b/src/plugins/qmlpreview/qmlpreviewplugin.h index 15a5d3e1815..30f0eb54f40 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.h +++ b/src/plugins/qmlpreview/qmlpreviewplugin.h @@ -25,8 +25,9 @@ using QmlPreviewFileClassifier = bool (*)(const QString &); using QmlPreviewFileLoader = QByteArray (*)(const QString &, bool *); using QmlPreviewFpsHandler = void (*)(quint16[8]); using QmlPreviewRunControlList = QList; -using QmlDebugTranslationClientCreator = +using QmlDebugTranslationClientFactoryFunction = std::function(QmlDebug::QmlDebugConnection *)>; +using QmlPreviewRefreshTranslationFunction = std::function; class QMLPREVIEW_EXPORT QmlPreviewPlugin : public ExtensionSystem::IPlugin { @@ -54,6 +55,7 @@ public: QString previewedFile() const; void setPreviewedFile(const QString &previewedFile); QmlPreviewRunControlList runningPreviews() const; + void stopAllPreviews(); void setFileLoader(QmlPreviewFileLoader fileLoader); QmlPreviewFileLoader fileLoader() const; @@ -70,7 +72,8 @@ public: QString localeIsoCode() const; void setLocaleIsoCode(const QString &localeIsoCode); - void setQmlDebugTranslationClientCreator(QmlDebugTranslationClientCreator creator); + void setQmlDebugTranslationClientCreator(QmlDebugTranslationClientFactoryFunction creator); + void setRefreshTranslationsFunction(QmlPreviewRefreshTranslationFunction refreshTranslationsFunction); void previewCurrentFile(); void addPreview(ProjectExplorer::RunControl *preview); diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index 19bf396ce7b..2857c15fbb1 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -13,22 +13,54 @@ #include #include +#include #include #include +#include #include #include #include #include +#include + using namespace ProjectExplorer; using namespace Utils; -using namespace QmlPreview::Internal; namespace QmlPreview { static const Key QmlServerUrl = "QmlServerUrl"; +class RefreshTranslationWorker final : public ProjectExplorer::RunWorker +{ +public: + explicit RefreshTranslationWorker(ProjectExplorer::RunControl *runControl, + const QmlPreviewRunnerSetting &runnerSettings) + : ProjectExplorer::RunWorker(runControl), m_runnerSettings(runnerSettings) + { + setId("RefreshTranslationWorker"); + connect(this, &RunWorker::started, this, &RefreshTranslationWorker::startRefreshTranslationsAsync); + connect(this, &RunWorker::stopped, &m_futureWatcher, &QFutureWatcher::cancel); + } + ~RefreshTranslationWorker() + { + m_futureWatcher.cancel(); + m_futureWatcher.waitForFinished(); + } + +private: + void startRefreshTranslationsAsync() + { + m_futureWatcher.setFuture(Utils::asyncRun([this] { + m_runnerSettings.refreshTranslationsFunction(); + stop(); + })); + } + QmlPreviewRunnerSetting m_runnerSettings; + QFutureWatcher m_futureWatcher; +}; + class QmlPreviewRunner : public ProjectExplorer::RunWorker { Q_OBJECT @@ -51,11 +83,12 @@ private: void start() override; void stop() override; - Internal::QmlPreviewConnectionManager m_connectionManager; + QmlPreviewConnectionManager m_connectionManager; + RefreshTranslationWorker m_refreshTranslationWorker; }; QmlPreviewRunner::QmlPreviewRunner(RunControl *runControl, const QmlPreviewRunnerSetting &settings) - : RunWorker(runControl) + : RunWorker(runControl), m_refreshTranslationWorker(runControl, settings) { setId("QmlPreviewRunner"); m_connectionManager.setFileLoader(settings.fileLoader); @@ -78,9 +111,10 @@ QmlPreviewRunner::QmlPreviewRunner(RunControl *runControl, const QmlPreviewRunne this, [this, settings]() { if (settings.zoomFactor > 0) emit zoom(settings.zoomFactor); - if (!settings.language.isEmpty()) - emit language(settings.language); - + if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current()) { + if (!multiLanguageAspect->currentLocale().isEmpty()) + emit language(multiLanguageAspect->currentLocale()); + } emit ready(); }); @@ -99,6 +133,8 @@ QmlPreviewRunner::QmlPreviewRunner(RunControl *runControl, const QmlPreviewRunne runControl->initiateStop(); }); + + addStartDependency(&m_refreshTranslationWorker); } void QmlPreviewRunner::start() diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.h b/src/plugins/qmlpreview/qmlpreviewruncontrol.h index 371ac180f68..d1fbdb88d1c 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.h +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.h @@ -15,8 +15,8 @@ struct QmlPreviewRunnerSetting QmlPreviewFileClassifier fileClassifier; QmlPreviewFpsHandler fpsHandler; float zoomFactor = -1.0; - QString language; - QmlDebugTranslationClientCreator createDebugTranslationClientMethod; + QmlDebugTranslationClientFactoryFunction createDebugTranslationClientMethod; + QmlPreviewRefreshTranslationFunction refreshTranslationsFunction; }; class QmlPreviewRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index adcb504fe8a..c4c5a621f65 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -137,7 +137,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("qdsVersion", versionConfig["designStudio"].toString()); appendString("quickVersion", versionConfig["qtQuick"].toString()); appendBool("qt6Project", versionConfig["qt"].toString() == "6"); - appendBool("qtForMCUs", rootObject["mcuConfig"].toObject().isEmpty()); + appendBool("qtForMCUs", !(rootObject["mcuConfig"].toObject().isEmpty())); appendBreak(); appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index c8f7f63bffa..4016cc6ac6f 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -81,7 +81,7 @@ QmlBuildSystem::QmlBuildSystem(Target *target) refresh(RefreshOptions::Project); updateDeploymentData(); - registerMenuButtons(); +// registerMenuButtons(); //is wip connect(target->project(), &Project::activeTargetChanged, this, [this](Target *target) { refresh(RefreshOptions::NoFileRefresh); @@ -117,24 +117,29 @@ void QmlBuildSystem::updateDeploymentData() setDeploymentData(deploymentData); } +//probably this method needs to be moved into QmlProjectPlugin::initialize to be called only once void QmlBuildSystem::registerMenuButtons() { Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); // QML Project file update button // This button saves the current configuration into the .qmlproject file - auto action = new QAction("Update QmlProject File", this); + auto action = new QAction(Tr::tr("Update QmlProject File"), this); + //this registerAction registers a new action for each opened project, + //causes the "action is already registered" warning if you have multiple opened projects, + //is not a big thing for qds, but is annoying for qtc and should be fixed. Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ProjectManager"); menu->addAction(cmd, Core::Constants::G_FILE_SAVE); QObject::connect(action, &QAction::triggered, this, &QmlBuildSystem::updateProjectFile); } +//wip: bool QmlBuildSystem::updateProjectFile() { qDebug() << "debug#1-mainfilepath" << mainFilePath(); QFile file(mainFilePath().fileName().append("project-test")); - if (file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { qCritical() << "Cannot open Qml Project file for editing!"; return false; } @@ -195,8 +200,8 @@ void QmlBuildSystem::refresh(RefreshOptions options) = modelManager->defaultProjectInfoForProject(project(), project()->files(Project::HiddenRccFolders)); - for (const QString &searchPath : customImportPaths()) { - projectInfo.importPaths.maybeInsert(projectDirectory().pathAppended(searchPath), + for (const QString &importPath : absoluteImportPaths()) { + projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(importPath), QmlJS::Dialect::Qml); } @@ -644,6 +649,16 @@ QStringList QmlBuildSystem::importPaths() const return m_projectItem->importPaths(); } +QStringList QmlBuildSystem::absoluteImportPaths() +{ + return Utils::transform(m_projectItem->importPaths(), [&](const QString &importPath) { + Utils::FilePath filePath = Utils::FilePath::fromString(importPath); + if (!filePath.isAbsolutePath()) + return (projectDirectory() / importPath).toString(); + return projectDirectory().resolvePath(importPath).toString(); + }); +} + Utils::FilePaths QmlBuildSystem::files() const { return m_projectItem->files(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 2d689a19712..95b66871eb7 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -68,18 +68,24 @@ public: Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const; Utils::EnvironmentItems environment() const; + + QStringList importPaths() const; + QStringList absoluteImportPaths(); QStringList customImportPaths() const; QStringList customFileSelectors() const; + bool multilanguageSupport() const; QStringList supportedLanguages() const; void setSupportedLanguages(QStringList languages); + QString primaryLanguage() const; void setPrimaryLanguage(QString language); + bool forceFreeType() const; bool widgetApp() const; + QStringList shaderToolArgs() const; QStringList shaderToolFiles() const; - QStringList importPaths() const; Utils::FilePaths files() const; QString versionQt() const; diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp index 8ca68acbc3e..1b3238a425b 100644 --- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp @@ -69,8 +69,10 @@ QmlMultiLanguageAspect::QmlMultiLanguageAspect(AspectContainer *container) connect(this, &BoolAspect::changed, this, [this] { for (RunControl *runControl : ProjectExplorerPlugin::allRunControls()) { - if (runControl->aspect()->origin == this) - runControl->initiateStop(); + if (auto aspect = runControl->aspect()) { + if (auto origin = aspect->origin; origin == this) + runControl->initiateStop(); + } } }); } diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 5e7e5eacfef..fd977a2d4c8 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp index 4bd6293c19a..91c03828d26 100644 --- a/src/plugins/studiowelcome/qdsnewdialog.cpp +++ b/src/plugins/studiowelcome/qdsnewdialog.cpp @@ -191,21 +191,16 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar auto userPreset = m_currentPreset->asUserPreset(); if (m_qmlDetailsLoaded) { - if (m_currentPreset->isUserPreset()) { - if (m_wizard.haveVirtualKeyboard()) - setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard); - - if (m_wizard.haveTargetQtVersion()) { - int index = m_wizard.targetQtVersionIndex(userPreset->qtVersion); - if (index != -1) - setTargetQtVersionIndex(index); - } - } else { - if (m_wizard.haveTargetQtVersion()) { - int index = m_wizard.targetQtVersionIndex(); - if (index != -1) - setTargetQtVersionIndex(index); - } + m_targetQtVersions.clear(); + if (m_currentPreset->isUserPreset() && m_wizard.haveVirtualKeyboard()) + setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard); + if (m_wizard.haveTargetQtVersion()) { + m_targetQtVersions = m_wizard.targetQtVersionNames(); + int index = m_currentPreset->isUserPreset() ? m_wizard.targetQtVersionIndex(userPreset->qtVersion) + : m_wizard.targetQtVersionIndex(); + emit targetQtVersionsChanged(); + if (index != -1) + setTargetQtVersionIndex(index); } emit haveVirtualKeyboardChanged(); diff --git a/src/plugins/studiowelcome/qdsnewdialog.h b/src/plugins/studiowelcome/qdsnewdialog.h index f63ec4748dc..f4f24d1810a 100644 --- a/src/plugins/studiowelcome/qdsnewdialog.h +++ b/src/plugins/studiowelcome/qdsnewdialog.h @@ -41,6 +41,7 @@ public: Q_PROPERTY(QString statusType MEMBER m_qmlStatusType READ getStatusType NOTIFY statusTypeChanged) Q_PROPERTY(bool fieldsValid MEMBER m_qmlFieldsValid READ getFieldsValid NOTIFY fieldsValidChanged) Q_PROPERTY(QString presetName MEMBER m_qmlPresetName) + Q_PROPERTY(QStringList targetQtVersions MEMBER m_targetQtVersions NOTIFY targetQtVersionsChanged) Q_PROPERTY(bool detailsLoaded MEMBER m_qmlDetailsLoaded) Q_PROPERTY(bool stylesLoaded MEMBER m_qmlStylesLoaded) @@ -108,6 +109,7 @@ signals: void targetQtVersionIndexChanged(); void userPresetSaved(); void lastUserPresetRemoved(); + void targetQtVersionsChanged(); private slots: void onStatusMessageChanged(Utils::InfoLabel::InfoType type, const QString &message); @@ -174,6 +176,7 @@ private: WizardHandler m_wizard; UserPresetsStore m_recentsStore; UserPresetsStore m_userPresetsStore; + QStringList m_targetQtVersions; }; } //namespace StudioWelcome diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 30902093c21..2f576b331b9 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -266,7 +266,7 @@ public: Q_INVOKABLE void showHelp() { - QDesktopServices::openUrl(QUrl("qthelp://org.qt-project.qtcreator/doc/index.html")); + QDesktopServices::openUrl(QUrl("qthelp://org.qt-project.qtdesignstudio/doc/index.html")); } Q_INVOKABLE void openExample(const QString &examplePath, @@ -423,8 +423,11 @@ static QString tags(const FilePath &projectFilePath) QVariant ProjectModel::data(const QModelIndex &index, int role) const { - if (index.row() >= ProjectExplorer::ProjectExplorerPlugin::recentProjects().count()) + if (!index.isValid() || + index.row() >= ProjectExplorer::ProjectExplorerPlugin::recentProjects().count()) { + return {}; + } const ProjectExplorer::RecentProjectsEntry data = ProjectExplorer::ProjectExplorerPlugin::recentProjects().at(index.row()); diff --git a/src/plugins/studiowelcome/wizardhandler.cpp b/src/plugins/studiowelcome/wizardhandler.cpp index d6d750d39eb..961fb701eeb 100644 --- a/src/plugins/studiowelcome/wizardhandler.cpp +++ b/src/plugins/studiowelcome/wizardhandler.cpp @@ -220,6 +220,21 @@ QString WizardHandler::targetQtVersionName(int index) const return text; } +QStringList WizardHandler::targetQtVersionNames() const +{ + auto *field = m_detailsPage->jsonField("TargetQtVersion"); + auto *cbfield = dynamic_cast(field); + QTC_ASSERT(cbfield, return {}); + + QStandardItemModel *model = cbfield->model(); + QStringList targetVersions; + + for (int i = 0; i < model->rowCount(); ++i) + targetVersions.append(model->item(i)->text()); + + return targetVersions; +} + int WizardHandler::targetQtVersionIndex(const QString &qtVersionName) const { auto *field = m_detailsPage->jsonField("TargetQtVersion"); diff --git a/src/plugins/studiowelcome/wizardhandler.h b/src/plugins/studiowelcome/wizardhandler.h index 1ceadd3a79b..e6009dd9362 100644 --- a/src/plugins/studiowelcome/wizardhandler.h +++ b/src/plugins/studiowelcome/wizardhandler.h @@ -35,6 +35,7 @@ public: void setTargetQtVersionIndex(int index); bool haveTargetQtVersion() const; QString targetQtVersionName(int index) const; + QStringList targetQtVersionNames() const; void setStyleIndex(int index); int styleIndex() const; diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp index 4cc16a488a6..856a181009d 100644 --- a/src/plugins/texteditor/highlightersettingspage.cpp +++ b/src/plugins/texteditor/highlightersettingspage.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 77bde355e9f..2ffebbc59db 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -17,16 +17,10 @@ if (NOT QT_CREATOR_API_DEFINED) include(QtCreatorIDEBranding) include(QtCreatorAPI) - find_package(QT 5.15.0 NAMES Qt6 Qt5 - COMPONENTS Core REQUIRED - ) - find_package(Qt${QT_VERSION_MAJOR} + find_package(Qt6 COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml REQUIRED ) -else() - # Qt Creator only supports Qt 6 - set(QT_VERSION_MAJOR ${Qt6_VERSION_MAJOR}) endif() if (NOT TARGET QmlPuppetCommunication) @@ -78,12 +72,6 @@ extend_qtc_executable(qml2puppet editor3d_qt6.qrc ) -extend_qtc_executable(qml2puppet - CONDITION Qt5_VERSION VERSION_LESS 6.0.0 - SOURCES - editor3d_qt5.qrc -) - extend_qtc_executable(qml2puppet CONDITION UNIX AND (NOT APPLE) DEPENDS rt @@ -95,7 +83,7 @@ extend_qtc_executable(qml2puppet nodeinstanceclientproxy.cpp nodeinstanceclientproxy.h ) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick3D QUIET) +find_package(Qt6 COMPONENTS Quick3D QUIET) extend_qtc_executable(qml2puppet CONDITION TARGET Qt::Quick3D FEATURE_INFO "Qt Quick 3D support" @@ -115,7 +103,7 @@ extend_qtc_executable(qml2puppet icongizmoimageprovider.cpp icongizmoimageprovider.h ) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick3DAssetImport QUIET) +find_package(Qt6 COMPONENTS Quick3DAssetImport QUIET) extend_qtc_executable(qml2puppet CONDITION TARGET Qt::Quick3DAssetImport FEATURE_INFO "Qt Quick 3D asset import" @@ -123,7 +111,7 @@ extend_qtc_executable(qml2puppet DEFINES IMPORT_QUICK3D_ASSETS ) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick3DParticles QUIET) +find_package(Qt6 COMPONENTS Quick3DParticles QUIET) extend_qtc_executable(qml2puppet CONDITION TARGET Qt::Quick3DParticles FEATURE_INFO "Qt Quick 3D particles" @@ -132,7 +120,7 @@ extend_qtc_executable(qml2puppet ) # Quick3DAssetUtils optionally depends on QuickTimeline, so find also it to make the CI build work -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick3DAssetUtils QuickTimeline QUIET) +find_package(Qt6 COMPONENTS Quick3DAssetUtils QuickTimeline QUIET) extend_qtc_executable(qml2puppet CONDITION TARGET Qt::Quick3DAssetUtils FEATURE_INFO "Qt Quick 3D asset utils" @@ -140,19 +128,6 @@ extend_qtc_executable(qml2puppet DEFINES QUICK3D_ASSET_UTILS_MODULE ) -extend_qtc_executable(qml2puppet - CONDITION Qt6_VERSION - SOURCES_PREFIX qml2puppet/editor3d/qt5compat - SOURCES - qquick3darealight.cpp qquick3darealight_p.h -) - -extend_qtc_executable(qml2puppet - SOURCES_PREFIX qml2puppet/iconrenderer - SOURCES - iconrenderer.cpp iconrenderer.h -) - extend_qtc_executable(qml2puppet SOURCES_PREFIX qml2puppet/import3d SOURCES diff --git a/src/tools/qml2puppet/editor3d_qt5.qrc b/src/tools/qml2puppet/editor3d_qt5.qrc deleted file mode 100644 index d4127574a69..00000000000 --- a/src/tools/qml2puppet/editor3d_qt5.qrc +++ /dev/null @@ -1,58 +0,0 @@ - - - mockfiles/meshes/arrow.mesh - mockfiles/meshes/scalerod.mesh - mockfiles/meshes/ring.mesh - mockfiles/meshes/ringselect.mesh - mockfiles/meshes/axishelper.mesh - mockfiles/images/editor_camera.png - mockfiles/images/editor_camera@2x.png - mockfiles/images/area.png - mockfiles/images/area@2x.png - mockfiles/images/directional.png - mockfiles/images/directional@2x.png - mockfiles/images/point.png - mockfiles/images/point@2x.png - mockfiles/images/static_floor.png - mockfiles/images/spot.png - mockfiles/images/spot@2x.png - mockfiles/images/preview_landscape.hdr - mockfiles/images/preview_studio.hdr - mockfiles/qt5/AdjustableArrow.qml - mockfiles/qt5/AreaLightHandle.qml - mockfiles/qt5/Arrow.qml - mockfiles/qt5/AutoScaleHelper.qml - mockfiles/qt5/AxisHelper.qml - mockfiles/qt5/AxisHelperArm.qml - mockfiles/qt5/CameraFrustum.qml - mockfiles/qt5/CameraGizmo.qml - mockfiles/qt5/DirectionalDraggable.qml - mockfiles/qt5/EditCameraController.qml - mockfiles/qt5/EditView3D.qml - mockfiles/qt5/FadeHandle.qml - mockfiles/qt5/HelperGrid.qml - mockfiles/qt5/IconGizmo.qml - mockfiles/qt5/IconRenderer3D.qml - mockfiles/qt5/LightGizmo.qml - mockfiles/qt5/LightIconGizmo.qml - mockfiles/qt5/LightModel.qml - mockfiles/qt5/Line3D.qml - mockfiles/qt5/MaterialNodeView.qml - mockfiles/qt5/ModelNode2DImageView.qml - mockfiles/qt5/ModelNode3DImageView.qml - mockfiles/qt5/ModelNodeView.qml - mockfiles/qt5/MoveGizmo.qml - mockfiles/qt5/NodeNodeView.qml - mockfiles/qt5/Overlay2D.qml - mockfiles/qt5/PlanarDraggable.qml - mockfiles/qt5/PlanarMoveHandle.qml - mockfiles/qt5/PlanarScaleHandle.qml - mockfiles/qt5/RotateGizmo.qml - mockfiles/qt5/RotateRing.qml - mockfiles/qt5/ScaleGizmo.qml - mockfiles/qt5/ScaleRod.qml - mockfiles/qt5/SceneView3D.qml - mockfiles/qt5/SelectionBox.qml - mockfiles/qt5/SpotLightHandle.qml - - diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index bb29d683a07..df498e643cf 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -9,8 +9,6 @@ mockfiles/images/editor_camera@2x.png mockfiles/images/editor_particlesystem.png mockfiles/images/editor_particlesystem@2x.png - mockfiles/images/area.png - mockfiles/images/area@2x.png mockfiles/images/directional.png mockfiles/images/directional@2x.png mockfiles/images/point.png @@ -21,7 +19,6 @@ mockfiles/images/preview_landscape.hdr mockfiles/images/preview_studio.hdr mockfiles/qt6/AdjustableArrow.qml - mockfiles/qt6/AreaLightHandle.qml mockfiles/qt6/Arrow.qml mockfiles/qt6/AutoScaleHelper.qml mockfiles/qt6/AxisHelper.qml @@ -57,5 +54,8 @@ mockfiles/qt6/SceneView3D.qml mockfiles/qt6/SelectionBox.qml mockfiles/qt6/SpotLightHandle.qml + mockfiles/qt6/GridMaterial.qml + mockfiles/shaders/gridmaterial.frag + mockfiles/shaders/gridmaterial.vert diff --git a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp index 489c8c840b7..1afdd52bb0c 100644 --- a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp @@ -69,11 +69,7 @@ namespace QmlDesigner { void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else = &QLocalSocket::errorOccurred; -#endif NodeInstanceClientProxy::NodeInstanceClientProxy(QObject *parent) : QObject(parent) diff --git a/src/tools/qml2puppet/mockfiles/GenericBackend.qml b/src/tools/qml2puppet/mockfiles/GenericBackend.qml deleted file mode 100644 index 30083c228f3..00000000000 --- a/src/tools/qml2puppet/mockfiles/GenericBackend.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.6 - -QtObject { - property int x - property int y - property int width - property int height -} diff --git a/src/tools/qml2puppet/mockfiles/images/area.png b/src/tools/qml2puppet/mockfiles/images/area.png deleted file mode 100644 index ed261ff2d50..00000000000 Binary files a/src/tools/qml2puppet/mockfiles/images/area.png and /dev/null differ diff --git a/src/tools/qml2puppet/mockfiles/images/area@2x.png b/src/tools/qml2puppet/mockfiles/images/area@2x.png deleted file mode 100644 index 1f6e37d7e99..00000000000 Binary files a/src/tools/qml2puppet/mockfiles/images/area@2x.png and /dev/null differ diff --git a/src/tools/qml2puppet/mockfiles/images/static_floor.png b/src/tools/qml2puppet/mockfiles/images/static_floor.png deleted file mode 100644 index 93073719f55..00000000000 Binary files a/src/tools/qml2puppet/mockfiles/images/static_floor.png and /dev/null differ diff --git a/src/tools/qml2puppet/mockfiles/qt5/AdjustableArrow.qml b/src/tools/qml2puppet/mockfiles/qt5/AdjustableArrow.qml deleted file mode 100644 index 9e1880643ae..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/AdjustableArrow.qml +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import LineGeometry 1.0 - -DirectionalDraggable { - id: arrowRoot - - Model { - geometry: LineGeometry { - id: lineGeometry - name: "Edit 3D ScalableArrow" - startPos: Qt.vector3d(0, 0, 0) - endPos: Qt.vector3d(0, 1, 0) - } - scale: Qt.vector3d(1, arrowRoot.length, 1) - materials: [ arrowRoot.material ] - } - - Model { - id: arrowHead - source: "#Cone" - materials: [ arrowRoot.material ] - y: arrowRoot.length - 3 - scale: Qt.vector3d(0.02, 0.035, 0.02) - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/AreaLightHandle.qml b/src/tools/qml2puppet/mockfiles/qt5/AreaLightHandle.qml deleted file mode 100644 index be29721aa47..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/AreaLightHandle.qml +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -DirectionalDraggable { - id: handleRoot - - property string currentLabel - property point currentMousePos - property string propName - property real propValue: 0 - property real newValue: 0 - property real baseScale: 5 - - scale: autoScaler.getScale(Qt.vector3d(baseScale, baseScale, baseScale)) - length: 3 - offset: -1.5 - - Model { - id: handle - source: "#Sphere" - materials: [ handleRoot.material ] - scale: Qt.vector3d(0.02, 0.02, 0.02) - } - - AutoScaleHelper { - id: autoScaler - active: handleRoot.active - view3D: handleRoot.view3D - } - - property real _startValue - property real _startScale - - signal valueCommit() - signal valueChange() - - function updateValue(relativeDistance, screenPos) - { - handleRoot.newValue = Math.round(Math.min(999999, Math.max(0, _startValue + (relativeDistance * _startScale)))); - var l = Qt.locale(); - handleRoot.currentLabel = propName + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 0); - handleRoot.currentMousePos = screenPos; - } - - onPressed: (mouseArea, screenPos)=> { - _startScale = autoScaler.relativeScale * baseScale; - _startValue = propValue; - updateValue(0, screenPos); - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateValue(relativeDistance, screenPos); - handleRoot.valueChange(); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateValue(relativeDistance, screenPos); - handleRoot.valueCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/Arrow.qml b/src/tools/qml2puppet/mockfiles/qt5/Arrow.qml deleted file mode 100644 index a77f7321e4c..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/Arrow.qml +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -DirectionalDraggable { - id: arrow - source: "../meshes/arrow.mesh" - - signal positionCommit() - signal positionMove() - - function localPos(sceneRelativeDistance) - { - var newScenePos = Qt.vector3d( - _targetStartPos.x + sceneRelativeDistance.x, - _targetStartPos.y + sceneRelativeDistance.y, - _targetStartPos.z + sceneRelativeDistance.z); - return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; - } - - onPressed: { - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - } - - onDragged: (mouseArea, sceneRelativeDistance)=> { - targetNode.position = localPos(sceneRelativeDistance); - if (targetNode == multiSelectionNode) - _generalHelper.moveMultiSelection(false); - positionMove(); - } - - onReleased: (mouseArea, sceneRelativeDistance)=> { - targetNode.position = localPos(sceneRelativeDistance); - if (targetNode == multiSelectionNode) - _generalHelper.moveMultiSelection(true); - positionCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/AutoScaleHelper.qml b/src/tools/qml2puppet/mockfiles/qt5/AutoScaleHelper.qml deleted file mode 100644 index 7beb6226c3e..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/AutoScaleHelper.qml +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Node { - id: overlayNode - - property View3D view3D - property Camera camera: view3D.camera - property bool active: true - - // Read-only - property real relativeScale: 1 - - onActiveChanged: updateScale() - onSceneTransformChanged: updateScale() - // Trigger delayed update on camera change to ensure camera values are correct - onCameraChanged: _generalHelper.requestOverlayUpdate(); - - Connections { - target: camera - function onSceneTransformChanged() { updateScale() } - } - - Connections { - target: _generalHelper - function onOverlayUpdateNeeded() { updateScale() } - } - - function getScale(baseScale) - { - return Qt.vector3d(baseScale.x * relativeScale, baseScale.y * relativeScale, - baseScale.z * relativeScale); - } - - function updateScale() - { - if (active) - relativeScale = helper.getRelativeScale(overlayNode); - else - relativeScale = 1; - } - - MouseArea3D { - id: helper - active: false - view3D: overlayNode.view3D - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/AxisHelper.qml b/src/tools/qml2puppet/mockfiles/qt5/AxisHelper.qml deleted file mode 100644 index 054fd9f0ba4..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/AxisHelper.qml +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -View3D { - id: axisHelperView - - property var editCameraCtrl - property Node selectedNode - - camera: axisHelperCamera - - Node { - OrthographicCamera { - id: axisHelperCamera - rotation: editCameraCtrl.camera ? editCameraCtrl.camera.rotation : Qt.quaternion(1, 0, 0, 0) - position: editCameraCtrl.camera ? editCameraCtrl.camera.position.minus(editCameraCtrl._lookAtPoint) - .normalized().times(600) : Qt.vector3d(0, 0, 0) - } - - AutoScaleHelper { - id: autoScale - view3D: axisHelperView - position: axisHelperGizmo.scenePosition - } - - Node { - id: axisHelperGizmo - scale: autoScale.getScale(Qt.vector3d(4, 4, 4)) - - AxisHelperArm { - id: armX - eulerRotation: Qt.vector3d(0, 0, -90) - color: Qt.rgba(1, 0, 0, 1) - hoverColor: Qt.lighter(Qt.rgba(1, 0, 0, 1)) - view3D: axisHelperView - camRotPos: Qt.vector3d(0, 90, 0) - camRotNeg: Qt.vector3d(0, -90, 0) - } - - AxisHelperArm { - id: armY - eulerRotation: Qt.vector3d(0, 0, 0) - color: Qt.rgba(0, 0.6, 0, 1) - hoverColor: Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - view3D: axisHelperView - camRotPos: Qt.vector3d(-90, 0, 0) - camRotNeg: Qt.vector3d(90, 0, 0) - } - - AxisHelperArm { - id: armZ - eulerRotation: Qt.vector3d(90, 0, 0) - color: Qt.rgba(0, 0, 1, 1) - hoverColor: Qt.lighter(Qt.rgba(0, 0, 1, 1)) - view3D: axisHelperView - camRotPos: Qt.vector3d(0, 0, 0) - camRotNeg: Qt.vector3d(0, 180, 0) - } - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton - - property var pickObj: null - - function cancelHover() - { - if (pickObj) { - pickObj.hovering = false; - pickObj = null; - } - } - - function pick(mouse) - { - var result = axisHelperView.pick(mouse.x, mouse.y); - if (result.objectHit) { - if (result.objectHit !== pickObj) { - cancelHover(); - pickObj = result.objectHit; - pickObj.hovering = true; - } - } else { - cancelHover(); - } - } - - onPositionChanged: (mouse)=> { - pick(mouse); - } - - onPressed: (mouse)=> { - pick(mouse); - if (pickObj) { - axisHelperView.editCameraCtrl.focusObject(axisHelperView.selectedNode, - pickObj.cameraRotation, false, false); - } else { - mouse.accepted = false; - } - } - - onExited: cancelHover() - onCanceled: cancelHover() - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/AxisHelperArm.qml b/src/tools/qml2puppet/mockfiles/qt5/AxisHelperArm.qml deleted file mode 100644 index 36bc0a360ec..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/AxisHelperArm.qml +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -Node { - id: armRoot - property alias posModel: posModel - property alias negModel: negModel - property View3D view3D - property color hoverColor - property color color - property vector3d camRotPos - property vector3d camRotNeg - - Model { - id: posModel - - property bool hovering: false - property vector3d cameraRotation: armRoot.camRotPos - - source: "../meshes/axishelper.mesh" - materials: DefaultMaterial { - id: posMat - diffuseColor: posModel.hovering ? armRoot.hoverColor : armRoot.color - lighting: DefaultMaterial.NoLighting - } - pickable: true - } - - Model { - id: negModel - - property bool hovering: false - property vector3d cameraRotation: armRoot.camRotNeg - - source: "#Sphere" - y: -6 - scale: Qt.vector3d(0.025, 0.025, 0.025) - materials: DefaultMaterial { - id: negMat - diffuseColor: negModel.hovering ? armRoot.hoverColor : armRoot.color - lighting: DefaultMaterial.NoLighting - } - pickable: true - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/CameraFrustum.qml b/src/tools/qml2puppet/mockfiles/qt5/CameraFrustum.qml deleted file mode 100644 index 22cf9d3c038..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/CameraFrustum.qml +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import CameraGeometry 1.0 - -Model { - id: cameraFrustum - - property alias geometryName: cameraGeometry.name // Name must be unique for each geometry - property alias viewPortRect: cameraGeometry.viewPortRect - property Node targetNode: null - property Node scene: null - property bool selected: false - - function updateGeometry() - { - cameraGeometry.update(); - } - - position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0) - rotation: targetNode ? targetNode.sceneRotation : Qt.quaternion(1, 0, 0, 0) - - geometry: cameraGeometry - materials: [ - DefaultMaterial { - id: defaultMaterial - diffuseColor: cameraFrustum.selected ? "#FF0000" : "#555555" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - - CameraGeometry { - id: cameraGeometry - camera: cameraFrustum.scene && cameraFrustum.targetNode ? cameraFrustum.targetNode : null - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/CameraGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/CameraGizmo.qml deleted file mode 100644 index 510dcc5ecec..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/CameraGizmo.qml +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -IconGizmo { - id: cameraGizmo - - property Model frustumModel: null - property bool globalShowFrustum: false - - iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_camera.png" - - function connectFrustum(frustum) - { - frustumModel = frustum; - - frustum.selected = selected; - frustum.selected = Qt.binding(function() {return selected;}); - - frustum.scene = scene; - frustum.scene = Qt.binding(function() {return scene;}); - - frustum.targetNode = targetNode; - frustum.targetNode = Qt.binding(function() {return targetNode;}); - - frustum.visible = (canBeVisible && globalShowFrustum) - || (targetNode && selected && activeScene === scene); - frustum.visible = Qt.binding(function() { - return (canBeVisible && globalShowFrustum) - || (targetNode && selected && activeScene === scene); - }); - } - - onActiveSceneChanged: { - if (frustumModel && activeScene == scene) - frustumModel.updateGeometry(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/DirectionalDraggable.qml b/src/tools/qml2puppet/mockfiles/qt5/DirectionalDraggable.qml deleted file mode 100644 index 8cb9d176e27..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/DirectionalDraggable.qml +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Model { - id: rootModel - - property View3D view3D - property alias color: material.diffuseColor - property Node targetNode: null - property bool dragging: mouseAreaYZ.dragging || mouseAreaXZ.dragging - property bool active: false - property MouseArea3D dragHelper: null - property alias material: material - property real length: 12 - property real offset: 0 - - readonly property bool hovering: mouseAreaYZ.hovering || mouseAreaXZ.hovering - - property vector3d _scenePosPressed - property real _posPressed - property vector3d _targetStartPos - - signal pressed(var mouseArea, point screenPos) - signal dragged(var mouseArea, vector3d sceneRelativeDistance, real relativeDistance, point screenPos) - signal released(var mouseArea, vector3d sceneRelativeDistance, real relativeDistance, point screenPos) - - DefaultMaterial { - id: material - diffuseColor: "white" - lighting: DefaultMaterial.NoLighting - } - - materials: [ material ] - - function handlePressed(mouseArea, planePos, screenPos) - { - if (!targetNode) - return; - - var maskedPosition = Qt.vector3d(planePos.x, 0, 0); - _posPressed = planePos.x; - _scenePosPressed = mouseArea.dragHelper.mapPositionToScene(maskedPosition); - _targetStartPos = mouseArea.pivotScenePosition(targetNode); - pressed(mouseArea, screenPos); - } - - function calcRelativeDistance(mouseArea, planePos) - { - var maskedPosition = Qt.vector3d(planePos.x, 0, 0); - var scenePointerPos = mouseArea.dragHelper.mapPositionToScene(maskedPosition); - return scenePointerPos.minus(_scenePosPressed); - } - - function handleDragged(mouseArea, planePos, screenPos) - { - if (!targetNode) - return; - - dragged(mouseArea, calcRelativeDistance(mouseArea, planePos), planePos.x - _posPressed, screenPos); - } - - function handleReleased(mouseArea, planePos, screenPos) - { - if (!targetNode) - return; - - released(mouseArea, calcRelativeDistance(mouseArea, planePos), planePos.x - _posPressed, screenPos); - } - - MouseArea3D { - id: mouseAreaYZ - view3D: rootModel.view3D - x: rootModel.offset - y: -1.5 - width: rootModel.length - height: 3 - eulerRotation: Qt.vector3d(0, 0, 90) - grabsMouse: targetNode - active: rootModel.active - dragHelper: rootModel.dragHelper - priority: 5 - - onPressed: (planePos, screenPos)=> { - rootModel.handlePressed(mouseAreaYZ, planePos, screenPos); - } - onDragged: (planePos, screenPos)=> { - rootModel.handleDragged(mouseAreaYZ, planePos, screenPos); - } - onReleased: (planePos, screenPos)=> { - rootModel.handleReleased(mouseAreaYZ, planePos, screenPos); - } - } - - MouseArea3D { - id: mouseAreaXZ - view3D: rootModel.view3D - x: rootModel.offset - y: -1.5 - width: rootModel.length - height: 3 - eulerRotation: Qt.vector3d(0, 90, 90) - grabsMouse: targetNode - active: rootModel.active - dragHelper: rootModel.dragHelper - priority: 5 - - onPressed: (planePos, screenPos)=> { - rootModel.handlePressed(mouseAreaXZ, planePos, screenPos); - } - onDragged: (planePos, screenPos)=> { - rootModel.handleDragged(mouseAreaXZ, planePos, screenPos); - } - onReleased: (planePos, screenPos)=> { - rootModel.handleReleased(mouseAreaXZ, planePos, screenPos); - } - } -} - diff --git a/src/tools/qml2puppet/mockfiles/qt5/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt5/EditCameraController.qml deleted file mode 100644 index eb493f6b106..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/EditCameraController.qml +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.12 -import QtQuick3D 1.15 - -Item { - id: cameraCtrl - - property Camera camera: null - property View3D view3d: null - property string sceneId - property vector3d _lookAtPoint - property vector3d _pressPoint - property vector3d _prevPoint - property vector3d _startRotation - property vector3d _startPosition - property vector3d _startLookAtPoint - property matrix4x4 _startTransform - property bool _dragging - property int _button - property real _zoomFactor: 1 - property Camera _prevCamera: null - readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600) - readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0) - readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length() - readonly property real _keyPanAmount: 5 - property bool ignoreToolState: false - - function restoreCameraState(cameraState) - { - if (!camera || ignoreToolState) - return; - - _lookAtPoint = cameraState[0]; - _zoomFactor = cameraState[1]; - camera.position = cameraState[2]; - camera.rotation = cameraState[3]; - _generalHelper.zoomCamera(view3d, camera, 0, _defaultCameraLookAtDistance, _lookAtPoint, - _zoomFactor, false); - } - - function restoreDefaultState() - { - if (!camera) - return; - - _lookAtPoint = Qt.vector3d(0, 0, 0); - _zoomFactor = 1; - camera.position = _defaultCameraPosition; - camera.eulerRotation = _defaultCameraRotation; - _generalHelper.zoomCamera(view3d, camera, 0, _defaultCameraLookAtDistance, _lookAtPoint, - _zoomFactor, false); - } - - function storeCameraState(delay) - { - if (!camera || ignoreToolState) - return; - - var cameraState = []; - cameraState[0] = _lookAtPoint; - cameraState[1] = _zoomFactor; - cameraState[2] = camera.position; - cameraState[3] = camera.rotation; - _generalHelper.storeToolState(sceneId, "editCamState", cameraState, delay); - } - - - function focusObject(targetNodes, rotation, updateZoom, closeUp) - { - if (!camera) - return; - - // targetNodes could be a list of nodes or a single node - var nodes = []; - if (targetNodes instanceof Node) - nodes.push(targetNodes); - else - nodes = targetNodes - - camera.eulerRotation = rotation; - var newLookAtAndZoom = _generalHelper.focusNodesToCamera( - camera, _defaultCameraLookAtDistance, nodes, view3d, _zoomFactor, - updateZoom, closeUp); - _lookAtPoint = newLookAtAndZoom.toVector3d(); - _zoomFactor = newLookAtAndZoom.w; - storeCameraState(0); - } - - function alignCameras(targetNodes) - { - if (!camera) - return; - - // targetNodes could be a list of nodes or a single node - var nodes = []; - if (targetNodes instanceof Node) - nodes.push(targetNodes); - else - nodes = targetNodes - - _generalHelper.alignCameras(camera, nodes); - } - - function alignView(targetNodes) - { - if (!camera) - return; - - // targetNodes could be a list of nodes or a single node - var nodes = []; - if (targetNodes instanceof Node) - nodes.push(targetNodes); - else - nodes = targetNodes - - _lookAtPoint = _generalHelper.alignView(camera, nodes, _lookAtPoint); - storeCameraState(0); - } - - function zoomRelative(distance) - { - if (!camera) - return; - - _zoomFactor = _generalHelper.zoomCamera(view3d, camera, distance, _defaultCameraLookAtDistance, - _lookAtPoint, _zoomFactor, true); - } - - onCameraChanged: { - if (camera && _prevCamera) { - // Reset zoom on previous camera to ensure it's properties are good to copy to new cam - _generalHelper.zoomCamera(view3d, _prevCamera, 0, _defaultCameraLookAtDistance, _lookAtPoint, - 1, false); - - camera.position = _prevCamera.position; - camera.rotation = _prevCamera.rotation; - - // Apply correct zoom to new camera - _generalHelper.zoomCamera(view3d, camera, 0, _defaultCameraLookAtDistance, _lookAtPoint, - _zoomFactor, false); - } - _prevCamera = camera; - } - - MouseArea { - id: mouseHandler - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - hoverEnabled: false - anchors.fill: parent - onPositionChanged: (mouse)=> { - if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) { - var currentPoint = Qt.vector3d(mouse.x, mouse.y, 0); - if (cameraCtrl._button == Qt.LeftButton) { - _generalHelper.orbitCamera(cameraCtrl.camera, cameraCtrl._startRotation, - cameraCtrl._lookAtPoint, cameraCtrl._pressPoint, - currentPoint); - } else if (cameraCtrl._button == Qt.MiddleButton) { - cameraCtrl._lookAtPoint = _generalHelper.panCamera( - cameraCtrl.camera, cameraCtrl._startTransform, - cameraCtrl._startPosition, cameraCtrl._startLookAtPoint, - cameraCtrl._pressPoint, currentPoint, _zoomFactor); - } else if (cameraCtrl._button == Qt.RightButton) { - cameraCtrl.zoomRelative(currentPoint.y - cameraCtrl._prevPoint.y) - cameraCtrl._prevPoint = currentPoint; - } - } - } - onPressed: (mouse)=> { - if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) { - cameraCtrl._dragging = true; - cameraCtrl._startRotation = cameraCtrl.camera.eulerRotation; - cameraCtrl._startPosition = cameraCtrl.camera.position; - cameraCtrl._startLookAtPoint = _lookAtPoint; - cameraCtrl._pressPoint = Qt.vector3d(mouse.x, mouse.y, 0); - cameraCtrl._prevPoint = cameraCtrl._pressPoint; - cameraCtrl._button = mouse.button; - cameraCtrl._startTransform = cameraCtrl.camera.sceneTransform; - } else { - mouse.accepted = false; - } - } - - function handleRelease() { - cameraCtrl._dragging = false; - cameraCtrl.storeCameraState(0); - } - - onReleased: handleRelease() - onCanceled: handleRelease() - - onWheel: (wheel)=> { - if (cameraCtrl.camera) { - // Empirically determined divisor for nice zoom - cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); - cameraCtrl.storeCameraState(500); - } - } - } - - Keys.onPressed: { - var pressPoint = Qt.vector3d(view3d.width / 2, view3d.height / 2, 0); - var currentPoint; - - switch (event.key) { - case Qt.Key_Left: - currentPoint = pressPoint.plus(Qt.vector3d(_keyPanAmount, 0, 0)); - break; - case Qt.Key_Right: - currentPoint = pressPoint.plus(Qt.vector3d(-_keyPanAmount, 0, 0)); - break; - case Qt.Key_Up: - currentPoint = pressPoint.plus(Qt.vector3d(0, _keyPanAmount, 0)); - break; - case Qt.Key_Down: - currentPoint = pressPoint.plus(Qt.vector3d(0, -_keyPanAmount, 0)); - break; - default: - break; - } - - if (currentPoint) { - _lookAtPoint = _generalHelper.panCamera( - camera, cameraCtrl.camera.sceneTransform, - cameraCtrl.camera.position, _lookAtPoint, - pressPoint, currentPoint, _zoomFactor); - event.accepted = true; - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt5/EditView3D.qml deleted file mode 100644 index 1860e70efb4..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/EditView3D.qml +++ /dev/null @@ -1,981 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.12 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Item { - id: viewRoot - width: 1024 - height: 768 - visible: true - - property Node activeScene: null - property View3D editView: null - property string sceneId - - property bool showEditLight: false - property bool showGrid: true - property bool showSelectionBox: true - property bool showIconGizmo: true - property bool showCameraFrustum: false - property bool usePerspective: true - property bool globalOrientation: false - property alias contentItem: contentItem - property color backgroundGradientColorStart: "#222222" - property color backgroundGradientColorEnd: "#999999" - property color gridColor: "#aaaaaa" - property bool syncBackgroundColor: false - - enum SelectionMode { Item, Group } - enum TransformMode { Move, Rotate, Scale } - - property int selectionMode: EditView3D.SelectionMode.Item - property int transformMode: EditView3D.TransformMode.Move - - property Node selectedNode: null // This is multiSelectionNode in multi-selection case - property var selectedNodes: [] // All selected nodes - - property var lightIconGizmos: [] - property var cameraGizmos: [] - property var selectionBoxes: [] - property rect viewPortRect: Qt.rect(0, 0, 1000, 1000) - - property bool shuttingDown: false - - property real fps: 0 - - signal selectionChanged(var selectedNodes) - signal commitObjectProperty(var objects, var propNames) - signal changeObjectProperty(var objects, var propNames) - signal notifyActiveSceneChange() - - onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective) - onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) - onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) - onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid); - onSyncBackgroundColorChanged: _generalHelper.storeToolState(sceneId, "syncBackgroundColor", syncBackgroundColor); - onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox); - onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo); - onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum); - onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); - onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); - - onActiveSceneChanged: updateActiveScene() - - function aboutToShutDown() - { - shuttingDown = true; - } - - function createEditView() - { - var component = Qt.createComponent("SceneView3D.qml"); - if (component.status === Component.Ready) { - editView = component.createObject(viewRect, - {"usePerspective": usePerspective, - "showSceneLight": showEditLight, - "showGrid": showGrid, - "gridColor": gridColor, - "importScene": activeScene, - "cameraZoomFactor": cameraControl._zoomFactor, - "z": 1}); - editView.usePerspective = Qt.binding(function() {return usePerspective;}); - editView.showSceneLight = Qt.binding(function() {return showEditLight;}); - editView.showGrid = Qt.binding(function() {return showGrid;}); - editView.gridColor = Qt.binding(function() {return gridColor;}); - editView.cameraZoomFactor = Qt.binding(function() {return cameraControl._zoomFactor;}); - - selectionBoxes.length = 0; - cameraControl.forceActiveFocus(); - return true; - } - return false; - } - - function updateActiveScene() - { - if (editView) { - // Destroy is async, so make sure we don't get any more updates for the old editView - _generalHelper.enableItemUpdate(editView, false); - editView.visible = false; - editView.destroy(); - } - - // importScene cannot be updated after initial set, so we need to reconstruct entire View3D - if (createEditView()) { - if (activeScene) { - var toolStates = _generalHelper.getToolStates(sceneId); - if (Object.keys(toolStates).length > 0) { - updateToolStates(toolStates, true); - } else { - // Don't inherit the edit light state from the previous scene, but rather - // turn the edit light on for scenes that do not have any scene - // lights, and turn it off for scenes that have. - var hasSceneLight = false; - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].scene === activeScene) { - hasSceneLight = true; - break; - } - } - showEditLight = !hasSceneLight; - storeCurrentToolStates(); - } - } else { - // When active scene is deleted, this function gets called by object deletion - // handlers without going through setActiveScene, so make sure sceneId is cleared. - // This is skipped during application shutdown, as calling QQuickText::setText() - // during application shutdown can crash the application. - if (!shuttingDown) { - sceneId = ""; - storeCurrentToolStates(); - } - } - - notifyActiveSceneChange(); - } - } - - function setActiveScene(newScene, newSceneId) - { - var needExplicitUpdate = !activeScene && !newScene; - - sceneId = newSceneId; - activeScene = newScene; - - if (needExplicitUpdate) - updateActiveScene(); - } - - // Disables edit view update if scene doesn't match current activeScene. - // If it matches, updates are enabled. - function enableEditViewUpdate(scene) - { - if (editView) - _generalHelper.enableItemUpdate(editView, (scene && scene === activeScene)); - } - - function handleActiveSceneIdChange(newId) - { - if (sceneId !== newId) { - sceneId = newId; - storeCurrentToolStates(); - } - } - - function fitToView() - { - if (editView) { - var boxModels = []; - if (selectedNodes.length > 1) { - for (var i = 0; i < selectedNodes.length; ++i) { - if (selectionBoxes.length > i) - boxModels.push(selectionBoxes[i].model) - } - } else if (selectedNodes.length > 0 && selectionBoxes.length > 0) { - boxModels.push(selectionBoxes[0].model); - } - cameraControl.focusObject(boxModels, editView.camera.eulerRotation, true, false); - } - } - - function alignCamerasToView(cameraNodes) - { - if (editView) { - cameraControl.alignCameras(cameraNodes); - var propertyNames = ["position", "eulerRotation"]; - viewRoot.changeObjectProperty(cameraNodes, propertyNames); - viewRoot.commitObjectProperty(cameraNodes, propertyNames); - } - } - - function alignViewToCamera(cameraNodes) - { - if (editView) - cameraControl.alignView(cameraNodes); - } - - function updateViewStates(viewStates) - { - if ("selectBackgroundColor" in viewStates) { - if (Array.isArray(viewStates.selectBackgroundColor)) { - var colors = viewStates.selectBackgroundColor - if (colors.length === 1) { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[0]; - } else { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[1]; - } - } else { - var color = viewStates.selectBackgroundColor - backgroundGradientColorStart = color; - backgroundGradientColorEnd = color; - } - } - - if ("selectGridColor" in viewStates) - viewRoot.gridColor = viewStates.selectGridColor - } - - // If resetToDefault is true, tool states not specifically set to anything will be reset to - // their default state. - function updateToolStates(toolStates, resetToDefault) - { - if ("showEditLight" in toolStates) - showEditLight = toolStates.showEditLight; - else if (resetToDefault) - showEditLight = false; - - if ("showGrid" in toolStates) - showGrid = toolStates.showGrid; - else if (resetToDefault) - showGrid = true; - - if ("syncBackgroundColor" in toolStates) { - syncBackgroundColor = toolStates.syncBackgroundColor; - if (syncBackgroundColor) { - var color = _generalHelper.sceneEnvironmentColor(sceneId); - updateViewStates({"selectBackgroundColor": color}) - } - } else if (resetToDefault) { - syncBackgroundColor = false; - } - - if ("showSelectionBox" in toolStates) - showSelectionBox = toolStates.showSelectionBox; - else if (resetToDefault) - showSelectionBox = true; - - if ("showIconGizmo" in toolStates) - showIconGizmo = toolStates.showIconGizmo; - else if (resetToDefault) - showIconGizmo = true; - - if ("showCameraFrustum" in toolStates) - showCameraFrustum = toolStates.showCameraFrustum; - else if (resetToDefault) - showCameraFrustum = false; - - if ("usePerspective" in toolStates) - usePerspective = toolStates.usePerspective; - else if (resetToDefault) - usePerspective = true; - - if ("globalOrientation" in toolStates) - globalOrientation = toolStates.globalOrientation; - else if (resetToDefault) - globalOrientation = false; - - if ("selectionMode" in toolStates) - selectionMode = toolStates.selectionMode; - else if (resetToDefault) - selectionMode = EditView3D.SelectionMode.Item; - - if ("transformMode" in toolStates) - transformMode = toolStates.transformMode; - else if (resetToDefault) - transformMode = EditView3D.TransformMode.Move; - - if ("editCamState" in toolStates) - cameraControl.restoreCameraState(toolStates.editCamState); - else if (resetToDefault) - cameraControl.restoreDefaultState(); - } - - function storeCurrentToolStates() - { - _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) - _generalHelper.storeToolState(sceneId, "showGrid", showGrid) - _generalHelper.storeToolState(sceneId, "syncBackgroundColor", syncBackgroundColor) - _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox) - _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo) - _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum) - _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective) - _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) - _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); - _generalHelper.storeToolState(sceneId, "transformMode", transformMode); - - cameraControl.storeCameraState(0); - } - - function ensureSelectionBoxes(count) - { - var needMore = count - selectionBoxes.length - if (needMore > 0) { - var component = Qt.createComponent("SelectionBox.qml"); - if (component.status === Component.Ready) { - for (var i = 0; i < needMore; ++i) { - var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry"); - var boxParent = null; - if (editView) - boxParent = editView.sceneHelpers; - var box = component.createObject(boxParent, {"view3D": editView, - "geometryName": geometryName}); - selectionBoxes[selectionBoxes.length] = box; - box.view3D = Qt.binding(function() {return editView;}); - box.visible = Qt.binding(function() {return showSelectionBox;}); - } - } - } - } - - function selectObjects(objects) - { - // Create selection boxes as necessary. One more box than is actually needed is created, so - // that we always have a previously created box to use for new selection. - // This fixes an occasional visual glitch when creating a new box. - ensureSelectionBoxes(objects.length + 1) - - var i; - for (i = 0; i < objects.length; ++i) - selectionBoxes[i].targetNode = objects[i]; - for (i = objects.length; i < selectionBoxes.length; ++i) - selectionBoxes[i].targetNode = null; - - selectedNodes = objects; - if (objects.length === 0) { - selectedNode = null; - } else if (objects.length > 1) { - selectedNode = multiSelectionNode; - _generalHelper.setMultiSelectionTargets(multiSelectionNode, objects); - } else { - selectedNode = objects[0]; - } - } - - function handleObjectClicked(object, button, multi) - { - if (object instanceof View3D) { - // View3D can be the resolved pick target in case the 3D editor is showing content - // of a component that has View3D as root. In that case locking is resolved on C++ side - // and we ignore multiselection. - selectObjects([]); - selectionChanged([object]); - return; - } - - var clickedObject; - - // Click on locked object is treated same as click on empty space - if (!_generalHelper.isLocked(object)) - clickedObject = object; - - if (selectionMode === EditView3D.SelectionMode.Group) { - while (clickedObject && clickedObject !== activeScene - && (activeScene instanceof Model || clickedObject.parent !== activeScene)) { - clickedObject = clickedObject.parent; - } - } - // Object selection logic: - // Regular click: Clear any multiselection, single-selects the clicked object - // Ctrl-click: No objects selected: Act as single select - // One or more objects selected: Multiselect - // Null object always clears entire selection - var newSelection = []; - if (clickedObject) { - if (button === Qt.RightButton) { - // Right-clicking does only single selection (when clickedObject is unselected) - // This is needed for selecting a target for the context menu - if (!selectedNodes.includes(clickedObject)) - newSelection[0] = clickedObject; - else - newSelection = selectedNodes; - } else if (multi && selectedNodes.length > 0) { - var deselect = false; - for (var i = 0; i < selectedNodes.length; ++i) { - // Multiselecting already selected object clears that object from selection - if (selectedNodes[i] !== clickedObject) - newSelection[newSelection.length] = selectedNodes[i]; - else - deselect = true; - } - if (!deselect) - newSelection[newSelection.length] = clickedObject; - } else { - newSelection[0] = clickedObject; - } - } - selectObjects(newSelection); - selectionChanged(newSelection); - } - - function addLightGizmo(scene, obj) - { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (!lightIconGizmos[i].targetNode) { - slotFound = i; - } else if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - lightIconGizmos[slotFound].scene = scene; - lightIconGizmos[slotFound].targetNode = obj; - lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj); - lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("LightIconGizmo.qml"); - if (gizmoComponent.status === Component.Ready) { - var gizmo = gizmoComponent.createObject(overlayView, - {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes, "scene": scene, - "activeScene": activeScene, - "locked": _generalHelper.isLocked(obj), - "hidden": _generalHelper.isHidden(obj), - "globalShow": showIconGizmo}); - lightIconGizmos[lightIconGizmos.length] = gizmo; - gizmo.clicked.connect(handleObjectClicked); - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); - } - } - - function addCameraGizmo(scene, obj) - { - // Insert into first available gizmo if we don't already have gizmo for this object - var slotFound = -1; - for (var i = 0; i < cameraGizmos.length; ++i) { - if (!cameraGizmos[i].targetNode) { - slotFound = i; - } else if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = scene; - return; - } - } - - if (slotFound !== -1) { - cameraGizmos[slotFound].scene = scene; - cameraGizmos[slotFound].targetNode = obj; - cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj); - cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj); - return; - } - - // No free gizmos available, create a new one - var gizmoComponent = Qt.createComponent("CameraGizmo.qml"); - var frustumComponent = Qt.createComponent("CameraFrustum.qml"); - if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) { - var geometryName = _generalHelper.generateUniqueName("CameraGeometry"); - var frustum = frustumComponent.createObject( - overlayScene, - {"geometryName": geometryName, "viewPortRect": viewPortRect}); - var gizmo = gizmoComponent.createObject( - overlayView, - {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes, "scene": scene, "activeScene": activeScene, - "locked": _generalHelper.isLocked(obj), "hidden": _generalHelper.isHidden(obj), - "globalShow": showIconGizmo, "globalShowFrustum": showCameraFrustum}); - - cameraGizmos[cameraGizmos.length] = gizmo; - gizmo.clicked.connect(handleObjectClicked); - gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); - gizmo.activeScene = Qt.binding(function() {return activeScene;}); - gizmo.globalShow = Qt.binding(function() {return showIconGizmo;}); - gizmo.globalShowFrustum = Qt.binding(function() {return showCameraFrustum;}); - frustum.viewPortRect = Qt.binding(function() {return viewPortRect;}); - gizmo.connectFrustum(frustum); - } - } - - function releaseLightGizmo(obj) - { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = null; - lightIconGizmos[i].targetNode = null; - return; - } - } - } - - function releaseCameraGizmo(obj) - { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = null; - cameraGizmos[i].targetNode = null; - return; - } - } - } - - function updateLightGizmoScene(scene, obj) - { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === obj) { - lightIconGizmos[i].scene = scene; - return; - } - } - } - - function updateCameraGizmoScene(scene, obj) - { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === obj) { - cameraGizmos[i].scene = scene; - return; - } - } - } - - function gizmoAt(x, y) - { - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y)) - return lightIconGizmos[i].targetNode; - } - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y)) - return cameraGizmos[i].targetNode; - } - return null; - } - - Component.onCompleted: { - createEditView(); - selectObjects([]); - // Work-around the fact that the projection matrix for the camera is not calculated until - // the first frame is rendered, so any initial calls to mapFrom3DScene() will fail. - _generalHelper.requestOverlayUpdate(); - } - - onWidthChanged: _generalHelper.requestOverlayUpdate() - onHeightChanged: _generalHelper.requestOverlayUpdate() - - Connections { - target: _generalHelper - function onLockedStateChanged(node) - { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === node) { - cameraGizmos[i].locked = _generalHelper.isLocked(node); - return; - } - } - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === node) { - lightIconGizmos[i].locked = _generalHelper.isLocked(node); - return; - } - } - } - function onHiddenStateChanged(node) - { - for (var i = 0; i < cameraGizmos.length; ++i) { - if (cameraGizmos[i].targetNode === node) { - cameraGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } - } - for (var i = 0; i < lightIconGizmos.length; ++i) { - if (lightIconGizmos[i].targetNode === node) { - lightIconGizmos[i].hidden = _generalHelper.isHidden(node); - return; - } - } - } - } - - Node { - id: overlayScene - - PerspectiveCamera { - id: overlayPerspectiveCamera - clipFar: viewRoot.editView ? viewRoot.editView.perspectiveCamera.clipFar : 1000 - clipNear: viewRoot.editView ? viewRoot.editView.perspectiveCamera.clipNear : 1 - position: viewRoot.editView ? viewRoot.editView.perspectiveCamera.position : Qt.vector3d(0, 0, 0) - rotation: viewRoot.editView ? viewRoot.editView.perspectiveCamera.rotation : Qt.quaternion(1, 0, 0, 0) - } - - OrthographicCamera { - id: overlayOrthoCamera - clipFar: viewRoot.editView ? viewRoot.editView.orthoCamera.clipFar : 1000 - clipNear: viewRoot.editView ? viewRoot.editView.orthoCamera.clipNear : 1 - position: viewRoot.editView ? viewRoot.editView.orthoCamera.position : Qt.vector3d(0, 0, 0) - rotation: viewRoot.editView ? viewRoot.editView.orthoCamera.rotation : Qt.quaternion(1, 0, 0, 0) - scale: viewRoot.editView ? viewRoot.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0) - } - - MouseArea3D { - id: gizmoDragHelper - view3D: overlayView - } - - Node { - id: multiSelectionNode - objectName: "multiSelectionNode" - } - - MoveGizmo { - id: moveGizmo - scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - globalOrientation: viewRoot.globalOrientation - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Move - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["position"] - - onPositionCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onPositionMove: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNames); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - } - - ScaleGizmo { - id: scaleGizmo - scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Scale - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["scale"] - property var propertyNamesMulti: ["position", "scale"] - - onScaleCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onScaleChange: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - } - - RotateGizmo { - id: rotateGizmo - scale: autoScale.getScale(Qt.vector3d(7, 7, 7)) - highlightOnHover: true - targetNode: viewRoot.selectedNode - globalOrientation: viewRoot.globalOrientation - visible: viewRoot.selectedNode && transformMode === EditView3D.TransformMode.Rotate - view3D: overlayView - dragHelper: gizmoDragHelper - property var propertyNames: ["eulerRotation"] - property var propertyNamesMulti: ["position", "eulerRotation"] - - onRotateCommit: { - if (targetNode == multiSelectionNode) - viewRoot.commitObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.commitObjectProperty([viewRoot.selectedNode], propertyNames); - } - onRotateChange: { - if (targetNode == multiSelectionNode) - viewRoot.changeObjectProperty(_generalHelper.multiSelectionTargets(), propertyNamesMulti); - else - viewRoot.changeObjectProperty([viewRoot.selectedNode], propertyNames); - } - } - - LightGizmo { - id: lightGizmo - targetNode: viewRoot.selectedNode != multiSelectionNode ? viewRoot.selectedNode : null - view3D: overlayView - dragHelper: gizmoDragHelper - - onPropertyValueCommit: (propName) => { - viewRoot.commitObjectProperty([targetNode], [propName]); - } - onPropertyValueChange: (propName) => { - viewRoot.changeObjectProperty([targetNode], [propName]); - } - } - - AutoScaleHelper { - id: autoScale - view3D: overlayView - position: moveGizmo.scenePosition - } - - AutoScaleHelper { - id: pivotAutoScale - view3D: overlayView - position: pivotLine.startPos - } - - Line3D { - id: pivotLine - visible: viewRoot.selectedNode && viewRoot.selectedNode != multiSelectionNode - name: "3D Edit View Pivot Line" - color: "#ddd600" - - startPos: viewRoot.selectedNode ? viewRoot.selectedNode.scenePosition - : Qt.vector3d(0, 0, 0) - Connections { - target: viewRoot - function onSelectedNodeChanged() - { - pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); - } - } - Connections { - target: viewRoot.selectedNode - function onSceneTransformChanged() - { - pivotLine.endPos = gizmoDragHelper.pivotScenePosition(viewRoot.selectedNode); - } - } - - Model { - id: pivotCap - source: "#Sphere" - scale: pivotAutoScale.getScale(Qt.vector3d(0.03, 0.03, 0.03)) - position: pivotLine.startPos - materials: [ - DefaultMaterial { - id: lineMat - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - diffuseColor: pivotLine.color - } - ] - } - } - } - - Item { - id: contentItem - anchors.fill: parent - - Rectangle { - id: viewRect - anchors.fill: parent - - gradient: Gradient { - GradientStop { position: 1.0; color: backgroundGradientColorStart } - GradientStop { position: 0.0; color: backgroundGradientColorEnd } - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: false - - property MouseArea3D freeDraggerArea - property point pressPoint - property bool initialMoveBlock: false - - onPressed: (mouse) => { - if (viewRoot.editView) { - var pickResult = viewRoot.editView.pick(mouse.x, mouse.y); - handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), mouse.button, - mouse.modifiers & Qt.ControlModifier); - - if (pickResult.objectHit && pickResult.objectHit instanceof Node) { - if (transformMode === EditView3D.TransformMode.Move) - freeDraggerArea = moveGizmo.freeDraggerArea; - else if (transformMode === EditView3D.TransformMode.Rotate) - freeDraggerArea = rotateGizmo.freeDraggerArea; - else if (transformMode === EditView3D.TransformMode.Scale) - freeDraggerArea = scaleGizmo.freeDraggerArea; - pressPoint.x = mouse.x; - pressPoint.y = mouse.y; - initialMoveBlock = true; - } else { - mouse.accepted = false; - } - } - } - onPositionChanged: (mouse) => { - if (freeDraggerArea) { - if (initialMoveBlock && Math.abs(pressPoint.x - mouse.x) + Math.abs(pressPoint.y - mouse.y) > 10) { - // Don't force press event at actual press, as that puts the gizmo - // in free-dragging state, which is bad UX if drag is not actually done - freeDraggerArea.forcePressEvent(pressPoint.x, pressPoint.y); - freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); - initialMoveBlock = false; - } else { - freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); - } - } - } - - function handleRelease(mouse) - { - if (freeDraggerArea) { - if (initialMoveBlock) - freeDraggerArea.forceReleaseEvent(pressPoint.x, pressPoint.y); - else - freeDraggerArea.forceReleaseEvent(mouse.x, mouse.y); - freeDraggerArea = null; - } - } - - onReleased: (mouse) => { - handleRelease(mouse); - } - onCanceled: (mouse) => { - handleRelease(mouse); - } - } - - DropArea { - anchors.fill: parent - } - - View3D { - id: overlayView - anchors.fill: parent - camera: viewRoot.usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera - importScene: overlayScene - z: 2 - } - - Overlay2D { - id: gizmoLabel - targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo - targetView: overlayView - visible: targetNode.dragging - z: 3 - - Rectangle { - color: "white" - x: -width / 2 - y: -height - 8 - width: gizmoLabelText.width + 4 - height: gizmoLabelText.height + 4 - border.width: 1 - Text { - id: gizmoLabelText - text: { - // This is skipped during application shutdown, as calling QQuickText::setText() - // during application shutdown can crash the application. - if (shuttingDown) - return text; - var l = Qt.locale(); - var targetProperty; - if (viewRoot.selectedNode) { - if (gizmoLabel.targetNode === moveGizmo) - targetProperty = viewRoot.selectedNode.position; - else - targetProperty = viewRoot.selectedNode.scale; - return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1) - + qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1) - + qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1); - } else { - return ""; - } - } - anchors.centerIn: parent - } - } - } - - Rectangle { - id: rotateGizmoLabel - color: "white" - x: rotateGizmo.currentMousePos.x - (10 + width) - y: rotateGizmo.currentMousePos.y - (10 + height) - width: rotateGizmoLabelText.width + 4 - height: rotateGizmoLabelText.height + 4 - border.width: 1 - visible: rotateGizmo.dragging - parent: rotateGizmo.view3D - z: 3 - - Text { - id: rotateGizmoLabelText - text: { - // This is skipped during application shutdown, as calling QQuickText::setText() - // during application shutdown can crash the application. - if (shuttingDown) - return text; - var l = Qt.locale(); - if (rotateGizmo.targetNode) { - var degrees = rotateGizmo.currentAngle * (180 / Math.PI); - return Number(degrees).toLocaleString(l, 'f', 1); - } else { - return ""; - } - } - anchors.centerIn: parent - } - } - - Rectangle { - id: lightGizmoLabel - color: "white" - x: lightGizmo.currentMousePos.x - (10 + width) - y: lightGizmo.currentMousePos.y - (10 + height) - width: lightGizmoLabelText.width + 4 - height: lightGizmoLabelText.height + 4 - border.width: 1 - visible: lightGizmo.dragging - parent: lightGizmo.view3D - z: 3 - - Text { - id: lightGizmoLabelText - text: lightGizmo.currentLabel - anchors.centerIn: parent - } - } - - EditCameraController { - id: cameraControl - camera: viewRoot.editView ? viewRoot.editView.camera : null - anchors.fill: parent - view3d: viewRoot.editView - sceneId: viewRoot.sceneId - } - } - - AxisHelper { - anchors.right: parent.right - anchors.top: parent.top - width: 100 - height: width - editCameraCtrl: cameraControl - selectedNode: viewRoot.selectedNodes.length === 1 ? viewRoot.selectionBoxes[0].model - : viewRoot.selectedNode - } - - Text { - id: sceneLabel - text: viewRoot.sceneId - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 4 - font.pixelSize: 14 - color: "white" - } - - Text { - id: fpsLabel - text: viewRoot.fps - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.margins: 4 - font.pixelSize: 12 - color: "white" - visible: viewRoot.fps > 0 - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/FadeHandle.qml b/src/tools/qml2puppet/mockfiles/qt5/FadeHandle.qml deleted file mode 100644 index d400133192d..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/FadeHandle.qml +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -DirectionalDraggable { - id: handleRoot - - property string currentLabel - property point currentMousePos - property real fadeScale - property real baseScale: 5 - property real dragScale: 1 - - scale: autoScaler.getScale(Qt.vector3d(baseScale, baseScale, baseScale)) - length: 3 - offset: -1.5 - - Model { - id: handle - source: "#Sphere" - materials: [ handleRoot.material ] - scale: Qt.vector3d(0.02, 0.02, 0.02) - } - - AutoScaleHelper { - id: autoScaler - active: handleRoot.active - view3D: handleRoot.view3D - } - - property real _q // quadratic fade - property real _l // linear fade - property real _c // constant fade - property real _d: 20 // Divisor from fadeScale calc in lightGizmo - property real _startScale - property real _startFadeScale - property string _currentProp - - signal valueCommit(string propName) - signal valueChange(string propName) - - function updateFade(relativeDistance, screenPos) - { - // Solved from fadeScale equation in LightGizmo - var newValue = 0; - var _x = Math.max(0, (_startFadeScale - (relativeDistance * _startScale)) / 100); - - // Fades capped to range 0-10 because property editor caps them to that range - if (_currentProp === "quadraticFade") { - if (_x === 0) - newValue = 10; - else - newValue = Math.max(0, Math.min(10, -(_c - _d + (_l * _x)) / (_x * _x))); - if (newValue < 0.01) - newValue = 0; // To avoid having tiny positive value when UI shows 0.00 - targetNode.quadraticFade = newValue; - } else if (_currentProp === "linearFade") { - if (_x === 0) - newValue = 10; - else - newValue = Math.max(0, Math.min(10, -(_c - _d) / _x)); - if (newValue < 0.01) - newValue = 0; // To avoid having tiny positive value when UI shows 0.00 - targetNode.linearFade = newValue; - } else { - // Since pure constant fade equates to infinitely long cone, fadeScale calc assumes - // linear fade of one in this case. - newValue = Math.max(0, Math.min(10, _d - _x)); - targetNode.constantFade = newValue; - } - - var l = Qt.locale(); - handleRoot.currentLabel = _currentProp + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 2); - handleRoot.currentMousePos = screenPos; - } - - onPressed: (mouseArea, screenPos)=> { - _startScale = autoScaler.relativeScale * baseScale * dragScale; - _startFadeScale = fadeScale; - _l = targetNode.linearFade; - _c = targetNode.constantFade; - _q = targetNode.quadraticFade; - if (targetNode.quadraticFade === 0) { - if (targetNode.linearFade === 0) { - _currentProp = "constantFade"; - } else { - _currentProp = "linearFade"; - } - } else { - _currentProp = "quadraticFade"; - } - updateFade(0, screenPos); - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateFade(relativeDistance, screenPos); - handleRoot.valueChange(_currentProp); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateFade(relativeDistance, screenPos); - handleRoot.valueCommit(_currentProp); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt5/HelperGrid.qml deleted file mode 100644 index 8b6e3b1b98c..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/HelperGrid.qml +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import GridGeometry 1.0 - -Node { - id: grid - - property alias lines: gridGeometry.lines - property alias step: gridGeometry.step - property alias subdivAlpha: subGridMaterial.opacity - property alias gridColor: mainGridMaterial.diffuseColor - - eulerRotation.x: 90 - - // Note: Only one instance of HelperGrid is supported, as the geometry names are fixed - - Model { // Main grid lines - castsShadows: false - receivesShadows: false - geometry: GridGeometry { - id: gridGeometry - name: "3D Edit View Helper Grid" - } - - materials: [ - DefaultMaterial { - id: mainGridMaterial - diffuseColor: "#aaaaaa" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } - - Model { // Subdivision lines - castsShadows: false - receivesShadows: false - geometry: GridGeometry { - lines: gridGeometry.lines - step: gridGeometry.step - isSubdivision: true - name: "3D Edit View Helper Grid subdivisions" - } - - materials: [ - DefaultMaterial { - id: subGridMaterial - diffuseColor: mainGridMaterial.diffuseColor - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } - - Model { // Z Axis - castsShadows: false - receivesShadows: false - geometry: GridGeometry { - lines: gridGeometry.lines - step: gridGeometry.step - isCenterLine: true - name: "3D Edit View Helper Grid Z Axis" - } - materials: [ - DefaultMaterial { - id: vCenterLineMaterial - diffuseColor: "#00a1d2" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } - Model { // X Axis - castsShadows: false - receivesShadows: false - eulerRotation.z: 90 - geometry: GridGeometry { - lines: gridGeometry.lines - step: gridGeometry.step - isCenterLine: true - name: "3D Edit View Helper Grid X Axis" - } - materials: [ - DefaultMaterial { - id: hCenterLineMaterial - diffuseColor: "#cb211a" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/IconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/IconGizmo.qml deleted file mode 100644 index d2da8cfdef4..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/IconGizmo.qml +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -Item { - id: iconGizmo - - property Node activeScene: null - property Node scene: null - property View3D view3D - property bool highlightOnHover: true - property Node targetNode: null - property var selectedNodes: [] - readonly property bool selected: { - for (var i = 0; i < selectedNodes.length; ++i) { - if (selectedNodes[i] === targetNode) - return true; - } - return false; - } - property bool hasMouse: false - property bool hidden: false - property bool locked: false - property bool globalShow: true - property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false) - - property alias iconSource: iconImage.source - - signal clicked(Node node, bool multi) - - function hasPoint(x, y) - { - if (!view3D || !targetNode) - return false; - - var point = view3D.mapToItem(iconMouseArea, x, y); - - return point.x >= iconMouseArea.x && (point.x <= iconMouseArea.x + iconMouseArea.width) - && point.y >= iconMouseArea.y && (point.y <= iconMouseArea.y + iconMouseArea.height); - } - - onSelectedChanged: { - if (selected) - hasMouse = false; - } - - visible: canBeVisible && globalShow - - Overlay2D { - id: iconOverlay - targetNode: iconGizmo.targetNode - targetView: view3D - visible: iconGizmo.visible && !isBehindCamera - - Rectangle { - id: iconRect - - width: iconImage.width - height: iconImage.height - x: -width / 2 - y: -height / 2 - color: "transparent" - border.color: "#7777ff" - border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0 - radius: 5 - opacity: iconGizmo.selected ? 0.2 : 1 - Image { - id: iconImage - fillMode: Image.Pad - MouseArea { - id: iconMouseArea - anchors.fill: parent - onPressed: (mouse)=> { - // Ignore singleselection mouse presses when we have single object selected - // so that the icon gizmo doesn't hijack mouse clicks meant for other gizmos - if (iconGizmo.selected && !(mouse.modifiers & Qt.ControlModifier) - && selectedNodes.length === 1) { - mouse.accepted = false; - } - } - - onClicked: (mouse)=> { - iconGizmo.clicked(iconGizmo.targetNode, - mouse.modifiers & Qt.ControlModifier); - } - hoverEnabled: iconGizmo.highlightOnHover && !iconGizmo.selected - acceptedButtons: Qt.LeftButton - - // onPositionChanged, onContainsMouseAreaChanged, and hasMouse are used instead - // of just using containsMouse directly, because containsMouse - // cannot be relied upon to update correctly in some situations. - // This is likely because the overlapping 3D mouse areas of the gizmos get - // the mouse events instead of this area, so mouse leaving the area - // doesn't always update containsMouse property. - onPositionChanged: { - if (!iconGizmo.selected) - iconGizmo.hasMouse = containsMouse; - } - - onContainsMouseChanged: { - if (!iconGizmo.selected) - iconGizmo.hasMouse = containsMouse; - else - iconGizmo.hasMouse = false; - } - } - } - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/IconRenderer3D.qml b/src/tools/qml2puppet/mockfiles/qt5/IconRenderer3D.qml deleted file mode 100644 index acda5d5ef21..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/IconRenderer3D.qml +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import QtQuick3D 1.15 - -Item { - id: viewRoot - width: 1024 - height: 1024 - visible: true - - property alias view3D: view3D - property alias camPos: viewCamera.position - - function setSceneToBox() - { - selectionBox.targetNode = view3D.importScene; - } - - function fitAndHideBox() - { - cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true, true); - if (cameraControl._zoomFactor < 0.1) - view3D.importScene.scale = view3D.importScene.scale.times(10); - if (cameraControl._zoomFactor > 10) - view3D.importScene.scale = view3D.importScene.scale.times(0.1); - - selectionBox.visible = false; - } - - View3D { - id: view3D - camera: viewCamera - environment: sceneEnv - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.VeryHigh - } - - PerspectiveCamera { - id: viewCamera - position: Qt.vector3d(-200, 200, 200) - eulerRotation: Qt.vector3d(-45, -45, 0) - } - - DirectionalLight { - rotation: viewCamera.rotation - } - - SelectionBox { - id: selectionBox - view3D: view3D - geometryName: "SB" - } - - EditCameraController { - id: cameraControl - camera: view3D.camera - view3d: view3D - ignoreToolState: true - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/LightGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/LightGizmo.qml deleted file mode 100644 index fe5727a83c3..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/LightGizmo.qml +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 -import LightUtils 1.0 - -Node { - id: lightGizmo - - property View3D view3D - property Node targetNode: null - property MouseArea3D dragHelper: null - property color color: Qt.rgba(1, 1, 0, 1) - property real fadeScale: { - // Value indicates area where intensity is above certain percent of total brightness. - if (lightGizmo.targetNode instanceof SpotLight || lightGizmo.targetNode instanceof PointLight) { - var l = targetNode.linearFade; - var q = targetNode.quadraticFade; - var c = targetNode.constantFade; - var d = 20; // divisor to target intensity value. E.g. 20 = 1/20 = 5% - if (l === 0 && q === 0) - l = 1; // For pure constant fade, cone would be infinite, so pretend we have linear fade - // Solved from equation in shader: - // 1 / d = 1 / (c + (l + q * dist) * dist); - if (q === 0) - return 100 * Math.max(((d - c) / l), 1); - else - return 100 * ((Math.sqrt(4 * q * (d - c) + (l * l)) - l) / (2 * q)); - } else { - return 100; - } - } - readonly property bool dragging: primaryArrow.dragging - || spotLightHandle.dragging - || spotLightInnerHandle.dragging - || spotLightFadeHandle.dragging - || areaHeightHandle.dragging - || areaWidthHandle.dragging - || pointLightFadeHandle.dragging - property point currentMousePos - property string currentLabel - property int brightnessDecimals: _generalHelper.brightnessScaler() > 10. ? 0 : 2; - property real brightnessMultiplier: Math.pow(10, brightnessDecimals); - - signal propertyValueCommit(string propName) - signal propertyValueChange(string propName) - - position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0) - visible: lightGizmo.targetNode instanceof SpotLight - || lightGizmo.targetNode instanceof AreaLight - || lightGizmo.targetNode instanceof DirectionalLight - || lightGizmo.targetNode instanceof PointLight - - AutoScaleHelper { - id: autoScaler - view3D: lightGizmo.view3D - } - - Node { - id: pointLightParts - rotation: lightGizmo.view3D.camera.rotation - visible: lightGizmo.targetNode instanceof PointLight - - LightModel { - id: pointModel - geometryName: "Edit 3D PointLight" - geometryType: LightGeometry.Point - material: lightMaterial - scale: Qt.vector3d(lightGizmo.fadeScale, lightGizmo.fadeScale, lightGizmo.fadeScale) - } - - FadeHandle { - id: pointLightFadeHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof PointLight ? Qt.vector3d(-pointModel.scale.x, 0, 0) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(0, 0, -90) - targetNode: lightGizmo.targetNode instanceof PointLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof PointLight - dragHelper: lightGizmo.dragHelper - fadeScale: lightGizmo.fadeScale - - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: (propName)=> { - lightGizmo.propertyValueChange(propName); - } - onValueCommit: (propName)=> { - lightGizmo.propertyValueCommit(propName); - } - } - } - - - Node { - rotation: !lightGizmo.targetNode ? Qt.quaternion(1, 0, 0, 0) - : lightGizmo.targetNode.sceneRotation - - Node { - id: spotParts - visible: lightGizmo.targetNode instanceof SpotLight - - LightModel { - id: spotModel - - property real coneXYScale: spotParts.visible && lightGizmo.targetNode - ? lightGizmo.fadeScale * Math.tan(Math.PI * lightGizmo.targetNode.coneAngle / 180) - : 1 - - geometryName: "Edit 3D SpotLight Cone" - geometryType: LightGeometry.Spot - material: lightMaterial - scale: Qt.vector3d(coneXYScale, coneXYScale, - spotParts.visible && lightGizmo.targetNode && lightGizmo.targetNode.coneAngle > 90 - ? -lightGizmo.fadeScale : lightGizmo.fadeScale) - } - - LightModel { - id: spotInnerModel - - property real coneXYScale: spotParts.visible && lightGizmo.targetNode - ? lightGizmo.fadeScale * Math.tan(Math.PI * lightGizmo.targetNode.innerConeAngle / 180) - : 1 - - geometryName: "Edit 3D SpotLight Inner Cone" - geometryType: LightGeometry.Spot - material: lightMaterial - scale: Qt.vector3d(coneXYScale, coneXYScale, - spotParts.visible && lightGizmo.targetNode && lightGizmo.targetNode.innerConeAngle > 90 - ? -lightGizmo.fadeScale : lightGizmo.fadeScale) - } - - SpotLightHandle { - id: spotLightHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(0, spotModel.scale.y, -spotModel.scale.z) - : Qt.vector3d(0, 0, 0) - targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof SpotLight - dragHelper: lightGizmo.dragHelper - propName: "coneAngle" - propValue: lightGizmo.targetNode instanceof SpotLight ? targetNode.coneAngle : 0 - - onNewValueChanged: targetNode.coneAngle = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: { - if (targetNode.innerConeAngle > targetNode.coneAngle) - targetNode.innerConeAngle = targetNode.coneAngle; - lightGizmo.propertyValueCommit(propName) - lightGizmo.propertyValueCommit(spotLightInnerHandle.propName); - } - } - - SpotLightHandle { - id: spotLightInnerHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(0, -spotInnerModel.scale.y, -spotInnerModel.scale.z) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(180, 0, 0) - targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof SpotLight - dragHelper: lightGizmo.dragHelper - propName: "innerConeAngle" - propValue: lightGizmo.targetNode instanceof SpotLight ? targetNode.innerConeAngle : 0 - - onNewValueChanged: targetNode.innerConeAngle = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: { - if (targetNode.coneAngle < targetNode.innerConeAngle) - targetNode.coneAngle = targetNode.innerConeAngle; - lightGizmo.propertyValueCommit(propName) - lightGizmo.propertyValueCommit(spotLightHandle.propName); - } - } - - FadeHandle { - id: spotLightFadeHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(spotModel.scale.x / 2, 0, -spotInnerModel.scale.z / 2) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(90, 0, 0) - targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof SpotLight - dragHelper: lightGizmo.dragHelper - fadeScale: lightGizmo.fadeScale - dragScale: 2 - - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: (propName)=> { - lightGizmo.propertyValueChange(propName); - } - onValueCommit: (propName)=> { - lightGizmo.propertyValueCommit(propName); - } - } - } - Node { - id: areaParts - visible: lightGizmo.targetNode instanceof AreaLight - - LightModel { - id: areaModel - geometryName: "Edit 3D AreaLight" - geometryType: LightGeometry.Area - material: lightMaterial - scale: areaParts.visible ? Qt.vector3d(lightGizmo.targetNode.width / 2, - lightGizmo.targetNode.height / 2, 1) - .times(lightGizmo.targetNode.scale) - : Qt.vector3d(1, 1, 1) - } - - AreaLightHandle { - id: areaWidthHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof AreaLight ? Qt.vector3d(-areaModel.scale.x, 0, 0) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(0, 0, 90) - targetNode: lightGizmo.targetNode instanceof AreaLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof AreaLight - dragHelper: lightGizmo.dragHelper - propName: "width" - propValue: lightGizmo.targetNode instanceof AreaLight ? targetNode.width : 0 - - onNewValueChanged: targetNode.width = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: lightGizmo.propertyValueCommit(propName) - } - - AreaLightHandle { - id: areaHeightHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof AreaLight ? Qt.vector3d(0, -areaModel.scale.y, 0) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(0, 0, 180) - targetNode: lightGizmo.targetNode instanceof AreaLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof AreaLight - dragHelper: lightGizmo.dragHelper - propName: "height" - propValue: lightGizmo.targetNode instanceof AreaLight ? targetNode.height : 0 - - onNewValueChanged: targetNode.height = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: lightGizmo.propertyValueCommit(propName) - } - } - - LightModel { - id: directionalModel - geometryName: "Edit 3D DirLight" - geometryType: LightGeometry.Directional - material: lightMaterial - visible: lightGizmo.targetNode instanceof DirectionalLight - scale: autoScaler.getScale(Qt.vector3d(50, 50, 50)) - } - - AdjustableArrow { - id: primaryArrow - eulerRotation: Qt.vector3d(-90, 0, 0) - targetNode: lightGizmo.targetNode - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - view3D: lightGizmo.view3D - active: lightGizmo.visible - dragHelper: lightGizmo.dragHelper - scale: autoScaler.getScale(Qt.vector3d(5, 5, 5)) - length: targetNode ? Math.max(1.0, 1.0 + targetNode.brightness / _generalHelper.brightnessScaler() * 10.0) + 3 : 10 - - property real _startBrightness - - function updateBrightness(relativeDistance, screenPos) - { - var currentValue = Math.max(0, (_startBrightness + relativeDistance * _generalHelper.brightnessScaler() / 10.0)); - currentValue *= brightnessMultiplier; - currentValue = Math.round(currentValue); - currentValue /= brightnessMultiplier; - - var l = Qt.locale(); - lightGizmo.currentLabel = "brightness" + qsTr(": ") + Number(currentValue).toLocaleString(l, 'f', brightnessDecimals); - lightGizmo.currentMousePos = screenPos; - targetNode.brightness = currentValue; - } - - onPressed: (mouseArea, screenPos)=> { - _startBrightness = targetNode.brightness; - updateBrightness(0, screenPos); - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateBrightness(relativeDistance, screenPos); - lightGizmo.propertyValueChange("brightness"); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateBrightness(relativeDistance, screenPos); - lightGizmo.propertyValueCommit("brightness"); - } - } - - DefaultMaterial { - id: lightMaterial - diffuseColor: lightGizmo.color - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/LightIconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/LightIconGizmo.qml deleted file mode 100644 index dd02cf30fb3..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/LightIconGizmo.qml +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import LightUtils 1.0 - -IconGizmo { - id: lightIconGizmo - - property color overlayColor: targetNode ? targetNode.color : "transparent" - - iconSource: targetNode - ? targetNode instanceof DirectionalLight - ? "image://IconGizmoImageProvider/directional.png:" + overlayColor - : targetNode instanceof AreaLight - ? "image://IconGizmoImageProvider/area.png:" + overlayColor - : targetNode instanceof PointLight - ? "image://IconGizmoImageProvider/point.png:" + overlayColor - : "image://IconGizmoImageProvider/spot.png:" + overlayColor - : "image://IconGizmoImageProvider/point.png:" + overlayColor -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/LightModel.qml b/src/tools/qml2puppet/mockfiles/qt5/LightModel.qml deleted file mode 100644 index 4d256cebf8a..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/LightModel.qml +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import LightUtils 1.0 - -Model { - id: lightModel - - property alias geometryName: lightGeometry.name // Name must be unique for each geometry - property alias geometryType: lightGeometry.lightType - property Material material - - function updateGeometry() - { - lightGeometry.update(); - } - - scale: Qt.vector3d(50, 50, 50) - - geometry: lightGeometry - materials: [ material ] - - LightGeometry { - id: lightGeometry - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/Line3D.qml b/src/tools/qml2puppet/mockfiles/qt5/Line3D.qml deleted file mode 100644 index 4bab4be315e..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/Line3D.qml +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import LineGeometry 1.0 - -Node { - id: pivotLine - - property alias startPos: lineGeometry.startPos - property alias endPos: lineGeometry.endPos - property alias name: lineGeometry.name // Name must be unique for each line - property alias color: lineMat.diffuseColor - - Model { - geometry: LineGeometry { - id: lineGeometry - } - materials: [ - DefaultMaterial { - id: lineMat - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/MaterialNodeView.qml b/src/tools/qml2puppet/mockfiles/qt5/MaterialNodeView.qml deleted file mode 100644 index 2a65838d0c4..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/MaterialNodeView.qml +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick3D 1.15 - -View3D { - id: root - anchors.fill: parent - environment: sceneEnv - camera: envMode === "SkyBox" && envValue === "preview_studio" ? studioCamera : defaultCamera - - property Material previewMaterial - property string envMode - property string envValue - property string modelSrc: "#Sphere" - - function fitToViewPort(closeUp) - { - // No need to zoom this view, this is here just to avoid runtime warnings - } - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.High - backgroundMode: envMode === "Color" ? SceneEnvironment.Color - : envMode === "SkyBox" ? SceneEnvironment.SkyBox - : SceneEnvironment.Transparent - clearColor: envMode === "Color" ? envValue : "#000000" - lightProbe: envMode === "SkyBox" ? skyBoxTex : null - - Texture { - id: skyBoxTex - source: envMode === "SkyBox" ? "../images/" + envValue + ".hdr" - : "" - } - } - - Node { - DirectionalLight { - eulerRotation.x: -26 - eulerRotation.y: modelSrc === "#Cube" ? -10 : -50 - brightness: envMode !== "SkyBox" ? 100 : 0 - } - - PerspectiveCamera { - id: defaultCamera - y: 70 - z: 200 - eulerRotation.x: -5.71 - clipNear: 1 - clipFar: 1000 - } - - PerspectiveCamera { - id: studioCamera - y: 232 - z: 85 - eulerRotation.x: -64.98 - clipNear: 1 - clipFar: 1000 - } - - Node { - rotation: root.camera.rotation - y: 50 - Node { - y: modelSrc === "#Cone" ? -40 : 10 - eulerRotation.x: 35 - Model { - id: model - source: modelSrc ? modelSrc : "#Sphere" - eulerRotation.y: 45 - materials: previewMaterial - scale: !modelSrc || modelSrc === "#Sphere" - ? Qt.vector3d(1.7, 1.7, 1.7) : Qt.vector3d(1.2, 1.2, 1.2) - } - } - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/ModelNode2DImageView.qml b/src/tools/qml2puppet/mockfiles/qt5/ModelNode2DImageView.qml deleted file mode 100644 index 9c9e88e3b68..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/ModelNode2DImageView.qml +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 - -Item { - id: root - width: 150 - height: 150 - - property alias contentItem: contentItem - - /* - View3D { - // Dummy view to hold the context in case View3D items are used in the component - // TODO remove when QTBUG-87678 is fixed - } - */ - - Item { - id: contentItem - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/ModelNode3DImageView.qml b/src/tools/qml2puppet/mockfiles/qt5/ModelNode3DImageView.qml deleted file mode 100644 index 12ae640251d..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/ModelNode3DImageView.qml +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import QtQuick3D 1.15 - -Item { - id: root - width: 150 - height: 150 - visible: true - - property View3D view: null - property alias contentItem: contentItem - - property var previewObject - - property var materialViewComponent - property var modelViewComponent - property var nodeViewComponent - - property bool closeUp: false - - function destroyView() - { - previewObject = null; - if (view) - view.destroy(); - } - - function createViewForObject(obj, env, envValue, model) - { - if (obj instanceof Material) - createViewForMaterial(obj, env, envValue, model); - else if (obj instanceof Model) - createViewForModel(obj); - else if (obj instanceof Node) - createViewForNode(obj); - } - - function createViewForMaterial(material, env, envValue, model) - { - if (!materialViewComponent) - materialViewComponent = Qt.createComponent("MaterialNodeView.qml"); - - // Always recreate the view to ensure material is up to date - if (materialViewComponent.status === Component.Ready) { - view = materialViewComponent.createObject(viewRect, {"previewMaterial": material, - "envMode": env, - "envValue": envValue, - "modelSrc": model}); - } - previewObject = material; - } - - function createViewForModel(model) - { - if (!modelViewComponent) - modelViewComponent = Qt.createComponent("ModelNodeView.qml"); - - // Always recreate the view to ensure model is up to date - if (modelViewComponent.status === Component.Ready) - view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); - - previewObject = model; - } - - function createViewForNode(node) - { - if (!nodeViewComponent) - nodeViewComponent = Qt.createComponent("NodeNodeView.qml"); - - // Always recreate the view to ensure node is up to date - if (nodeViewComponent.status === Component.Ready) - view = nodeViewComponent.createObject(viewRect, {"importScene": node}); - - previewObject = node; - } - - function fitToViewPort() - { - view.fitToViewPort(closeUp); - } - - // Enables/disables icon mode. When in icon mode, camera is zoomed bit closer to reduce margins - // and the background is removed, in order to generate a preview suitable for library icons. - function setIconMode(enable) - { - closeUp = enable; - backgroundRect.visible = !enable; - } - - View3D { - // Dummy view to hold the context - // TODO remove when QTBUG-87678 is fixed - } - - Item { - id: contentItem - anchors.fill: parent - - Item { - id: viewRect - anchors.fill: parent - } - - // We can use static image in Qt5 as only small previews will be generated - Image { - id: backgroundRect - anchors.fill: parent - z: -1 - source: "../images/static_floor.png" - fillMode: Image.Stretch - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/ModelNodeView.qml b/src/tools/qml2puppet/mockfiles/qt5/ModelNodeView.qml deleted file mode 100644 index f1d4ebfe046..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/ModelNodeView.qml +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import QtQuick3D 1.15 - -View3D { - id: root - anchors.fill: parent - environment: sceneEnv - camera: theCamera - - property Model sourceModel - - function fitToViewPort(closeUp) - { - // The magic number is the distance from camera default pos to origin - _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, model, root, - 1040, closeUp); - } - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.High - } - - DirectionalLight { - eulerRotation.x: -30 - eulerRotation.y: -30 - } - - PerspectiveCamera { - id: theCamera - z: 600 - y: 600 - x: 600 - eulerRotation.x: -45 - eulerRotation.y: -45 - clipFar: 10000 - clipNear: 1 - } - - Model { - id: model - source: sourceModel.source - geometry: sourceModel.geometry - - materials: [ - DefaultMaterial { - diffuseColor: "#999999" - } - ] - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/MoveGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/MoveGizmo.qml deleted file mode 100644 index d2c3dc7d5dd..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/MoveGizmo.qml +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Node { - id: moveGizmo - - property View3D view3D - property bool highlightOnHover: false - property Node targetNode: null - property bool globalOrientation: true - readonly property bool dragging: arrowX.dragging || arrowY.dragging || arrowZ.dragging - || planeX.dragging || planeY.dragging || planeZ.dragging - || centerBall.dragging - property MouseArea3D dragHelper: null - property alias freeDraggerArea: centerBall.mouseArea - - position: dragHelper.pivotScenePosition(targetNode) - - onTargetNodeChanged: position = dragHelper.pivotScenePosition(targetNode) - - Connections { - target: moveGizmo.targetNode - function onSceneTransformChanged() - { - moveGizmo.position = moveGizmo.dragHelper.pivotScenePosition(moveGizmo.targetNode); - } - } - - signal positionCommit() - signal positionMove() - - Node { - rotation: globalOrientation || !moveGizmo.targetNode ? Qt.quaternion(1, 0, 0, 0) - : moveGizmo.targetNode.sceneRotation - Arrow { - id: arrowX - eulerRotation: Qt.vector3d(0, 0, -90) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) - : Qt.rgba(1, 0, 0, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - - Arrow { - id: arrowY - eulerRotation: Qt.vector3d(0, 0, 0) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - : Qt.rgba(0, 0.6, 0, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - - Arrow { - id: arrowZ - eulerRotation: Qt.vector3d(90, 0, 0) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) - : Qt.rgba(0, 0, 1, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - - PlanarMoveHandle { - id: planeX - - y: 10 - z: 10 - - eulerRotation: Qt.vector3d(0, 90, 0) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) - : Qt.rgba(1, 0, 0, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - - PlanarMoveHandle { - id: planeY - - x: 10 - z: 10 - - eulerRotation: Qt.vector3d(90, 0, 0) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - : Qt.rgba(0, 0.6, 0, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - - PlanarMoveHandle { - id: planeZ - - x: 10 - y: 10 - - eulerRotation: Qt.vector3d(0, 0, 0) - targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) - : Qt.rgba(0, 0, 1, 1) - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } - } - - PlanarMoveHandle { - id: centerBall - - source: "#Sphere" - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) - : Qt.rgba(0.5, 0.5, 0.5, 1) - rotation: view3D.camera.rotation - priority: 10 - targetNode: moveGizmo.targetNode - - view3D: moveGizmo.view3D - active: moveGizmo.visible - dragHelper: moveGizmo.dragHelper - - onPositionCommit: moveGizmo.positionCommit() - onPositionMove: moveGizmo.positionMove() - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/NodeNodeView.qml b/src/tools/qml2puppet/mockfiles/qt5/NodeNodeView.qml deleted file mode 100644 index 279913576bb..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/NodeNodeView.qml +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import QtQuick3D 1.15 - -View3D { - id: root - anchors.fill: parent - environment: sceneEnv - camera: theCamera - - function fitToViewPort(closeUp) - { - // The magic number is the distance from camera default pos to origin - _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040, closeUp); - } - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.High - } - - DirectionalLight { - eulerRotation.x: -30 - eulerRotation.y: -30 - } - - PerspectiveCamera { - id: theCamera - z: 600 - y: 600 - x: 600 - eulerRotation.x: -45 - eulerRotation.y: -45 - clipFar: 10000 - clipNear: 1 - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/Overlay2D.qml b/src/tools/qml2puppet/mockfiles/qt5/Overlay2D.qml deleted file mode 100644 index 3ec2959394d..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/Overlay2D.qml +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -Item { - id: root - property Node targetNode - property View3D targetView - - property vector3d offset: Qt.vector3d(0, 0, 0) - - property bool isBehindCamera - - onTargetNodeChanged: updateOverlay() - - Connections { - target: targetNode - function onSceneTransformChanged() { updateOverlay() } - } - - Connections { - target: targetView.camera - function onSceneTransformChanged() { updateOverlay() } - } - - Connections { - target: _generalHelper - function onOverlayUpdateNeeded() { updateOverlay() } - } - - function updateOverlay() - { - var scenePos = targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0); - // Need separate variable as scenePos is reference to read-only property - var scenePosWithOffset = Qt.vector3d(scenePos.x + offset.x, - scenePos.y + offset.y, - scenePos.z + offset.z); - var viewPos = targetView ? targetView.mapFrom3DScene(scenePosWithOffset) - : Qt.vector3d(0, 0, 0); - root.x = viewPos.x; - root.y = viewPos.y; - - isBehindCamera = viewPos.z <= 0; - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/PlanarDraggable.qml b/src/tools/qml2puppet/mockfiles/qt5/PlanarDraggable.qml deleted file mode 100644 index 8efc4445edf..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/PlanarDraggable.qml +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Model { - id: rootModel - - property View3D view3D - property alias color: gizmoMaterial.diffuseColor - property alias priority: mouseArea.priority - property Node targetNode: null - property bool dragging: mouseArea.dragging - property bool active: false - property MouseArea3D dragHelper: null - property alias mouseArea: mouseArea - - readonly property bool hovering: mouseArea.hovering - - property vector3d _scenePosPressed - property vector2d _planePosPressed - property vector3d _targetStartPos - - signal pressed(var mouseArea) - signal dragged(var mouseArea, vector3d sceneRelativeDistance, vector2d relativeDistance) - signal released(var mouseArea, vector3d sceneRelativeDistance, vector2d relativeDistance) - - source: "#Rectangle" - - DefaultMaterial { - id: gizmoMaterial - diffuseColor: "white" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - materials: gizmoMaterial - - function handlePressed(mouseArea, planePos) - { - if (!targetNode) - return; - - _planePosPressed = planePos; - _scenePosPressed = mouseArea.dragHelper.mapPositionToScene(planePos.toVector3d()); - _targetStartPos = mouseArea.pivotScenePosition(targetNode); - pressed(mouseArea); - } - - function calcRelativeDistance(mouseArea, planePos) - { - var scenePointerPos = mouseArea.dragHelper.mapPositionToScene(planePos.toVector3d()); - return scenePointerPos.minus(_scenePosPressed); - } - - function handleDragged(mouseArea, planePos) - { - if (!targetNode) - return; - - dragged(mouseArea, calcRelativeDistance(mouseArea, planePos), - planePos.minus(_planePosPressed)); - } - - function handleReleased(mouseArea, planePos) - { - if (!targetNode) - return; - - released(mouseArea, calcRelativeDistance(mouseArea, planePos), - planePos.minus(_planePosPressed)); - } - - MouseArea3D { - id: mouseArea - view3D: rootModel.view3D - x: -60 - y: -60 - width: 120 - height: 120 - grabsMouse: targetNode - active: rootModel.active - dragHelper: rootModel.dragHelper - - onPressed: (planePos)=> { - rootModel.handlePressed(mouseArea, planePos); - } - onDragged: (planePos)=> { - rootModel.handleDragged(mouseArea, planePos); - } - onReleased: (planePos)=> { - rootModel.handleReleased(mouseArea, planePos); - } - } -} - diff --git a/src/tools/qml2puppet/mockfiles/qt5/PlanarMoveHandle.qml b/src/tools/qml2puppet/mockfiles/qt5/PlanarMoveHandle.qml deleted file mode 100644 index 451c8220c8e..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/PlanarMoveHandle.qml +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -PlanarDraggable { - id: planarHandle - scale: Qt.vector3d(0.024, 0.024, 0.024) - - signal positionCommit() - signal positionMove() - - function localPos(sceneRelativeDistance) - { - var newScenePos = Qt.vector3d( - _targetStartPos.x + sceneRelativeDistance.x, - _targetStartPos.y + sceneRelativeDistance.y, - _targetStartPos.z + sceneRelativeDistance.z); - return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; - } - - onPressed: { - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - } - - onDragged: (mouseArea, sceneRelativeDistance)=> { - targetNode.position = localPos(sceneRelativeDistance); - if (targetNode == multiSelectionNode) - _generalHelper.moveMultiSelection(false); - positionMove(); - } - - onReleased: (mouseArea, sceneRelativeDistance)=> { - targetNode.position = localPos(sceneRelativeDistance); - if (targetNode == multiSelectionNode) - _generalHelper.moveMultiSelection(true); - positionCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/PlanarScaleHandle.qml b/src/tools/qml2puppet/mockfiles/qt5/PlanarScaleHandle.qml deleted file mode 100644 index 77ed5603a88..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/PlanarScaleHandle.qml +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -PlanarDraggable { - id: planarHandle - scale: Qt.vector3d(0.024, 0.024, 0.024) - - property bool globalOrientation: false - property vector3d axisX - property vector3d axisY - - signal scaleCommit() - signal scaleChange() - - property vector3d _startScale - - onPressed: { - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - _startScale = targetNode.scale; - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance)=> { - targetNode.scale = mouseArea.getNewScale(_startScale, relativeDistance.times(scale.x), - axisX, axisY); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(false); - scaleChange(); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance)=> { - targetNode.scale = mouseArea.getNewScale(_startScale, relativeDistance.times(scale.x), - axisX, axisY); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(true); - scaleCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/RotateGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/RotateGizmo.qml deleted file mode 100644 index 0130bacb534..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/RotateGizmo.qml +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Node { - id: rotateGizmo - - property View3D view3D - property bool highlightOnHover: true - property Node targetNode: null - property bool globalOrientation: true - readonly property bool dragging: cameraRing.dragging - || rotRingX.dragging || rotRingY.dragging || rotRingZ.dragging - property MouseArea3D dragHelper: null - property real currentAngle - property point currentMousePos - property alias freeDraggerArea: mouseAreaFree - property bool blocked: false - - position: dragHelper.pivotScenePosition(targetNode) - - onTargetNodeChanged: { - position = dragHelper.pivotScenePosition(targetNode); - blocked = _generalHelper.isRotationBlocked(targetNode); - } - - Connections { - target: rotateGizmo.targetNode - function onSceneTransformChanged() - { - rotateGizmo.position = dragHelper.pivotScenePosition(targetNode); - } - } - - Connections { - target: _generalHelper - function onRotationBlocksChanged() - { - blocked = _generalHelper.isRotationBlocked(targetNode); - } - } - - signal rotateCommit() - signal rotateChange() - - function copyRingProperties(srcRing) { - draggingRing.rotation = srcRing.sceneRotation; - draggingRing.color = srcRing.color; - draggingRing.scale = srcRing.scale; - } - - onDraggingChanged: { - if (rotRingX.dragging) - copyRingProperties(rotRingX) - else if (rotRingY.dragging) - copyRingProperties(rotRingY) - else if (rotRingZ.dragging) - copyRingProperties(rotRingZ) - } - - Node { - id: rotNode - rotation: globalOrientation || !rotateGizmo.targetNode ? Qt.quaternion(1, 0, 0, 0) - : rotateGizmo.targetNode.sceneRotation - visible: !rotateGizmo.dragging && !freeRotator.dragging - - RotateRing { - id: rotRingX - objectName: "Rotate Ring X" - eulerRotation: Qt.vector3d(0, 90, 0) - targetNode: rotateGizmo.targetNode - color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1) - : highlightOnHover && (hovering || dragging) - ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) : Qt.rgba(1, 0, 0, 1) - priority: 40 - view3D: rotateGizmo.view3D - active: rotateGizmo.visible && !rotateGizmo.blocked - dragHelper: rotateGizmo.dragHelper - - onRotateCommit: rotateGizmo.rotateCommit() - onRotateChange: rotateGizmo.rotateChange() - onCurrentAngleChanged: rotateGizmo.currentAngle = currentAngle - onCurrentMousePosChanged: rotateGizmo.currentMousePos = currentMousePos - } - - RotateRing { - id: rotRingY - objectName: "Rotate Ring Y" - eulerRotation: Qt.vector3d(90, 0, 0) - targetNode: rotateGizmo.targetNode - color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1) - : highlightOnHover && (hovering || dragging) - ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) : Qt.rgba(0, 0.6, 0, 1) - // Just a smidge smaller than higher priority rings so that it doesn't obscure them - scale: Qt.vector3d(0.998, 0.998, 0.998) - priority: 30 - view3D: rotateGizmo.view3D - active: rotateGizmo.visible && !rotateGizmo.blocked - dragHelper: rotateGizmo.dragHelper - - onRotateCommit: rotateGizmo.rotateCommit() - onRotateChange: rotateGizmo.rotateChange() - onCurrentAngleChanged: rotateGizmo.currentAngle = currentAngle - onCurrentMousePosChanged: rotateGizmo.currentMousePos = currentMousePos - } - - RotateRing { - id: rotRingZ - objectName: "Rotate Ring Z" - eulerRotation: Qt.vector3d(0, 0, 0) - targetNode: rotateGizmo.targetNode - color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1) - : highlightOnHover && (hovering || dragging) - ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) : Qt.rgba(0, 0, 1, 1) - // Just a smidge smaller than higher priority rings so that it doesn't obscure them - scale: Qt.vector3d(0.996, 0.996, 0.996) - priority: 20 - view3D: rotateGizmo.view3D - active: rotateGizmo.visible && !rotateGizmo.blocked - dragHelper: rotateGizmo.dragHelper - - onRotateCommit: rotateGizmo.rotateCommit() - onRotateChange: rotateGizmo.rotateChange() - onCurrentAngleChanged: rotateGizmo.currentAngle = currentAngle - onCurrentMousePosChanged: rotateGizmo.currentMousePos = currentMousePos - } - } - - RotateRing { - // This ring is used as visual proxy during dragging to display the currently dragged - // plane in static position, as rotation planes can wobble when ancestors don't have - // uniform scaling. - // Camera ring doesn't need dragging proxy as it doesn't wobble. - id: draggingRing - objectName: "draggingRing" - targetNode: rotateGizmo.targetNode - view3D: rotateGizmo.view3D - active: false - visible: rotRingX.dragging || rotRingY.dragging || rotRingZ.dragging - } - - RotateRing { - id: cameraRing - objectName: "cameraRing" - rotation: rotateGizmo.view3D.camera.rotation - targetNode: rotateGizmo.targetNode - color: rotateGizmo.blocked ? Qt.rgba(0.5, 0.5, 0.5, 1) - : highlightOnHover && (hovering || dragging) - ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) - : Qt.rgba(0.5, 0.5, 0.5, 1) - scale: Qt.vector3d(1.1, 1.1, 1.1) - priority: 10 - view3D: rotateGizmo.view3D - active: rotateGizmo.visible && !rotateGizmo.blocked - dragHelper: rotateGizmo.dragHelper - visible: !rotRingX.dragging && !rotRingY.dragging && !rotRingZ.dragging && !freeRotator.dragging - - onRotateCommit: rotateGizmo.rotateCommit() - onRotateChange: rotateGizmo.rotateChange() - onCurrentAngleChanged: rotateGizmo.currentAngle = currentAngle - onCurrentMousePosChanged: rotateGizmo.currentMousePos = currentMousePos - } - - Model { - id: freeRotator - - source: "#Sphere" - materials: DefaultMaterial { - id: material - diffuseColor: "black" - opacity: mouseAreaFree.hovering && !rotateGizmo.blocked ? 0.15 : 0 - lighting: DefaultMaterial.NoLighting - } - scale: Qt.vector3d(0.15, 0.15, 0.15) - visible: !rotateGizmo.dragging && !dragging - - property bool dragging: false - property vector3d _pointerPosPressed - property vector3d _startRotation - - function handlePressed(screenPos) - { - if (!rotateGizmo.targetNode) - return; - - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - - // Need to recreate vector as we need to adjust it and we can't do that on reference of - // scenePosition, which is read-only property - var scenePos = rotateGizmo.dragHelper.pivotScenePosition(rotateGizmo.targetNode); - _pointerPosPressed = Qt.vector3d(screenPos.x, screenPos.y, 0); - - // Recreate vector so we don't follow the changes in targetNode.rotation - _startRotation = Qt.vector3d(rotateGizmo.targetNode.eulerRotation.x, - rotateGizmo.targetNode.eulerRotation.y, - rotateGizmo.targetNode.eulerRotation.z); - // Ensure we never set NaN values for rotation, even if target node originally has them - if (isNaN(_startRotation.x)) - _startRotation.x = 0; - if (isNaN(_startRotation.y)) - _startRotation.y = 0; - if (isNaN(_startRotation.z)) - _startRotation.z = 0; - - dragging = true; - } - - function handleDragged(screenPos) - { - if (!rotateGizmo.targetNode) - return; - - mouseAreaFree.applyFreeRotation( - rotateGizmo.targetNode, _startRotation, _pointerPosPressed, - Qt.vector3d(screenPos.x, screenPos.y, 0)); - - if (targetNode == multiSelectionNode) - _generalHelper.rotateMultiSelection(false); - - rotateGizmo.rotateChange(); - } - - function handleReleased(screenPos) - { - if (!rotateGizmo.targetNode) - return; - - mouseAreaFree.applyFreeRotation( - rotateGizmo.targetNode, _startRotation, _pointerPosPressed, - Qt.vector3d(screenPos.x, screenPos.y, 0)); - - if (targetNode == multiSelectionNode) - _generalHelper.rotateMultiSelection(true); - - rotateGizmo.rotateCommit(); - dragging = false; - - if (targetNode == multiSelectionNode) - _generalHelper.resetMultiSelectionNode(); - } - - MouseArea3D { - id: mouseAreaFree - view3D: rotateGizmo.view3D - rotation: rotateGizmo.view3D.camera.rotation - objectName: "Free rotator plane" - x: -50 - y: -50 - width: 100 - height: 100 - circlePickArea: Qt.point(25, 50) - grabsMouse: rotateGizmo.targetNode - active: rotateGizmo.visible && !rotateGizmo.blocked - dragHelper: rotateGizmo.dragHelper - - onPressed: (planePos, screenPos)=> { - freeRotator.handlePressed(screenPos); - } - onDragged: (planePos, screenPos)=> { - freeRotator.handleDragged(screenPos); - } - onReleased: (planePos, screenPos)=> { - freeRotator.handleReleased(screenPos); - } - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/RotateRing.qml b/src/tools/qml2puppet/mockfiles/qt5/RotateRing.qml deleted file mode 100644 index 3fe92413f71..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/RotateRing.qml +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Model { - id: rotateRing - - property View3D view3D - property alias color: material.diffuseColor - property Node targetNode: null - property bool dragging: mouseAreaMain.dragging - property bool active: false - property alias hovering: mouseAreaMain.hovering - property alias priority: mouseAreaMain.priority - property real currentAngle - property point currentMousePos - property MouseArea3D dragHelper: null - - property vector3d _pointerPosPressed - property vector3d _targetPosOnScreen - property vector3d _startRotation - property bool _trackBall - - signal rotateCommit() - signal rotateChange() - - source: "../meshes/ring.mesh" - - Model { - id: pickModel - objectName: "PickModel for " + rotateRing.objectName - source: "../meshes/ringselect.mesh" - pickable: true - } - - materials: DefaultMaterial { - id: material - diffuseColor: "white" - lighting: DefaultMaterial.NoLighting - } - - function applyLocalRotation(screenPos) - { - currentAngle = mouseAreaMain.dragHelper.getNewRotationAngle( - targetNode, _pointerPosPressed, Qt.vector3d(screenPos.x, screenPos.y, 0), - _targetPosOnScreen, currentAngle, _trackBall); - mouseAreaMain.dragHelper.applyRotationAngleToNode(targetNode, _startRotation, currentAngle); - } - - function handlePressed(screenPos, angle) - { - if (!targetNode) - return; - - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - - // Need to recreate vector as we need to adjust it and we can't do that on reference of - // scenePosition, which is read-only property - var scenePos = mouseAreaMain.pivotScenePosition(targetNode); - - _targetPosOnScreen = view3D.mapFrom3DScene(scenePos); - _targetPosOnScreen.z = 0; - _pointerPosPressed = Qt.vector3d(screenPos.x, screenPos.y, 0); - _trackBall = angle < 0.1; - - // Recreate vector so we don't follow the changes in targetNode.eulerRotation - _startRotation = Qt.vector3d(targetNode.eulerRotation.x, - targetNode.eulerRotation.y, - targetNode.eulerRotation.z); - // Ensure we never set NaN values for rotation, even if target node originally has them - if (isNaN(_startRotation.x)) - _startRotation.x = 0; - if (isNaN(_startRotation.y)) - _startRotation.y = 0; - if (isNaN(_startRotation.z)) - _startRotation.z = 0; - currentAngle = 0; - currentMousePos = screenPos; - } - - function handleDragged(screenPos) - { - if (!targetNode) - return; - - applyLocalRotation(screenPos); - - if (targetNode == multiSelectionNode) - _generalHelper.rotateMultiSelection(false); - - currentMousePos = screenPos; - rotateChange(); - } - - function handleReleased(screenPos) - { - if (!targetNode) - return; - - applyLocalRotation(screenPos); - - if (targetNode == multiSelectionNode) - _generalHelper.rotateMultiSelection(true); - - rotateCommit(); - currentAngle = 0; - currentMousePos = screenPos; - - if (targetNode == multiSelectionNode) - _generalHelper.resetMultiSelectionNode(); - } - - MouseArea3D { - id: mouseAreaMain - view3D: rotateRing.view3D - objectName: "Main plane of " + rotateRing.objectName - x: -30 - y: -30 - width: 60 - height: 60 - circlePickArea: Qt.point(9.2, 1.4) - grabsMouse: targetNode - active: rotateRing.active - pickNode: pickModel - minAngle: 0.05 - dragHelper: rotateRing.dragHelper - - onPressed: (planePos, screenPos, angle)=> { - rotateRing.handlePressed(screenPos, angle); - } - onDragged: (planePos, screenPos)=> { - rotateRing.handleDragged(screenPos); - } - onReleased: (planePos, screenPos)=> { - rotateRing.handleReleased(screenPos); - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/ScaleGizmo.qml b/src/tools/qml2puppet/mockfiles/qt5/ScaleGizmo.qml deleted file mode 100644 index b0fe1d1b476..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/ScaleGizmo.qml +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -Node { - id: scaleGizmo - - property View3D view3D - property bool highlightOnHover: false - property Node targetNode: null - readonly property bool dragging: scaleRodX.dragging || scaleRodY.dragging || scaleRodZ.dragging - || planeX.dragging || planeY.dragging || planeZ.dragging - || centerMouseArea.dragging - property MouseArea3D dragHelper: null - property alias freeDraggerArea: centerMouseArea - - position: dragHelper.pivotScenePosition(targetNode) - - onTargetNodeChanged: position = dragHelper.pivotScenePosition(targetNode) - - Connections { - target: scaleGizmo.targetNode - function onSceneTransformChanged() - { - scaleGizmo.position = scaleGizmo.dragHelper.pivotScenePosition(scaleGizmo.targetNode); - } - } - - signal scaleCommit() - signal scaleChange() - - Node { - rotation: !targetNode ? Qt.quaternion(1, 0, 0, 0) : targetNode.sceneRotation - - ScaleRod { - id: scaleRodX - eulerRotation: Qt.vector3d(0, 0, -90) - axis: Qt.vector3d(1, 0, 0) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) - : Qt.rgba(1, 0, 0, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - - ScaleRod { - id: scaleRodY - eulerRotation: Qt.vector3d(0, 0, 0) - axis: Qt.vector3d(0, 1, 0) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - : Qt.rgba(0, 0.6, 0, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - - ScaleRod { - id: scaleRodZ - eulerRotation: Qt.vector3d(90, 0, 0) - axis: Qt.vector3d(0, 0, 1) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) - : Qt.rgba(0, 0, 1, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - - PlanarScaleHandle { - id: planeX - - y: 10 - z: 10 - - eulerRotation: Qt.vector3d(0, 90, 0) - axisX: Qt.vector3d(0, 0, -1) - axisY: Qt.vector3d(0, 1, 0) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) - : Qt.rgba(1, 0, 0, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - - PlanarScaleHandle { - id: planeY - - x: 10 - z: 10 - - eulerRotation: Qt.vector3d(90, 0, 0) - axisX: Qt.vector3d(1, 0, 0) - axisY: Qt.vector3d(0, 0, 1) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - : Qt.rgba(0, 0.6, 0, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - - PlanarScaleHandle { - id: planeZ - - x: 10 - y: 10 - - eulerRotation: Qt.vector3d(0, 0, 0) - axisX: Qt.vector3d(1, 0, 0) - axisY: Qt.vector3d(0, 1, 0) - targetNode: scaleGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) - : Qt.rgba(0, 0, 1, 1) - view3D: scaleGizmo.view3D - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - onScaleCommit: scaleGizmo.scaleCommit() - onScaleChange: scaleGizmo.scaleChange() - } - } - - Model { - id: centerCube - - source: "#Cube" - scale: Qt.vector3d(0.024, 0.024, 0.024) - materials: DefaultMaterial { - id: material - diffuseColor: highlightOnHover - && (centerMouseArea.hovering || centerMouseArea.dragging) - ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) - : Qt.rgba(0.5, 0.5, 0.5, 1) - lighting: DefaultMaterial.NoLighting - } - - MouseArea3D { - id: centerMouseArea - view3D: scaleGizmo.view3D - x: -60 - y: -60 - width: 120 - height: 120 - rotation: view3D.camera.rotation - grabsMouse: scaleGizmo.targetNode - priority: 10 - active: scaleGizmo.visible - dragHelper: scaleGizmo.dragHelper - - property vector3d _startScale - property point _startScreenPos - - function localScale(screenPos) - { - var yDelta = screenPos.y - _startScreenPos.y; - if (yDelta === 0) - return _startScale; - var scaler = 1.0 + (yDelta * 0.025); - if (scaler === 0) - scaler = 0.0001; - return Qt.vector3d(scaler * _startScale.x, - scaler * _startScale.y, - scaler * _startScale.z); - } - - onPressed: (planePos, screenPos)=> { - if (!scaleGizmo.targetNode) - return; - - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - - // Recreate vector so we don't follow the changes in targetNode.scale - _startScale = Qt.vector3d(scaleGizmo.targetNode.scale.x, - scaleGizmo.targetNode.scale.y, - scaleGizmo.targetNode.scale.z); - _startScreenPos = screenPos; - } - onDragged: (planePos, screenPos)=> { - if (!scaleGizmo.targetNode) - return; - scaleGizmo.targetNode.scale = localScale(screenPos); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(false); - scaleGizmo.scaleChange(); - } - onReleased: (planePos, screenPos)=> { - if (!scaleGizmo.targetNode) - return; - - scaleGizmo.targetNode.scale = localScale(screenPos); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(true); - scaleGizmo.scaleCommit(); - } - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/ScaleRod.qml b/src/tools/qml2puppet/mockfiles/qt5/ScaleRod.qml deleted file mode 100644 index 07675d3a7d1..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/ScaleRod.qml +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import MouseArea3D 1.0 - -DirectionalDraggable { - id: scaleRod - source: "../meshes/scalerod.mesh" - - property vector3d axis - - signal scaleCommit() - signal scaleChange() - - property vector3d _startScale - - Model { - source: "#Cube" - y: 10 - scale: Qt.vector3d(0.020, 0.020, 0.020) - materials: DefaultMaterial { - id: material - diffuseColor: scaleRod.color - lighting: DefaultMaterial.NoLighting - } - } - - onPressed: { - if (targetNode == multiSelectionNode) - _generalHelper.restartMultiSelection(); - _startScale = targetNode.scale; - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance)=> { - targetNode.scale = mouseArea.getNewScale(_startScale, Qt.vector2d(relativeDistance, 0), - axis, Qt.vector3d(0, 0, 0)); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(false); - scaleChange(); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance)=> { - targetNode.scale = mouseArea.getNewScale(_startScale, Qt.vector2d(relativeDistance, 0), - axis, Qt.vector3d(0, 0, 0)); - if (targetNode == multiSelectionNode) - _generalHelper.scaleMultiSelection(true); - scaleCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt5/SceneView3D.qml deleted file mode 100644 index cfd565c6cbf..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/SceneView3D.qml +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick3D 1.15 - -View3D { - id: sceneView - anchors.fill: parent - - property bool usePerspective: false - property alias showSceneLight: sceneLight.visible - property alias showGrid: helperGrid.visible - property alias gridColor: helperGrid.gridColor - property alias sceneHelpers: sceneHelpers - property alias perspectiveCamera: scenePerspectiveCamera - property alias orthoCamera: sceneOrthoCamera - property double cameraZoomFactor: .55; - - // Empirical cameraZoomFactor values at which the grid zoom level is doubled. The values are - // approximately uniformally distributed over the non-linear range of cameraZoomFactor. - readonly property var grid_thresholds: [0.55, 1.10, 2.35, 4.9, 10.0, 20.5, 42.0, 85.0, 999999.0] - property var thresIdx: 1 - property var thresPerc: 1.0 // percentage of cameraZoomFactor to the current grid zoom threshold (0.0 - 1.0) - - camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera - - onCameraZoomFactorChanged: { - thresIdx = Math.max(1, grid_thresholds.findIndex(v => v > cameraZoomFactor)); - thresPerc = (grid_thresholds[thresIdx] - cameraZoomFactor) / (grid_thresholds[thresIdx] - grid_thresholds[thresIdx - 1]); - } - - Node { - id: sceneHelpers - - HelperGrid { - id: helperGrid - lines: Math.pow(2, grid_thresholds.length - thresIdx - 1); - step: 100 * grid_thresholds[0] * Math.pow(2, thresIdx - 1); - subdivAlpha: thresPerc; - } - - PointLight { - id: sceneLight - position: usePerspective ? scenePerspectiveCamera.position - : sceneOrthoCamera.position - quadraticFade: 0 - linearFade: 0 - } - - // Initial camera position and rotation should be such that they look at origin. - // Otherwise EditCameraController._lookAtPoint needs to be initialized to correct - // point. - PerspectiveCamera { - id: scenePerspectiveCamera - z: 600 - y: 600 - eulerRotation.x: -45 - clipFar: 100000 - clipNear: 1 - } - - OrthographicCamera { - id: sceneOrthoCamera - z: 600 - y: 600 - eulerRotation.x: -45 - clipFar: 100000 - clipNear: -10000 - } - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/SelectionBox.qml b/src/tools/qml2puppet/mockfiles/qt5/SelectionBox.qml deleted file mode 100644 index 0044888bfe8..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/SelectionBox.qml +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 -import SelectionBoxGeometry 1.0 - -Node { - id: selectionBox - - property View3D view3D - property Node targetNode: null - property alias model: selectionBoxModel - property alias geometryName: selectionBoxGeometry.name - - SelectionBoxGeometry { - id: selectionBoxGeometry - name: "Selection Box of 3D Edit View" - view3D: selectionBox.view3D - targetNode: selectionBox.targetNode - rootNode: selectionBox - } - - Model { - id: selectionBoxModel - geometry: selectionBoxGeometry - - scale: selectionBox.targetNode ? selectionBox.targetNode.scale : Qt.vector3d(1, 1, 1) - rotation: selectionBox.targetNode ? selectionBox.targetNode.rotation : Qt.quaternion(1, 0, 0, 0) - position: selectionBox.targetNode ? selectionBox.targetNode.position : Qt.vector3d(0, 0, 0) - pivot: selectionBox.targetNode ? selectionBox.targetNode.pivot : Qt.vector3d(0, 0, 0) - - visible: selectionBox.targetNode && !selectionBoxGeometry.isEmpty - - castsShadows: false - receivesShadows: false - - materials: [ - DefaultMaterial { - diffuseColor: "#fff600" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt5/SpotLightHandle.qml b/src/tools/qml2puppet/mockfiles/qt5/SpotLightHandle.qml deleted file mode 100644 index f8974207c00..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt5/SpotLightHandle.qml +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.0 -import QtQuick3D 1.15 - -DirectionalDraggable { - id: handleRoot - - property string currentLabel - property point currentMousePos - property string propName - property real propValue: 0 - property real newValue: 0 - - scale: autoScaler.getScale(Qt.vector3d(5, 5, 5)) - length: 3 - offset: -1.5 - - Model { - id: handle - source: "#Sphere" - materials: [ handleRoot.material ] - scale: Qt.vector3d(0.02, 0.02, 0.02) - } - - AutoScaleHelper { - id: autoScaler - active: handleRoot.active - view3D: handleRoot.view3D - } - - property real _startAngle - - signal valueCommit() - signal valueChange() - - function updateAngle(relativeDistance, screenPos) - { - handleRoot.newValue = Math.round(Math.min(180, Math.max(0, _startAngle + relativeDistance))); - var l = Qt.locale(); - handleRoot.currentLabel = propName + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 0); - handleRoot.currentMousePos = screenPos; - } - - onPressed: (mouseArea, screenPos)=> { - _startAngle = propValue; - updateAngle(0, screenPos); - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateAngle(relativeDistance, screenPos); - handleRoot.valueChange(); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateAngle(relativeDistance, screenPos); - handleRoot.valueCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt6/AreaLightHandle.qml b/src/tools/qml2puppet/mockfiles/qt6/AreaLightHandle.qml deleted file mode 100644 index 26ad41d19f7..00000000000 --- a/src/tools/qml2puppet/mockfiles/qt6/AreaLightHandle.qml +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 6.0 -import QtQuick3D 6.0 - -DirectionalDraggable { - id: handleRoot - - property string currentLabel - property point currentMousePos - property string propName - property real propValue: 0 - property real newValue: 0 - property real baseScale: 5 - - scale: autoScaler.getScale(Qt.vector3d(baseScale, baseScale, baseScale)) - length: 3 - offset: -1.5 - - Model { - id: handle - readonly property bool _edit3dLocked: true // Make this non-pickable - source: "#Sphere" - materials: [ handleRoot.material ] - scale: Qt.vector3d(0.02, 0.02, 0.02) - } - - AutoScaleHelper { - id: autoScaler - active: handleRoot.active - view3D: handleRoot.view3D - } - - property real _startValue - property real _startScale - - signal valueCommit() - signal valueChange() - - function updateValue(relativeDistance, screenPos) - { - handleRoot.newValue = Math.round(Math.min(999999, Math.max(0, _startValue + (relativeDistance * _startScale)))); - var l = Qt.locale(); - handleRoot.currentLabel = propName + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 0); - handleRoot.currentMousePos = screenPos; - } - - onPressed: (mouseArea, screenPos)=> { - _startScale = autoScaler.relativeScale * baseScale; - _startValue = propValue; - updateValue(0, screenPos); - } - - onDragged: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateValue(relativeDistance, screenPos); - handleRoot.valueChange(); - } - - onReleased: (mouseArea, sceneRelativeDistance, relativeDistance, screenPos)=> { - updateValue(relativeDistance, screenPos); - handleRoot.valueCommit(); - } -} diff --git a/src/tools/qml2puppet/mockfiles/qt6/Arrow.qml b/src/tools/qml2puppet/mockfiles/qt6/Arrow.qml index 59ac5a5f081..a80bc4b85a5 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/Arrow.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/Arrow.qml @@ -8,6 +8,9 @@ DirectionalDraggable { id: arrow source: "../meshes/arrow.mesh" + property vector3d dragAxis + property bool globalOrientation + signal positionCommit() signal positionMove() @@ -17,6 +20,9 @@ DirectionalDraggable { _targetStartPos.x + sceneRelativeDistance.x, _targetStartPos.y + sceneRelativeDistance.y, _targetStartPos.z + sceneRelativeDistance.z); + newScenePos = _generalHelper.adjustTranslationForSnap(newScenePos, _targetStartPos, + dragAxis, globalOrientation, + targetNode); return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 964f47941bf..7cd8dd7f4a3 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -26,7 +26,7 @@ Item { property alias contentItem: contentItem property color backgroundGradientColorStart: "#222222" property color backgroundGradientColorEnd: "#999999" - property color gridColor: "#aaaaaa" + property color gridColor: "#cccccc" property bool syncBackgroundColor: false enum SelectionMode { Item, Group } @@ -83,13 +83,13 @@ Item { "showGrid": showGrid, "gridColor": gridColor, "importScene": activeScene, - "cameraZoomFactor": cameraControl._zoomFactor, + "cameraLookAt": cameraControl._lookAtPoint, "z": 1}); editView.usePerspective = Qt.binding(function() {return usePerspective;}); editView.showSceneLight = Qt.binding(function() {return showEditLight;}); editView.showGrid = Qt.binding(function() {return showGrid;}); editView.gridColor = Qt.binding(function() {return gridColor;}); - editView.cameraZoomFactor = Qt.binding(function() {return cameraControl._zoomFactor;}); + editView.cameraLookAt = Qt.binding(function() {return cameraControl._lookAtPoint;}); selectionBoxes.length = 0; cameraControl.forceActiveFocus(); @@ -136,6 +136,11 @@ Item { } } + if (syncBackgroundColor) + updateBackgroundColors([_generalHelper.sceneEnvironmentColor(sceneId)]); + else + updateBackgroundColors(_generalHelper.bgColor); + notifyActiveSceneChange(); } } @@ -191,21 +196,14 @@ Item { cameraControl.alignView(cameraNodes); } - function updateViewStates(viewStates) - { - if ("selectBackgroundColor" in viewStates) { - var colors = viewStates.selectBackgroundColor - if (colors.length === 1) { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[0]; - } else { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[1]; - } + function updateBackgroundColors(colors) { + if (colors.length === 1) { + backgroundGradientColorStart = colors[0]; + backgroundGradientColorEnd = colors[0]; + } else { + backgroundGradientColorStart = colors[0]; + backgroundGradientColorEnd = colors[1]; } - - if ("selectGridColor" in viewStates && viewStates.selectGridColor.length === 1) - viewRoot.gridColor = viewStates.selectGridColor[0] } // If resetToDefault is true, tool states not specifically set to anything will be reset to @@ -224,12 +222,13 @@ Item { if ("syncBackgroundColor" in toolStates) { syncBackgroundColor = toolStates.syncBackgroundColor; - if (syncBackgroundColor) { - var color = _generalHelper.sceneEnvironmentColor(sceneId); - updateViewStates({"selectBackgroundColor": color}) - } + if (syncBackgroundColor) + updateBackgroundColors([_generalHelper.sceneEnvironmentColor(sceneId)]); + else + updateBackgroundColors(_generalHelper.bgColor); } else if (resetToDefault) { syncBackgroundColor = false; + updateBackgroundColors(_generalHelper.bgColor); } if ("showSelectionBox" in toolStates) @@ -310,7 +309,7 @@ Item { "geometryName": geometryName}); selectionBoxes[selectionBoxes.length] = box; box.view3D = Qt.binding(function() {return editView;}); - box.visible = Qt.binding(function() {return showSelectionBox;}); + box.showBox = Qt.binding(function() {return showSelectionBox;}); } } } @@ -747,7 +746,8 @@ Item { clipNear: viewRoot.editView ? viewRoot.editView.orthoCamera.clipNear : 1 position: viewRoot.editView ? viewRoot.editView.orthoCamera.position : Qt.vector3d(0, 0, 0) rotation: viewRoot.editView ? viewRoot.editView.orthoCamera.rotation : Qt.quaternion(1, 0, 0, 0) - scale: viewRoot.editView ? viewRoot.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0) + horizontalMagnification: viewRoot.editView ? viewRoot.editView.orthoCamera.horizontalMagnification : 1 + verticalMagnification: viewRoot.editView ? viewRoot.editView.orthoCamera.verticalMagnification : 1 } MouseArea3D { diff --git a/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml b/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml new file mode 100644 index 00000000000..37c23806b2b --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick3D + +CustomMaterial { + property real alphaStartDepth: 500 + property real alphaEndDepth: 40000 + property real generalAlpha: 1 + property color color: "#000000" + property real density: 50 + property bool orthoMode: false + + vertexShader: Qt.resolvedUrl("../shaders/gridmaterial.vert") + fragmentShader: Qt.resolvedUrl("../shaders/gridmaterial.frag") + sourceBlend: CustomMaterial.NoBlend + destinationBlend: CustomMaterial.NoBlend + shadingMode: CustomMaterial.Unshaded + depthDrawMode: Material.AlwaysDepthDraw + cullMode: Material.NoCulling +} diff --git a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml index 399f116cb0d..2a7f921ffe2 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/HelperGrid.qml @@ -8,38 +8,76 @@ import GridGeometry 1.0 Node { id: grid - property alias lines: gridGeometry.lines - property alias step: gridGeometry.step - property alias subdivAlpha: subGridMaterial.opacity - property alias gridColor: mainGridMaterial.diffuseColor + property alias gridColor: mainGridMaterial.color + property double density: 2500 / (gridGeometry.step + gridGeometry.step * (1.0 - subGridMaterial.generalAlpha)) + property bool orthoMode: false + property double distance: 500 + + readonly property int minGridStep: 50 + readonly property int maxGridStep: 32 * minGridStep + readonly property int gridArea: minGridStep * 512 + + // Step of the main lines of the grid, between those is always one subdiv line + property int gridStep: 100 + + // Minimum grid spacing in radians when viewed perpendicularly and lookAt is on origin. + // If spacing would go smaller, gridStep is doubled and line count halved. + // Note that spacing can stay smaller than this after maxGridStep has been reached. + readonly property double minGridRad: 0.1 eulerRotation.x: 90 + function calcRad(step) + { + return Math.atan(step / distance) + } + + onDistanceChanged: { + if (distance === 0) + return + + // Calculate new grid step + let newStep = gridStep + let gridRad = calcRad(newStep) + while (gridRad < minGridRad && newStep < maxGridStep) { + newStep *= 2 + if (newStep > maxGridStep) + newStep = maxGridStep + gridRad = calcRad(newStep) + } + while (gridRad > minGridRad * 2 && newStep > minGridStep) { + newStep /= 2 + if (newStep < minGridStep) + newStep = minGridStep + gridRad = calcRad(newStep) + } + gridStep = newStep + subGridMaterial.generalAlpha = Math.min(1, 2 * (1 - (minGridRad / gridRad))) + } + // Note: Only one instance of HelperGrid is supported, as the geometry names are fixed Model { // Main grid lines readonly property bool _edit3dLocked: true // Make this non-pickable - castsShadows: false - receivesShadows: false geometry: GridGeometry { id: gridGeometry + lines: grid.gridArea / grid.gridStep + step: grid.gridStep name: "3D Edit View Helper Grid" } materials: [ - DefaultMaterial { + GridMaterial { id: mainGridMaterial - diffuseColor: "#aaaaaa" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling + color: "#cccccc" + density: grid.density + orthoMode: grid.orthoMode } ] } Model { // Subdivision lines readonly property bool _edit3dLocked: true // Make this non-pickable - castsShadows: false - receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -48,19 +86,17 @@ Node { } materials: [ - DefaultMaterial { + GridMaterial { id: subGridMaterial - diffuseColor: mainGridMaterial.diffuseColor - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling + color: mainGridMaterial.color + density: grid.density + orthoMode: grid.orthoMode } ] } Model { // Z Axis readonly property bool _edit3dLocked: true // Make this non-pickable - castsShadows: false - receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -68,18 +104,16 @@ Node { name: "3D Edit View Helper Grid Z Axis" } materials: [ - DefaultMaterial { + GridMaterial { id: vCenterLineMaterial - diffuseColor: "#00a1d2" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling + color: "#00a1d2" + density: grid.density + orthoMode: grid.orthoMode } ] } Model { // X Axis readonly property bool _edit3dLocked: true // Make this non-pickable - castsShadows: false - receivesShadows: false eulerRotation.z: 90 geometry: GridGeometry { lines: gridGeometry.lines @@ -88,11 +122,11 @@ Node { name: "3D Edit View Helper Grid X Axis" } materials: [ - DefaultMaterial { + GridMaterial { id: hCenterLineMaterial - diffuseColor: "#cb211a" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling + color: "#cb211a" + density: grid.density + orthoMode: grid.orthoMode } ] } diff --git a/src/tools/qml2puppet/mockfiles/qt6/LightGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/LightGizmo.qml index 9a65ac9c753..bdc9dc2f753 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/LightGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/LightGizmo.qml @@ -36,20 +36,15 @@ Node { || spotLightHandle.dragging || spotLightInnerHandle.dragging || spotLightFadeHandle.dragging - || areaHeightHandle.dragging - || areaWidthHandle.dragging || pointLightFadeHandle.dragging property point currentMousePos property string currentLabel - property int brightnessDecimals: _generalHelper.brightnessScaler() > 10. ? 0 : 2; - property real brightnessMultiplier: Math.pow(10, brightnessDecimals); signal propertyValueCommit(string propName) signal propertyValueChange(string propName) position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0) visible: lightGizmo.targetNode instanceof SpotLight - || lightGizmo.targetNode instanceof AreaLight || lightGizmo.targetNode instanceof DirectionalLight || lightGizmo.targetNode instanceof PointLight @@ -213,65 +208,6 @@ Node { } } } - Node { - id: areaParts - visible: lightGizmo.targetNode instanceof AreaLight - - LightModel { - id: areaModel - geometryName: "Edit 3D AreaLight" - geometryType: LightGeometry.Area - material: lightMaterial - scale: areaParts.visible ? Qt.vector3d(lightGizmo.targetNode.width / 2, - lightGizmo.targetNode.height / 2, 1) - .times(lightGizmo.targetNode.scale) - : Qt.vector3d(1, 1, 1) - } - - AreaLightHandle { - id: areaWidthHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof AreaLight ? Qt.vector3d(-areaModel.scale.x, 0, 0) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(0, 0, 90) - targetNode: lightGizmo.targetNode instanceof AreaLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof AreaLight - dragHelper: lightGizmo.dragHelper - propName: "width" - propValue: lightGizmo.targetNode instanceof AreaLight ? targetNode.width : 0 - - onNewValueChanged: targetNode.width = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: lightGizmo.propertyValueCommit(propName) - } - - AreaLightHandle { - id: areaHeightHandle - view3D: lightGizmo.view3D - color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color - position: lightGizmo.targetNode instanceof AreaLight ? Qt.vector3d(0, -areaModel.scale.y, 0) - : Qt.vector3d(0, 0, 0) - eulerRotation: Qt.vector3d(0, 0, 180) - targetNode: lightGizmo.targetNode instanceof AreaLight ? lightGizmo.targetNode : null - active: lightGizmo.targetNode instanceof AreaLight - dragHelper: lightGizmo.dragHelper - propName: "height" - propValue: lightGizmo.targetNode instanceof AreaLight ? targetNode.height : 0 - - onNewValueChanged: targetNode.height = newValue - onCurrentMousePosChanged: { - lightGizmo.currentMousePos = currentMousePos; - lightGizmo.currentLabel = currentLabel; - } - onValueChange: lightGizmo.propertyValueChange(propName) - onValueCommit: lightGizmo.propertyValueCommit(propName) - } - } LightModel { id: directionalModel @@ -291,19 +227,19 @@ Node { active: lightGizmo.visible dragHelper: lightGizmo.dragHelper scale: autoScaler.getScale(Qt.vector3d(5, 5, 5)) - length: targetNode ? Math.max(1.0, 1.0 + targetNode.brightness / _generalHelper.brightnessScaler() * 10.0) + 3 : 10 + length: targetNode ? Math.max(1.0, 1.0 + targetNode.brightness * 10.0) + 3 : 10 property real _startBrightness function updateBrightness(relativeDistance, screenPos) { - var currentValue = Math.max(0, (_startBrightness + relativeDistance * _generalHelper.brightnessScaler() / 10.0)); - currentValue *= brightnessMultiplier; + var currentValue = Math.max(0, (_startBrightness + relativeDistance / 10.0)); + currentValue *= 100; currentValue = Math.round(currentValue); - currentValue /= brightnessMultiplier; + currentValue /= 100; var l = Qt.locale(); - lightGizmo.currentLabel = "brightness" + qsTr(": ") + Number(currentValue).toLocaleString(l, 'f', brightnessDecimals); + lightGizmo.currentLabel = "brightness" + qsTr(": ") + Number(currentValue).toLocaleString(l, 'f', 2); lightGizmo.currentMousePos = screenPos; targetNode.brightness = currentValue; } diff --git a/src/tools/qml2puppet/mockfiles/qt6/LightIconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/LightIconGizmo.qml index 542482469e0..4b3fb33d7fb 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/LightIconGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/LightIconGizmo.qml @@ -13,10 +13,8 @@ IconGizmo { iconSource: targetNode ? targetNode instanceof DirectionalLight ? "image://IconGizmoImageProvider/directional.png:" + overlayColor - : targetNode instanceof AreaLight - ? "image://IconGizmoImageProvider/area.png:" + overlayColor - : targetNode instanceof PointLight - ? "image://IconGizmoImageProvider/point.png:" + overlayColor - : "image://IconGizmoImageProvider/spot.png:" + overlayColor + : targetNode instanceof PointLight + ? "image://IconGizmoImageProvider/point.png:" + overlayColor + : "image://IconGizmoImageProvider/spot.png:" + overlayColor : "image://IconGizmoImageProvider/point.png:" + overlayColor } diff --git a/src/tools/qml2puppet/mockfiles/qt6/MoveGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/MoveGizmo.qml index fc6e0b6fa4f..b937cbbdb6d 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/MoveGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/MoveGizmo.qml @@ -46,6 +46,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxis: Qt.vector3d(1, 0, 0) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -60,6 +63,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxis: Qt.vector3d(0, 1, 0) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -74,6 +80,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxis: Qt.vector3d(0, 0, 1) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -92,6 +101,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxes: Qt.vector3d(0, 1, 1) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -110,6 +122,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxes: Qt.vector3d(1, 0, 1) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -128,6 +143,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxes: Qt.vector3d(1, 1, 0) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } @@ -147,6 +165,9 @@ Node { active: moveGizmo.visible dragHelper: moveGizmo.dragHelper + dragAxes: Qt.vector3d(1, 1, 1) + globalOrientation: moveGizmo.globalOrientation + onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() } diff --git a/src/tools/qml2puppet/mockfiles/qt6/PlanarMoveHandle.qml b/src/tools/qml2puppet/mockfiles/qt6/PlanarMoveHandle.qml index 6193ec4b8f7..bd4ed634916 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/PlanarMoveHandle.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/PlanarMoveHandle.qml @@ -9,6 +9,9 @@ PlanarDraggable { id: planarHandle scale: Qt.vector3d(0.024, 0.024, 0.024) + property vector3d dragAxes + property bool globalOrientation + signal positionCommit() signal positionMove() @@ -18,6 +21,9 @@ PlanarDraggable { _targetStartPos.x + sceneRelativeDistance.x, _targetStartPos.y + sceneRelativeDistance.y, _targetStartPos.z + sceneRelativeDistance.z); + newScenePos = _generalHelper.adjustTranslationForSnap(newScenePos, _targetStartPos, + dragAxes, globalOrientation, + targetNode); return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; } diff --git a/src/tools/qml2puppet/mockfiles/qt6/RotateRing.qml b/src/tools/qml2puppet/mockfiles/qt6/RotateRing.qml index eac1b0b46cb..914512603af 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/RotateRing.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/RotateRing.qml @@ -49,6 +49,7 @@ Model { currentAngle = mouseAreaMain.dragHelper.getNewRotationAngle( targetNode, _pointerPosPressed, Qt.vector3d(screenPos.x, screenPos.y, 0), _targetPosOnScreen, currentAngle, _trackBall); + currentAngle = _generalHelper.adjustRotationForSnap(currentAngle); mouseAreaMain.dragHelper.applyRotationAngleToNode(targetNode, _startRotation, currentAngle); } diff --git a/src/tools/qml2puppet/mockfiles/qt6/ScaleGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/ScaleGizmo.qml index 225c4e66430..37bc3cc7963 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/ScaleGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/ScaleGizmo.qml @@ -180,6 +180,7 @@ Node { var scaler = 1.0 + (yDelta * 0.025); if (scaler === 0) scaler = 0.0001; + scaler = _generalHelper.adjustScalerForSnap(scaler); return Qt.vector3d(scaler * _startScale.x, scaler * _startScale.y, scaler * _startScale.z); diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 9f3e25d44a2..e3f585de545 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -1,6 +1,7 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +import QtQuick 6.0 import QtQuick3D 6.0 View3D { @@ -14,21 +15,23 @@ View3D { property alias sceneHelpers: sceneHelpers property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera - property double cameraZoomFactor: .55; + property vector3d cameraLookAt - // Empirical cameraZoomFactor values at which the grid zoom level is doubled. The values are - // approximately uniformally distributed over the non-linear range of cameraZoomFactor. - readonly property var grid_thresholds: [0.55, 1.10, 2.35, 4.9, 10.0, 20.5, 42.0, 85.0, 999999.0] - property var thresIdx: 1 - property var thresPerc: 1.0 // percentage of cameraZoomFactor to the current grid zoom threshold (0.0 - 1.0) + // Measuring the distance from camera to lookAt plus the distance of lookAt from grid plane + // gives a reasonable grid spacing in most cases while keeping spacing constant when + // orbiting the camera. + readonly property double cameraDistance: { + if (usePerspective) + return cameraLookAt.minus(camera.position).length() + Math.abs(cameraLookAt.y) + + // Orthocamera should only care about camera magnification, + // as grid will be same size regardless of distance, so setting steps based on distance + // makes no sense. + return 500 / orthoCamera.horizontalMagnification + } camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera - onCameraZoomFactorChanged: { - thresIdx = Math.max(1, grid_thresholds.findIndex(v => v > cameraZoomFactor)); - thresPerc = (grid_thresholds[thresIdx] - cameraZoomFactor) / (grid_thresholds[thresIdx] - grid_thresholds[thresIdx - 1]); - } - environment: sceneEnv SceneEnvironment { id: sceneEnv @@ -41,15 +44,14 @@ View3D { HelperGrid { id: helperGrid - lines: Math.pow(2, grid_thresholds.length - thresIdx - 1); - step: 100 * grid_thresholds[0] * Math.pow(2, thresIdx - 1); - subdivAlpha: thresPerc; + orthoMode: !sceneView.usePerspective + distance: sceneView.cameraDistance } PointLight { id: sceneLight - position: usePerspective ? scenePerspectiveCamera.position - : sceneOrthoCamera.position + position: sceneView.usePerspective ? scenePerspectiveCamera.position + : sceneOrthoCamera.position quadraticFade: 0 linearFade: 0 } @@ -72,7 +74,7 @@ View3D { y: 600 eulerRotation.x: -45 clipFar: 100000 - clipNear: -10000 + clipNear: -100000 } } } diff --git a/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml b/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml index f300fd9502e..fe6cc0af373 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SelectionBox.qml @@ -12,6 +12,7 @@ Node { property Node targetNode: null property alias model: selectionBoxModel property alias geometryName: selectionBoxGeometry.name + property bool showBox: true SelectionBoxGeometry { id: selectionBoxGeometry @@ -31,17 +32,21 @@ Node { position: selectionBox.targetNode ? selectionBox.targetNode.position : Qt.vector3d(0, 0, 0) pivot: selectionBox.targetNode ? selectionBox.targetNode.pivot : Qt.vector3d(0, 0, 0) - visible: selectionBox.targetNode && !selectionBoxGeometry.isEmpty + visible: selectionBox.targetNode castsShadows: false receivesShadows: false - materials: [ - DefaultMaterial { - diffuseColor: "#fff600" - lighting: DefaultMaterial.NoLighting - cullMode: Material.NoCulling - } - ] + DefaultMaterial { + id: boxMaterial + diffuseColor: "#fff600" + lighting: DefaultMaterial.NoLighting + cullMode: Material.NoCulling + } + // We custom set the render node transform for the selectionBox node to correspond + // targetNode's parent transform when we calculate the box geometry. + // This transform gets reset if visibility of the box changes, so instead we hide the box + // by setting null material to it. + materials: selectionBox.showBox && !selectionBoxGeometry.isEmpty ? boxMaterial : null } } diff --git a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag new file mode 100644 index 00000000000..6ec50859028 --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag @@ -0,0 +1,22 @@ +VARYING vec3 pos; +VARYING float worldPos; + +void MAIN() +{ + if (orthoMode) { + // No fadeout in orthographic mode + FRAGCOLOR = vec4(color.xyz, 1); + } else { + vec3 camDir = CAMERA_POSITION - worldPos; + vec3 camLevel = vec3(camDir.x, 0, camDir.z); + float depth; + depth = length(camDir); + float cosAngle = dot(normalize(camDir), normalize(camLevel)); + float angleDepth = density * pow(cosAngle, 8); + float alpha = generalAlpha * clamp((1.0 - ((angleDepth * depth - alphaStartDepth) / (alphaEndDepth - alphaStartDepth))), 0, 1); + if (alpha > 0.01) + FRAGCOLOR = vec4(color.x * alpha, color.y * alpha, color.z * alpha, alpha); + else + discard; + } +} diff --git a/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.vert b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.vert new file mode 100644 index 00000000000..0aa4158ecf3 --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/shaders/gridmaterial.vert @@ -0,0 +1,10 @@ +VARYING vec3 pos; +VARYING vec3 worldPos; + +void MAIN() +{ + pos = VERTEX; + vec4 pos4 = vec4(pos, 1.0); + POSITION = MODELVIEWPROJECTION_MATRIX * pos4; + worldPos = (MODEL_MATRIX * pos4).xyz; +} diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp index 9d5f7221b46..f2eab9f5656 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp @@ -120,16 +120,6 @@ void CameraGeometry::doUpdateGeometry() if (!QQuick3DObjectPrivate::get(m_camera)->spatialNode) { // Doing explicit viewport mapping forces cameraNode creation m_camera->mapToViewport({}, m_viewPortRect.width(), m_viewPortRect.height()); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (!m_nodeCreationUpdateDone) { - // Post-node creation update is done only once to avoid infinite loop in case the node - // creation fails. - m_nodeCreationUpdateDone = true; - m_cameraUpdatePending = true; - update(); - return; - } -#endif } GeometryBase::doUpdateGeometry(); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.h b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.h index d161d8a1aa3..a58ca735af2 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.h @@ -45,9 +45,6 @@ private: QQuick3DCamera *m_camera = nullptr; QRectF m_viewPortRect; bool m_cameraUpdatePending = false; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - bool m_nodeCreationUpdateDone = false; -#endif }; } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri b/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri index 6eedaad8f1f..da7ba93e78c 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri +++ b/src/tools/qml2puppet/qml2puppet/editor3d/editor3d.pri @@ -17,8 +17,3 @@ SOURCES += $$PWD/generalhelper.cpp \ $$PWD/selectionboxgeometry.cpp \ $$PWD/linegeometry.cpp \ $$PWD/icongizmoimageprovider.cpp - -versionAtLeast(QT_VERSION, 6.0.0) { - HEADERS += $$PWD/qt5compat/qquick3darealight_p.h - SOURCES += $$PWD/qt5compat/qquick3darealight.cpp -} diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index bfa2fcb1ae8..47f176b4219 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -6,6 +6,7 @@ #include "selectionboxgeometry.h" +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -154,16 +154,16 @@ float GeneralHelper::zoomCamera([[maybe_unused]] QQuick3DViewport *viewPort, float newZoomFactor = relative ? qBound(.01f, zoomFactor * multiplier, 100.f) : zoomFactor; - if (qobject_cast(camera)) { - // Ortho camera we can simply scale - float orthoFactor = newZoomFactor; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - if (viewPort) { - if (const QQuickWindow *w = viewPort->window()) - orthoFactor *= w->devicePixelRatio(); + if (auto orthoCamera = qobject_cast(camera)) { + // Ortho camera we can simply magnify + if (newZoomFactor != 0.f) { + orthoCamera->setHorizontalMagnification(1.f / newZoomFactor); + orthoCamera->setVerticalMagnification(1.f / newZoomFactor); + // Force update on transform, so gizmos get correctly scaled and positioned + float x = orthoCamera->x(); + orthoCamera->setX(x + 1.f); + orthoCamera->setX(x); } -#endif - camera->setScale(QVector3D(orthoFactor, orthoFactor, orthoFactor)); } else if (qobject_cast(camera)) { // Perspective camera is zoomed by moving camera forward or backward while keeping the // look-at point the same @@ -217,11 +217,7 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul if (window) { #if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); -#else context = targetPriv->sceneManager->rci; -#endif if (!context.isNull()) { #else const auto &sm = targetPriv->sceneManager; @@ -234,11 +230,7 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul bounds = geometry->bounds(); } else { const auto &bufferManager(context->bufferManager()); -#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) - bounds = renderModel->getModelBounds(bufferManager); -#else bounds = bufferManager->getModelBounds(renderModel); -#endif } center = renderModel->globalTransform.map(bounds.center()); @@ -352,8 +344,32 @@ void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) } for (QQuick3DCamera *node : std::as_const(nodeList)) { - node->setPosition(camera->position()); - node->setRotation(camera->rotation()); + QMatrix4x4 parentTransform; + QMatrix4x4 parentRotationTransform; + if (node->parentNode()) { + QMatrix4x4 rotMat; + rotMat.rotate(node->parentNode()->sceneRotation()); + parentRotationTransform = rotMat.inverted(); + parentTransform = node->parentNode()->sceneTransform().inverted(); + } + + QMatrix4x4 localTransform; + localTransform.translate(camera->position()); + localTransform.rotate(camera->rotation()); + + QMatrix4x4 finalTransform = parentTransform * localTransform; + QVector3D newPos = QVector3D(finalTransform.column(3).x(), + finalTransform.column(3).y(), + finalTransform.column(3).z()); + + // Rotation must be calculated with sanitized transform that only contains rotation so + // that the scaling of ancestor nodes won't distort it + QMatrix4x4 finalRotTransform = parentRotationTransform * localTransform; + QMatrix3x3 rotationMatrix = finalRotTransform.toGenericMatrix<3, 3>(); + QQuaternion newRot = QQuaternion::fromRotationMatrix(rotationMatrix).normalized(); + + node->setPosition(newPos); + node->setRotation(newRot); } } @@ -375,8 +391,8 @@ QVector3D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes } if (cameraNode) { - camera->setPosition(cameraNode->position()); - QVector3D newRotation = cameraNode->eulerRotation(); + camera->setPosition(cameraNode->scenePosition()); + QVector3D newRotation = cameraNode->sceneRotation().toEulerAngles(); newRotation.setZ(0.f); camera->setEulerRotation(newRotation); } @@ -405,7 +421,6 @@ QQuick3DPickResult GeneralHelper::pickViewAt(QQuick3DViewport *view, float posX, if (!view) return QQuick3DPickResult(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 1) // Make sure global picking is on view->setGlobalPickingEnabled(true); @@ -415,12 +430,6 @@ QQuick3DPickResult GeneralHelper::pickViewAt(QQuick3DViewport *view, float posX, if (isPickable(pickResult.objectHit())) return pickResult; } -#else - // With older Qt version we'll just pick the single object - auto pickResult = view->pick(posX, posY); - if (isPickable(pickResult.objectHit())) - return pickResult; -#endif return QQuick3DPickResult(); } @@ -461,13 +470,11 @@ bool GeneralHelper::isPickable(QQuick3DNode *node) const if (!node) return false; -#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) // Instancing doesn't hide child nodes, so only check for instancing on the requested node if (auto model = qobject_cast(node)) { if (model->instancing()) return false; } -#endif QQuick3DNode *n = node; while (n) { @@ -543,6 +550,11 @@ QColor GeneralHelper::sceneEnvironmentColor(const QString &sceneId) const return m_sceneEnvironmentColor[sceneId]; } +void GeneralHelper::clearSceneEnvironmentColors() +{ + m_sceneEnvironmentColor.clear(); +} + void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates) { m_toolStates[sceneId] = toolStates; @@ -577,16 +589,6 @@ QString GeneralHelper::rootSizeKey() const return _rootSizeKey; } -double GeneralHelper::brightnessScaler() const -{ - // Light brightness was rescaled in Qt6 from 100 -> 1. -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - return 100.; -#else - return 1.; -#endif -} - void GeneralHelper::setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode, const QVariantList &selectedList) { @@ -770,6 +772,196 @@ bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const return m_rotationBlockedNodes.contains(node); } +// false is returned when keyboard modifiers result in no snapping. +// increment is adjusted according to keyboard modifiers +static bool queryKeyboardForSnapping(bool enabled, double &increment) +{ + if (increment <= 0.) + return false; + + // Need to do a hard query for key mods as puppet is not handling real events + Qt::KeyboardModifiers mods = QGuiApplication::queryKeyboardModifiers(); + const bool shiftMod = mods & Qt::ShiftModifier; + const bool ctrlMod = mods & Qt::ControlModifier; + + if ((!ctrlMod && !enabled) || (ctrlMod && enabled)) + return false; + + if (shiftMod) + increment *= 0.1; + + return true; +} + +QVector3D GeneralHelper::adjustTranslationForSnap(const QVector3D &newPos, + const QVector3D &startPos, + const QVector3D &snapAxes, + bool globalOrientation, + QQuick3DNode *node) +{ + bool snapPos = m_snapPosition; + bool snapAbs = m_snapAbsolute; + double increment = m_snapPositionInterval; + + if (!node || snapAxes.isNull() || qFuzzyIsNull((newPos - startPos).length()) + || !queryKeyboardForSnapping(snapPos, increment)) { + return newPos; + } + + // The node is aligned if there is only 0/90/180/270 degree sceneRotation on the node + // on the drag axis, or the drag plane normal for plane drags + QVector3D mappedSnapAxes = snapAxes; + bool isAligned = globalOrientation; + if (!isAligned) { + isAligned = true; + int axisCount = 0; + QVector3D planeNormal(1.f, 1.f, 1.f); + QVector3D checkAxis; + for (int i = 0; i < 3; ++i) { + if (snapAxes[i] != 0) { + ++axisCount; + checkAxis[i] = snapAxes[i]; + planeNormal[i] = 0.f; + } + } + + // If all 3 axes are snapped, we always use aligned snapping, and we also so not need + // snapAxes remapping + if (axisCount == 1 || axisCount == 2) { + if (axisCount == 2) + checkAxis = planeNormal; + + QMatrix4x4 m; + m.rotate(node->sceneRotation()); + QVector3D rotatedAxis = m.mapVector(checkAxis); + + // If the axis vector is still aligned with any global axis after rotate, + // we can use the aligned math + int ones = 0; + int zeros = 0; + for (int j = 0; j < 3; ++j) { + if (qFuzzyIsNull(rotatedAxis[j])) { + ++zeros; + if (axisCount == 2) + mappedSnapAxes[j] = 1.f; + else + mappedSnapAxes[j] = 0.f; + } else if (qFuzzyCompare(qAbs(rotatedAxis[j]), 1.f)) { + ++ones; + if (axisCount == 1) + mappedSnapAxes[j] = 1.f; + else + mappedSnapAxes[j] = 0.f; + } + } + if (ones != 1 || zeros != 2) + isAligned = false; + } + } + + if (isAligned) { + // When dragging along the global axes, we can snap to grid + auto snapAxis = [&](int axis) -> float { + if (mappedSnapAxes[axis] != 0.f) { + double c = newPos[axis]; + + if (!snapAbs) + c -= startPos[axis]; + + const double snapMult = double(int(c / increment)); + const double comp1 = snapMult * increment; + const double comp2 = c < 0 ? comp1 - increment : comp1 + increment; + c = qAbs(c - comp1) < qAbs(comp2 - c) ? comp1 : comp2; + + if (!snapAbs) + c += startPos[axis]; + + return float(c); + } else { + return newPos[axis]; + } + }; + return QVector3D(snapAxis(0), snapAxis(1), snapAxis(2)); + } else { + // When drag is not aligned along global axes, just snap to interval along the drag vector + QVector3D dragVector = newPos - startPos; + float len = dragVector.length(); + float comp1 = double(int(len / increment)) * increment; + float comp2 = comp1 + increment; + float snapLen = len - comp1 > comp2 - len ? comp2 : comp1; + dragVector.normalize(); + dragVector *= snapLen; + + return startPos + dragVector; + } + return newPos; +} + +// newAngle and return are radians +double GeneralHelper::adjustRotationForSnap(double newAngle) +{ + bool snapRot = m_snapRotation; + double increment = m_snapRotationInterval; + + if (qFuzzyIsNull(newAngle) || !queryKeyboardForSnapping(snapRot, increment)) + return newAngle; + + double angleDeg = qRadiansToDegrees(newAngle); + + double comp1 = double(int(angleDeg / increment)) * increment; + double comp2 = angleDeg > 0 ? comp1 + increment : comp1 - increment; + + return qAbs(angleDeg - comp1) > qAbs(angleDeg - comp2) ? + qDegreesToRadians(comp2) : qDegreesToRadians(comp1); +} + +static double adjustScaler(double newScale, double increment) +{ + double absScale = qAbs(newScale); + double comp1 = 1. + double(int((absScale / increment) - (1. / increment))) * increment; + double comp2 = comp1 + increment; + double retVal = absScale - comp1 > comp2 - absScale ? comp2 : comp1; + if (newScale < 0) + retVal *= -1.; + return retVal; +} + +double GeneralHelper::adjustScalerForSnap(double newScale) +{ + bool snapScale = m_snapScale; + double increment = m_snapScaleInterval; + + if (qFuzzyIsNull(newScale) || !queryKeyboardForSnapping(snapScale, increment)) + return newScale; + + return adjustScaler(newScale, increment); +} + +QVector3D GeneralHelper::adjustScaleForSnap(const QVector3D &newScale) +{ + bool snapScale = m_snapScale; + double increment = m_snapScaleInterval; + + if (qFuzzyIsNull(newScale.length()) || !queryKeyboardForSnapping(snapScale, increment)) + return newScale; + + QVector3D adjScale = newScale; + for (int i = 0; i < 3; ++i) { + if (!qFuzzyCompare(newScale[i], 1.f)) + adjScale[i] = adjustScaler(newScale[i], increment); + } + + return adjScale; +} + +void GeneralHelper::setBgColor(const QVariant &colors) +{ + if (m_bgColor != colors) { + m_bgColor = colors; + emit bgColorChanged(); + } +} + void GeneralHelper::handlePendingToolStateUpdate() { m_toolStateUpdateTimer.stop(); @@ -800,9 +992,8 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const QMatrix4x4 localTransform; localTransform.translate(node->position()); - const QMatrix4x4 sceneTransform = parent->sceneTransform() * localTransform; - - return mat44::getPosition(sceneTransform); + const QMatrix4x4 m = parent->sceneTransform() * localTransform; + return QVector3D(m(0, 3), m(1, 3), m(2, 3)); } // Calculate bounds for given node, including all child nodes. @@ -822,15 +1013,10 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec auto renderNode = static_cast(nodePriv->spatialNode); if (renderNode) { -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) - renderNode->calculateLocalTransform(); -#else if (renderNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty)) { renderNode->localTransform = QSSGRenderNode::calculateTransformMatrix( node->position(), node->scale(), node->pivot(), node->rotation()); } -#endif localTransform = renderNode->localTransform; } @@ -897,11 +1083,7 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec if (window) { #if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); -#else context = QQuick3DObjectPrivate::get(node)->sceneManager->rci; -#endif if (!context.isNull()) { #else const auto &sm = QQuick3DObjectPrivate::get(node)->sceneManager; @@ -909,11 +1091,7 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec if (context) { #endif const auto &bufferManager(context->bufferManager()); -#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) - QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); -#else QSSGBounds3 bounds = bufferManager->getModelBounds(renderModel); -#endif QVector3D center = bounds.center(); QVector3D extents = bounds.extents(); QVector3D localMin = center - extents; diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 089ebf14919..a80ab516ae2 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -32,6 +32,7 @@ class GeneralHelper : public QObject { Q_OBJECT Q_PROPERTY(bool isMacOS READ isMacOS CONSTANT) + Q_PROPERTY(QVariant bgColor READ bgColor NOTIFY bgColorChanged FINAL) public: GeneralHelper(); @@ -81,8 +82,6 @@ public: QString lastSceneIdKey() const; QString rootSizeKey() const; - Q_INVOKABLE double brightnessScaler() const; - Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode, const QVariantList &selectedList); Q_INVOKABLE void resetMultiSelectionNode(); @@ -94,6 +93,7 @@ public: void setSceneEnvironmentColor(const QString &sceneId, const QColor &color); Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; + void clearSceneEnvironmentColors(); bool isMacOS() const; @@ -101,13 +101,33 @@ public: void removeRotationBlocks(const QSet &nodes); Q_INVOKABLE bool isRotationBlocked(QQuick3DNode *node) const; + Q_INVOKABLE QVector3D adjustTranslationForSnap(const QVector3D &newPos, + const QVector3D &startPos, + const QVector3D &snapAxes, + bool globalOrientation, + QQuick3DNode *node); + Q_INVOKABLE double adjustRotationForSnap(double newAngle); + Q_INVOKABLE double adjustScalerForSnap(double newScale); + QVector3D adjustScaleForSnap(const QVector3D &newScale); + + void setSnapAbsolute(bool enable) { m_snapAbsolute = enable; } + void setSnapPosition(bool enable) { m_snapPosition = enable; } + void setSnapRotation(bool enable) { m_snapRotation = enable; } + void setSnapScale(bool enable) { m_snapScale = enable; } + void setSnapPositionInterval(double interval) { m_snapPositionInterval = interval; } + void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } + void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } + + void setBgColor(const QVariant &colors); + QVariant bgColor() const { return m_bgColor; } + signals: void overlayUpdateNeeded(); void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); void hiddenStateChanged(QQuick3DNode *node); void lockedStateChanged(QQuick3DNode *node); void rotationBlocksChanged(); - + void bgColorChanged(); private: void handlePendingToolStateUpdate(); QVector3D pivotScenePosition(QQuick3DNode *node) const; @@ -134,6 +154,16 @@ private: QQuick3DNode *m_multiSelectRootNode = nullptr; QList m_multiSelectConnections; bool m_blockMultiSelectionNodePositioning = false; + + bool m_snapAbsolute = true; + bool m_snapPosition = false; + bool m_snapRotation = false; + bool m_snapScale = false; + double m_snapPositionInterval = 50.; + double m_snapRotationInterval = 5.; + double m_snapScaleInterval = .1; + + QVariant m_bgColor; }; } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.cpp index 8d049d4fb35..0f014c3796c 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.cpp @@ -35,7 +35,6 @@ void GeometryBase::doUpdateGeometry() update(); } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QString GeometryBase::name() const { return objectName(); @@ -46,7 +45,6 @@ void GeometryBase::setName(const QString &name) setObjectName(name); emit nameChanged(); } -#endif void GeometryBase::updateGeometry() { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.h b/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.h index 4b26698fe1c..acab87f13ba 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/geometrybase.h @@ -16,7 +16,6 @@ class GeometryBase : public QQuick3DGeometry { Q_OBJECT -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // Name property was removed in Qt 6, so define it here for compatibility. // Name maps to object name. Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) @@ -25,7 +24,6 @@ public: void setName(const QString &name); signals: void nameChanged(); -#endif public: GeometryBase(); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp index 2e78d84ac82..118227360f7 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -5,17 +5,20 @@ #include "mousearea3d.h" +#include "generalhelper.h" + #include #include #include #include #include -#include #include namespace QmlDesigner { namespace Internal { +static GeneralHelper *s_generalHelper = nullptr; + // Double precision vector for cases where float calculations can suffer from rounding errors class DoubleVec3D { public: @@ -632,6 +635,10 @@ QVector3D MouseArea3D::getNewScale(const QVector3D &startScale, const QVector2D yScaler += axisY * relativeDistance.y() * distanceFactor; scaleVec *= xScaler; scaleVec *= yScaler; + + if (s_generalHelper) + scaleVec = s_generalHelper->adjustScaleForSnap(scaleVec); + return startScale * scaleVec; } @@ -692,8 +699,8 @@ qreal QmlDesigner::Internal::MouseArea3D::getNewRotationAngle( void QmlDesigner::Internal::MouseArea3D::applyRotationAngleToNode( QQuick3DNode *node, const QVector3D &startRotation, qreal angle) { + node->setEulerRotation(startRotation); if (!qFuzzyIsNull(angle)) { - node->setEulerRotation(startRotation); QVector3D normal = getNormal(); node->rotate(qRadiansToDegrees(angle), normal, QQuick3DNode::SceneSpace); } @@ -713,12 +720,14 @@ void MouseArea3D::applyFreeRotation(QQuick3DNode *node, const QVector3D &startRo QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized(); QVector3D finalAxis = (dragVector.x() * yAxis + dragVector.y() * xAxis); - qreal degrees = qRadiansToDegrees(qreal(finalAxis.length()) * mouseDragMultiplier()); + qreal radians = qreal(finalAxis.length()) * mouseDragMultiplier(); + if (s_generalHelper) + radians = s_generalHelper->adjustRotationForSnap(radians); finalAxis.normalize(); node->setEulerRotation(startRotation); - node->rotate(degrees, finalAxis, QQuick3DNode::SceneSpace); + node->rotate(qRadiansToDegrees(radians), finalAxis, QQuick3DNode::SceneSpace); } // Calculate scene position of the node's pivot point, which in practice is just the position @@ -735,9 +744,8 @@ QVector3D MouseArea3D::pivotScenePosition(QQuick3DNode *node) const QMatrix4x4 localTransform; localTransform.translate(node->position()); - const QMatrix4x4 sceneTransform = parent->sceneTransform() * localTransform; - - return mat44::getPosition(sceneTransform); + const QMatrix4x4 m = parent->sceneTransform() * localTransform; + return QVector3D(m(0, 3), m(1, 3), m(2, 3)); } double MouseArea3D::getRelativeScale(QQuick3DNode *node) const @@ -818,6 +826,11 @@ QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper, return sceneTrans.inverted().transform(intersectGlobalPos).toVec3(); } +void QmlDesigner::Internal::MouseArea3D::setGeneralHelper(GeneralHelper *helper) +{ + s_generalHelper = helper; +} + static QPoint getPosFromMoveEvent(QEvent *event) { switch (event->type()) { @@ -877,10 +890,6 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) // a problem onCircle = false; if (m_pickNode) { -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) - QQuick3DPickResult pr = m_view3D->pick(float(mousePos.x()), float(mousePos.y())); - pickSuccess = pr.objectHit() == m_pickNode; -#else // With the introduction of global picking API, // we need to pick all as various other geometries can often be the first // pick result, such as camera frustum or light geometry @@ -892,7 +901,6 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) break; } } -#endif } } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.h b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.h index c0a81a0ee37..8d705c6eb0b 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.h @@ -19,6 +19,8 @@ namespace QmlDesigner { namespace Internal { +class GeneralHelper; + class MouseArea3D : public QQuick3DNode { Q_OBJECT @@ -62,6 +64,7 @@ public: QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const; static qreal mouseDragMultiplier() { return .02; } + static void setGeneralHelper(GeneralHelper *helper); Q_INVOKABLE QVector3D rayIntersectsPlane(const QVector3D &rayPos0, const QVector3D &rayPos1, diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight.cpp deleted file mode 100644 index 3006be15da2..00000000000 --- a/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifdef QUICK3D_MODULE - -#include "qquick3darealight_p.h" -#include - -#include - -namespace QmlDesigner::Internal { - -float QQuick3DAreaLight::width() const -{ - return m_width; -} - -float QQuick3DAreaLight::height() const -{ - return m_height; -} - -void QQuick3DAreaLight::setWidth(float width) -{ - m_width = width; - emit widthChanged(); -} - -void QQuick3DAreaLight::setHeight(float height) -{ - m_height = height; - emit heightChanged(); -} - -} - -#endif diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight_p.h b/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight_p.h deleted file mode 100644 index e82babcf4ea..00000000000 --- a/src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight_p.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#ifdef QUICK3D_MODULE - -// This is a dummy class for Qt 5 compatibility purposes only - -#include - -namespace QmlDesigner::Internal { - -class QQuick3DAreaLight : public QQuick3DAbstractLight -{ - Q_OBJECT - Q_PROPERTY(float width READ width WRITE setWidth NOTIFY widthChanged) - Q_PROPERTY(float height READ height WRITE setHeight NOTIFY heightChanged) - -public: - ~QQuick3DAreaLight() override {} - - float width() const; - float height() const; - -public slots: - void setWidth(float width); - void setHeight(float height); - -signals: - void widthChanged(); - void heightChanged(); - -private: - float m_width = 100.0f; - float m_height = 100.0f; -}; - -} - -QML_DECLARE_TYPE(QmlDesigner::Internal::QQuick3DAreaLight) - -#endif diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp index 1157b1c5f84..64c3c25a2ab 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp @@ -195,11 +195,7 @@ void SelectionBoxGeometry::doUpdateGeometry() m = targetRN->parent->globalTransform; } rootRN->localTransform = m; -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); -#else rootRN->markDirty(QSSGRenderNode::DirtyFlag::TransformDirty); -#endif rootRN->calculateGlobalVariables(); } else if (!m_spatialNodeUpdatePending) { // Necessary spatial nodes do not yet exist. Defer selection box creation one frame. @@ -243,15 +239,10 @@ void SelectionBoxGeometry::getBounds( if (node != m_targetNode) { if (renderNode) { -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) - renderNode->calculateLocalTransform(); -#else if (renderNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty)) { renderNode->localTransform = QSSGRenderNode::calculateTransformMatrix( node->position(), node->scale(), node->pivot(), node->rotation()); } -#endif localTransform = renderNode->localTransform; } trackNodeChanges(node); @@ -314,11 +305,7 @@ void SelectionBoxGeometry::getBounds( if (window) { #if QT_VERSION < QT_VERSION_CHECK(6, 5, 1) QSSGRef context; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); -#elif QT_VERSION < QT_VERSION_CHECK(6, 5, 1) context = QQuick3DObjectPrivate::get(this)->sceneManager->rci; -#endif if (!context.isNull()) { #else const auto &sm = QQuick3DObjectPrivate::get(this)->sceneManager; @@ -326,11 +313,7 @@ void SelectionBoxGeometry::getBounds( if (context) { #endif const auto &bufferManager(context->bufferManager()); -#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) - QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); -#else QSSGBounds3 bounds = bufferManager->getModelBounds(renderModel); -#endif QVector3D center = bounds.center(); QVector3D extents = bounds.extents(); QVector3D localMin = center - extents; diff --git a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.cpp b/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.cpp deleted file mode 100644 index 71e73026d1c..00000000000 --- a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.cpp +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "iconrenderer.h" - -#include "../editor3d/selectionboxgeometry.h" -#include "../editor3d/generalhelper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef QUICK3D_MODULE -#include -#include -#endif - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -#include -#include -#include -#endif - -#include - -IconRenderer::IconRenderer(int size, const QString &filePath, const QString &source) - : QObject(nullptr) - , m_size(size) - , m_filePath(filePath) - , m_source(source) -{ -} - -IconRenderer::~IconRenderer() = default; - -void IconRenderer::setupRender() -{ - QQuickDesignerSupport::activateDesignerMode(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DesignerSupport::activateDesignerWindowManager(); -#endif - - QQmlEngine *engine = nullptr; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - auto view = new QQuickView; - engine = view->engine(); - m_window = view; - QSurfaceFormat surfaceFormat = view->requestedFormat(); - surfaceFormat.setVersion(4, 1); - surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); - view->setFormat(surfaceFormat); - DesignerSupport::createOpenGLContext(view); -#else - engine = new QQmlEngine; - m_renderControl = new QQuickRenderControl; - m_window = new QQuickWindow(m_renderControl); - m_window->setDefaultAlphaBuffer(true); - m_window->setColor(Qt::transparent); - m_renderControl->initialize(); -#endif - - QQmlComponent component(engine); - component.loadUrl(QUrl::fromLocalFile(m_source)); - QObject *iconItem = component.create(); - - if (iconItem) { -#ifdef QUICK3D_MODULE - if (auto scene = qobject_cast(iconItem)) { - qmlRegisterType("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry"); - QQmlComponent component(engine); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt5/IconRenderer3D.qml")); - m_containerItem = qobject_cast(component.create()); - DesignerSupport::setRootItem(view, m_containerItem); -#else - component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/IconRenderer3D.qml")); - m_containerItem = qobject_cast(component.create()); - m_window->contentItem()->setSize(m_containerItem->size()); - m_window->setGeometry(0, 0, m_containerItem->width(), m_containerItem->height()); - m_containerItem->setParentItem(m_window->contentItem()); -#endif - - auto helper = new QmlDesigner::Internal::GeneralHelper(); - engine->rootContext()->setContextProperty("_generalHelper", helper); - - m_contentItem = QQmlProperty::read(m_containerItem, "view3D").value(); - auto view3D = qobject_cast(m_contentItem); - view3D->setImportScene(scene); - m_is3D = true; - } else -#endif - if (auto scene = qobject_cast(iconItem)) { - m_contentItem = scene; - m_containerItem = new QQuickItem(); - m_containerItem->setSize(QSizeF(1024, 1024)); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DesignerSupport::setRootItem(view, m_containerItem); -#else - m_window->contentItem()->setSize(m_containerItem->size()); - m_window->setGeometry(0, 0, m_containerItem->width(), m_containerItem->height()); - m_containerItem->setParentItem(m_window->contentItem()); -#endif - m_contentItem->setParentItem(m_containerItem); - } - - if (m_containerItem && m_contentItem) { - resizeContent(m_size); - if (!initRhi()) - QTimer::singleShot(0, qGuiApp, &QGuiApplication::quit); - QTimer::singleShot(0, this, &IconRenderer::startCreateIcon); - } else { - QTimer::singleShot(0, qGuiApp, &QGuiApplication::quit); - } - } else { - QTimer::singleShot(0, qGuiApp, &QGuiApplication::quit); - } -} - -void IconRenderer::startCreateIcon() -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - m_designerSupport->refFromEffectItem(m_containerItem, false); -#endif - QQuickDesignerSupportItems::disableNativeTextRendering(m_containerItem); - - if (m_is3D) - QTimer::singleShot(0, this, &IconRenderer::focusCamera); - else - QTimer::singleShot(0, this, &IconRenderer::finishCreateIcon); -} - -void IconRenderer::focusCamera() -{ -#ifdef QUICK3D_MODULE - if (m_focusStep >= 10) { - QTimer::singleShot(0, this, &IconRenderer::finishCreateIcon); - return; - } - - render({}); - - if (m_focusStep == 0) { - QMetaObject::invokeMethod(m_containerItem, "setSceneToBox"); - } else if (m_focusStep > 1 && m_focusStep < 10) { - QMetaObject::invokeMethod(m_containerItem, "fitAndHideBox"); - } - ++m_focusStep; - QTimer::singleShot(0, this, &IconRenderer::focusCamera); -#endif -} - -void IconRenderer::finishCreateIcon() -{ - QFileInfo fi(m_filePath); - - // Render regular size image - render(fi.absoluteFilePath()); - - // Render @2x image - resizeContent(m_size * 2); - if (!initRhi()) - QTimer::singleShot(1000, qGuiApp, &QGuiApplication::quit); - - QString saveFile; - saveFile = fi.absolutePath() + '/' + fi.completeBaseName() + "@2x"; - if (!fi.suffix().isEmpty()) - saveFile += '.' + fi.suffix(); - - fi.absoluteDir().mkpath("."); - - render(saveFile); - - QTimer::singleShot(0, qGuiApp, &QGuiApplication::quit); -} - -void IconRenderer::render(const QString &fileName) -{ - std::function updateNodesRecursive; - updateNodesRecursive = [&updateNodesRecursive](QQuickItem *item) { - const auto childItems = item->childItems(); - for (QQuickItem *childItem : childItems) - updateNodesRecursive(childItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DesignerSupport::updateDirtyNode(item); -#else - if (item->flags() & QQuickItem::ItemHasContents) - item->update(); -#endif - }; - updateNodesRecursive(m_containerItem); - - QRect rect(QPoint(), m_contentItem->size().toSize()); - QImage renderImage; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - renderImage = m_designerSupport->renderImageForItem(m_containerItem, rect, rect.size()); -#else - m_renderControl->polishItems(); - m_renderControl->beginFrame(); - m_renderControl->sync(); - m_renderControl->render(); - - bool readCompleted = false; - QRhiReadbackResult readResult; - readResult.completed = [&] { - readCompleted = true; - QImage wrapperImage(reinterpret_cast(readResult.data.constData()), - readResult.pixelSize.width(), readResult.pixelSize.height(), - QImage::Format_RGBA8888_Premultiplied); - if (m_rhi->isYUpInFramebuffer()) - renderImage = wrapperImage.mirrored().copy(0, 0, rect.width(), rect.height()); - else - renderImage = wrapperImage.copy(0, 0, rect.width(), rect.height()); - }; - QRhiResourceUpdateBatch *readbackBatch = m_rhi->nextResourceUpdateBatch(); - readbackBatch->readBackTexture(m_texture, &readResult); - - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl); - rd->cb->resourceUpdate(readbackBatch); - - m_renderControl->endFrame(); -#endif - if (!fileName.isEmpty()) { - QFileInfo fi(fileName); - if (fi.suffix().isEmpty()) - renderImage.save(fileName, "PNG"); - else - renderImage.save(fileName); - } -} - -void IconRenderer::resizeContent(int dimensions) -{ - QSizeF size(dimensions, dimensions); - m_contentItem->setSize(size); - if (m_contentItem->width() > m_containerItem->width()) - m_containerItem->setWidth(m_contentItem->width()); - if (m_contentItem->height() > m_containerItem->height()) - m_containerItem->setHeight(m_contentItem->height()); -} - -bool IconRenderer::initRhi() -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - if (!m_rhi) { - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl); - m_rhi = rd->rhi; - if (!m_rhi) { - qWarning() << __FUNCTION__ << "Rhi is null"; - return false; - } - } - - // Don't bother deleting old ones as iconrender is a single shot executable - m_texTarget = nullptr; - m_rpDesc = nullptr; - m_buffer = nullptr; - m_texture = nullptr; - - const QSize size = m_containerItem->size().toSize(); - m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, - QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); - if (!m_texture->create()) { - qWarning() << __FUNCTION__ << "QRhiTexture creation failed"; - return false; - } - - m_buffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, 1); - if (!m_buffer->create()) { - qWarning() << __FUNCTION__ << "Depth/stencil buffer creation failed"; - return false; - } - - QRhiTextureRenderTargetDescription rtDesc {QRhiColorAttachment(m_texture)}; - rtDesc.setDepthStencilBuffer(m_buffer); - m_texTarget = m_rhi->newTextureRenderTarget(rtDesc); - m_rpDesc = m_texTarget->newCompatibleRenderPassDescriptor(); - m_texTarget->setRenderPassDescriptor(m_rpDesc); - if (!m_texTarget->create()) { - qWarning() << __FUNCTION__ << "Texture render target creation failed"; - return false; - } - - // redirect Qt Quick rendering into our texture - m_window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_texTarget)); -#endif - return true; -} diff --git a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.h b/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.h deleted file mode 100644 index f76f49dbed0..00000000000 --- a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -#include - -QT_BEGIN_NAMESPACE -class QQuickWindow; -class QQuickItem; -class QQuickDesignerSupport; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -class QQuickRenderControl; -class QRhi; -class QRhiTexture; -class QRhiRenderBuffer; -class QRhiTextureRenderTarget; -class QRhiRenderPassDescriptor; -#endif -QT_END_NAMESPACE - -class IconRenderer : public QObject -{ - Q_OBJECT - -public: - explicit IconRenderer(int size, const QString &filePath, const QString &source); - ~IconRenderer(); - - void setupRender(); - -private: - void startCreateIcon(); - void focusCamera(); - void finishCreateIcon(); - void render(const QString &fileName); - void resizeContent(int dimensions); - bool initRhi(); - - int m_size = 16; - QString m_filePath; - QString m_source; - QQuickWindow *m_window = nullptr; - QQuickItem *m_contentItem = nullptr; - QQuickItem *m_containerItem = nullptr; - std::unique_ptr m_designerSupport; - bool m_is3D = false; - int m_focusStep = 0; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QQuickRenderControl *m_renderControl = nullptr; - QRhi *m_rhi = nullptr; - QRhiTexture *m_texture = nullptr; - QRhiRenderBuffer *m_buffer = nullptr; - QRhiTextureRenderTarget *m_texTarget = nullptr; - QRhiRenderPassDescriptor *m_rpDesc = nullptr; -#endif -}; diff --git a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.pri b/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.pri deleted file mode 100644 index 59d5b12cee1..00000000000 --- a/src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.pri +++ /dev/null @@ -1,3 +0,0 @@ -HEADERS += $$PWD/iconrenderer.h - -SOURCES += $$PWD/iconrenderer.cpp diff --git a/src/tools/qml2puppet/qml2puppet/import3d/import3d.cpp b/src/tools/qml2puppet/qml2puppet/import3d/import3d.cpp index 67f16241bef..962fa8c4291 100644 --- a/src/tools/qml2puppet/qml2puppet/import3d/import3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/import3d/import3d.cpp @@ -32,11 +32,7 @@ void import3D([[maybe_unused]] const QString &sourceAsset, if (!optDoc.isNull() && optDoc.isObject()) { QJsonObject optObj = optDoc.object(); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) const auto &optionsMap = optObj; -#else - const auto optionsMap = optObj.toVariantMap(); -#endif // QT_VERSION >= 6.4.0 if (importer->importFile(sourceAsset, outDir, optionsMap, &errorStr) != QSSGAssetImportManager::ImportState::Success) { } diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp index 3d6445e2404..46a8c605320 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -19,9 +19,7 @@ #include -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #include -#endif #include #include @@ -728,7 +726,6 @@ void NodeInstanceServer::setupMockupTypes(const QVector &co { for (const MockupTypeContainer &mockupType : container) { if (!isTypeAvailable(mockupType, engine())) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) if (mockupType.majorVersion() == -1 && mockupType.minorVersion() == -1) { QQuickDesignerSupportMetaInfo::registerMockupObject(mockupType.importUri().toUtf8(), 1, @@ -740,13 +737,6 @@ void NodeInstanceServer::setupMockupTypes(const QVector &co mockupType.minorVersion(), mockupType.typeName()); } -#else - qmlRegisterType(QUrl("qrc:/qtquickplugin/mockfiles/GenericBackend.qml"), - mockupType.importUri().toUtf8(), - mockupType.majorVersion(), - mockupType.minorVersion(), - mockupType.typeName()); -#endif } } } @@ -1412,10 +1402,8 @@ void NodeInstanceServer::loadDummyContextObjectFile(const QFileInfo &qmlFileInfo void NodeInstanceServer::setTranslationLanguage(const QString &language) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) // if there exists an /i18n directory it sets default translators engine()->setUiLanguage(language); -#endif static QPointer multilanguageTranslator; if (!MultiLanguage::databaseFilePath().isEmpty() && QFileInfo::exists(QString::fromUtf8(MultiLanguage::databaseFilePath()))) { diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h index f4eee1e8745..65542dedc25 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h @@ -40,11 +40,7 @@ namespace QtHelpers { template QListtoList(const QSet &set) { -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - return set.toList(); -#else return QList(set.begin(), set.end()); -#endif } } // QtHelpers diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 74a45b92b6e..e66a55e82f2 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -282,11 +282,7 @@ static void removeObjectFromList(const QQmlProperty &property, QObject *objectToBeRemoved, [[maybe_unused]] QQmlEngine *engine) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) QQmlListReference listReference(property.object(), property.name().toUtf8()); -#else - QQmlListReference listReference(property.object(), property.name().toUtf8(), engine); -#endif if (!QmlPrivateGate::hasFullImplementedListInterface(listReference)) { qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!"; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qmlstatenodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/qmlstatenodeinstance.cpp index a62246b4a4a..5357e57ae6d 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qmlstatenodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qmlstatenodeinstance.cpp @@ -34,14 +34,12 @@ QmlStateNodeInstance::Pointer void setAllNodesDirtyRecursive([[maybe_unused]] QQuickItem *parentItem) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!parentItem) return; const QList children = parentItem->childItems(); for (QQuickItem *childItem : children) setAllNodesDirtyRecursive(childItem); QQuickDesignerSupport::addDirty(parentItem, QQuickDesignerSupport::Content); -#endif } void QmlStateNodeInstance::activateState() diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c5b3c184838..663adcf3e17 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -68,9 +68,7 @@ #include #include -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include -#endif #ifdef QUICK3D_MODULE #include @@ -81,10 +79,7 @@ #include #include #include -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -#include "../editor3d/qt5compat/qquick3darealight_p.h" -#endif -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) +#if defined(QUICK3D_ASSET_UTILS_MODULE) #include #endif #endif @@ -172,16 +167,10 @@ static QList toPropertyNameList(const QVariant &variantList) void Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - viewData.window = new QQuickView(quickView()->engine(), quickView()); - viewData.window->setFormat(quickView()->format()); - QQuickDesignerSupport::createOpenGLContext(static_cast(viewData.window.data())); -#else viewData.renderControl = new QQuickRenderControl; viewData.window = new QQuickWindow(viewData.renderControl); setPipelineCacheConfig(viewData.window); viewData.renderControl->initialize(); -#endif QQmlComponent component(engine()); component.loadUrl(url); viewData.rootItem = qobject_cast(component.create()); @@ -191,13 +180,9 @@ void Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url, return; } -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QQuickDesignerSupport::setRootItem(static_cast(viewData.window.data()), viewData.rootItem); -#else viewData.window->contentItem()->setSize(viewData.rootItem->size()); viewData.window->setGeometry(0, 0, viewData.rootItem->width(), viewData.rootItem->height()); viewData.rootItem->setParentItem(viewData.window->contentItem()); -#endif } void Qt5InformationNodeInstanceServer::updateLockedAndHiddenStates(const QSet &instances) @@ -237,16 +222,10 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() } } QWheelEvent *we -#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) = new QWheelEvent(command.pos(), command.pos(), {0, 0}, {0, angleDelta + command.angleDelta()}, command.buttons(), command.modifiers(), Qt::NoScrollPhase, false); -#else - = new QWheelEvent(command.pos(), command.pos(), {0, 0}, {0, command.angleDelta()}, - 0, Qt::Horizontal, command.buttons(), command.modifiers(), - Qt::NoScrollPhase, Qt::MouseEventNotSynthesized); -#endif angleDelta = 0; QGuiApplication::sendEvent(m_editView3DData.window, we); } else if (command.type() == QEvent::KeyPress || command.type() == QEvent::KeyRelease) { @@ -289,17 +268,7 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() #ifdef IMPORT_QUICK3D_ASSETS QSSGAssetImportManager importManager; const QHash supportedExtensions = importManager.getSupportedExtensions(); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) -#define AS_VARIANT_MAP(IT) IT.value().toVariantMap() - using PluginOptionMaps = QSSGAssetImportManager::PluginOptionMaps; -#else -#define AS_VARIANT_MAP(IT) IT.value() - using PluginOptionMaps = QHash; -#endif // QT_VERSION >= 6.4.0 - - const PluginOptionMaps supportedOptions = importManager.getAllOptions(); - - QVariantMap supportMap; + const QSSGAssetImportManager::PluginOptionMaps supportedOptions = importManager.getAllOptions(); QVariantMap extMap; auto itExt = supportedExtensions.constBegin(); @@ -311,10 +280,11 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() QVariantMap optMap; auto itOpt = supportedOptions.constBegin(); while (itOpt != supportedOptions.constEnd()) { - optMap.insert(itOpt.key(), AS_VARIANT_MAP(itOpt)); + optMap.insert(itOpt.key(), itOpt.value().toVariantMap()); ++itOpt; } + QVariantMap supportMap; supportMap.insert("options", optMap); supportMap.insert("extensions", extMap); nodeInstanceClient()->handlePuppetToCreatorCommand( @@ -368,6 +338,52 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks( #endif } +void Qt5InformationNodeInstanceServer::updateSnapSettings( + [[maybe_unused]] const QVector &valueChanges) +{ +#ifdef QUICK3D_MODULE + auto helper = qobject_cast(m_3dHelper); + if (helper) { + for (const auto &container : valueChanges) { + if (container.name() == "snapPos3d") + helper->setSnapPosition(container.value().toBool()); + else if (container.name() == "snapPosInt3d") + helper->setSnapPositionInterval(container.value().toDouble()); + else if (container.name() == "snapRot3d") + helper->setSnapRotation(container.value().toBool()); + else if (container.name() == "snapRotInt3d") + helper->setSnapRotationInterval(container.value().toDouble()); + else if (container.name() == "snapScale3d") + helper->setSnapScale(container.value().toBool()); + else if (container.name() == "snapScaleInt3d") + helper->setSnapScaleInterval(container.value().toDouble()); + else if (container.name() == "snapAbs3d") + helper->setSnapAbsolute(container.value().toBool()); + } + } +#endif +} + +void Qt5InformationNodeInstanceServer::updateColorSettings( + [[maybe_unused]] const QVector &valueChanges) +{ +#ifdef QUICK3D_MODULE + if (m_editView3DData.rootItem) { + for (const auto &container : valueChanges) { + if (container.name() == "edit3dGridColor") { + QQmlProperty gridProp(m_editView3DData.rootItem, "gridColor", context()); + gridProp.write(container.value()); + } else if (container.name() == "edit3dBgColor") { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateBackgroundColors", + Q_ARG(QVariant, container.value())); + if (auto helper = qobject_cast(m_3dHelper)) + helper->setBgColor(container.value()); + } + } + } +#endif +} + void Qt5InformationNodeInstanceServer::removeRotationBlocks( [[maybe_unused]] const QVector &instanceIds) { @@ -388,7 +404,7 @@ void Qt5InformationNodeInstanceServer::removeRotationBlocks( #endif } -void Qt5InformationNodeInstanceServer::getNodeAtPos(const QPointF &pos) +void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPointF &pos) { #ifdef QUICK3D_MODULE // pick a Quick3DModel at view position @@ -437,8 +453,6 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos(const QPointF &pos) data.append(pos3d); nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, QVariant::fromValue(data)}); -#else - Q_UNUSED(pos) #endif } @@ -454,9 +468,6 @@ void Qt5InformationNodeInstanceServer::createEditView3D() qmlRegisterType("GridGeometry", 1, 0, "GridGeometry"); qmlRegisterType("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry"); qmlRegisterType("LineGeometry", 1, 0, "LineGeometry"); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - qmlRegisterType("LightUtils", 1, 0, "AreaLight"); -#endif auto helper = new QmlDesigner::Internal::GeneralHelper(); QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged, @@ -465,12 +476,9 @@ void Qt5InformationNodeInstanceServer::createEditView3D() engine()->addImageProvider(QLatin1String("IconGizmoImageProvider"), new QmlDesigner::Internal::IconGizmoImageProvider); m_3dHelper = helper; + Internal::MouseArea3D::setGeneralHelper(helper); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt6/EditView3D.qml"), m_editView3DData); -#else - createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt5/EditView3D.qml"), m_editView3DData); -#endif if (m_editView3DData.rootItem) helper->setParent(m_editView3DData.rootItem); #endif @@ -482,9 +490,7 @@ void Qt5InformationNodeInstanceServer::resetParticleSystem() if (!m_targetParticleSystem) return; m_targetParticleSystem->reset(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2) m_targetParticleSystem->setEditorTime(0); -#endif if (m_particleAnimationDriver) m_particleAnimationDriver->reset(); } @@ -510,13 +516,12 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DPart // Ensure clean slate for newly selected system resetParticleSystem(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2) QObject::disconnect(m_particleAnimationConnection); m_particleAnimationConnection = connect(m_particleAnimationDriver, &AnimationDriver::advanced, [this] () { if (m_targetParticleSystem) m_targetParticleSystem->setEditorTime(m_particleAnimationDriver->elapsed()); }); -#endif + if (m_particleAnimationPlaying && m_targetParticleSystem->visible()) m_particleAnimationDriver->restart(); QObject::connect(m_targetParticleSystem, &QQuick3DNode::visibleChanged, [this] () { @@ -878,12 +883,6 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D([[maybe_unu return; QVariant activeSceneVar = objectToVariant(m_active3DScene); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // Active scene change handling on qml side is async, so a deleted importScene would crash - // editView when it updates next. Disable/enable edit view update synchronously to avoid this. - QMetaObject::invokeMethod(m_editView3DData.rootItem, "enableEditViewUpdate", - Q_ARG(QVariant, activeSceneVar)); -#endif ServerNodeInstance sceneInstance = active3DSceneInstance(); const QString sceneId = sceneInstance.id(); @@ -917,20 +916,8 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D([[maybe_unu updateView3DRect(m_active3DView); - auto helper = qobject_cast(m_3dHelper); - if (helper) { + if (auto helper = qobject_cast(m_3dHelper)) helper->storeToolState(helper->globalStateId(), helper->lastSceneIdKey(), QVariant(sceneId), 0); - QVariantMap toolStates = helper->getToolStates(sceneId); - if (toolStates.contains("syncBackgroundColor")) { - bool sync = toolStates["syncBackgroundColor"].toBool(); - if (sync) { - QList colors{helper->sceneEnvironmentColor(sceneId)}; - View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, - QVariant::fromValue(colors)); - view3DAction(cmd); - } - } - } #endif } @@ -1010,6 +997,9 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots() } ++it; } + + updateSceneEnvColorsToHelper(); + if (updateActiveScene) { m_active3DView = findView3DForSceneRoot(m_active3DScene); updateActiveSceneToEditView3D(); @@ -1033,22 +1023,15 @@ void Qt5InformationNodeInstanceServer::updateNodesRecursive(QQuickItem *item) for (QQuickItem *childItem : childItems) updateNodesRecursive(childItem); - if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { - if (item->flags() & QQuickItem::ItemHasContents) - item->update(); - } else { - QQuickDesignerSupport::updateDirtyNode(item); - } + if (item->flags() & QQuickItem::ItemHasContents) + item->update(); } QQuickItem *Qt5InformationNodeInstanceServer::getContentItemForRendering(QQuickItem *rootItem) { QQuickItem *contentItem = QQmlProperty::read(rootItem, "contentItem").value(); - if (contentItem) { - if (!Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) - designerSupport()->refFromEffectItem(contentItem, false); + if (contentItem) QmlDesigner::Internal::QmlPrivateGate::disableNativeTextRendering(contentItem); - } return contentItem; } @@ -1070,22 +1053,6 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView() updateNodesRecursive(m_editView3DData.contentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - renderImage = m_editView3DData.window->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - m_editView3DData.window->beforeSynchronizing(); - m_editView3DData.window->beforeRendering(); - - QSizeF size = qobject_cast(m_editView3DData.contentItem)->size(); - QRectF renderRect(QPointF(0., 0.), size); - renderImage = designerSupport()->renderImageForItem(m_editView3DData.contentItem, - renderRect, size.toSize()); - - m_editView3DData.window->afterRendering(); - } -#else #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) static bool justOnce = true; if (justOnce) { @@ -1094,7 +1061,6 @@ void Qt5InformationNodeInstanceServer::doRender3DEditView() } #endif renderImage = grabRenderControl(m_editView3DData); -#endif // There's no instance related to image, so instance id is -1. // Key number is selected so that it is unlikely to conflict other ImageContainer use. @@ -1177,8 +1143,6 @@ void Qt5InformationNodeInstanceServer::renderModelNodeImageView() void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView() { - // This crashes on Qt 6.0.x due to QtQuick3D issue, so the preview generation is disabled -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) if (!m_priorityView3DsToRender.isEmpty()) { // Postpone any preview renders until we have rendered the priority views to ensure // materials in material library are properly initialized @@ -1201,7 +1165,6 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView() m_modelNodePreviewImageCommands.remove(cmd); if (!m_modelNodePreviewImageCommands.isEmpty()) m_renderModelNodeImageViewTimer.start(17); -#endif } void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView( @@ -1237,18 +1200,15 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView( instanceObj = instance.internalObject(); } QSize renderSize = cmd.size(); - if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { - // Requested size is already adjusted for target pixel ratio, so we have to adjust - // back if ratio is not default for our window. - double ratio = m_modelNode3DImageViewData.window->devicePixelRatio(); - renderSize.setWidth(qRound(qreal(renderSize.width()) / ratio)); - renderSize.setHeight(qRound(qreal(renderSize.height()) / ratio)); - } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Requested size is already adjusted for target pixel ratio, so we have to adjust + // back if ratio is not default for our window. + double ratio = m_modelNode3DImageViewData.window->devicePixelRatio(); + renderSize.setWidth(qRound(qreal(renderSize.width()) / ratio)); + renderSize.setHeight(qRound(qreal(renderSize.height()) / ratio)); + m_modelNode3DImageViewData.bufferDirty = m_modelNode3DImageViewData.bufferDirty || m_modelNode3DImageViewData.rootItem->width() != renderSize.width() || m_modelNode3DImageViewData.rootItem->height() != renderSize.height(); -#endif m_modelNode3DImageViewData.window->resize(renderSize); m_modelNode3DImageViewData.rootItem->setSize(renderSize); @@ -1273,23 +1233,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView( , Qt::DirectConnection); updateNodesRecursive(m_modelNode3DImageViewData.contentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - renderImage = m_modelNode3DImageViewData.window->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - m_modelNode3DImageViewData.window->beforeSynchronizing(); - m_modelNode3DImageViewData.window->beforeRendering(); - - QSizeF size = qobject_cast(m_modelNode3DImageViewData.contentItem)->size(); - QRectF renderRect(QPointF(0., 0.), size); - renderImage = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem, - renderRect, size.toSize()); - m_modelNode3DImageViewData.window->afterRendering(); - } -#else renderImage = grabRenderControl(m_modelNode3DImageViewData); -#endif } QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); @@ -1373,11 +1317,9 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView(const Reques renderRect = QRectF(QPointF(0., 0.), QSizeF(renderSize)); } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_modelNode2DImageViewData.bufferDirty = m_modelNode2DImageViewData.bufferDirty || m_modelNode2DImageViewData.rootItem->width() != renderSize.width() || m_modelNode2DImageViewData.rootItem->height() != renderSize.height(); -#endif m_modelNode2DImageViewData.window->resize(renderSize); m_modelNode2DImageViewData.rootItem->setSize(renderSize); @@ -1385,16 +1327,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView(const Reques updateNodesRecursive(m_modelNode2DImageViewData.contentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - renderImage = m_modelNode2DImageViewData.window->grabWindow(); - } else { - renderImage = designerSupport()->renderImageForItem(m_modelNode2DImageViewData.contentItem, - renderRect, renderSize); - } -#else - renderImage = grabRenderControl(m_modelNode2DImageViewData); -#endif + renderImage = grabRenderControl(m_modelNode2DImageViewData); if (!imageHasContent(renderImage)) renderImage = nonVisualComponentPreviewImage(); @@ -1467,15 +1400,6 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer() if (m_editView3DData.rootItem) QMetaObject::invokeMethod(m_editView3DData.rootItem, "aboutToShutDown", Qt::DirectConnection); - - if (!Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { - if (m_editView3DData.contentItem) - designerSupport()->derefFromEffectItem(m_editView3DData.contentItem); - if (m_modelNode3DImageViewData.contentItem) - designerSupport()->derefFromEffectItem(m_modelNode3DImageViewData.contentItem); - if (m_modelNode2DImageViewData.contentItem) - designerSupport()->derefFromEffectItem(m_modelNode2DImageViewData.contentItem); - } } void Qt5InformationNodeInstanceServer::sendTokenBack() @@ -1569,22 +1493,12 @@ void Qt5InformationNodeInstanceServer::initializeAuxiliaryViews() #ifdef QUICK3D_MODULE if (ViewConfig::isQuick3DMode()) createEditView3D(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml"), m_modelNode3DImageViewData); -#else - createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt5/ModelNode3DImageView.qml"), - m_modelNode3DImageViewData); -#endif #endif -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode2DImageView.qml"), m_modelNode2DImageViewData); -#else - createAuxiliaryQuickView(QUrl("qrc:/qtquickplugin/mockfiles/qt5/ModelNode2DImageView.qml"), - m_modelNode2DImageViewData); -#endif m_modelNode2DImageViewData.window->setDefaultAlphaBuffer(true); m_modelNode2DImageViewData.window->setColor(Qt::transparent); } @@ -1603,24 +1517,6 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout() void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() { for (auto obj : std::as_const(m_dynamicObjectConstructors)) { -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) -#ifdef QUICK3D_MODULE - auto handleHiding = [this](QQuick3DNode *node) -> bool { - if (node && hasInstanceForObject(node)) { - ServerNodeInstance instance = instanceForObject(node); - handleInstanceHidden(instance, instance.internalInstance()->isHiddenInEditor(), - false); - return true; - } - return false; - }; - auto nodeObj = qobject_cast(obj); - if (!handleHiding(nodeObj)) { - if (auto pickTarget = obj->property("_pickTarget").value()) - handleHiding(pickTarget); - } -#endif -#else auto handlePicking = [this](QObject *object) -> bool { if (object && hasInstanceForObject(object)) { ServerNodeInstance instance = instanceForObject(object); @@ -1633,7 +1529,6 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() if (auto pickTarget = obj->property("_pickTarget").value()) handlePicking(pickTarget); } -#endif } m_dynamicObjectConstructors.clear(); } @@ -1949,18 +1844,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( m_editView3DSetupDone = true; - auto activeView = qobject_cast(m_active3DView); - if (activeView) { - QQuick3DSceneEnvironment *activeEnv = activeView->environment(); - QColor clearColor = activeEnv->clearColor(); - - if (clearColor.isValid() && helper) { - ServerNodeInstance activeSceneInstance = active3DSceneInstance(); - const QString sceneId = activeSceneInstance.id(); - - helper->setSceneEnvironmentColor(sceneId, clearColor); - } - } + updateSceneEnvColorsToHelper(); if (toolStates.contains({})) { // Update tool state to an existing no-scene state before updating the active scene to @@ -1974,19 +1858,6 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( createCameraAndLightGizmos(instanceList); - if (!command.edit3dBackgroundColor.isEmpty()) { - View3DActionCommand backgroundColorCommand(View3DActionType::SelectBackgroundColor, - QVariant::fromValue( - command.edit3dBackgroundColor)); - view3DAction(backgroundColorCommand); - } - - if (command.edit3dGridColor.isValid()) { - View3DActionCommand backgroundColorCommand(View3DActionType::SelectGridColor, - QVariant::fromValue(command.edit3dGridColor)); - view3DAction(backgroundColorCommand); - } - // Queue two renders to make sure icon gizmos update properly render3DEditView(2); #endif @@ -2113,6 +1984,8 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com setup3DEditView(instanceList, command); updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); + updateSnapSettings(command.auxiliaryChanges); + updateColorSettings(command.auxiliaryChanges); } QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, @@ -2239,7 +2112,7 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm if (node) { const auto childItems = node->childItems(); for (const auto &childItem : childItems) { - if (qobject_cast(childItem)) + if (qobject_cast(childItem) && !hasInstanceForObject(childItem)) return true; } } @@ -2291,7 +2164,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm render3DEditView(2); } -void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyValueContainer &container) +void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor( + [[maybe_unused]] const PropertyValueContainer &container) { #ifdef QUICK3D_MODULE auto helper = qobject_cast(m_3dHelper); @@ -2319,15 +2193,12 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa if (toolStates.contains("syncBackgroundColor")) { bool sync = toolStates["syncBackgroundColor"].toBool(); - QList colors{color}; if (sync) { - View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, - QVariant::fromValue(colors)); - view3DAction(cmd); + QList colors{color}; + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateBackgroundColors", + Q_ARG(QVariant, QVariant::fromValue(colors))); } } -#else - Q_UNUSED(container) #endif } @@ -2337,8 +2208,8 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa // If scene is not View3D scene, return first camera in the scene QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const { -#ifdef QUICK3D_MODULE QVariantList cameras; +#ifdef QUICK3D_MODULE if (m_selectedCameras.contains(m_active3DScene)) { const QObjectList cameraList = m_selectedCameras[m_active3DScene]; for (const auto camera : cameraList) { @@ -2365,10 +2236,43 @@ QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const } } } - +#endif return cameras; -#else - return {}; +} + +void Qt5InformationNodeInstanceServer::updateSceneEnvColorsToHelper() +{ +#ifdef QUICK3D_MODULE + // Update stored scene environment colors for all scenes + auto helper = qobject_cast(m_3dHelper); + if (!helper) + return; + + helper->clearSceneEnvironmentColors(); + + const auto sceneRoots = m_3DSceneMap.uniqueKeys(); + for (QObject *sceneRoot : sceneRoots) { + auto view3D = qobject_cast(findView3DForSceneRoot(sceneRoot)); + if (!view3D) + continue; + + QQuick3DSceneEnvironment *env = view3D->environment(); + if (!env) + continue; + + QColor clearColor = env->clearColor(); + if (clearColor.isValid() && helper) { + ServerNodeInstance sceneInstance; + if (hasInstanceForObject(sceneRoot)) + sceneInstance = instanceForObject(sceneRoot); + else if (hasInstanceForObject(view3D)) + sceneInstance = instanceForObject(view3D); + + const QString sceneId = sceneInstance.id(); + + helper->setSceneEnvironmentColor(sceneId, clearColor); + } + } #endif } @@ -2486,13 +2390,6 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::SyncBackgroundColor: updatedToolState.insert("syncBackgroundColor", command.isEnabled()); break; - case View3DActionType::SelectBackgroundColor: - updatedViewState.insert("selectBackgroundColor", command.value()); - break; - case View3DActionType::SelectGridColor: { - updatedViewState.insert("selectGridColor", command.value()); - break; - } #ifdef QUICK3D_PARTICLES_MODULE case View3DActionType::ShowParticleEmitter: updatedToolState.insert("showParticleEmitter", command.isEnabled()); @@ -2556,6 +2453,8 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia { updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); + updateSnapSettings(command.auxiliaryChanges); + updateColorSettings(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); } @@ -2602,6 +2501,14 @@ void Qt5InformationNodeInstanceServer::changeState(const ChangeStateCommand &com void Qt5InformationNodeInstanceServer::removeProperties(const RemovePropertiesCommand &command) { + const QVector props = command.properties(); + for (const PropertyAbstractContainer &container : props) { + if (container.name() == "clearColor") { + setSceneEnvironmentColor(PropertyValueContainer(container.instanceId(), + container.name(), {}, {})); + } + } + Qt5NodeInstanceServer::removeProperties(command); render3DEditView(); @@ -2688,65 +2595,12 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden( auto helper = qobject_cast(m_3dHelper); if (helper) emit helper->hiddenStateChanged(node); -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) - if (auto model = qobject_cast(node)) - model->setPickable(!edit3dHidden); // allow 3D objects to receive mouse clicks -#endif const auto childItems = node->childItems(); for (auto childItem : childItems) { const ServerNodeInstance quick3dInstance = getQuick3DInstanceAndHidden(childItem); if (quick3dInstance.isValid()) { // Don't override explicit hide in children handleInstanceHidden(quick3dInstance, edit3dHidden || isInstanceHidden, false); - } else { -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) - // Children of components do not have instances, but will still need to be pickable - std::function checkChildren; - checkChildren = [&](QQuick3DNode *checkNode) { - const auto childItems = checkNode->childItems(); - for (auto child : childItems) { - if (auto childNode = qobject_cast(child)) - checkChildren(childNode); - } - if (auto checkModel = qobject_cast(checkNode)) { - QVariant value; - if (!edit3dHidden) - value = QVariant::fromValue(node); - // Specify the actual pick target with dynamic property - checkModel->setProperty("_pickTarget", value); - checkModel->setPickable(!edit3dHidden); - } else { - auto checkRepeater = qobject_cast(checkNode); - auto checkLoader = qobject_cast(checkNode); -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) - auto checkRunLoader = qobject_cast(checkNode); - if (checkRepeater || checkLoader || checkRunLoader) { -#else - if (checkRepeater || checkLoader) { -#endif - // Repeaters/loaders may not yet have created their children, so we set - // _pickTarget on them and connect the notifier. - if (checkNode->property("_pickTarget").isNull()) { - if (checkRepeater) { - QObject::connect(checkRepeater, &QQuick3DRepeater::objectAdded, - this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) - } else if (checkRunLoader) { - QObject::connect(checkRunLoader, &QQuick3DRuntimeLoader::statusChanged, - this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); -#endif - } else { - QObject::connect(checkLoader, &QQuick3DLoader::loaded, - this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); - } - } - checkNode->setProperty("_pickTarget", QVariant::fromValue(node)); - } - } - }; - if (auto childNode = qobject_cast(childItem)) - checkChildren(childNode); -#endif } } } @@ -2756,10 +2610,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden( void Qt5InformationNodeInstanceServer::handlePickTarget( [[maybe_unused]] const ServerNodeInstance &instance) { -#if defined(QUICK3D_MODULE) && (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) - // Picking is dependent on hidden status prior to global picking support (<6.2.1), so it is - // handled in handleInstanceHidden() method in those builds - +#ifdef QUICK3D_MODULE if (!ViewConfig::isQuick3DMode()) return; @@ -2861,12 +2712,10 @@ void Qt5InformationNodeInstanceServer::update3DViewState( if (command.type() == Update3dViewStateCommand::SizeChange) { if (m_editView3DSetupDone) { m_editView3DData.rootItem->setSize(command.size()); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_editView3DData.window->contentItem()->setSize(m_editView3DData.rootItem->size()); m_editView3DData.window->setGeometry(0, 0, m_editView3DData.rootItem->width(), m_editView3DData.rootItem->height()); m_editView3DData.bufferDirty = true; -#endif auto helper = qobject_cast(m_3dHelper); if (helper) helper->storeToolState(helper->globalStateId(), helper->rootSizeKey(), QVariant(command.size()), 0); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 8d02b586c20..c774bc8f33f 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -125,6 +125,8 @@ private: void resolveImportSupport(); void updateMaterialPreviewData(const QVector &valueChanges); void updateRotationBlocks(const QVector &valueChanges); + void updateSnapSettings(const QVector &valueChanges); + void updateColorSettings(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); void getNodeAtPos(const QPointF &pos); @@ -136,6 +138,7 @@ private: #endif void setSceneEnvironmentColor(const PropertyValueContainer &container); QVariantList alignCameraList() const; + void updateSceneEnvColorsToHelper(); RenderViewData m_editView3DData; RenderViewData m_modelNode3DImageViewData; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index b0726b152a0..f4d01d28db4 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -15,10 +15,6 @@ #include "qt5testnodeinstanceserver.h" #include "quickitemnodeinstance.h" -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - #include -#endif - #if defined(Q_OS_UNIX) #include #elif defined(Q_OS_WIN) @@ -44,10 +40,6 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : if (unifiedRenderPath) Internal::QuickItemNodeInstance::enableUnifiedRenderPath(true); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - else - QQuickDesignerSupport::activateDesignerWindowManager(); -#endif if (QCoreApplication::arguments().at(1) == QLatin1String("--readcapturedstream")) { qputenv("DESIGNER_DONT_USE_SHARED_MEMORY", "1"); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index 813e40f4a3a..2f905d21cf7 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -28,7 +28,6 @@ #include #include -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #include #include @@ -37,9 +36,6 @@ #include #include #include -#else -#include -#endif #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) #include @@ -47,6 +43,13 @@ #include #include #include +#define USE_PIPELINE_CACHE 1 + +#if defined(QUICK3D_MODULE) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 2) +#include +#include +#define USE_SHADER_CACHE 1 +#endif #endif namespace QmlDesigner { @@ -67,11 +70,7 @@ Qt5NodeInstanceServer::~Qt5NodeInstanceServer() QQuickView *Qt5NodeInstanceServer::quickView() const { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - return static_cast(m_viewData.window.data()); -#else return nullptr; -#endif } QQuickWindow *Qt5NodeInstanceServer::quickWindow() const @@ -83,25 +82,11 @@ void Qt5NodeInstanceServer::initializeView() { Q_ASSERT(!quickWindow()); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - auto view = new QQuickView; - m_viewData.window = view; - /* enables grab window without show */ - QSurfaceFormat surfaceFormat = view->requestedFormat(); - surfaceFormat.setVersion(4, 1); - surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); - QSurfaceFormat::setDefaultFormat(surfaceFormat); - view->setFormat(surfaceFormat); - - QQuickDesignerSupport::createOpenGLContext(view); - m_qmlEngine = view->engine(); -#else m_viewData.renderControl = new QQuickRenderControl; m_viewData.window = new QQuickWindow(m_viewData.renderControl); setPipelineCacheConfig(m_viewData.window); m_viewData.renderControl->initialize(); m_qmlEngine = new QQmlEngine; -#endif if (qEnvironmentVariableIsSet("QML_FILE_SELECTORS")) { QQmlFileSelector *fileSelector = new QQmlFileSelector(engine(), engine()); @@ -125,9 +110,6 @@ QQuickItem *Qt5NodeInstanceServer::rootItem() const void Qt5NodeInstanceServer::setRootItem(QQuickItem *item) { m_viewData.rootItem = item; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QQuickDesignerSupport::setRootItem(quickView(), item); -#else quickWindow()->setGeometry(0, 0, item->width(), item->height()); // Insert an extra item above the root to adjust root item position to 0,0 to make entire // item to be always rendered. @@ -135,7 +117,6 @@ void Qt5NodeInstanceServer::setRootItem(QQuickItem *item) m_viewData.contentItem = new QQuickItem(quickWindow()->contentItem()); m_viewData.contentItem->setPosition(-item->position()); item->setParentItem(m_viewData.contentItem); -#endif } QQmlEngine *Qt5NodeInstanceServer::engine() const @@ -145,11 +126,9 @@ QQmlEngine *Qt5NodeInstanceServer::engine() const void Qt5NodeInstanceServer::resizeCanvasToRootItem() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_viewData.bufferDirty = true; if (m_viewData.contentItem) m_viewData.contentItem->setPosition(-m_viewData.rootItem->position()); -#endif quickWindow()->resize(rootNodeInstance().boundingRect().size().toSize()); QQuickDesignerSupport::addDirty(rootNodeInstance().rootQuickItem(), QQuickDesignerSupport::Size); } @@ -170,7 +149,7 @@ void Qt5NodeInstanceServer::setupScene(const CreateSceneCommand &command) setupInstances(command); resizeCanvasToRootItem(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) +#ifdef USE_PIPELINE_CACHE if (!m_pipelineCacheLocation.isEmpty()) { QString fileId = command.fileUrl.toLocalFile(); fileId.remove(':'); @@ -181,6 +160,10 @@ void Qt5NodeInstanceServer::setupScene(const CreateSceneCommand &command) QFile cacheFile(m_pipelineCacheFile); if (cacheFile.open(QIODevice::ReadOnly)) m_pipelineCacheData = cacheFile.readAll(); + +#ifdef USE_SHADER_CACHE + m_shaderCacheFile = m_pipelineCacheFile + ".qsbc"; +#endif } #endif } @@ -213,7 +196,7 @@ bool Qt5NodeInstanceServer::rootIsRenderable3DObject() const void Qt5NodeInstanceServer::savePipelineCacheData() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) +#ifdef USE_PIPELINE_CACHE if (!m_viewData.rhi) return; @@ -239,10 +222,24 @@ void Qt5NodeInstanceServer::savePipelineCacheData() // Cache file can grow indefinitely, so let's just purge it every so often. // The count is stored as the last char in the data. char count = m_pipelineCacheData[m_pipelineCacheData.size() - 1]; - if (count > 25) + const char maxCount = 25; + if (count > maxCount) cacheFile.remove(); else if (cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) cacheFile.write(m_pipelineCacheData); + +#ifdef USE_SHADER_CACHE + auto wa = QQuick3DSceneManager::getOrSetWindowAttachment(*m_viewData.window); + auto context = wa ? wa->rci().get() : nullptr; + if (context && context->shaderCache()) { + if (count > maxCount) { + QFile shaderCacheFile(m_shaderCacheFile); + shaderCacheFile.remove(); + } else { + context->shaderCache()->persistentShaderBakingCache().save(m_shaderCacheFile); + } + } +#endif }); } #endif @@ -250,7 +247,7 @@ void Qt5NodeInstanceServer::savePipelineCacheData() void Qt5NodeInstanceServer::setPipelineCacheConfig([[maybe_unused]] QQuickWindow *w) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) +#ifdef USE_PIPELINE_CACHE // This dummy file is not actually used for cache as we manage cache save/load ourselves, // but some file needs to be set to enable pipeline caching const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); @@ -262,12 +259,28 @@ void Qt5NodeInstanceServer::setPipelineCacheConfig([[maybe_unused]] QQuickWindow config.setPipelineCacheSaveFile(dummyCache); config.setAutomaticPipelineCache(false); w->setGraphicsConfiguration(config); + +#ifdef USE_SHADER_CACHE + QtQuick3DEditorHelpers::ShaderCache::setAutomaticDiskCache(false); + auto wa = QQuick3DSceneManager::getOrSetWindowAttachment(*w); + connect(wa, &QQuick3DWindowAttachment::renderContextInterfaceChanged, + this, &Qt5NodeInstanceServer::handleRciSet); +#endif +#endif +} + +void Qt5NodeInstanceServer::handleRciSet() +{ +#ifdef USE_SHADER_CACHE + auto wa = qobject_cast(sender()); + auto context = wa ? wa->rci().get() : nullptr; + if (context && context->shaderCache()) + context->shaderCache()->persistentShaderBakingCache().load(m_shaderCacheFile); #endif } bool Qt5NodeInstanceServer::initRhi([[maybe_unused]] RenderViewData &viewData) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!viewData.renderControl) { qWarning() << __FUNCTION__ << "Render control not created"; return false; @@ -281,7 +294,7 @@ bool Qt5NodeInstanceServer::initRhi([[maybe_unused]] RenderViewData &viewData) qWarning() << __FUNCTION__ << "Rhi is null"; return false; } -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 1) +#ifdef USE_PIPELINE_CACHE if (!m_pipelineCacheData.isEmpty()) viewData.rhi->setPipelineCacheData(m_pipelineCacheData.left(m_pipelineCacheData.size() - 1)); #endif @@ -347,7 +360,7 @@ bool Qt5NodeInstanceServer::initRhi([[maybe_unused]] RenderViewData &viewData) viewData.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(viewData.texTarget)); viewData.bufferDirty = false; -#endif + return true; } @@ -356,7 +369,6 @@ QImage Qt5NodeInstanceServer::grabRenderControl([[maybe_unused]] RenderViewData NANOTRACE_SCOPE("Update", "GrabRenderControl"); QImage renderImage; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (viewData.bufferDirty && !initRhi(viewData)) return renderImage; @@ -384,14 +396,13 @@ QImage Qt5NodeInstanceServer::grabRenderControl([[maybe_unused]] RenderViewData rd->cb->resourceUpdate(readbackBatch); viewData.renderControl->endFrame(); -#endif + return renderImage; } // This method simply renders the window without grabbing it bool Qt5NodeInstanceServer::renderWindow() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!m_viewData.rootItem || (m_viewData.bufferDirty && !initRhi(m_viewData))) return false; @@ -401,8 +412,6 @@ bool Qt5NodeInstanceServer::renderWindow() m_viewData.renderControl->render(); m_viewData.renderControl->endFrame(); return true; -#endif - return false; } QImage Qt5NodeInstanceServer::grabWindow() @@ -429,7 +438,6 @@ QQuickItem *Qt5NodeInstanceServer::parentEffectItem(QQuickItem *item) return nullptr; } -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) static bool isEffectItem(QQuickItem *item, QQuickShaderEffectSource *sourceItem) { QQuickItemPrivate *pItem = QQuickItemPrivate::get(sourceItem); @@ -450,12 +458,10 @@ static bool isLayerEnabled(QQuickItemPrivate *item) { return item && item->layer() && item->layer()->enabled(); } -#endif // QT_VERSION check QImage Qt5NodeInstanceServer::grabItem([[maybe_unused]] QQuickItem *item) { QImage renderImage; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!m_viewData.rootItem || (m_viewData.bufferDirty && !initRhi(m_viewData))) return {}; @@ -581,7 +587,7 @@ QImage Qt5NodeInstanceServer::grabItem([[maybe_unused]] QQuickItem *item) if (!isLayerEnabled(pItem)) pItem->derefFromEffectItem(false); -#endif + return renderImage; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h index b76e639897a..9294a064e08 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -12,14 +12,12 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQmlEngine; class QQuickDesignerSupport; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) class QQuickRenderControl; class QRhi; class QRhiTexture; class QRhiRenderBuffer; class QRhiTextureRenderTarget; class QRhiRenderPassDescriptor; -#endif QT_END_NAMESPACE namespace QmlDesigner { @@ -66,7 +64,6 @@ protected: QPointer window = nullptr; QQuickItem *rootItem = nullptr; QQuickItem *contentItem = nullptr; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) bool bufferDirty = true; QQuickRenderControl *renderControl = nullptr; QRhi *rhi = nullptr; @@ -74,17 +71,19 @@ protected: QRhiRenderBuffer *buffer = nullptr; QRhiTextureRenderTarget *texTarget = nullptr; QRhiRenderPassDescriptor *rpDesc = nullptr; -#endif }; virtual bool initRhi(RenderViewData &viewData); virtual QImage grabRenderControl(RenderViewData &viewData); private: + void handleRciSet(); + RenderViewData m_viewData; QByteArray m_pipelineCacheData; QString m_pipelineCacheLocation; QString m_pipelineCacheFile; + QString m_shaderCacheFile; std::unique_ptr m_designerSupport; QQmlEngine *m_qmlEngine = nullptr; }; diff --git a/src/tools/qml2puppet/qml2puppet/instances/quick3dnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quick3dnodeinstance.cpp index 343dc363b56..9ab66649cfa 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -11,7 +11,7 @@ #include #include #include -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) +#if defined(QUICK3D_ASSET_UTILS_MODULE) #include #endif #endif @@ -42,7 +42,7 @@ void Quick3DNodeInstance::initialize( QObject *obj = object(); auto repObj = qobject_cast(obj); auto loadObj = qobject_cast(obj); -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) +#if defined(QUICK3D_ASSET_UTILS_MODULE) auto runLoadObj = qobject_cast(obj); if (repObj || loadObj || runLoadObj) { #else @@ -52,7 +52,7 @@ void Quick3DNodeInstance::initialize( if (repObj) { QObject::connect(repObj, &QQuick3DRepeater::objectAdded, infoServer, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); -#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) +#if defined(QUICK3D_ASSET_UTILS_MODULE) } else if (runLoadObj) { QObject::connect(runLoadObj, &QQuick3DRuntimeLoader::statusChanged, infoServer, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); diff --git a/src/tools/qml2puppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp index 45540799cba..4f07f5fb866 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp @@ -31,7 +31,6 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer InstanceContainer::NodeFlags flags) { #ifdef QUICK3D_MODULE -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // In case this is the scene root, we need to create a dummy View3D for the scene // in preview puppets if (instanceId() == 0 && (!nodeInstanceServer()->isInformationServer())) { @@ -49,14 +48,12 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer nodeInstanceServer()->setRootItem(m_dummyRootView); } -#endif // QT_VERSION #endif // QUICK3D_MODULE ObjectNodeInstance::initialize(objectNodeInstance, flags); } QImage Quick3DRenderableNodeInstance::renderImage() const { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!isRootNodeInstance() || !m_dummyRootView) return {}; @@ -83,14 +80,11 @@ QImage Quick3DRenderableNodeInstance::renderImage() const renderImage.setDevicePixelRatio(1); return renderImage; -#endif - return {}; } QImage Quick3DRenderableNodeInstance::renderPreviewImage( [[maybe_unused]] const QSize &previewImageSize) const { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!isRootNodeInstance() || !m_dummyRootView) return {}; @@ -118,7 +112,6 @@ QImage Quick3DRenderableNodeInstance::renderPreviewImage( return transparentImage; } } -#endif return {}; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp index f4da2fcc7bd..9d432fe0471 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -44,15 +44,6 @@ QuickItemNodeInstance::~QuickItemNodeInstance() { } -void QuickItemNodeInstance::handleObjectDeletion(QObject *object) -{ - auto item = qobject_cast(object); - if (item && checkIfRefFromEffect(instanceId())) - designerSupport()->derefFromEffectItem(item); - - ObjectNodeInstance::handleObjectDeletion(object); -} - static bool isContentItem(QQuickItem *item, NodeInstanceServer *nodeInstanceServer) { @@ -153,18 +144,6 @@ void QuickItemNodeInstance::enableUnifiedRenderPath(bool unifiedRenderPath) s_unifiedRenderPath = unifiedRenderPath; } -bool QuickItemNodeInstance::checkIfRefFromEffect([[maybe_unused]] qint32 id) -{ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (s_unifiedRenderPath) - return false; - - return (s_createEffectItem || id == 0); -#else - return false; -#endif -} - void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, InstanceContainer::NodeFlags flags) { @@ -174,12 +153,6 @@ void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &object else quickItem()->setParentItem(nodeInstanceServer()->rootItem()); - if (quickItem()->window() && checkIfRefFromEffect(instanceId())) { - designerSupport()->refFromEffectItem(quickItem(), - !flags.testFlag( - InstanceContainer::ParentTakesOverRendering)); - } - ObjectNodeInstance::initialize(objectNodeInstance, flags); } @@ -256,11 +229,6 @@ QStringList QuickItemNodeInstance::allStates() const void QuickItemNodeInstance::updateDirtyNode([[maybe_unused]] QQuickItem *item) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (s_unifiedRenderPath) - return; - QQuickDesignerSupport::updateDirtyNode(item); -#endif } bool QuickItemNodeInstance::unifiedRenderPath() @@ -268,15 +236,6 @@ bool QuickItemNodeInstance::unifiedRenderPath() return s_unifiedRenderPath; } -bool QuickItemNodeInstance::unifiedRenderPathOrQt6() -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - return true; -#else - return s_unifiedRenderPath; -#endif -} - void QuickItemNodeInstance::setHiddenInEditor(bool hide) { ObjectNodeInstance::setHiddenInEditor(hide); @@ -462,24 +421,6 @@ QImage QuickItemNodeInstance::renderImage() const QRectF renderBoundingRect = boundingRect(); QImage renderImage; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QSize size = renderBoundingRect.size().toSize(); - static double devicePixelRatio = qgetenv("FORMEDITOR_DEVICE_PIXEL_RATIO").toDouble(); - size *= devicePixelRatio; - - if (s_unifiedRenderPath) { - renderImage = nodeInstanceServer()->quickWindow()->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - nodeInstanceServer()->quickWindow()->beforeSynchronizing(); - nodeInstanceServer()->quickWindow()->beforeRendering(); - - renderImage = designerSupport()->renderImageForItem(quickItem(), renderBoundingRect, size); - - nodeInstanceServer()->quickWindow()->afterRendering(); - } - renderImage.setDevicePixelRatio(devicePixelRatio); -#else if (s_unifiedRenderPath) { renderImage = nodeInstanceServer()->grabWindow(); renderImage = renderImage.copy(renderBoundingRect.toRect()); @@ -489,8 +430,6 @@ QImage QuickItemNodeInstance::renderImage() const renderImage = nodeInstanceServer()->grabItem(quickItem()); } -#endif - return renderImage; } @@ -503,27 +442,9 @@ QImage QuickItemNodeInstance::renderPreviewImage(const QSize &previewImageSize) const QSize size = previewImageSize * devicePixelRatio; if (quickItem()->isVisible()) { QImage image; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (s_unifiedRenderPath) { - image = nodeInstanceServer()->quickWindow()->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - nodeInstanceServer()->quickWindow()->beforeSynchronizing(); - nodeInstanceServer()->quickWindow()->beforeRendering(); - - image = designerSupport()->renderImageForItem(quickItem(), - previewItemBoundingRect, - size); - - nodeInstanceServer()->quickWindow()->afterRendering(); - } -#else image = nodeInstanceServer()->grabWindow(); image = image.copy(previewItemBoundingRect.toRect()); -#endif - image = image.scaledToWidth(size.width()); - return image; } else { QImage transparentImage(size, QImage::Format_ARGB32_Premultiplied); @@ -605,9 +526,6 @@ void QuickItemNodeInstance::updateDirtyNodesRecursive(QQuickItem *parentItem) co } QmlPrivateGate::disableNativeTextRendering(parentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QQuickDesignerSupport::updateDirtyNode(parentItem); -#endif } void QuickItemNodeInstance::updateAllDirtyNodesRecursive(QQuickItem *parentItem) const @@ -621,12 +539,10 @@ void QuickItemNodeInstance::updateAllDirtyNodesRecursive(QQuickItem *parentItem) void QuickItemNodeInstance::setAllNodesDirtyRecursive([[maybe_unused]] QQuickItem *parentItem) const { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QList children = parentItem->childItems(); for (QQuickItem *childItem : children) setAllNodesDirtyRecursive(childItem); QQuickDesignerSupport::addDirty(parentItem, QQuickDesignerSupport::Content); -#endif } static inline bool isRectangleSane(const QRectF &rect) @@ -636,9 +552,6 @@ static inline bool isRectangleSane(const QRectF &rect) static bool isEffectItem([[maybe_unused]] QQuickItem *item) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - return false; -#else if (qobject_cast(item)) return true; @@ -658,7 +571,6 @@ static bool isEffectItem([[maybe_unused]] QQuickItem *item) } return false; -#endif } QRectF QuickItemNodeInstance::boundingRectWithStepChilds(QQuickItem *parentItem) const diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.h index 5955eaaa1b6..69ee6c1671a 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.h @@ -23,7 +23,6 @@ public: using WeakPointer = QWeakPointer; ~QuickItemNodeInstance() override; - void handleObjectDeletion(QObject *object) override; static Pointer create(QObject *objectToBeWrapped); static void createEffectItem(bool createEffectItem); @@ -86,7 +85,6 @@ public: static void updateDirtyNode(QQuickItem *item); static bool unifiedRenderPath(); - static bool unifiedRenderPathOrQt6(); void setHiddenInEditor(bool b) override; @@ -111,7 +109,6 @@ protected: double x() const; double y() const; - bool checkIfRefFromEffect(qint32 id); void markRepeaterParentDirty() const; private: //variables diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp index eca115d4328..758e18df359 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp @@ -309,12 +309,7 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags()); -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) - // Handle hidden state to initialize pickable state - nodeInstanceServer->handleInstanceHidden(instance, false, false); -#else nodeInstanceServer->handlePickTarget(instance); -#endif return instance; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h index abb16d74bdf..079c2fad2ba 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.h @@ -16,10 +16,8 @@ class QStyleOptionGraphicsItem; class QQmlContext; class QGraphicsItem; class QGraphicsTransform; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) class QQuickItem; class QQuickItemGrabResult; -#endif QT_END_NAMESPACE namespace QmlDesigner { @@ -46,11 +44,7 @@ namespace Internal { class ServerNodeInstance { public: -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -using QHashValueType = uint; -#else -using QHashValueType = size_t; -#endif + using QHashValueType = size_t; friend class NodeInstanceServer; friend class Qt4NodeInstanceServer; diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index 610685499c2..86dce8cb90d 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -9,7 +9,6 @@ #endif #include -#include #include #include @@ -73,7 +72,6 @@ void QmlPuppet::populateParser() // we're not using the commandline parser but just populating the help text m_argParser.addOptions( {{"readcapturedstream", "Read captured stream.", "inputStream, [outputStream]"}, - {"rendericon", "Renders icon.", "size, fileName, sourceQml"}, {"import3dAsset", "Import 3d asset.", "sourceAsset, outDir, importOptJson"}}); } @@ -99,7 +97,6 @@ void QmlPuppet::initQmlRunner() { if (m_coreApp->arguments().count() < 2 || (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 3) - || (m_argParser.isSet("rendericon") && m_coreApp->arguments().count() < 5) || (m_argParser.isSet("import3dAsset") && m_coreApp->arguments().count() < 6) || (!m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 4)) { qDebug() << "Wrong argument count: " << m_coreApp->arguments().count(); @@ -122,14 +119,7 @@ void QmlPuppet::initQmlRunner() } } - if (m_argParser.isSet("rendericon")) { - int size = m_coreApp->arguments().at(2).toInt(); - QString iconFileName = m_coreApp->arguments().at(3); - QString iconSource = m_coreApp->arguments().at(4); - - m_iconRenderer.reset(new IconRenderer(size, iconFileName, iconSource)); - m_iconRenderer->setupRender(); - } else if (m_argParser.isSet("import3dAsset")) { + if (m_argParser.isSet("import3dAsset")) { QString sourceAsset = m_coreApp->arguments().at(2); QString outDir = m_coreApp->arguments().at(3); QString options = m_coreApp->arguments().at(4); diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.h b/src/tools/qml2puppet/qml2puppet/qmlpuppet.h index 506b1c2a080..c89c4fb98d1 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.h +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.h @@ -5,7 +5,6 @@ #include "../qmlbase.h" -class IconRenderer; class QmlPuppet : public QmlBase { using QmlBase::QmlBase; @@ -15,7 +14,4 @@ private: void populateParser() override; int startTestMode() override; void initQmlRunner() override; - -private: - QSharedPointer m_iconRenderer; }; diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp index a8c715c9958..8abfed59160 100644 --- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp +++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp @@ -41,10 +41,6 @@ #include #endif -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - #include -#endif - namespace QmlDesigner { namespace Internal { @@ -56,93 +52,9 @@ bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName) return QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName); } -#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - -static void addToPropertyNameListIfNotBlackListed( - PropertyNameList *propertyNameList, const QQuickDesignerSupport::PropertyName &propertyName) -{ - if (!QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName)) - propertyNameList->append(propertyName); -} - -PropertyNameList allPropertyNamesInline(QObject *object, - const PropertyName &baseName = {}, - QObjectList *inspectedObjects = nullptr, - int depth = 0) -{ - QQuickDesignerSupport::PropertyNameList propertyNameList; - - QObjectList localObjectList; - - if (inspectedObjects == nullptr) - inspectedObjects = &localObjectList; - - if (depth > 2) - return propertyNameList; - - if (!inspectedObjects->contains(object)) - inspectedObjects->append(object); - - const QMetaObject *metaObject = object->metaObject(); - - QStringList deferredPropertyNames; - const int namesIndex = metaObject->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = metaObject->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - - for (int index = 0; index < metaObject->propertyCount(); ++index) { - QMetaProperty metaProperty = metaObject->property(index); - QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); - if (declarativeProperty.isValid() - && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { - if (declarativeProperty.name() != QLatin1String("parent") - && !deferredPropertyNames.contains(declarativeProperty.name())) { - QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); - if (childObject) - propertyNameList.append( - allPropertyNamesInline(childObject, - baseName - + QQuickDesignerSupport::PropertyName( - metaProperty.name()) - + '.', - inspectedObjects, - depth + 1)); - } - } else if (QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( - qmlEngine(object), metaProperty.typeId())) { - valueType->setValue(metaProperty.read(object)); - propertyNameList.append(baseName - + QQuickDesignerSupport::PropertyName(metaProperty.name())); - propertyNameList.append( - allPropertyNamesInline(valueType, - baseName - + QQuickDesignerSupport::PropertyName(metaProperty.name()) - + '.', - inspectedObjects, - depth + 1)); - } else { - addToPropertyNameListIfNotBlackListed(&propertyNameList, - baseName - + QQuickDesignerSupport::PropertyName( - metaProperty.name())); - } - } - - return propertyNameList; -} -#endif - PropertyNameList allPropertyNames(QObject *object) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) return QQuickDesignerSupportProperties::allPropertyNames(object); -#elif QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - return allPropertyNamesInline(object); -#else - return QQuickDesignerSupportProperties::allPropertyNames(object); -#endif } PropertyNameList propertyNameListForWritableProperties(QObject *object) @@ -204,15 +116,10 @@ static bool isConnections(QObject *object) // This is used in share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp QObject *createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QTypeRevision revision = QTypeRevision::zero(); if (majorNumber > 0) revision = QTypeRevision::fromVersion(majorNumber, minorNumber); return QQuickDesignerSupportItems::createPrimitive(typeName, revision, context); -#else - return QQuickDesignerSupportItems::createPrimitive(typeName, majorNumber, minorNumber, context); -#endif } static QString qmlDesignerRCPath() @@ -347,21 +254,13 @@ void emitComponentComplete(QObject *item) QQmlData *data = QQmlData::get(item); if (data && data->context) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QQmlComponentAttached *componentAttached = data->context->componentAttached; -#else QQmlComponentAttached *componentAttached = data->context->componentAttacheds(); -#endif while (componentAttached) { - if (componentAttached->parent()) + if (componentAttached->parent()) { if (componentAttached->parent() == item) emit componentAttached->completed(); - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - componentAttached = componentAttached->next; -#else + } componentAttached = componentAttached->next(); -#endif } } } @@ -552,12 +451,8 @@ bool isSubclassOf(QObject *object, const QByteArray &superTypeName) void getPropertyCache(QObject *object, QQmlEngine *engine) { -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - QQuickDesignerSupportProperties::getPropertyCache(object, engine); -#else Q_UNUSED(engine); QQuickDesignerSupportProperties::getPropertyCache(object); -#endif } void registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const PropertyName &)) diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.h b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.h index 916aac3c7b0..fcc366ea1b1 100644 --- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.h +++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.h @@ -27,16 +27,9 @@ namespace QmlPrivateGate { class ComponentCompleteDisabler { public: -#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)) ComponentCompleteDisabler(); ~ComponentCompleteDisabler(); -#else - ComponentCompleteDisabler() - { - //nothing not available yet - } -#endif }; void createNewDynamicProperty(QObject *object, QQmlEngine *engine, const QString &name); diff --git a/src/tools/qml2puppet/qmlpuppet.qrc b/src/tools/qml2puppet/qmlpuppet.qrc index 7ec26950034..24acebb134a 100644 --- a/src/tools/qml2puppet/qmlpuppet.qrc +++ b/src/tools/qml2puppet/qmlpuppet.qrc @@ -7,7 +7,6 @@ images/non-visual-component@2x.png mockfiles/Window.qml mockfiles/SwipeView.qml - mockfiles/GenericBackend.qml mockfiles/Dialog.qml mockfiles/ToolBarButton.qml mockfiles/ToggleButton.qml diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt index 4aa7c363f9b..cb692ff48a4 100644 --- a/tests/auto/qml/CMakeLists.txt +++ b/tests/auto/qml/CMakeLists.txt @@ -3,6 +3,7 @@ if (NOT TARGET QmlJSTools) endif() add_subdirectory(codemodel) +add_subdirectory(connectioneditor) add_subdirectory(persistenttrie) add_subdirectory(qmldesigner) add_subdirectory(qmleditor) diff --git a/tests/auto/qml/connectioneditor/CMakeLists.txt b/tests/auto/qml/connectioneditor/CMakeLists.txt new file mode 100644 index 00000000000..0f668174c2b --- /dev/null +++ b/tests/auto/qml/connectioneditor/CMakeLists.txt @@ -0,0 +1,8 @@ +add_qtc_test(tst_connection_view + DEPENDS QmlJS Utils QmlDesigner + DEFINES + QT_CREATOR + QTCREATORDIR="${PROJECT_SOURCE_DIR}" + TESTSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}" + SOURCES tst_connectioneditor.cpp +) diff --git a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp new file mode 100644 index 00000000000..61e19d1253d --- /dev/null +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -0,0 +1,407 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmldesigner/components/connectioneditor/connectioneditorevaluator.h" +#include "qmljs/parser/qmljsast_p.h" +#include "qmljs/qmljsdocument.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlDesigner; + +class tst_ConnectionEditor : public QObject +{ + Q_OBJECT +public: + tst_ConnectionEditor(); + +private slots: + void test01(); + void test01_data(); + void invalidSyntax(); + void displayStrings(); + void displayStrings_data(); + void parseAssignment(); + void parseSetProperty(); + void parseSetState(); + void parseConsoleLog(); + void parseCallFunction(); + void parseEmpty(); + void parseComplexCondition(); + QByteArray countOne(); + +private: + static int m_cnt; + ConnectionEditorEvaluator evaluator; +}; + +int tst_ConnectionEditor::m_cnt = 0; + +tst_ConnectionEditor::tst_ConnectionEditor() {} + +#define QCOMPARE_NOEXIT(actual, expected) \ + QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__) + +void tst_ConnectionEditor::test01_data() +{ + QTest::addColumn("statement"); + QTest::addColumn("expectedValue"); + + QTest::newRow("if then else property set") + << "if (object.atr === 3 && kmt === 43) {kMtr.lptr = 3;} else {kMtr.ptr = 4;}" << true; + + QTest::newRow("empty") << "{}" << true; + QTest::newRow("console.log") << "{console.log(\"test\")}" << true; + QTest::newRow("function call") << "{someItem.functionCall()}" << true; + QTest::newRow("property set number - extra .") << "{someItem.width.kkk = 10}" << true; + QTest::newRow("property set color") << "{someItem.color = \"red\"}" << true; + QTest::newRow("set state") << "{someItem.state = \"state\"}" << true; + QTest::newRow("property set string") << "{someItem.text = \"some string\"}" << true; + QTest::newRow("property set") << "{someItem.speed = 1.0}" << true; + QTest::newRow("assignment") << "{someItem.speed = someOtherItem.speed}" << true; + QTest::newRow("if assignemnt") + << "{if (someItem.bool) { someItem.speed = someOtherItem.speed }}" << true; + QTest::newRow("if function call") << "{if (someItem.bool) { someItem.functionCall() }}" << true; + QTest::newRow("if console log") << "{if (someItem.bool) { console.log(\"ok\") }}" << true; + QTest::newRow("if else console log") + << "{if (someItem.bool) { console.log(\"ok\") } else {console.log(\"ko\")}}" << true; + QTest::newRow("if else empty") + << "{if (someItem.bool && someItem.someBool2) { } else { } }" << true; + QTest::newRow("if > number") << "{if (someItem.width > 10) { }}" << true; + QTest::newRow("if > && bool ") << "{if (someItem.width > 10 && someItem.someBool) { }}" << true; + QTest::newRow("if ||") << "{if (someItem.width > 10 || someItem.someBool) { }}" << true; + QTest::newRow("if ===") << "{if (someItem.width === someItem.height) { }}" << true; + QTest::newRow("if === 2") << "{if (someItem.width === someItem.height) {} }" << true; + + // False Conditions + QTest::newRow("call with argument") << "{someItem.complexCall(blah)}" << false; + QTest::newRow("+ in condition") << "{someItem.width = someItem.Height + 10}" << false; + QTest::newRow("type not matching") + << "if (someItem.bool) {console.log(\"ok\") } else { someItem.functionCall() }" << false; + QTest::newRow("type not matching 2") << "{if (someItem.width == someItem.height) {} }" << false; + QTest::newRow("if = in condition") << "{if (someItem.width = someItem.height) {} }" << false; + QTest::newRow("if + in condition with >") + << "{if (someItem.width > someItem.height + 10) {} }" << false; + QTest::newRow("if {} missing") << "{if (someItem.width > someItem.height) ak = 2; }" << false; + QTest::newRow("two statements") + << "{if (someItem.bool) { console.log(\"ok\");console.log(\"ok\") }}" << false; + QTest::newRow("if with string") + << "{if (someItem.string === \"test\") { console.log(\"ok\") }}" << true; + QTest::newRow("if just with true") << "{if (true) { console.log(\"ok\") }}" << true; +} + +void tst_ConnectionEditor::invalidSyntax() +{ + const QString statement1 = "!! This is no valid QML !!"; + const QString statement2 + = "{someItem.complexCall(blah)}"; //valid QML bit not valid for ConnectionEditor + + QString result = ConnectionEditorEvaluator::getDisplayStringForType(statement1); + QCOMPARE(result, ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME); + + result = ConnectionEditorEvaluator::getDisplayStringForType(statement2); + QCOMPARE(result, ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME); + + auto resultHandler = ConnectionEditorEvaluator::parseStatement(statement1); + auto parsedStatement = ConnectionEditorStatements::okStatement(resultHandler); + QVERIFY(std::holds_alternative(parsedStatement)); + + resultHandler = ConnectionEditorEvaluator::parseStatement(statement2); + parsedStatement = ConnectionEditorStatements::okStatement(resultHandler); + QVERIFY(std::holds_alternative(parsedStatement)); +} + +void tst_ConnectionEditor::displayStrings() +{ + QFETCH(QString, statement); + QFETCH(QString, expectedValue); + + const QString result = ConnectionEditorEvaluator::getDisplayStringForType(statement); + + QCOMPARE(result, expectedValue); +} + +void tst_ConnectionEditor::displayStrings_data() +{ + QTest::addColumn("statement"); + QTest::addColumn("expectedValue"); + + QTest::newRow("Empty") << "{}" << ConnectionEditorStatements::EMPTY_DISPLAY_NAME; + + QTest::newRow("Pure Console log") + << "{console.log(\"test\")}" << ConnectionEditorStatements::LOG_DISPLAY_NAME; + QTest::newRow("Condition Console log") << "{if (someItem.bool) { console.log(\"ok\") }}" + << ConnectionEditorStatements::LOG_DISPLAY_NAME; + QTest::newRow("Pure Set Property") + << "{someItem.speed = 1.0}" << ConnectionEditorStatements::SETPROPERTY_DISPLAY_NAME; + QTest::newRow("Condition Set Property") << "{if (someItem.bool) {someItem.speed = 1.0}}" + << ConnectionEditorStatements::SETPROPERTY_DISPLAY_NAME; + + QTest::newRow("Pure Function Call") + << "{someItem.functionCall()}" << ConnectionEditorStatements::FUNCTION_DISPLAY_NAME; + + QTest::newRow("Condition Function Call") << "{if (someItem.bool) {someItem.functionCall()}}" + << ConnectionEditorStatements::FUNCTION_DISPLAY_NAME; + + QTest::newRow("Pure Assignment") << "{someItem.speed = someOtherItem.speed}" + << ConnectionEditorStatements::ASSIGNMENT_DISPLAY_NAME; + + QTest::newRow("Condition Assignment") + << "{if (someItem.bool) {someItem.speed = someOtherItem.speed}}" + << ConnectionEditorStatements::ASSIGNMENT_DISPLAY_NAME; + + QTest::newRow("Pure State") << "{someItem.state = \"state\"}" + << ConnectionEditorStatements::SETSTATE_DISPLAY_NAME; + + QTest::newRow("Condition State") << "{if (someItem.bool) {someItem.state = \"state\"}}" + << ConnectionEditorStatements::SETSTATE_DISPLAY_NAME; + + QTest::newRow("Pure Set Property Color") + << "{someItem.color = \"red\"}" << ConnectionEditorStatements::SETPROPERTY_DISPLAY_NAME; + + QTest::newRow("Custom function call assignment") + << "{someItem.color = item.functionCall()}" + << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + + QTest::newRow("Custom function call with argument") + << "{someItem.color = item.functionCall(\"test\")}" + << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; + + QTest::newRow("Custom function call with argument 2") + << "{item.functionCall(\"test\")}" << ConnectionEditorStatements::UNKNOWN_DISPLAY_NAME; +} + +void tst_ConnectionEditor::parseAssignment() +{ + auto result = ConnectionEditorEvaluator::parseStatement( + "{someItem.speed = someOtherItem.speed2}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + auto assignment = std::get(parsedStatement); + + QCOMPARE(assignment.lhs.nodeId, "someItem"); + QCOMPARE(assignment.lhs.propertyName, "speed"); + + QCOMPARE(assignment.rhs.nodeId, "someOtherItem"); + QCOMPARE(assignment.rhs.propertyName, "speed2"); + + result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.bool) {someItem.speed = someOtherItem.speed2}}"); + parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + assignment = std::get(parsedStatement); + + QCOMPARE(assignment.lhs.nodeId, "someItem"); + QCOMPARE(assignment.lhs.propertyName, "speed"); + + QCOMPARE(assignment.rhs.nodeId, "someOtherItem"); + QCOMPARE(assignment.rhs.propertyName, "speed2"); + + result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.bool) {someItem1.speed = someOtherItem.speed2} else {someItem.speed = " + "someOtherItem2.speed3}}"); + parsedStatement = ConnectionEditorStatements::koStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + assignment = std::get(parsedStatement); + + QCOMPARE(assignment.lhs.nodeId, "someItem"); + QCOMPARE(assignment.lhs.propertyName, "speed"); + + QCOMPARE(assignment.rhs.nodeId, "someOtherItem2"); + QCOMPARE(assignment.rhs.propertyName, "speed3"); +} + +void tst_ConnectionEditor::parseSetProperty() +{ + auto result = ConnectionEditorEvaluator::parseStatement("{someItem.color = \"red\"}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + auto propertySet = std::get(parsedStatement); + + QCOMPARE(propertySet.lhs.nodeId, "someItem"); + QCOMPARE(propertySet.lhs.propertyName, "color"); + + QVERIFY(std::holds_alternative(propertySet.rhs)); + + auto string = std::get(propertySet.rhs); + + QCOMPARE(string, "red"); + + result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.bool){someItem.color = \"red\"}}"); + parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + propertySet = std::get(parsedStatement); + + QCOMPARE(propertySet.lhs.nodeId, "someItem"); + QCOMPARE(propertySet.lhs.propertyName, "color"); + + QVERIFY(std::holds_alternative(propertySet.rhs)); + + string = std::get(propertySet.rhs); + + QCOMPARE(string, "red"); + + result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.bool){someItem.color = \"red\"} else {someItem2.color = \"green\"}}"); + parsedStatement = ConnectionEditorStatements::koStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + propertySet = std::get(parsedStatement); + + QCOMPARE(propertySet.lhs.nodeId, "someItem2"); + QCOMPARE(propertySet.lhs.propertyName, "color"); + + QVERIFY(std::holds_alternative(propertySet.rhs)); + + string = std::get(propertySet.rhs); + + QCOMPARE(string, "green"); +} + +void tst_ConnectionEditor::parseSetState() +{ + auto result = ConnectionEditorEvaluator::parseStatement("{someItem.state = \"myState\"}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + auto stateSet = std::get(parsedStatement); + + QCOMPARE(stateSet.nodeId, "someItem"); // TODO should be just someItem + QCOMPARE(stateSet.stateName, "\"myState\""); // TODO quotes should be removed. + + //skipping if/else case, since this test requires adjustments +} + +void tst_ConnectionEditor::parseConsoleLog() +{ + auto result = ConnectionEditorEvaluator::parseStatement("{console.log(\"teststring\")}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + auto consoleLog = std::get(parsedStatement); + + QVERIFY(std::holds_alternative(consoleLog.argument)); + auto argument = std::get(consoleLog.argument); //TODO could be just a string + QCOMPARE(argument, "teststring"); +} + +void tst_ConnectionEditor::parseCallFunction() +{ + auto result = ConnectionEditorEvaluator::parseStatement("{someItem.trigger()}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + auto functionCall = std::get(parsedStatement); + + QCOMPARE(functionCall.nodeId, "someItem"); + QCOMPARE(functionCall.functionName, "trigger"); + + result = ConnectionEditorEvaluator::parseStatement("{if (someItem.bool){someItem.trigger()}}"); + parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + functionCall = std::get(parsedStatement); + + QCOMPARE(functionCall.nodeId, "someItem"); + QCOMPARE(functionCall.functionName, "trigger"); + + result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.bool){someItem.trigger()} else {someItem2.trigger2()}}"); + parsedStatement = ConnectionEditorStatements::koStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); + functionCall = std::get(parsedStatement); + + QCOMPARE(functionCall.nodeId, "someItem2"); + QCOMPARE(functionCall.functionName, "trigger2"); +} + +void tst_ConnectionEditor::parseEmpty() +{ + auto result = ConnectionEditorEvaluator::parseStatement("{}"); + auto parsedStatement = ConnectionEditorStatements::okStatement(result); + + QVERIFY(std::holds_alternative(parsedStatement)); +} + +void tst_ConnectionEditor::parseComplexCondition() +{ + auto result = ConnectionEditorEvaluator::parseStatement( + "{if (someItem.deeper.gothis && thistest.is.getit){someItem.trigger()}}"); + auto &matchedCondition = ConnectionEditorStatements::matchedCondition(result); + QCOMPARE(matchedCondition.statements.count(), 2); + + auto firstStatement = matchedCondition.statements.first(); + QVERIFY(std::holds_alternative(firstStatement)); + + auto variable = std::get(firstStatement); + + QCOMPARE(variable.nodeId, "someItem"); + QCOMPARE(variable.propertyName, "deeper.gothis"); + + auto lastStatement = matchedCondition.statements.last(); + QVERIFY(std::holds_alternative(lastStatement)); + + variable = std::get(lastStatement); + + QCOMPARE(variable.nodeId, "thistest"); + QCOMPARE(variable.propertyName, "is.getit"); + + matchedCondition.statements.clear(); + matchedCondition.tokens.clear(); + + variable.nodeId = "justId"; + variable.propertyName = {}; + + matchedCondition.statements.append(variable); + + const QString source = ConnectionEditorStatements::toJavascript(result); + + QVERIFY(source.startsWith("if (justId)")); +} + +void tst_ConnectionEditor::test01() +{ + QFETCH(QString, statement); + QFETCH(bool, expectedValue); + + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( + ""), + QmlJS::Dialect::JavaScript); + + newDoc->setSource(statement); + newDoc->parseJavaScript(); + newDoc->ast()->accept(&evaluator); + + bool parseCheck = newDoc->isParsedCorrectly(); + bool valid = evaluator.status() == ConnectionEditorEvaluator::Succeeded; + QVERIFY(parseCheck); + //QVERIFY(valid); + + bool result = parseCheck && valid; + + QCOMPARE(result, expectedValue); +} + +QByteArray tst_ConnectionEditor::countOne() +{ + return QByteArray::number(m_cnt++); +} + +QTEST_GUILESS_MAIN(tst_ConnectionEditor); + +#include "tst_connectioneditor.moc" diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 95fd241eefc..7ce2617e417 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -146,8 +146,6 @@ public: QString defaultPuppetToplevelBuildDirectory() const override { return {}; } QString qmlPuppetFallbackDirectory() const override { return {}; } QUrl projectUrl() const override { return {}; } - QList designerSettingsEdit3DViewBackgroundColor() const override { return {}; } - QColor designerSettingsEdit3DViewGridColor() const override { return {}; } void parseItemLibraryDescriptions() override {} const QmlDesigner::DesignerSettings &designerSettings() const override { return settings; } void undoOnCurrentDesignDocument() override {} diff --git a/tests/manual/fakevim/main.cpp b/tests/manual/fakevim/main.cpp index 288c244e43c..dfdaa188f47 100644 --- a/tests/manual/fakevim/main.cpp +++ b/tests/manual/fakevim/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit/tests/matchers/CMakeLists.txt b/tests/unit/tests/matchers/CMakeLists.txt index 32e5ea0b178..fc10cdc4f04 100644 --- a/tests/unit/tests/matchers/CMakeLists.txt +++ b/tests/unit/tests/matchers/CMakeLists.txt @@ -4,7 +4,7 @@ add_qtc_library(TestMatchers OBJECT PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} CONDITION QmlDesigner DEPENDS - Googletest Utils QmlDesigner + Googletest Utils TestDesignerCore SOURCES info_exportedtypenames-matcher.h import-matcher.h diff --git a/tests/unit/tests/matchers/unittest-matchers.h b/tests/unit/tests/matchers/unittest-matchers.h index 315252dd454..7c52d973b88 100644 --- a/tests/unit/tests/matchers/unittest-matchers.h +++ b/tests/unit/tests/matchers/unittest-matchers.h @@ -114,7 +114,7 @@ public: class IsNullMatcher { public: - template>> + template bool MatchAndExplain(const Type &value, testing::MatchResultListener *) const { if constexpr (std::is_pointer_v) @@ -131,10 +131,13 @@ public: class IsValidMatcher { public: - template>> + template bool MatchAndExplain(const Type &value, testing::MatchResultListener *) const { - return value.isValid(); + if constexpr (std::is_pointer_v) + return value != nullptr; + else + return value.isValid(); } void DescribeTo(std::ostream *os) const { *os << "is null"; } @@ -144,9 +147,9 @@ public: } // namespace Internal -inline auto EndsWith(const Utils::SmallString &suffix) +inline auto EndsWith(const Utils::SmallStringView &suffix) { - return Internal::EndsWithMatcher(suffix); + return ::testing::PolymorphicMatcher(Internal::EndsWithMatcher(suffix)); } inline auto EndsWith(const QStringView &suffix) diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index 91a45acab0c..5323f09e407 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -11,7 +11,6 @@ add_qtc_library(TestMocks OBJECT imagecachecollectormock.h mockimagecachegenerator.h mockimagecachestorage.h - mocklistmodeleditorview.h mockmutex.h mockqfilesystemwatcher.h mocksqlitestatement.h diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index c3a3be581cb..ed97e7e443c 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -7,6 +7,8 @@ #include +#include + class AbstractViewMock : public QmlDesigner::AbstractView { public: @@ -14,4 +16,44 @@ public: : QmlDesigner::AbstractView{*externalDependencies} {} MOCK_METHOD(void, nodeOrderChanged, (const QmlDesigner::NodeListProperty &listProperty), (override)); + MOCK_METHOD(void, + variantPropertiesChanged, + (const QList &propertyList, + PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, nodeCreated, (const QmlDesigner::ModelNode &createdNode), (override)); + MOCK_METHOD(void, + nodeReparented, + (const QmlDesigner::ModelNode &node, + const QmlDesigner::NodeAbstractProperty &newPropertyParent, + const QmlDesigner::NodeAbstractProperty &oldPropertyParent, + AbstractView::PropertyChangeFlags propertyChange), + (override)); + + MOCK_METHOD(void, + propertiesRemoved, + (const QList &propertyList), + (override)); + MOCK_METHOD(void, + propertiesAboutToBeRemoved, + (const QList &propertyList), + (override)); + + MOCK_METHOD(void, + bindingPropertiesChanged, + (const QList &propertyList, + PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, + bindingPropertiesAboutToBeChanged, + (const QList &propertyList), + (override)); + + MOCK_METHOD(void, + nodeRemoved, + (const QmlDesigner::ModelNode &removedNode, + const QmlDesigner::NodeAbstractProperty &parentProperty, + AbstractView::PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); }; diff --git a/tests/unit/tests/mocks/externaldependenciesmock.h b/tests/unit/tests/mocks/externaldependenciesmock.h index 37c2da1850c..673363a2148 100644 --- a/tests/unit/tests/mocks/externaldependenciesmock.h +++ b/tests/unit/tests/mocks/externaldependenciesmock.h @@ -16,8 +16,6 @@ public: MOCK_METHOD(QString, defaultPuppetToplevelBuildDirectory, (), (const, override)); MOCK_METHOD(QUrl, projectUrl, (), (const, override)); MOCK_METHOD(QString, currentProjectDirPath, (), (const, override)); - MOCK_METHOD(QList, designerSettingsEdit3DViewBackgroundColor, (), (const, override)); - MOCK_METHOD(QColor, designerSettingsEdit3DViewGridColor, (), (const, override)); MOCK_METHOD(QUrl, currentResourcePath, (), (const, override)); MOCK_METHOD(void, parseItemLibraryDescriptions, (), (override)); MOCK_METHOD(const QmlDesigner::DesignerSettings &, designerSettings, (), (const, override)); diff --git a/tests/unit/tests/mocks/mocklistmodeleditorview.h b/tests/unit/tests/mocks/mocklistmodeleditorview.h deleted file mode 100644 index df228dcf2f7..00000000000 --- a/tests/unit/tests/mocks/mocklistmodeleditorview.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../utils/googletest.h" - -#include - -class MockListModelEditorView : public QmlDesigner::AbstractView -{ -public: - MockListModelEditorView(QmlDesigner::ExternalDependenciesInterface *externalDependencies = nullptr) - : AbstractView{*externalDependencies} - {} - MOCK_METHOD(void, - variantPropertiesChanged, - (const QList &propertyList, - PropertyChangeFlags propertyChange), - (override)); - MOCK_METHOD(void, nodeCreated, (const QmlDesigner::ModelNode &createdNode), (override)); - MOCK_METHOD(void, - nodeReparented, - (const QmlDesigner::ModelNode &node, - const QmlDesigner::NodeAbstractProperty &newPropertyParent, - const QmlDesigner::NodeAbstractProperty &oldPropertyParent, - AbstractView::PropertyChangeFlags propertyChange), - (override)); - - MOCK_METHOD(void, - propertiesRemoved, - (const QList &propertyList), - (override)); - MOCK_METHOD(void, - propertiesAboutToBeRemoved, - (const QList &propertyList), - (override)); - - MOCK_METHOD(void, - bindingPropertiesChanged, - (const QList &propertyList, - PropertyChangeFlags propertyChange), - (override)); - MOCK_METHOD(void, - bindingPropertiesAboutToBeChanged, - (const QList &propertyList), - (override)); - - MOCK_METHOD(void, - nodeRemoved, - (const QmlDesigner::ModelNode &removedNode, - const QmlDesigner::NodeAbstractProperty &parentProperty, - AbstractView::PropertyChangeFlags propertyChange), - (override)); - MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); -}; diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index d83ae759acb..105b8c73469 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -51,6 +51,7 @@ ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName) incrementBasicId(moduleId); ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); + ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId)); return moduleId; } @@ -158,6 +159,20 @@ void ProjectStorageMock::createFunction(QmlDesigner::TypeId typeId, Utils::Small ON_CALL(*this, functionDeclarationNames(Eq(typeId))).WillByDefault(Return(functionNames)); } +namespace { +void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &storage) +{ + for (TypeId baseTypeId : baseTypeIds) { + for (const auto &propertyId : storage.localPropertyDeclarationIds(baseTypeId)) { + auto data = storage.propertyDeclaration(propertyId); + if (data) { + storage.createProperty(typeId, data->name, data->traits, data->propertyTypeId); + } + } + } +} +} // namespace + TypeId ProjectStorageMock::createType(ModuleId moduleId, Utils::SmallStringView typeName, Utils::SmallStringView defaultPropertyName, @@ -175,6 +190,8 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, incrementBasicId(typeId); ON_CALL(*this, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); + ON_CALL(*this, fetchTypeIdByModuleIdAndExportedName(Eq(moduleId), Eq(typeName))) + .WillByDefault(Return(typeId)); PropertyDeclarationId defaultPropertyDeclarationId; if (defaultPropertyName.size()) { if (!defaultPropertyTypeId) { @@ -203,6 +220,8 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, ON_CALL(*this, prototypeAndSelfIds(Eq(typeId))).WillByDefault(Return(selfAndPrototypes)); ON_CALL(*this, prototypeIds(Eq(typeId))).WillByDefault(Return(baseTypeIds)); + addBaseProperties(typeId, baseTypeIds, *this); + return typeId; } @@ -240,6 +259,13 @@ TypeId ProjectStorageMock::createObject(ModuleId moduleId, return createType(moduleId, typeName, Storage::TypeTraits::Reference, baseTypeIds); } +QmlDesigner::TypeId ProjectStorageMock::createValue(QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + QmlDesigner::TypeIds baseTypeIds) +{ + return createType(moduleId, typeName, Storage::TypeTraits::Value, baseTypeIds); +} + void ProjectStorageMock::setupQtQuick() { setupIsBasedOn(*this); @@ -250,8 +276,14 @@ void ProjectStorageMock::setupQtQuick() auto qtQuickModuleId = createModule("QtQuick"); auto qtQuickNativeModuleId = createModule("QtQuick-cppnative"); - createType(qmlModuleId, "int", Storage::TypeTraits::Value); - createType(qmlNativeModuleId, "float", Storage::TypeTraits::Value); + auto boolId = createValue(qmlModuleId, "bool"); + auto intId = createValue(qmlModuleId, "int"); + createValue(qmlNativeModuleId, "uint"); + auto doubleId = createValue(qmlModuleId, "double"); + createValue(qmlNativeModuleId, "float"); + createValue(qmlModuleId, "date"); + auto stringId = createValue(qmlModuleId, "string"); + createValue(qmlModuleId, "url"); auto qtObjectId = createObject(qmlModuleId, "QtObject", @@ -260,6 +292,7 @@ void ProjectStorageMock::setupQtQuick() TypeId{}); auto listElementId = createObject(qtQmlModelsModuleId, "ListElement", {qtObjectId}); + createObject(qtQmlModelsModuleId, "ListModel", "children", @@ -267,12 +300,25 @@ void ProjectStorageMock::setupQtQuick() listElementId, {qtObjectId}); + auto fontValueTypeId = createValue(qtQuickModuleId, "QQuickFontValueType"); + createProperty(fontValueTypeId, "family", stringId); + createProperty(fontValueTypeId, "styleName", stringId); + createProperty(fontValueTypeId, "underline", boolId); + createProperty(fontValueTypeId, "overline", boolId); + createProperty(fontValueTypeId, "pointSize", doubleId); + createProperty(fontValueTypeId, "pixelSize", intId); + createValue(qtQuickModuleId, "font", {fontValueTypeId}); + auto itemId = createObject(qtQuickModuleId, "Item", "data", PropertyDeclarationTraits::IsList, qtObjectId, {qtObjectId}); + + auto inputDeviceId = createObject(qtQuickModuleId, "InputDevice", {qtObjectId}); + createProperty(inputDeviceId, "seatName", stringId); + createObject(qtQuickModuleId, "ListView", "data", diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 7d0dfd9924f..61788b46db8 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -69,6 +69,10 @@ public: Utils::SmallStringView typeName, QmlDesigner::TypeIds baseTypeIds = {}); + QmlDesigner::TypeId createValue(QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + QmlDesigner::TypeIds baseTypeIds = {}); + QmlDesigner::PropertyDeclarationId createProperty( QmlDesigner::TypeId typeId, Utils::SmallString name, @@ -136,11 +140,11 @@ public: (QmlDesigner::SourceId sourceId, ::Utils::SmallStringView typeName), (override)); - MOCK_METHOD(QmlDesigner::PropertyDeclarationIds, + MOCK_METHOD((QVarLengthArray), propertyDeclarationIds, (QmlDesigner::TypeId typeId), (const, override)); - MOCK_METHOD(QmlDesigner::PropertyDeclarationIds, + MOCK_METHOD((QVarLengthArray), localPropertyDeclarationIds, (QmlDesigner::TypeId typeId), (const, override)); @@ -259,6 +263,19 @@ public: MOCK_METHOD(std::vector, fetchAllSourceContexts, (), ()); MOCK_METHOD(std::vector, fetchAllSources, (), ()); + MOCK_METHOD(QmlDesigner::SourceId, + propertyEditorPathId, + (QmlDesigner::TypeId typeId), + (const, override)); + MOCK_METHOD(QmlDesigner::ModuleId, + fetchModuleIdUnguarded, + (Utils::SmallStringView name), + (const, override)); + MOCK_METHOD(QmlDesigner::TypeId, + fetchTypeIdByModuleIdAndExportedName, + (QmlDesigner::ModuleId moduleId, Utils::SmallStringView name), + (const, override)); + QmlDesigner::Storage::Info::CommonTypeCache typeCache{*this}; }; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 84fc8f63a1c..d4654dba19b 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -473,6 +473,17 @@ std::ostream &operator<<(std::ostream &out, const NodeMetaInfo &metaInfo) return out << "(" << metaInfo.id() << ")"; } +std::ostream &operator<<(std::ostream &out, const PropertyMetaInfo &metaInfo) +{ + return out << "(" << metaInfo.type() << ", " << metaInfo.name() << ", " + << metaInfo.propertyType() << ")"; +} + +std::ostream &operator<<(std::ostream &out, const CompoundPropertyMetaInfo &metaInfo) +{ + return out << "(" << metaInfo.property << ", " << metaInfo.parent << ")"; +} + std::ostream &operator<<(std::ostream &out, const VariantProperty &property) { if (!property.isValid()) @@ -698,7 +709,10 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", fileStatuses: " << package.fileStatuses << ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds << ", updatedProjectSourceIds: " << package.updatedProjectSourceIds - << ", projectDatas: " << package.projectDatas << ")"; + << ", projectDatas: " << package.projectDatas + << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths + << ", updatedPropertyEditorQmlPathSourceIds: " + << package.updatedPropertyEditorQmlPathSourceIds << ")"; } std::ostream &operator<<(std::ostream &out, const ProjectData &data) @@ -797,6 +811,11 @@ std::ostream &operator<<(std::ostream &out, const ModuleExportedImport &import) << import.version << ", " << import.isAutoVersion << ")"; } +std::ostream &operator<<(std::ostream &out, const PropertyEditorQmlPath &path) +{ + return out << "(" << path.moduleId << ", " << path.typeName << ", " << path.pathId << ")"; +} + } // namespace Storage::Synchronization namespace ImageCache { diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index 50fdec5308d..b01bbf1fb8d 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -122,6 +122,8 @@ enum class SourceType : int; class FileStatus; class Import; class NodeMetaInfo; +class PropertyMetaInfo; +struct CompoundPropertyMetaInfo; std::ostream &operator<<(std::ostream &out, const ModelNode &node); std::ostream &operator<<(std::ostream &out, const VariantProperty &property); @@ -135,6 +137,8 @@ std::ostream &operator<<(std::ostream &out, const Import &import); std::ostream &operator<<(std::ostream &out, const ModelResourceSet::SetExpression &setExpression); std::ostream &operator<<(std::ostream &out, const ModelResourceSet &modelResourceSet); std::ostream &operator<<(std::ostream &out, const NodeMetaInfo &metaInfo); +std::ostream &operator<<(std::ostream &out, const PropertyMetaInfo &metaInfo); +std::ostream &operator<<(std::ostream &out, const CompoundPropertyMetaInfo &metaInfo); namespace Cache { class SourceContext; @@ -197,6 +201,7 @@ class SynchronizationPackage; enum class FileType : char; enum class ChangeLevel : char; class ModuleExportedImport; +class PropertyEditorQmlPath; std::ostream &operator<<(std::ostream &out, const Type &type); std::ostream &operator<<(std::ostream &out, const ExportedType &exportedType); @@ -215,6 +220,7 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag std::ostream &operator<<(std::ostream &out, FileType fileType); std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel); std::ostream &operator<<(std::ostream &out, const ModuleExportedImport &import); +std::ostream &operator<<(std::ostream &out, const PropertyEditorQmlPath &path); } // namespace Storage::Synchronization diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index be7f5376abe..9a10b6d7449 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -52,6 +52,7 @@ add_qtc_library(TestDesignerCore OBJECT imagecache/imagecachedispatchcollector.h imagecache/imagecachestorageinterface.h imagecache/synchronousimagecache.cpp + imagecache/taskqueue.h imagecache/timestampproviderinterface.h include/abstractproperty.h include/asynchronousexplicitimagecache.h diff --git a/tests/unit/tests/unittests/imagecache/CMakeLists.txt b/tests/unit/tests/unittests/imagecache/CMakeLists.txt index 7f004221d3a..32ae6937a2c 100644 --- a/tests/unit/tests/unittests/imagecache/CMakeLists.txt +++ b/tests/unit/tests/unittests/imagecache/CMakeLists.txt @@ -8,4 +8,5 @@ extend_qtc_test(unittest imagecachegenerator-test.cpp imagecachestorage-test.cpp synchronousimagecache-test.cpp + taskqueue-test.cpp ) diff --git a/tests/unit/tests/unittests/imagecache/asynchronousexplicitimagecache-test.cpp b/tests/unit/tests/unittests/imagecache/asynchronousexplicitimagecache-test.cpp index 15d8f1dd7e5..7c43f96147e 100644 --- a/tests/unit/tests/unittests/imagecache/asynchronousexplicitimagecache-test.cpp +++ b/tests/unit/tests/unittests/imagecache/asynchronousexplicitimagecache-test.cpp @@ -15,11 +15,11 @@ class AsynchronousExplicitImageCache : public testing::Test protected: Notification notification; Notification waitInThread; + NiceMock mockStorage; NiceMock> mockAbortCallback; NiceMock> mockAbortCallback2; NiceMock> mockCaptureCallback; NiceMock> mockCaptureCallback2; - NiceMock mockStorage; QmlDesigner::AsynchronousExplicitImageCache cache{mockStorage}; QImage image1{10, 10, QImage::Format_ARGB32}; QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; @@ -111,7 +111,8 @@ TEST_F(AsynchronousExplicitImageCache, request_mid_size_image_fetches_mid_size_i notification.wait(); } -TEST_F(AsynchronousExplicitImageCache, request_mid_size_image_calls_capture_callback_with_image_from_storage) +TEST_F(AsynchronousExplicitImageCache, + request_mid_size_image_calls_capture_callback_with_image_from_storage) { ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage1})); @@ -140,7 +141,8 @@ TEST_F(AsynchronousExplicitImageCache, request_mid_size_image_calls_abort_callba notification.wait(); } -TEST_F(AsynchronousExplicitImageCache, request_mid_size_image_calls_abort_callback_without_mid_size_image) +TEST_F(AsynchronousExplicitImageCache, + request_mid_size_image_calls_abort_callback_without_mid_size_image) { ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}})); @@ -168,7 +170,8 @@ TEST_F(AsynchronousExplicitImageCache, request_small_image_fetches_small_image_f notification.wait(); } -TEST_F(AsynchronousExplicitImageCache, request_small_image_calls_capture_callback_with_image_from_storage) +TEST_F(AsynchronousExplicitImageCache, + request_small_image_calls_capture_callback_with_image_from_storage) { ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1})); @@ -211,7 +214,7 @@ TEST_F(AsynchronousExplicitImageCache, request_small_image_calls_abort_callback_ notification.wait(); } -TEST_F(AsynchronousExplicitImageCache, clean_removes_entries) +TEST_F(AsynchronousExplicitImageCache, DISABLED_clean_removes_entries) { ON_CALL(mockStorage, fetchSmallImage(_, _)).WillByDefault([&](Utils::SmallStringView, auto) { return QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1}; @@ -219,8 +222,10 @@ TEST_F(AsynchronousExplicitImageCache, clean_removes_entries) ON_CALL(mockCaptureCallback2, Call(_)).WillByDefault([&](auto) { waitInThread.wait(); }); cache.requestSmallImage("/path/to/Component1.qml", mockCaptureCallback2.AsStdFunction(), - mockAbortCallback.AsStdFunction()); + mockAbortCallback2.AsStdFunction()); + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort))) + .WillOnce([&](auto) { notification.notify(); }); EXPECT_CALL(mockCaptureCallback, Call(_)).Times(0); cache.requestSmallImage("/path/to/Component3.qml", @@ -228,26 +233,32 @@ TEST_F(AsynchronousExplicitImageCache, clean_removes_entries) mockAbortCallback.AsStdFunction()); cache.clean(); waitInThread.notify(); + notification.wait(); } TEST_F(AsynchronousExplicitImageCache, clean_calls_abort) { + QmlDesigner::AsynchronousExplicitImageCache cache{mockStorage}; ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component1.qml"), _)) .WillByDefault([&](Utils::SmallStringView, auto) { + notification.notify(); waitInThread.wait(); return QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1}; }); cache.requestSmallImage("/path/to/Component1.qml", mockCaptureCallback.AsStdFunction(), mockAbortCallback2.AsStdFunction()); + notification.wait(); cache.requestSmallImage("/path/to/Component2.qml", mockCaptureCallback.AsStdFunction(), mockAbortCallback.AsStdFunction()); - EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort))); + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort))) + .WillOnce([&](auto) { notification.notify(); }); cache.clean(); waitInThread.notify(); + notification.wait(); } TEST_F(AsynchronousExplicitImageCache, after_clean_new_jobs_works) @@ -268,9 +279,10 @@ TEST_F(AsynchronousExplicitImageCache, after_clean_new_jobs_works) TEST_F(AsynchronousExplicitImageCache, request_image_with_extra_id_fetches_image_from_storage) { + ON_CALL(mockAbortCallback, Call(_)).WillByDefault([&](auto) { notification.notify(); }); + EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml+extraId1"), _)) .WillRepeatedly([&](Utils::SmallStringView, auto) { - notification.notify(); return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; }); diff --git a/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp b/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp index e98f0d70e52..b26b4b7c708 100644 --- a/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp +++ b/tests/unit/tests/unittests/imagecache/asynchronousimagefactory-test.cpp @@ -124,8 +124,12 @@ TEST_F(AsynchronousImageFactory, request_image_request_image_from_collector_if_f TEST_F(AsynchronousImageFactory, clean_removes_entries) { EXPECT_CALL(collectorMock, start(Eq("/path/to/Component1.qml"), _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); + .WillRepeatedly([&](auto, auto, auto, auto, auto) { + notification.notify(); + waitInThread.wait(); + }); factory.generate("/path/to/Component1.qml"); + notification.wait(); EXPECT_CALL(collectorMock, start(Eq("/path/to/Component3.qml"), _, _, _, _)).Times(0); diff --git a/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp b/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp index dfbcdee3dd0..878552964a8 100644 --- a/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp +++ b/tests/unit/tests/unittests/imagecache/imagecachegenerator-test.cpp @@ -391,7 +391,7 @@ TEST_F(ImageCacheGenerator, wait_for_finished) generator.generateImage( "name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); - EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(2); + EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(AtMost(2)); waitInThread.notify(); generator.waitForFinished(); diff --git a/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp b/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp new file mode 100644 index 00000000000..537f4b3ea32 --- /dev/null +++ b/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../utils/googletest.h" +#include "../utils/notification.h" + +#include + +namespace { +struct Task +{ + Task(int i) + : i{i} + {} + + int i = 5; + + friend bool operator==(Task first, Task second) { return first.i == second.i; } +}; + +template +auto IsTask(Matcher matcher) +{ + return Field(&Task::i, matcher); +} + +class TaskQueue : public testing::Test +{ +protected: + Notification notification; + Notification waitInThread; + NiceMock> mockDispatchCallback; + NiceMock> mockCleanCallback; + using Queue = QmlDesigner::TaskQueue; +}; + +TEST_F(TaskQueue, add_task_dispatches_task) +{ + Queue queue{mockDispatchCallback.AsStdFunction(), mockCleanCallback.AsStdFunction()}; + + EXPECT_CALL(mockDispatchCallback, Call(IsTask(22))).WillRepeatedly([&](Task) { + notification.notify(); + }); + + queue.addTask(22); + notification.wait(); +} + +TEST_F(TaskQueue, depatches_task_in_order) +{ + InSequence s; + Queue queue{mockDispatchCallback.AsStdFunction(), mockCleanCallback.AsStdFunction()}; + + EXPECT_CALL(mockDispatchCallback, Call(IsTask(22))).WillRepeatedly([&](Task) {}); + EXPECT_CALL(mockDispatchCallback, Call(IsTask(32))).WillRepeatedly([&](Task) { + notification.notify(); + }); + + queue.addTask(22); + queue.addTask(32); + notification.wait(); +} + +TEST_F(TaskQueue, cleanup_at_destruction) +{ + InSequence s; + ON_CALL(mockDispatchCallback, Call(IsTask(22))).WillByDefault([&](Task) { + notification.notify(); + waitInThread.wait(); + }); + + EXPECT_CALL(mockCleanCallback, Call(IsTask(32))).WillRepeatedly([&](Task) {}); + + { + Queue queue{mockDispatchCallback.AsStdFunction(), mockCleanCallback.AsStdFunction()}; + queue.addTask(22); + queue.addTask(32); + notification.wait(); + waitInThread.notify(); + } +} + +TEST_F(TaskQueue, clean_task_in_queue) +{ + InSequence s; + ON_CALL(mockDispatchCallback, Call(IsTask(22))).WillByDefault([&](Task) { + notification.notify(); + waitInThread.wait(); + }); + Queue queue{mockDispatchCallback.AsStdFunction(), mockCleanCallback.AsStdFunction()}; + queue.addTask(22); + queue.addTask(32); + + EXPECT_CALL(mockCleanCallback, Call(IsTask(32))).WillRepeatedly([&](Task) {}); + + notification.wait(); + waitInThread.notify(); + queue.clean(); +} + +} // namespace diff --git a/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp b/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp index bc05c0056c6..f8bef106acf 100644 --- a/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp +++ b/tests/unit/tests/unittests/listmodeleditor/listmodeleditor-test.cpp @@ -3,7 +3,7 @@ #include "../utils/googletest.h" -#include +#include #include #include @@ -193,7 +193,7 @@ protected: {QmlDesigner::Import::createLibraryImport("QtQml.Models"), QmlDesigner::Import::createLibraryImport("QtQuick")}, pathCacheMock.path.toQString())}; - NiceMock mockView; + NiceMock mockView; QmlDesigner::ListModelEditorModel model{[&] { return mockView.createModelNode("ListModel"); }, [&] { return mockView.createModelNode("ListElement"); }, goIntoComponentMock.AsStdFunction()}; @@ -209,7 +209,7 @@ protected: {QmlDesigner::Import::createLibraryImport("QtQml.Models"), QmlDesigner::Import::createLibraryImport("QtQuick")}, pathCacheMock.path.toQString())}; - NiceMock mockComponentView; + NiceMock mockComponentView; ModelNode componentElement; }; diff --git a/tests/unit/tests/unittests/metainfo/CMakeLists.txt b/tests/unit/tests/unittests/metainfo/CMakeLists.txt index 7f2728f078b..77a9760cc23 100644 --- a/tests/unit/tests/unittests/metainfo/CMakeLists.txt +++ b/tests/unit/tests/unittests/metainfo/CMakeLists.txt @@ -2,4 +2,5 @@ extend_qtc_test(unittest SOURCES nodemetainfo-test.cpp + propertymetainfo-test.cpp ) diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index 4d645ec1681..c1f3b4f8d7c 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -22,6 +22,26 @@ auto PropertyId(const Matcher &matcher) return Property(&QmlDesigner::PropertyMetaInfo::id, matcher); } +template +auto CompoundProperty(const PropertyMatcher &propertyMatcher, + const ParentPropertyMatcher &parentPropertyMatcher, + const NameMatcher &nameMatcher) +{ + return AllOf(Field(&QmlDesigner::CompoundPropertyMetaInfo::property, propertyMatcher), + Field(&QmlDesigner::CompoundPropertyMetaInfo::parent, parentPropertyMatcher), + Property(&QmlDesigner::CompoundPropertyMetaInfo::name, nameMatcher)); +} + +template +auto CompoundPropertyIds(const PropertyIdMatcher &propertyIdMatcher, + const ParentPropertyIdMatcher &parentPropertyIdMatcher, + const NameMatcher &nameMatcher) +{ + return CompoundProperty(PropertyId(propertyIdMatcher), + PropertyId(parentPropertyIdMatcher), + nameMatcher); +} + class NodeMetaInfo : public testing::Test { protected: @@ -307,6 +327,74 @@ TEST_F(NodeMetaInfo, invalid_has_not_property) ASSERT_FALSE(hasProperty); } +TEST_F(NodeMetaInfo, has_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", itemMetaInfo.id()); + + bool hasProperty = metaInfo.hasProperty("parent.data"); + + ASSERT_TRUE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_no_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + + bool hasProperty = metaInfo.hasProperty("foo.data"); + + ASSERT_FALSE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_no_dot_property_for_last_entry) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + bool hasProperty = metaInfo.hasProperty("parent.foo"); + + ASSERT_FALSE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_dot_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + bool hasProperty = metaInfo.hasProperty("parent.parent.data"); + + ASSERT_TRUE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_no_dot_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + + bool hasProperty = metaInfo.hasProperty("parent.foo.data"); + + ASSERT_FALSE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_no_dot_dot_property_for_last_entry) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + bool hasProperty = metaInfo.hasProperty("parent.parent.foo"); + + ASSERT_FALSE(hasProperty); +} + +TEST_F(NodeMetaInfo, has_no_dot_dot_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + bool hasProperty = metaInfo.hasProperty("parent.parent.parent.data"); + + ASSERT_FALSE(hasProperty); +} + TEST_F(NodeMetaInfo, get_property) { auto metaInfo = model.qtQuickItemMetaInfo(); @@ -345,6 +433,46 @@ TEST_F(NodeMetaInfo, get_invalid_property_if_meta_info_is_invalid) ASSERT_THAT(property, PropertyId(IsFalse())); } +TEST_F(NodeMetaInfo, get_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + auto propertyId = metaInfo.property("data").id(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + auto property = metaInfo.property("parent.data"); + + ASSERT_THAT(property, PropertyId(propertyId)); +} + +TEST_F(NodeMetaInfo, get_no_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + + auto property = metaInfo.property("foo.data"); + + ASSERT_THAT(property, PropertyId(IsFalse())); +} + +TEST_F(NodeMetaInfo, get_no_dot_property_for_last_entry) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + auto property = metaInfo.property("parent.foo"); + + ASSERT_THAT(property, PropertyId(IsFalse())); +} + +TEST_F(NodeMetaInfo, get_no_dot_dot_dot_property) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + projectStorageMock.createProperty(metaInfo.id(), "parent", metaInfo.id()); + + auto property = metaInfo.property("parent.parent.parent.data"); + + ASSERT_THAT(property, PropertyId(IsFalse())); +} + TEST_F(NodeMetaInfo, get_properties) { auto metaInfo = model.qtQuickItemMetaInfo(); @@ -375,6 +503,72 @@ TEST_F(NodeMetaInfo, get_no_properties_if_is_invalid) ASSERT_THAT(properties, IsEmpty()); } +TEST_F(NodeMetaInfo, inflate_value_properties) +{ + auto metaInfo = model.qtQuickItemMetaInfo(); + auto fontTypeId = projectStorageMock.typeId(qtQuickModuleId, "font"); + auto xPropertyId = projectStorageMock.createProperty(metaInfo.id(), "x", intTypeId); + auto fontPropertyId = projectStorageMock.createProperty(metaInfo.id(), "font", fontTypeId); + auto familyPropertyId = projectStorageMock.propertyDeclarationId(fontTypeId, "family"); + auto pixelSizePropertyId = projectStorageMock.propertyDeclarationId(fontTypeId, "pixelSize"); + + auto properties = QmlDesigner::MetaInfoUtils::inflateValueProperties(metaInfo.properties()); + + ASSERT_THAT(properties, + AllOf(Contains(CompoundPropertyIds(xPropertyId, IsFalse(), "x")), + Not(Contains(CompoundPropertyIds(fontPropertyId, IsFalse(), _))), + Contains(CompoundPropertyIds(familyPropertyId, fontPropertyId, "font.family")), + Contains( + CompoundPropertyIds(pixelSizePropertyId, fontPropertyId, "font.pixelSize")))); +} + +TEST_F(NodeMetaInfo, inflate_value_properties_handles_invalid) +{ + QmlDesigner::PropertyMetaInfos propertiesWithInvalid = {QmlDesigner::PropertyMetaInfo{}}; + + auto properties = QmlDesigner::MetaInfoUtils::inflateValueProperties(propertiesWithInvalid); + + ASSERT_THAT(properties, ElementsAre(CompoundProperty(IsFalse(), IsFalse(), IsEmpty()))); +} + +TEST_F(NodeMetaInfo, inflate_value_and_readonly_properties) +{ + using QmlDesigner::Storage::PropertyDeclarationTraits; + auto metaInfo = model.qtQuickItemMetaInfo(); + auto fontTypeId = projectStorageMock.typeId(qtQuickModuleId, "font"); + auto inputDeviceId = projectStorageMock.typeId(qtQuickModuleId, "InputDevice"); + auto xPropertyId = projectStorageMock.createProperty(metaInfo.id(), "x", intTypeId); + auto fontPropertyId = projectStorageMock.createProperty(metaInfo.id(), "font", fontTypeId); + auto familyPropertyId = projectStorageMock.propertyDeclarationId(fontTypeId, "family"); + auto pixelSizePropertyId = projectStorageMock.propertyDeclarationId(fontTypeId, "pixelSize"); + auto devicePropertyId = projectStorageMock.createProperty(metaInfo.id(), + "device", + PropertyDeclarationTraits::IsReadOnly, + inputDeviceId); + auto seatNamePropertyId = projectStorageMock.propertyDeclarationId(inputDeviceId, "seatName"); + + auto properties = QmlDesigner::MetaInfoUtils::inflateValueAndReadOnlyProperties( + metaInfo.properties()); + + ASSERT_THAT( + properties, + AllOf(Contains(CompoundPropertyIds(xPropertyId, IsFalse(), "x")), + Not(Contains(CompoundPropertyIds(fontPropertyId, IsFalse(), _))), + Contains(CompoundPropertyIds(familyPropertyId, fontPropertyId, "font.family")), + Contains(CompoundPropertyIds(pixelSizePropertyId, fontPropertyId, "font.pixelSize")), + Contains(CompoundPropertyIds(seatNamePropertyId, devicePropertyId, "device.seatName")))); +} + +TEST_F(NodeMetaInfo, inflate_value_and_readonly_properties_handles_invalid) +{ + QmlDesigner::PropertyMetaInfos propertiesWithInvalid = {QmlDesigner::PropertyMetaInfo{}}; + + auto properties = QmlDesigner::MetaInfoUtils::inflateValueAndReadOnlyProperties( + propertiesWithInvalid); + + ASSERT_THAT(properties, ElementsAre(CompoundProperty(IsFalse(), IsFalse(), IsEmpty()))); +} + TEST_F(NodeMetaInfo, get_local_properties) { auto metaInfo = model.qtQuickItemMetaInfo(); @@ -2345,4 +2539,70 @@ TEST_F(NodeMetaInfo, default_is_not_number) ASSERT_THAT(isType, IsFalse()); } + +TEST_F(NodeMetaInfo, property_editor_specifics_path) +{ + auto metaInfo = createMetaInfo("QtQuick", "Item"); + auto pathId = QmlDesigner::SourceId::create(45); + ON_CALL(projectStorageMock, propertyEditorPathId(metaInfo.id())).WillByDefault(Return(pathId)); + + auto id = metaInfo.propertyEditorPathId(); + + ASSERT_THAT(id, pathId); +} + +TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty) +{ + QmlDesigner::NodeMetaInfo metaInfo; + + auto id = metaInfo.propertyEditorPathId(); + + ASSERT_THAT(id, IsFalse()); +} + +TEST_F(NodeMetaInfo, is_reference) +{ + auto metaInfo = createMetaInfo("QtQuick", "Item", QmlDesigner::Storage::TypeTraits::Reference); + + auto type = metaInfo.type(); + + ASSERT_THAT(type, QmlDesigner::MetaInfoType::Reference); +} + +TEST_F(NodeMetaInfo, is_value) +{ + auto metaInfo = createMetaInfo("QML", "bool", QmlDesigner::Storage::TypeTraits::Value); + + auto type = metaInfo.type(); + + ASSERT_THAT(type, QmlDesigner::MetaInfoType::Value); +} + +TEST_F(NodeMetaInfo, is_sequence) +{ + auto metaInfo = createMetaInfo("QML", "QObjectList", QmlDesigner::Storage::TypeTraits::Sequence); + + auto type = metaInfo.type(); + + ASSERT_THAT(type, QmlDesigner::MetaInfoType::Sequence); +} + +TEST_F(NodeMetaInfo, is_none) +{ + auto metaInfo = createMetaInfo("QML", "void", QmlDesigner::Storage::TypeTraits::None); + + auto type = metaInfo.type(); + + ASSERT_THAT(type, QmlDesigner::MetaInfoType::None); +} + +TEST_F(NodeMetaInfo, default_is_none) +{ + QmlDesigner::NodeMetaInfo metaInfo; + + auto type = metaInfo.type(); + + ASSERT_THAT(type, QmlDesigner::MetaInfoType::None); +} + } // namespace diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp new file mode 100644 index 00000000000..923743799e3 --- /dev/null +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -0,0 +1,960 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../utils/googletest.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace { + +using QmlDesigner::Enumeration; +using QmlDesigner::ModelNode; +using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::PropertyDeclarationTraits; +using QmlDesigner::Storage::TypeTraits; + +class PropertyMetaInfo : public ::testing::Test +{ +protected: + QmlDesigner::NodeMetaInfo createNodeMetaInfo(Utils::SmallStringView moduleName, + Utils::SmallStringView typeName, + QmlDesigner::Storage::TypeTraits typeTraits = {}) + { + auto moduleId = projectStorageMock.createModule(moduleName); + auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits); + + return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}; + } + +protected: + NiceMock pathCache{"/path/foo.qml"}; + NiceMock projectStorageMock{pathCache.sourceId}; + QmlDesigner::Model model{{projectStorageMock, pathCache}, + "Item", + {QmlDesigner::Import::createLibraryImport("QML"), + QmlDesigner::Import::createLibraryImport("QtQuick"), + QmlDesigner::Import::createLibraryImport("QtQml.Models")}, + QUrl::fromLocalFile(pathCache.path.toQString())}; + QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", "Foo"); +}; + +TEST_F(PropertyMetaInfo, name) +{ + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + auto name = propertyInfo.name(); + + ASSERT_THAT(name, "bar"); +} + +TEST_F(PropertyMetaInfo, default_has_no_name) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + auto name = propertyInfo.name(); + + ASSERT_THAT(name, IsEmpty()); +} + +TEST_F(PropertyMetaInfo, property_type) +{ + auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + auto type = propertyInfo.propertyType(); + + ASSERT_THAT(type, barInfo); +} + +TEST_F(PropertyMetaInfo, default_hads_invalid_property_type) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + auto type = propertyInfo.propertyType(); + + ASSERT_THAT(type, IsFalse()); +} + +TEST_F(PropertyMetaInfo, type) +{ + auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + auto type = propertyInfo.type(); + + ASSERT_THAT(type, nodeInfo); +} + +TEST_F(PropertyMetaInfo, default_has_invalid_type) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + auto type = propertyInfo.type(); + + ASSERT_THAT(type, IsFalse()); +} + +TEST_F(PropertyMetaInfo, is_writable) +{ + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isList = propertyInfo.isWritable(); + + ASSERT_THAT(isList, IsTrue()); +} + +TEST_F(PropertyMetaInfo, is_not_writable) +{ + projectStorageMock.createProperty(nodeInfo.id(), + "bar", + PropertyDeclarationTraits::IsReadOnly, + nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isList = propertyInfo.isWritable(); + + ASSERT_THAT(isList, IsFalse()); +} + +TEST_F(PropertyMetaInfo, default_is_not_writable) +{ + projectStorageMock.createProperty(nodeInfo.id(), + "bar", + PropertyDeclarationTraits::IsReadOnly, + nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isList = propertyInfo.isWritable(); + + ASSERT_THAT(isList, IsFalse()); +} + +TEST_F(PropertyMetaInfo, is_list) +{ + projectStorageMock.createProperty(nodeInfo.id(), + "bar", + PropertyDeclarationTraits::IsList, + nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isList = propertyInfo.isListProperty(); + + ASSERT_THAT(isList, IsTrue()); +} + +TEST_F(PropertyMetaInfo, is_not_list) +{ + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isList = propertyInfo.isListProperty(); + + ASSERT_THAT(isList, IsFalse()); +} + +TEST_F(PropertyMetaInfo, default_is_not_list) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + bool isList = propertyInfo.isListProperty(); + + ASSERT_THAT(isList, IsFalse()); +} + +TEST_F(PropertyMetaInfo, is_enumeration) +{ + auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, enumInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isEnum = propertyInfo.isEnumType(); + + ASSERT_THAT(isEnum, IsTrue()); +} + +TEST_F(PropertyMetaInfo, is_not_enumeration) +{ + auto notEnumInfo = createNodeMetaInfo("QtQuick", "NoEnum", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, notEnumInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isEnum = propertyInfo.isEnumType(); + + ASSERT_THAT(isEnum, IsFalse()); +} + +TEST_F(PropertyMetaInfo, default_is_not_enumeration) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + bool isEnum = propertyInfo.isEnumType(); + + ASSERT_THAT(isEnum, IsFalse()); +} + +TEST_F(PropertyMetaInfo, is_private) +{ + projectStorageMock.createProperty(nodeInfo.id(), "__bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("__bar"); + + bool isPrivate = propertyInfo.isPrivate(); + + ASSERT_THAT(isPrivate, IsTrue()); +} + +TEST_F(PropertyMetaInfo, is_not_private) +{ + projectStorageMock.createProperty(nodeInfo.id(), "_bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("_bar"); + + bool isPrivate = propertyInfo.isPrivate(); + + ASSERT_THAT(isPrivate, IsFalse()); +} + +TEST_F(PropertyMetaInfo, default_is_not_private) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + bool isPrivate = propertyInfo.isPrivate(); + + ASSERT_THAT(isPrivate, IsFalse()); +} + +TEST_F(PropertyMetaInfo, is_pointer) +{ + projectStorageMock.createProperty(nodeInfo.id(), + "bar", + PropertyDeclarationTraits::IsPointer, + nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isPointer = propertyInfo.isPointer(); + + ASSERT_THAT(isPointer, IsTrue()); +} + +TEST_F(PropertyMetaInfo, is_not_pointer) +{ + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, nodeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + + bool isPointer = propertyInfo.isPointer(); + + ASSERT_THAT(isPointer, IsFalse()); +} + +TEST_F(PropertyMetaInfo, default_is_not_pointer) +{ + QmlDesigner::PropertyMetaInfo propertyInfo; + + bool isPointer = propertyInfo.isPointer(); + + ASSERT_THAT(isPointer, IsFalse()); +} + +TEST_F(PropertyMetaInfo, cast_to_enumeration) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + Enumeration enumeration{"MyEnum.Foo"}; + auto value = QVariant::fromValue(enumeration); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(enumeration)); +} + +TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_property_type_is_not_enumeration) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + Enumeration enumeration{"MyEnum.Foo"}; + auto value = QVariant::fromValue(enumeration); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, QVariantIsValid(IsFalse())); +} + +TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", TypeTraits::IsEnum); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"enumeration"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, QVariantIsValid(IsFalse())); +} + +TEST_F(PropertyMetaInfo, cast_to_model_node) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(model.rootModelNode()); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, value); +} + +TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the_property_type_is_var) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant("foo")); +} + +TEST_F(PropertyMetaInfo, cast_double_to_double) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14.2); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14.2)); +} + +TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14.0)); +} + +TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_float_to_float) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14.2f); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14.2f)); +} + +TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14.0)); +} + +TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_int_to_int) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14)); +} + +TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14.2); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(14)); +} + +TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(0)); +} + +TEST_F(PropertyMetaInfo, cast_bool_to_bool) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(true); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14.2f); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14.2); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14L); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14LL); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(true)); +} + +TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(false)); +} + +TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(false)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_string) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant("foo")); +} + +TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QByteArray{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsEmpty())); +} + +TEST_F(PropertyMetaInfo, cast_int_to_empty_string) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsEmpty())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_empty_string) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsEmpty())); +} + +TEST_F(PropertyMetaInfo, cast_datatime_to_datetime) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto dataTime = QDateTime::currentDateTime(); + auto value = QVariant::fromValue(dataTime); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(dataTime)); +} + +TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"Monday"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_url_to_url) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto url = QUrl("http://www.qt.io/future"); + auto value = QVariant::fromValue(url); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(url)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_empty_url) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant::fromValue(QString{"http://www.qt.io/future"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsEmpty())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_empty_url) +{ + auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsEmpty())); +} + +TEST_F(PropertyMetaInfo, cast_color_to_color) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto color = QColor(Qt::red); + auto value = QVariant(color); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(color)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_null_color) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant("red"); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(Not(IsValid()))); +} + +TEST_F(PropertyMetaInfo, cast_int_to_null_color) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(14); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(Not(IsValid()))); +} + +TEST_F(PropertyMetaInfo, cast_default_to_null_color) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(Not(IsValid()))); +} + +TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto vector2d = QVector2D{32.2f, 2.2f}; + auto value = QVariant(vector2d); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(vector2d)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(12); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QVector3D{32.2f, 2.2f, 784.f}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto vector3d = QVector3D{32.2f, 2.2f, 44.4f}; + auto value = QVariant(vector3d); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(vector3d)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(12); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QVector4D{32.2f, 2.2f, 784.f, 99.f}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto vector4d = QVector4D{32.2f, 2.2f, 44.4f, 23.f}; + auto value = QVariant(vector4d); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(vector4d)); +} + +TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QString{"foo"}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(12); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(QVector2D{32.2f, 2.2f}); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, cast_default_to_vector4d_returns_an_empty_vector4d) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, IsQVariant(IsNull())); +} + +TEST_F(PropertyMetaInfo, default_cast_to_invalid_variant) +{ + auto propertyInfo = QmlDesigner::PropertyMetaInfo{}; + auto value = QVariant(43); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, Not(IsValid())); +} + +TEST_F(PropertyMetaInfo, not_existing_property_cast_returns_invalid_value) +{ + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyInfo = nodeInfo.property("bar"); + auto value = QVariant(43); + + auto castedValue = propertyInfo.castedValue(value); + + ASSERT_THAT(castedValue, Not(IsValid())); +} +} // namespace diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 83a8d936c6d..52cc04564a8 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -4,7 +4,7 @@ #include "../utils/googletest.h" #include -#include +#include #include #include #include @@ -77,7 +77,7 @@ protected: } protected: - NiceMock viewMock; + NiceMock viewMock; NiceMock pathCacheMock{"/path/foo.qml"}; NiceMock projectStorageMock{pathCacheMock.sourceId}; NiceMock resourceManagementMock; diff --git a/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp b/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp index 38f4a5591bb..40b94f872d5 100644 --- a/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp +++ b/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp @@ -3,7 +3,7 @@ #include "../../utils/googletest.h" -#include "../mocks/mocklistmodeleditorview.h" +#include "../mocks/abstractviewmock.h" #include "../mocks/modelresourcemanagementmock.h" #include "../mocks/projectstoragemock.h" #include "../mocks/sourcepathcachemock.h" @@ -70,7 +70,7 @@ protected: } protected: - NiceMock viewMock; + NiceMock viewMock; NiceMock pathCacheMock{"/path/foo.qml"}; NiceMock projectStorageMock{pathCacheMock.sourceId}; QmlDesigner::ModelResourceManagement management; diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index fbf07ed9a54..e666493e60c 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -234,11 +234,15 @@ private: MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) { + using std::begin; + using std::end; return std::is_sorted(begin(arg), end(arg)); } MATCHER(StringsAreSorted, std::string(negation ? "isn't sorted" : "is sorted")) { + using std::begin; + using std::end; return std::is_sorted(begin(arg), end(arg), [](const auto &first, const auto &second) { return Sqlite::compare(first, second) < 0; }); @@ -391,10 +395,32 @@ protected: package.updatedModuleDependencySourceIds.push_back(sourceId1); importsSourceId1.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); - moduleDependenciesSourceId1.emplace_back(QMLModuleId, - Storage::Version{}, - sourceId1); + moduleDependenciesSourceId1.emplace_back(QMLModuleId, Storage::Version{}, sourceId1); + package.types.push_back( + Storage::Synchronization::Type{"bool", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, + "bool"}}}); + package.types.push_back( + Storage::Synchronization::Type{"int", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, + "int"}}}); + package.types.push_back( + Storage::Synchronization::Type{"uint", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLNativeModuleId, + "uint"}}}); package.types.push_back( Storage::Synchronization::Type{"double", Storage::Synchronization::ImportedType{}, @@ -403,6 +429,38 @@ protected: sourceId1, {Storage::Synchronization::ExportedType{QMLModuleId, "double"}}}); + package.types.push_back( + Storage::Synchronization::Type{"float", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLNativeModuleId, + "float"}}}); + package.types.push_back( + Storage::Synchronization::Type{"date", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, + "date"}}}); + package.types.push_back( + Storage::Synchronization::Type{"string", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, + "string"}}}); + package.types.push_back( + Storage::Synchronization::Type{"url", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Value, + sourceId1, + {Storage::Synchronization::ExportedType{QMLModuleId, + "url"}}}); package.types.push_back( Storage::Synchronization::Type{"var", Storage::Synchronization::ImportedType{}, @@ -965,6 +1023,48 @@ protected: return package; } + auto createPropertyEditorPathsSynchronizationPackage() + { + SynchronizationPackage package; + + package.updatedModuleIds.push_back(qtQuickModuleId); + package.types.push_back(Storage::Synchronization::Type{ + "QQuickItem", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + sourceId1, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{1, 0}}}}); + package.types.push_back( + Storage::Synchronization::Type{"QObject", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{ + qtQuickModuleId, "QtObject", Storage::Version{1, 0}}}}); + package.updatedModuleIds.push_back(qtQuick3DModuleId); + package.types.push_back( + Storage::Synchronization::Type{"QQuickItem3d", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraits::Reference, + sourceId3, + {Storage::Synchronization::ExportedType{ + qtQuickModuleId, "Item3D", Storage::Version{1, 0}}}}); + + package.imports.emplace_back(qtQuick3DModuleId, Storage::Version{1}, sourceId4); + + package.updatedSourceIds = {sourceId1, sourceId2, sourceId3}; + + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "QtObject", sourceId1, sourceIdPath); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item", sourceId2, sourceIdPath); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath); + package.updatedPropertyEditorQmlPathSourceIds.emplace_back(sourceIdPath); + + return package; + } + template static FileStatuses convert(const Range &range) { @@ -1027,12 +1127,14 @@ protected: QmlDesigner::SourcePathView path4{"/path4/to"}; QmlDesigner::SourcePathView path5{"/path5/to"}; QmlDesigner::SourcePathView path6{"/path6/to"}; + QmlDesigner::SourcePathView pathPath{"/path6/."}; SourceId sourceId1{sourcePathCache.sourceId(path1)}; SourceId sourceId2{sourcePathCache.sourceId(path2)}; SourceId sourceId3{sourcePathCache.sourceId(path3)}; SourceId sourceId4{sourcePathCache.sourceId(path4)}; SourceId sourceId5{sourcePathCache.sourceId(path5)}; SourceId sourceId6{sourcePathCache.sourceId(path6)}; + SourceId sourceIdPath{sourcePathCache.sourceId(path6)}; SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")}; SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")}; ModuleId qmlModuleId{storage.moduleId("Qml")}; @@ -1043,6 +1145,7 @@ protected: ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D")}; ModuleId myModuleModuleId{storage.moduleId("MyModule")}; ModuleId QMLModuleId{storage.moduleId("QML")}; + ModuleId QMLNativeModuleId{storage.moduleId("QML-cppnative")}; Storage::Imports importsSourceId1; Storage::Imports importsSourceId2; Storage::Imports importsSourceId3; @@ -5067,18 +5170,20 @@ TEST_F(ProjectStorage, throw_for_invalid_source_id_in_project_data) QmlDesigner::ProjectDataHasInvalidSourceId); } -TEST_F(ProjectStorage, throw_for_invalid_module_id_in_project_data) +TEST_F(ProjectStorage, insert_project_data_with_invalid_module_id) { Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, sourceId1, ModuleId{}, Storage::Synchronization::FileType::QmlDocument}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidModuleId); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + + ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(projectData1)); } -TEST_F(ProjectStorage, throw_for_updating_with_invalid_module_id_in_project_data) +TEST_F(ProjectStorage, update_project_data_with_invalid_module_id) { Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, sourceId1, @@ -5087,8 +5192,10 @@ TEST_F(ProjectStorage, throw_for_updating_with_invalid_module_id_in_project_data storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); projectData1.moduleId = ModuleId{}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidModuleId); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + + ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(projectData1)); } TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_project_data) @@ -6577,6 +6684,15 @@ TEST_F(ProjectStorage, get_common_type_after_changing_type) ASSERT_THAT(typeId, fetchTypeId(sourceId1, "QQuickItem2")); } +TEST_F(ProjectStorage, type_ids_without_properties_get_initialized) +{ + auto package{createBuiltinSynchronizationPackage()}; + + storage.synchronize(package); + + ASSERT_THAT(storage.commonTypeCache().typeIdsWithoutProperties(), Each(IsTrue())); +} + TEST_F(ProjectStorage, get_builtin_type) { auto package{createBuiltinSynchronizationPackage()}; @@ -6602,14 +6718,14 @@ TEST_F(ProjectStorage, get_builtin_type_after_changing_type) { auto package{createBuiltinSynchronizationPackage()}; storage.synchronize(package); - auto oldTypeId = storage.builtinTypeId(); - package.types.front().typeName = "float"; + auto oldTypeId = storage.builtinTypeId(); + package.types.front().typeName = "bool2"; storage.synchronize(package); - auto typeId = storage.builtinTypeId(); + auto typeId = storage.builtinTypeId(); ASSERT_THAT(typeId, Ne(oldTypeId)); - ASSERT_THAT(typeId, fetchTypeId(sourceId1, "float")); + ASSERT_THAT(typeId, fetchTypeId(sourceId1, "bool2")); } TEST_F(ProjectStorage, get_builtin_string_type) @@ -7007,4 +7123,73 @@ TEST_F(ProjectStorage, get_no_exported_type_names_for_source_id_for_non_matching ASSERT_THAT(exportedTypeNames, IsEmpty()); } + +TEST_F(ProjectStorage, get_property_editor_path_is) +{ + TypeId typeId = TypeId::create(21); + SourceId sourceId = SourceId::create(5); + storage.setPropertyEditorPathId(typeId, sourceId); + + auto id = storage.propertyEditorPathId(typeId); + + ASSERT_THAT(id, sourceId); +} + +TEST_F(ProjectStorage, get_empty_property_editor_specifics_path_id_if_not_exists) +{ + TypeId typeId = TypeId::create(21); + + auto id = storage.propertyEditorPathId(typeId); + + ASSERT_THAT(id, IsFalse()); +} + +TEST_F(ProjectStorage, synchronize_property_editor_paths) +{ + auto package{createPropertyEditorPathsSynchronizationPackage()}; + + storage.synchronize(package); + + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId2, "QObject")), sourceId1); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId1, "QQuickItem")), sourceId2); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId3, "QQuickItem3d")), sourceId3); +} + +TEST_F(ProjectStorage, synchronize_property_editor_paths_removes_path) +{ + auto package{createPropertyEditorPathsSynchronizationPackage()}; + storage.synchronize(package); + package.propertyEditorQmlPaths.pop_back(); + + storage.synchronize(package); + + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId2, "QObject")), sourceId1); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId1, "QQuickItem")), sourceId2); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId3, "QQuickItem3d")), IsFalse()); +} + +TEST_F(ProjectStorage, synchronize_property_editor_adds_path) +{ + auto package{createPropertyEditorPathsSynchronizationPackage()}; + package.propertyEditorQmlPaths.pop_back(); + storage.synchronize(package); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath); + + storage.synchronize(package); + + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId2, "QObject")), sourceId1); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId1, "QQuickItem")), sourceId2); + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId3, "QQuickItem3d")), sourceId3); +} + +TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name) +{ + auto package{createPropertyEditorPathsSynchronizationPackage()}; + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item4D", sourceId4, sourceIdPath); + + storage.synchronize(package); + + ASSERT_THAT(storage.propertyEditorPathId(fetchTypeId(sourceId4, "Item4D")), IsFalse()); +} + } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 6a0a294771c..2b7c4b83648 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -117,7 +117,20 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) && package.updatedSourceIds.empty() && package.projectDatas.empty() && package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty() && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty() - && package.moduleExportedImports.empty() && package.updatedModuleIds.empty(); + && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() + && package.propertyEditorQmlPaths.empty() + && package.updatedPropertyEditorQmlPathSourceIds.empty(); +} + +template +auto IsPropertyEditorQmlPath(const ModuleIdMatcher &moduleIdMatcher, + const TypeNameMatcher &typeNameMatcher, + const SourceIdMatcher &pathIdMatcher) +{ + using QmlDesigner::Storage::Synchronization::PropertyEditorQmlPath; + return AllOf(Field(&PropertyEditorQmlPath::moduleId, moduleIdMatcher), + Field(&PropertyEditorQmlPath::typeName, typeNameMatcher), + Field(&PropertyEditorQmlPath::pathId, pathIdMatcher)); } class ProjectStorageUpdater : public testing::Test @@ -378,7 +391,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_dir_paths_if_file_status_is_di EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir")))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) @@ -387,7 +400,7 @@ TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_types) @@ -399,7 +412,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_types) EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_types_if_project_storage_file_status_is_invalid) @@ -412,7 +425,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_types_if_project_storage_file_ EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_types) @@ -431,7 +444,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_types) EXPECT_CALL(qmlTypesParserMock, parse(qmltypes2, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) @@ -440,7 +453,7 @@ TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_types) @@ -475,7 +488,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_exists) @@ -483,7 +496,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_e Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; setFilesDontExists({qmltypesPathSourceId}); - ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlTypesFile); + ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlTypesFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_types_are_empty_if_file_does_not_changed) @@ -496,7 +509,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types_are_empty_if_file_does_not_c EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_documents) @@ -517,7 +530,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_documents) EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/OldSecond.qml")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_documents) @@ -538,7 +551,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_documents) EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _)); EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _, _)); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_documents_with_non_existing_qml_document_throws) @@ -547,7 +560,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_documents_with_non_existing_qml_document NonexitingType 1.0 NonexitingType.qml)"}; setContent(u"/path/qmldir", qmldir); - ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlDocumentFile); + ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents) @@ -620,7 +633,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) @@ -676,7 +689,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) @@ -739,7 +752,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) @@ -794,7 +807,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) @@ -852,7 +865,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) @@ -907,7 +920,7 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_date) @@ -977,7 +990,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) @@ -1028,7 +1041,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) qmlDocumentSourceId2)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some_updated_files) @@ -1063,7 +1076,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_removed_files) @@ -1078,7 +1091,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_rem setFilesDontChanged({qmlDirPathSourceId, qmltypes2PathSourceId, qmlDocumentSourceId2}); setFilesRemoved({qmltypesPathSourceId, qmlDocumentSourceId1}); - ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlTypesFile); + ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlTypesFile); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_removed_files) @@ -1130,7 +1143,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem exampleCppNativeModuleId, FileType::QmlTypes)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) @@ -1145,34 +1158,36 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) Field(&SynchronizationPackage::projectDatas, IsEmpty()), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {}); + updater.update({}, {}, {}); } TEST_F(ProjectStorageUpdater, update_qml_types_files) { EXPECT_CALL(projectStorageMock, - synchronize(AllOf( - Field(&SynchronizationPackage::imports, UnorderedElementsAre(import4, import5)), - Field(&SynchronizationPackage::types, UnorderedElementsAre(objectType, itemType)), - Field(&SynchronizationPackage::updatedSourceIds, - UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::fileStatuses, - UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), - IsFileStatus(qmltypes2PathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedFileStatusSourceIds, - UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(qmltypes2PathSourceId, - qmltypes2PathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); + synchronize( + AllOf(Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre(objectType, itemType)), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21), + IsFileStatus(qmltypes2PathSourceId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes), + IsProjectData(qmltypes2PathSourceId, + qmltypes2PathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); } TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) @@ -1196,7 +1211,7 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) FileType::QmlTypes))), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_but_same_type_name_and_file_name) @@ -1239,7 +1254,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name_but_same_version_and_file_name) @@ -1280,7 +1295,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, dont_synchronize_selectors) @@ -1298,7 +1313,7 @@ TEST_F(ProjectStorageUpdater, dont_synchronize_selectors) Contains(Field(&Storage::Synchronization::Type::exportedTypes, Contains(IsExportedType(exampleModuleId, "FirstType", 1, 0)))))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies) @@ -1323,7 +1338,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies) Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_double_entries) @@ -1349,7 +1364,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_double_entrie Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_colliding_imports) @@ -1375,7 +1390,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_colliding_imp Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_dependencies) @@ -1392,7 +1407,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_dependencies) Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports) @@ -1434,7 +1449,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_imports) @@ -1448,7 +1463,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) @@ -1491,7 +1506,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) @@ -1533,7 +1548,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directories) @@ -1543,7 +1558,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directories) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId, path3SourceId}}))); - updater.update(directories3, {}); + updater.update(directories3, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_exists) @@ -1555,7 +1570,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_exists) QmlDesigner::SourceType::Directory, {path1SourceId, path3SourceId}}))); - updater.update(directories3, {}); + updater.update(directories3, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_changed) @@ -1567,7 +1582,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_changed) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_removed) @@ -1578,7 +1593,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_removed) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {path2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldirs) @@ -1588,7 +1603,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldirs) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}); + updater.update(directories3, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_exists) @@ -1600,7 +1615,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_exists) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}); + updater.update(directories3, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_changed) @@ -1612,7 +1627,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_changed) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_removed) @@ -1623,7 +1638,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_removed) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmldir2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files) @@ -1640,7 +1655,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_dont_changed) @@ -1658,7 +1673,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_dont_changed) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) @@ -1676,7 +1691,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont_changed) @@ -1699,7 +1714,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_in_qmldir) @@ -1718,7 +1733,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_in_qmldir) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_in_qmldir_dont_changed) @@ -1736,7 +1751,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_in_qmldir_ QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) @@ -1753,7 +1768,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories_dont_changed) @@ -1774,7 +1789,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}); + updater.update(directories2, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_builtin_qmltypes_files) @@ -1789,7 +1804,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_builtin_qmltypes_files) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}); + updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) @@ -1856,7 +1871,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_qml_document_does_not_exists) @@ -1864,7 +1879,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if setFilesDontExists({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({directoryPathSourceId}); - ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlDocumentFile); + ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) @@ -1894,7 +1909,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_document) @@ -1943,7 +1958,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_qml_document) @@ -1982,7 +1997,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}); + updater.update(directories, {}, {}); } TEST_F(ProjectStorageUpdater, watcher_updates_directories) @@ -3262,22 +3277,23 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ synchronize(AllOf( Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import4, import5)), - Field(&SynchronizationPackage::types, - UnorderedElementsAre( - Eq(objectType), - Eq(itemType), - AllOf(IsStorageType("First.qml", - Storage::Synchronization::ImportedType{"Object"}, - TypeTraits::Reference, - qmlDocumentSourceId1, - Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), - Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), - AllOf(IsStorageType("First2.qml", - Storage::Synchronization::ImportedType{"Object2"}, - TypeTraits::Reference, - qmlDocumentSourceId2, - Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), - Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraits::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraits::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Synchronization::Type::exportedTypes, IsEmpty())))), Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, @@ -3408,4 +3424,76 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); } +const QString propertyEditorQmlPath = QDir::cleanPath( + UNITTEST_DIR "/../../../../share/qtcreator/qmldesigner/propertyEditorQmlSources/"); + +TEST_F(ProjectStorageUpdater, update_property_editor_panes) +{ + ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + auto sourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QML/QtObjectPane.qml"}); + auto directoryId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QML/."}); + setFilesChanged({directoryId}); + auto qmlModuleId = storage.moduleId("QML"); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryId, 1, 21))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryId)), + Field(&SynchronizationPackage::propertyEditorQmlPaths, + Contains(IsPropertyEditorQmlPath(qmlModuleId, "QtObject", sourceId))), + Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, + ElementsAre(directoryId))))); + + updater.update({}, {}, propertyEditorQmlPath); +} + +TEST_F(ProjectStorageUpdater, update_property_editor_specifics) +{ + ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + auto sourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/TextSpecifics.qml"}); + auto directoryId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/."}); + setFilesChanged({directoryId}); + auto qtQuickModuleId = storage.moduleId("QtQuick"); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::propertyEditorQmlPaths, + Contains(IsPropertyEditorQmlPath(qtQuickModuleId, "Text", sourceId))), + Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, + ElementsAre(directoryId))))); + + updater.update({}, {}, propertyEditorQmlPath); +} + +TEST_F(ProjectStorageUpdater, update_property_editor_panes_is_empty_if_directory_has_not_changed) +{ + updater.update({}, {}, propertyEditorQmlPath); + ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.update({}, {}, propertyEditorQmlPath); +} + } // namespace diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject index 1ff457cdd87..d3e15d20be2 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject @@ -91,6 +91,8 @@ Project { quickVersion: "6.2" + qtForMCUs: true + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ widgetApp: true diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson index 9abc7a76c3c..293b8e96524 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson @@ -147,6 +147,7 @@ ] }, "mcuConfig": { + "mcuEnabled": true }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml index a05b24e9e61..e1ec5b97566 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml @@ -13,7 +13,7 @@ Project { qdsVersion: "" quickVersion: "" qt6Project: false - qtForMCUs: true + qtForMCUs: false multilanguageSupport: false primaryLanguage: "" diff --git a/tests/unit/tests/unittests/sqlite/sqlitealgorithms-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitealgorithms-test.cpp index a29a552ce59..6413cbc39a9 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitealgorithms-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitealgorithms-test.cpp @@ -79,7 +79,7 @@ public: auto select() { return selectViewsStatement.range(); } - auto fetchKeyValues() { return selectValuesStatement.values(24); } + auto fetchKeyValues() { return selectValuesStatement.values(); } protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; diff --git a/tests/unit/tests/unittests/sqlite/sqlitedatabase-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitedatabase-test.cpp index 50b5c012c52..79f3058845b 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitedatabase-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitedatabase-test.cpp @@ -47,7 +47,7 @@ protected: std::vector names() const { - return Sqlite::ReadStatement<1>("SELECT name FROM test", database).values(8); + return Sqlite::ReadStatement<1>("SELECT name FROM test", database).values(); } static void updateHookCallback( diff --git a/tests/unit/tests/unittests/sqlite/sqlitesessions-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitesessions-test.cpp index e6d5fcc1426..4e436631560 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitesessions-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitesessions-test.cpp @@ -102,8 +102,9 @@ class SqliteSessions : public testing::Test protected: SqliteSessions() { sessions.setAttachedTables({"data", "tags"}); } - std::vector fetchData() { return selectData.values(8); } - std::vector fetchTags() { return selectTags.values(8); } + std::vector fetchData() { return selectData.values(); } + + std::vector fetchTags() { return selectTags.values(); } protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; diff --git a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp index f56de53f041..f533c651e96 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp @@ -519,7 +519,7 @@ TEST_F(SqliteStatement, write_pointer_values) SqliteTestStatement<1, 2> statement("SELECT value FROM carray(?, ?, 'int64')", database); std::vector values{1, 1, 2, 3, 5}; - auto results = statement.values(5, values.data(), int(values.size())); + auto results = statement.values(values.data(), int(values.size())); ASSERT_THAT(results, ElementsAre(1, 1, 2, 3, 5)); } @@ -529,7 +529,7 @@ TEST_F(SqliteStatement, write_int_carray_values) SqliteTestStatement<1, 1> statement("SELECT value FROM carray(?)", database); std::vector values{3, 10, 20, 33, 55}; - auto results = statement.values(5, Utils::span(values)); + auto results = statement.values(Utils::span(values)); ASSERT_THAT(results, ElementsAre(3, 10, 20, 33, 55)); } @@ -539,7 +539,7 @@ TEST_F(SqliteStatement, write_long_long_carray_values) SqliteTestStatement<1, 1> statement("SELECT value FROM carray(?)", database); std::vector values{3, 10, 20, 33, 55}; - auto results = statement.values(5, Utils::span(values)); + auto results = statement.values(Utils::span(values)); ASSERT_THAT(results, ElementsAre(3, 10, 20, 33, 55)); } @@ -549,7 +549,7 @@ TEST_F(SqliteStatement, write_double_carray_values) SqliteTestStatement<1, 1> statement("SELECT value FROM carray(?)", database); std::vector values{3.3, 10.2, 20.54, 33.21, 55}; - auto results = statement.values(5, Utils::span(values)); + auto results = statement.values(Utils::span(values)); ASSERT_THAT(results, ElementsAre(3.3, 10.2, 20.54, 33.21, 55)); } @@ -559,7 +559,7 @@ TEST_F(SqliteStatement, write_text_carray_values) SqliteTestStatement<1, 1> statement("SELECT value FROM carray(?)", database); std::vector values{"yi", "er", "san", "se", "wu"}; - auto results = statement.values(5, Utils::span(values)); + auto results = statement.values(Utils::span(values)); ASSERT_THAT(results, ElementsAre("yi", "er", "san", "se", "wu")); } @@ -725,7 +725,7 @@ TEST_F(SqliteStatement, get_tuple_values_without_arguments) using Tuple = std::tuple; ReadStatement<3> statement("SELECT name, number, value FROM test", database); - auto values = statement.values(3); + auto values = statement.values(); ASSERT_THAT(values, UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3})); @@ -851,7 +851,7 @@ TEST_F(SqliteStatement, get_single_values_without_arguments) { ReadStatement<1> statement("SELECT name FROM test", database); - std::vector values = statement.values(3); + std::vector values = statement.values(); ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo")); } @@ -901,7 +901,7 @@ TEST_F(SqliteStatement, get_single_sqlite_values_without_arguments) ReadStatement<1> statement("SELECT number FROM test", database); database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)"); - std::vector values = statement.values(3); + std::vector values = statement.values(); ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull())); } @@ -933,7 +933,7 @@ TEST_F(SqliteStatement, get_struct_values_without_arguments) { ReadStatement<3> statement("SELECT name, number, value FROM test", database); - auto values = statement.values(3); + auto values = statement.values(); ASSERT_THAT(values, UnorderedElementsAre(Output{"bar", "blah", 1}, @@ -971,9 +971,9 @@ TEST_F(SqliteStatement, get_struct_range_with_transaction_without_arguments) TEST_F(SqliteStatement, get_values_for_single_output_with_binding_multiple_times) { ReadStatement<1, 1> statement("SELECT name FROM test WHERE number=?", database); - statement.values(3, 40); + statement.values(40); - std::vector values = statement.values(3, 40); + std::vector values = statement.values(40); ASSERT_THAT(values, ElementsAre("poo")); } @@ -981,7 +981,7 @@ TEST_F(SqliteStatement, get_values_for_single_output_with_binding_multiple_times TEST_F(SqliteStatement, get_range_for_single_output_with_binding_multiple_times) { ReadStatement<1, 1> statement("SELECT name FROM test WHERE number=?", database); - statement.values(3, 40); + statement.values(40); auto range = statement.range(40); std::vector values{range.begin(), range.end()}; @@ -992,7 +992,7 @@ TEST_F(SqliteStatement, get_range_for_single_output_with_binding_multiple_times) TEST_F(SqliteStatement, get_range_with_transaction_for_single_output_with_binding_multiple_times) { ReadStatement<1, 1> statement("SELECT name FROM test WHERE number=?", database); - statement.values(3, 40); + statement.values(40); database.unlock(); std::vector values = toValues( @@ -1008,7 +1008,7 @@ TEST_F(SqliteStatement, get_values_for_multiple_output_values_and_multiple_query ReadStatement<3, 3> statement( "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); - auto values = statement.values(3, "bar", "blah", 1); + auto values = statement.values("bar", "blah", 1); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); } @@ -1043,9 +1043,9 @@ TEST_F(SqliteStatement, call_get_values_for_multiple_output_values_and_multiple_ using Tuple = std::tuple; ReadStatement<3, 2> statement("SELECT name, number, value FROM test WHERE name=? AND number=?", database); - statement.values(3, "bar", "blah"); + statement.values("bar", "blah"); - auto values = statement.values(3, "bar", "blah"); + auto values = statement.values("bar", "blah"); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); } @@ -1086,7 +1086,7 @@ TEST_F(SqliteStatement, get_struct_output_values_and_multiple_query_value) ReadStatement<3, 3> statement( "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); - auto values = statement.values(3, "bar", "blah", 1); + auto values = statement.values("bar", "blah", 1); ASSERT_THAT(values, ElementsAre(Output{"bar", "blah", 1})); } @@ -1099,7 +1099,7 @@ TEST_F(SqliteStatement, get_blob_values) auto bytePointer = reinterpret_cast(&value); Sqlite::BlobView bytes{bytePointer, 4}; - auto values = statement.values(1); + auto values = statement.values(); ASSERT_THAT(values, ElementsAre(Field(&Sqlite::Blob::bytes, Eq(bytes)))); } @@ -1299,7 +1299,7 @@ TEST_F(SqliteStatement, get_values_without_arguments_calls_reset) EXPECT_CALL(mockStatement, reset()); - mockStatement.values(3); + mockStatement.values(); } TEST_F(SqliteStatement, get_range_without_arguments_calls_reset) @@ -1332,7 +1332,7 @@ TEST_F(SqliteStatement, get_values_without_arguments_calls_reset_if_exception_is EXPECT_CALL(mockStatement, reset()); - EXPECT_THROW(mockStatement.values(3), Sqlite::StatementHasError); + EXPECT_THROW(mockStatement.values(), Sqlite::StatementHasError); } TEST_F(SqliteStatement, get_range_without_arguments_calls_reset_if_exception_is_thrown) @@ -1372,7 +1372,7 @@ TEST_F(SqliteStatement, get_values_with_simple_arguments_calls_reset) EXPECT_CALL(mockStatement, reset()); - mockStatement.values(3, "foo", "bar"); + mockStatement.values("foo", "bar"); } TEST_F(SqliteStatement, get_values_with_simple_arguments_calls_reset_if_exception_is_thrown) @@ -1382,7 +1382,7 @@ TEST_F(SqliteStatement, get_values_with_simple_arguments_calls_reset_if_exceptio EXPECT_CALL(mockStatement, reset()); - EXPECT_THROW(mockStatement.values(3, "foo", "bar"), Sqlite::StatementHasError); + EXPECT_THROW((mockStatement.values("foo", "bar")), Sqlite::StatementHasError); } TEST_F(SqliteStatement, reset_if_write_is_throwing_exception) @@ -1580,7 +1580,7 @@ TEST_F(SqliteStatement, read_statement_values_with_transactions) database); database.unlock(); - std::vector values = statement.valuesWithTransaction(1024, "bar", "blah"); + std::vector values = statement.valuesWithTransaction("bar", "blah"); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); database.lock(); @@ -1646,7 +1646,7 @@ TEST_F(SqliteStatement, read_write_statement_values_with_transactions) "SELECT name, number, value FROM test WHERE name=? AND number=?", database); database.unlock(); - std::vector values = statement.valuesWithTransaction(1024, "bar", "blah"); + std::vector values = statement.valuesWithTransaction("bar", "blah"); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); database.lock(); diff --git a/tests/unit/tests/unittests/sqlite/sqlitetransaction-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitetransaction-test.cpp index ac98a258c96..9edbfe87488 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitetransaction-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitetransaction-test.cpp @@ -323,6 +323,59 @@ TEST_F(SqliteTransaction, immediate_session_transaction_begin_throws_and_not_rol ASSERT_ANY_THROW(ImmediateSessionTransaction{mockTransactionBackend}); } +TEST_F(SqliteTransaction, with_implicit_transaction_no_return_does_not_commit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()).Times(0); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()).Times(0); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withImplicitTransaction(mockTransactionBackend, callableMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, with_implicit_transaction_with_return_does_not_commit) +{ + InSequence s; + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()).Times(0); + EXPECT_CALL(callableWithReturnMock, Call()); + EXPECT_CALL(mockTransactionBackend, commit()).Times(0); + EXPECT_CALL(mockTransactionBackend, unlock()); + + Sqlite::withImplicitTransaction(mockTransactionBackend, callableWithReturnMock.AsStdFunction()); +} + +TEST_F(SqliteTransaction, with_implicit_transaction_returns_value) +{ + auto callable = callableWithReturnMock.AsStdFunction(); + + auto value = Sqlite::withImplicitTransaction(mockTransactionBackend, + callableWithReturnMock.AsStdFunction()); + + ASSERT_THAT(value, Eq(212)); +} + +TEST_F(SqliteTransaction, with_implicit_transaction_do_calls_rollsback_for_exception) +{ + InSequence s; + ON_CALL(callableMock, Call()).WillByDefault(Throw(std::exception{})); + + EXPECT_CALL(mockTransactionBackend, lock()); + EXPECT_CALL(mockTransactionBackend, deferredBegin()).Times(0); + EXPECT_CALL(callableMock, Call()); + EXPECT_CALL(mockTransactionBackend, rollback()).Times(0); + EXPECT_CALL(mockTransactionBackend, unlock()); + + try { + Sqlite::withImplicitTransaction(mockTransactionBackend, callableMock.AsStdFunction()); + } catch (...) { + } +} + TEST_F(SqliteTransaction, with_deferred_transaction_no_return_commit) { InSequence s; diff --git a/tests/unit/tests/unittests/utils/smallstring-test.cpp b/tests/unit/tests/unittests/utils/smallstring-test.cpp index 134891e5839..17124b1373b 100644 --- a/tests/unit/tests/unittests/utils/smallstring-test.cpp +++ b/tests/unit/tests/unittests/utils/smallstring-test.cpp @@ -618,21 +618,21 @@ TEST(SmallString, to_q_string) class FromQString : public testing::TestWithParam { protected: - QString randomString(qsizetype size) + QString exampleString(qsizetype size) { static constexpr char16_t characters[] = u"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - static std::mt19937 generator{std::random_device{}()}; - static std::uniform_int_distribution pick(0, std::size(characters) - 1); - QString string; string.reserve(size); + std::size_t index = 0; std::generate_n(std::back_inserter(string), size, [&]() { - return characters[pick(generator)]; + if (index >= std::size(characters)) + index = 0; + + return characters[index++]; }); return string; @@ -646,7 +646,7 @@ INSTANTIATE_TEST_SUITE_P(SmallString, FromQString, testing::Range(0, TEST_P(FromQString, from_qstring) { - const QString qStringText = randomString(size); + const QString qStringText = exampleString(size); auto text = SmallString(qStringText); diff --git a/tests/unit/tests/utils/google-using-declarations.h b/tests/unit/tests/utils/google-using-declarations.h index 0d63479fbe1..2d5fb1119af 100644 --- a/tests/unit/tests/utils/google-using-declarations.h +++ b/tests/unit/tests/utils/google-using-declarations.h @@ -20,7 +20,6 @@ using testing::ByRef; using testing::ContainerEq; using testing::Contains; using testing::ElementsAre; -using testing::EndsWith; using testing::Eq; using testing::Exactly; using testing::Field; diff --git a/tests/unit/tests/utils/googletest.h b/tests/unit/tests/utils/googletest.h index f4dfd2f988c..7da860f7ffa 100644 --- a/tests/unit/tests/utils/googletest.h +++ b/tests/unit/tests/utils/googletest.h @@ -24,3 +24,5 @@ #include #include #include + +QT_WARNING_DISABLE_CLANG("-Wpadded") diff --git a/tests/unit/tools/CMakeLists.txt b/tests/unit/tools/CMakeLists.txt index 123825fe81b..05b561491fc 100644 --- a/tests/unit/tools/CMakeLists.txt +++ b/tests/unit/tools/CMakeLists.txt @@ -1,3 +1 @@ -if (Qt6_Version VERSION_GREATER_EQUAL "6.4.3") - add_subdirectory(qmlprojectmanager) -endif () +add_subdirectory(qmlprojectmanager)