From f1b5fb6e117aa9855080f2a3d1251c826f576ae6 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Wed, 19 Jul 2023 14:10:12 +0200 Subject: [PATCH 001/266] Add interface to check incompatible qml ids Task-number: QDS-10121 Change-Id: I112bee4e9323dc4eff30ec7a4693c1e9632d6ba9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/libs/qmljs/qmljscheck.cpp | 7 ++++++- src/libs/qmljs/qmljscheck.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index f1e72a7dc24..05273c579ae 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -680,6 +680,11 @@ QList Check::defaultDisabledMessagesForNonQuickUi() 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) @@ -1099,7 +1104,7 @@ bool Check::visit(UiScriptBinding *ast) return false; } - if (idsThatShouldNotBeUsedInDesigner->contains(id)) { + if (incompatibleDesignerQmlId(id)) { addMessage(ErrInvalidIdeInVisualDesigner, loc); } 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; From 3d7f1a0f7f1e0cd2179df1b9d3152c14460e2c53 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Jul 2023 18:35:01 +0200 Subject: [PATCH 002/266] UnitTests: Optimize example string generation Change-Id: I9cc17269812dd9a61b192598f947e2754673c96e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../tests/unittests/utils/smallstring-test.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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); From 0adb3f8cca57717ffc07699cc23ed08fe3452eee Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 25 Jul 2023 16:14:09 +0200 Subject: [PATCH 003/266] StudioWelcome: fix crash when opening nonexistent recent projects Task-number: QDS-10085 Change-Id: I038c95f1a5d0975c0f39bb275a6e2f5a2c05c8d6 Reviewed-by: Tim Jenssen --- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index d5ad29e44a2..f1e9ac284d2 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -422,8 +422,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()); From 891744b66700586824e8a7eee58b7c842a45500d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Thu, 13 Jul 2023 14:10:12 +0300 Subject: [PATCH 004/266] Doc: Add Creating Projects for MCUs topic Added a new Creating Projects for MCUs topic and updated the related topics, including the ToC, accordingly. Task-number: QDS-9288 Change-Id: Ia6e3afad48e98f549bdbb210b8ec7d3ebc5687c5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Yasser Grimes Reviewed-by: Leena Miettinen --- .../images/studio-preset-for-mcus.png | Bin 0 -> 26494 bytes ...signstudio-creating-projects-for-mcus.qdoc | 148 ++++++++++++++++++ ...designstudio-features-on-mcu-projects.qdoc | 2 +- .../src/qtdesignstudio-help-overview.qdoc | 2 +- .../src/qtdesignstudio-projects.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 1 + 6 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 doc/qtdesignstudio/images/studio-preset-for-mcus.png create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc 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 0000000000000000000000000000000000000000..0177791ffd2437dc6528ed1f6e2d0b1241e1cae4 GIT binary patch literal 26494 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�^0CD&7EK2N$K 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 literal 0 HcmV?d00001 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..3ca1ff99db3 --- /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-help.html + + \title Creating Projects for MCUs + + Use the \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 \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-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/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 578129024a9..8fe0627488c 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-projects-for-mcus.html \nextpage creator-help.html \title Help 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 cee8f1fe7ad..0ae755b263d 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -256,6 +256,7 @@ \list \li \l {\QDS Version Compatibility with \QMCU SDKs} \li \l {\QDS Features on MCU Projects} + \li \l {Creating Projects for MCUs} \endlist \endlist \li \l Help From d59d27e16bc81ee9e504159c2ec660cae61a3c80 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 13 Jul 2023 20:34:26 +0200 Subject: [PATCH 005/266] QmlDesigner: Add property meta info tests The cast function is now returning an invalid variant if the meta info is invalid. It is too not anymore converting the value magically. Task-number: QDS-10290 Change-Id: I125aab9ac46c9ac0605364ede32fb1a10d20cd89 Reviewed-by: Tim Jenssen --- .../designercore/metainfo/nodemetainfo.cpp | 67 +- .../tests/unittests/metainfo/CMakeLists.txt | 1 + .../metainfo/propertymetainfo-test.cpp | 940 ++++++++++++++++++ 3 files changed, 984 insertions(+), 24 deletions(-) create mode 100644 tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 5121e6a663c..5208428ca42 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2985,7 +2985,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(), @@ -3012,7 +3013,7 @@ 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()); } @@ -3020,7 +3021,7 @@ bool PropertyMetaInfo::isWritable() const 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 +3037,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 +3045,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 +3073,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 +3102,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 +3128,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 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/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp new file mode 100644 index 00000000000..43620baeb91 --- /dev/null +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -0,0 +1,940 @@ +// 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, 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 From 41024753eace4aa46fa56222b071d88b2a9c9817 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 27 Jul 2023 15:43:09 +0200 Subject: [PATCH 006/266] QmlDesigner: fix effect_bundle path on macOS Change-Id: I4ae9583dbea4f83b30a8b47d107edf08187f7d12 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../contentlibrary/contentlibraryeffectsmodel.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 3bfd374bd81..370713c7a50 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 @@ -116,7 +117,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() == ".") { From f6e162f8a2e35e33b45889e23dc5a8aad199f3d7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 27 Jul 2023 13:53:24 +0200 Subject: [PATCH 007/266] ClangFormat: Use C++17 instead of C++11 Change-Id: I7119128708f4f7911e6fe683be29f60cc00dc130 Reviewed-by: Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From b051606ed2aa9ab9f8be8d0035dce7008e818fa2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 31 Jul 2023 10:40:21 +0300 Subject: [PATCH 008/266] QmlDesigner: Fix build without quick3d Change-Id: I6a9ddebfc994dc187e837059c45e9b069ff743cc Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../instances/qt5informationnodeinstanceserver.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index b4c47a0a2a9..268e694bc30 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -2337,8 +2337,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,9 +2365,8 @@ QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const } } } - - return cameras; #endif + return cameras; } void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command) From 816e8f78dd3aee24083c193e696681d5d6ae992c Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Mon, 31 Jul 2023 11:07:28 +0200 Subject: [PATCH 009/266] Docs: Add properties and snippet docs for Figma Change-Id: I90d9f9e79be14706380fdf3a7bac6f051511e31f Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Vikas Pachdha --- .../src/qtbridge/qtbridge-figma-using.qdoc | 24 +++++++++++++++++++ .../src/qtbridge/qtbridge-xd-using.qdoc | 1 + 2 files changed, 25 insertions(+) diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index 1af2351e7da..8874186c6f4 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -170,6 +170,30 @@ \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 value to the existing properties of + the component. You can also add and modify properties in \QDS. + Following are few examples of properties: + \code + property int counter: 5 + property string label: "ok" + antialiasing : true + width: parent.width / 2 + \endcode + \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-xd-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index 40c72ab697a..0f16df676d3 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc @@ -134,6 +134,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 From 7d482601770caf249480815a423ca5576b0cd378 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 31 Jul 2023 13:58:43 +0300 Subject: [PATCH 010/266] QmlDesigner: Prevent bundle effect add when no active 3D scene found Fixes: QDS-10205 Change-Id: I88f3e6f95ea9beab1470dd8057d2b01497c2caf0 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ContentLibraryEffectContextMenu.qml | 2 +- .../contentlibrary/contentlibraryview.cpp | 2 ++ .../contentlibrary/contentlibrarywidget.cpp | 14 ++++++++++++++ .../contentlibrary/contentlibrarywidget.h | 8 +++++++- .../qmldesigner/components/edit3d/edit3dwidget.cpp | 3 ++- 5 files changed, 26 insertions(+), 3 deletions(-) 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/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 3f64f8d8b4f..241c8bdd500 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -181,6 +181,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 +215,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, diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 78bc1f07c3c..1b5bdb8c768 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -662,6 +662,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..a5baa66dd1c 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); @@ -75,6 +79,7 @@ signals: void updateSceneEnvStateRequested(); void hasQuick3DImportChanged(); void hasMaterialLibraryChanged(); + void hasActive3DSceneChanged(); void isDraggingChanged(); protected: @@ -111,6 +116,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/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 163cfc951ed..be0afacfaba 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -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()) { From d1623c38c6e7c41d6d8c1420855217e024b58949 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 27 Jul 2023 16:25:47 +0200 Subject: [PATCH 011/266] QmlDesigner: Remove Update QmlProject File action - removes the action from the file menu - adds tr::tr to the menu string - fixes an if condition - adds some comments on how we can improve it Change-Id: I5f816a8dc9245bec5aa0db13318b702414ad6bcd Reviewed-by: Aleksei German Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Burak Hancerli Reviewed-by: --- .../qmlprojectmanager/buildsystem/qmlbuildsystem.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 307b6d61451..df52b599aa8 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; } From 855c27de2a06667c9a8dae6e9a38a38581a63624 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 1 Aug 2023 13:50:14 +0300 Subject: [PATCH 012/266] QmlDesigner: Fix Qt version handling in new project dialog Different wizards may have different options for target Qt version, so don't hardcode them, but query them from the wizard. Also initialize the default target Qt version index to correct value in 3D wizard. Fixes: QDS-10223 Change-Id: I75f6bf60655692c52b1350182bf3ac122efc1c74 Reviewed-by: Mahmoud Badri --- .../imports/NewProjectDialog/Details.qml | 8 +----- .../projects/application-3d/wizard.json | 2 +- src/plugins/studiowelcome/qdsnewdialog.cpp | 25 ++++++++----------- src/plugins/studiowelcome/qdsnewdialog.h | 3 +++ src/plugins/studiowelcome/wizardhandler.cpp | 15 +++++++++++ src/plugins/studiowelcome/wizardhandler.h | 1 + 6 files changed, 31 insertions(+), 23 deletions(-) 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/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/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/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; From b68802e08b490985f28fc3ba2c281c9d81eb5754 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 1 Aug 2023 16:24:30 +0300 Subject: [PATCH 013/266] QmlDesigner: Fix shader baker (qsb) file autogeneration '-o' command line parameter was missing, so the tool interpreted the target output file to be the source file. Fixes: QDS-10207 Change-Id: Idb9468d56189314bfb9138e4237f7734b65cd4eb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qmldesigner/designercore/instances/nodeinstanceview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index bdbb2b7ee7b..d49ed5b7d46 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -2175,6 +2175,7 @@ void NodeInstanceView::handleShaderChanges() } QStringList args = baseArgs; + args.append("-o"); args.append(outPath.toString()); args.append(shader); auto qsbProcess = new Utils::Process(this); From e0197a791fedf281985ea1a17cb95f215bd37787 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 1 Aug 2023 15:30:45 +0200 Subject: [PATCH 014/266] QmlDesigner: Fix crash on Design View reopening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10383 Change-Id: I21665b59a078a640eef50632628532b12149c435 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- src/plugins/qmlpreview/qmlpreviewplugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index d18b8c37b0e..ed2d7fcb948 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -44,6 +44,7 @@ #include #include +#include #include using namespace ProjectExplorer; @@ -127,7 +128,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; From 787f17ce91ac3eeb44a716af6a43813e2b96a35c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 1 Aug 2023 14:57:15 +0300 Subject: [PATCH 015/266] QmlDesigner: Fix enumeration generation from scope and name Fixes: QDS-10363 Fixes: QDS-10387 Change-Id: I32b7b4a01aeeb6b8d6cd3a4e1fd819c0fa4753e4 Reviewed-by: Marco Bubke Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/libs/qmlpuppetcommunication/types/enumeration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } From 6f5e9417d8c0abb3acd3a35130140be6c355130a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 3 Aug 2023 09:29:52 +0200 Subject: [PATCH 016/266] QmlDesigner: Add contains functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After extensive benchmarking(https://quick-bench.com/q/ajHEd5ZE-zmI2FoLQLGX9NREgmw) there was a clear result that a forced inline variatic function provides the fastest result. static bool search(QByteArrayView input) { return CoreUtils::contains(input, "children", "data", "childrenRect", "icon", "left", "transform", "visibleChildren"); } Change-Id: Id492bb4351bf3e87fc8e85c2b99e8d2a9b3efd0d Reviewed-by: Miikka Heikkinen Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../qmldesigner/utils/designeralgorithm.h | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/plugins/qmldesigner/utils/designeralgorithm.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 143da043e25..166ff286dab 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -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 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 From 9f7f82aeff88aff332e5a407e4445762ead0813e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 3 Aug 2023 16:27:50 +0300 Subject: [PATCH 017/266] ADS: Fix workspace corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't insert widget into area twice as that corrupts the workspace. Fixes: QDS-10351 Fixes: QDS-10388 Change-Id: Icacddf2a95243beee5d6314fbdc38e284639ecec Reviewed-by: Henning Gründl --- src/libs/advanceddockingsystem/dockcontainerwidget.cpp | 1 - 1 file changed, 1 deletion(-) 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()) From 5195b0296bd65d396bc1f15b01c0c0d38abfaacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Wed, 2 Aug 2023 13:51:03 +0300 Subject: [PATCH 018/266] Doc: Move Creating UIs for MCUs section to a separate page Moved the Creating UIs for MCUs section from the Creating Custom Components page to a separate page in the QDS on MCUs subset. Modified the content to match the current implementation, and added links to the related pages in the Qt for MCUs documentation set. Also modified the related QDS pages, including the ToC, accordingly. Task-number: QDS-10329 Change-Id: Ibfefa2d023e71a369979b4a7cb74f0b284919290 Reviewed-by: Yasser Grimes Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Leena Miettinen --- .../images/qmldesigner-mcu-support.png | Bin 50795 -> 0 bytes .../studio-mcu-components-and-properties.webp | Bin 0 -> 40202 bytes .../components/qtquick-components-custom.qdoc | 2 - .../components/qtquick-mcu-support.qdocinc | 27 --------- ...signstudio-creating-projects-for-mcus.qdoc | 2 +- .../qtdesignstudio-creating-uis-for-mcus.qdoc | 52 ++++++++++++++++++ .../src/qtdesignstudio-help-overview.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 1 + 8 files changed, 55 insertions(+), 31 deletions(-) delete mode 100644 doc/qtdesignstudio/images/qmldesigner-mcu-support.png create mode 100644 doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp delete mode 100644 doc/qtdesignstudio/src/components/qtquick-mcu-support.qdocinc create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc diff --git a/doc/qtdesignstudio/images/qmldesigner-mcu-support.png b/doc/qtdesignstudio/images/qmldesigner-mcu-support.png deleted file mode 100644 index 98ee56b2a3ef0ad26c799a576e54cbafbb7f5f73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50795 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 literal 0 HcmV?d00001 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-creating-projects-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc index 3ca1ff99db3..2371222c394 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc @@ -4,7 +4,7 @@ /*! \previouspage studio-features-on-mcu-projects.html \page studio-projects-for-mcus.html - \nextpage studio-help.html + \nextpage studio-creating-uis-for-mcus.html \title Creating Projects for MCUs 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..78ddf12f1a3 --- /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-help.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 \QDS to create application UIs for MCU devices + + With \l{\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/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 8fe0627488c..38d20e06883 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-projects-for-mcus.html + \previouspage studio-creating-uis-for-mcus.html \nextpage creator-help.html \title Help diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 0ae755b263d..960226a9917 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -257,6 +257,7 @@ \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} \endlist \endlist \li \l Help From a80dc7789e21f36044aa319ef26c0536b529e91f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Aug 2023 11:14:03 +0300 Subject: [PATCH 019/266] ADS: Focus content widget when tab is clicked or dockwidget gets focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QDS-10396 Change-Id: Ic7d4a73ff535c0c0cb73e1e5bd636bec00a1336c Reviewed-by: Henning Gründl Reviewed-by: Mahmoud Badri --- src/libs/advanceddockingsystem/dockareatabbar.cpp | 8 ++++++++ src/libs/advanceddockingsystem/dockfocuscontroller.cpp | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp index e1ac0c9b861..c7dab3c2297 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) 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) From 60ea887d5fc0dd4765e230dbd9ec3017f2604d08 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 4 Aug 2023 13:48:15 +0300 Subject: [PATCH 020/266] QmlDesigner: Improve keyboard navigation on assets view keyboard navigation now works when you focus the tab without requiring to also click on the treeview itself. Fixes: QDS-10397 Change-Id: I2cda08365c1a68c72121166d4fd50f1786f913bc Reviewed-by: Mahmoud Badri --- .../assetsLibraryQmlSources/Assets.qml | 1 + .../assetsLibraryQmlSources/AssetsView.qml | 43 +++++++------------ .../assetslibrary/assetslibrarywidget.cpp | 2 + 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml index 211d3449a85..502b67f6c65 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml @@ -264,6 +264,7 @@ Item { contextMenu: contextMenu width: parent.width height: parent.height - assetsView.y + focus: true } } } diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index ede7f58cafb..e4380a19b39 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -348,22 +348,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 +370,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/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 From a05ba1696fe2e9365172fcc00276a0544c95be28 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 8 Aug 2023 13:00:18 +0300 Subject: [PATCH 021/266] QmlDesigner: Fix orthographic camera zoom Scale no longer affects cameras, so use magnification instead to do orthographic camera zooming. Fixes: QDS-10241 Change-Id: Ic31abfdf741369a494b8178109fa1c5b95fbdd1c Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 3 ++- .../qml2puppet/editor3d/generalhelper.cpp | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 964f47941bf..05bc0f5e2c7 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -747,7 +747,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/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index bfa2fcb1ae8..b057b6e1f96 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -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 From 64377d9e0ca33b8732072af120f45a76ed18d956 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 29 Jun 2023 15:06:11 +0300 Subject: [PATCH 022/266] QmlDesigner: Implement effect maker view Initial basic logic for a view and simple functionality to be extended. The view is hidden by default during development and enabled via an env var. Task-number: QDS-10401 Change-Id: I5c2e1e20aca6c53c1ed273136ee6204145f15def Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Amr Elsayed --- .../effectMakerQmlSources/EffectMaker.qml | 106 +++++++++++++++ .../effectMakerQmlSources/EffectNode.qml | 72 +++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 11 ++ .../components/componentcore/viewmanager.cpp | 9 ++ .../effectmaker/effectmakermodel.cpp | 121 ++++++++++++++++++ .../components/effectmaker/effectmakermodel.h | 61 +++++++++ .../effectmaker/effectmakerview.cpp | 68 ++++++++++ .../components/effectmaker/effectmakerview.h | 34 +++++ .../effectmaker/effectmakerwidget.cpp | 115 +++++++++++++++++ .../effectmaker/effectmakerwidget.h | 50 ++++++++ .../components/effectmaker/effectnode.cpp | 16 +++ .../components/effectmaker/effectnode.h | 21 +++ .../effectmaker/effectscategory.cpp | 22 ++++ .../components/effectmaker/effectscategory.h | 25 ++++ src/plugins/qmldesigner/designmodecontext.cpp | 13 ++ src/plugins/qmldesigner/designmodecontext.h | 9 ++ .../qmldesigner/qmldesignerconstants.h | 3 + 17 files changed, 756 insertions(+) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.h create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnode.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnode.h create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectscategory.h diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml new file mode 100644 index 00000000000..82a5ab44193 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -0,0 +1,106 @@ +// 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 QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import EffectMakerBackend + +Item { + id: root + + property var effectMakerModel: EffectMakerBackend.effectMakerModel + property var rootView: EffectMakerBackend.rootView + + Column { + id: col + anchors.fill: parent + spacing: 5 + + Rectangle { + id: topHeader + + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + Row { + // TODO: Filter row + } + } + + Rectangle { + id: previewHeader + + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + // TODO + } + + Image { + id: previewImage + + // TODO + } + + HelperWidgets.ScrollView { + id: scrollView + + width: parent.width + height: parent.height - y + clip: true + + Behavior on contentY { + id: contentYBehavior + PropertyAnimation { + id: scrollViewAnim + easing.type: Easing.InOutQuad + } + } + + Column { + Item { + width: scrollView.width + height: categories.height + + Column { + Repeater { + id: categories + width: root.width + model: effectMakerModel + + delegate: HelperWidgets.Section { + id: effectsSection + width: root.width + caption: model.categoryName + category: "EffectMaker" + + property var effectList: model.effectNames + + onExpandedChanged: { + effects.visible = expanded // TODO: update + } + + Repeater { + id: effects + model: effectList + width: parent.width + height: parent.height + delegate: EffectNode { + width: parent.width + //height: StudioTheme.Values.checkIndicatorHeight * 2 // TODO: update or remove + } + } + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml new file mode 100644 index 00000000000..808abf89abe --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml @@ -0,0 +1,72 @@ +// 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.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +// TODO: this will be redone based on new specs + +Rectangle { + property real margin: StudioTheme.Values.marginTopBottom + + id: root + height: col.height + margin * 2 + + signal showContextMenu() + + border.width: EffectMakerBackend.effectMakerModel.selectedIndex === index ? 3 : 0 + border.color: EffectMakerBackend.effectMakerModel.selectedIndex === index + ? StudioTheme.Values.themeControlOutlineInteraction + : "transparent" + color: "transparent" + visible: true // TODO: from rolename -> effectVisible + + MouseArea { + id: mouseArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onPressed: (mouse) => { + EffectMakerBackend.effectMakerModel.selectEffect(index) + EffectMakerBackend.rootView.focusSection(0) // TODO: Set the correct section based on current effect + + if (mouse.button === Qt.LeftButton) + // TODO: Start dragging here + ; + else if (mouse.button === Qt.RightButton) + root.showContextMenu() + } + } + + ColumnLayout { + id: col + Layout.topMargin: margin + Layout.bottomMargin: margin + Row { + + width: parent.width + + Text { + anchors.verticalCenter: parent.verticalCenter + rightPadding: margin * 3 + text: '⋮⋮' + color: StudioTheme.Values.themeTextColorDisabled + font.letterSpacing: -10 + font.bold: true + font.pointSize: StudioTheme.Values.mediumIconFontSize + } + + StudioControls.CheckBox { + text: modelData + actionIndicatorVisible: false + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 166ff286dab..78895e0a340 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -457,6 +457,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 @@ -705,6 +706,16 @@ 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 + effectnode.cpp effectnode.h + effectscategory.cpp effectscategory.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/navigator SOURCES diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index fc30ab99f9d..b61f09885d7 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ public: , contentLibraryView{externalDependencies} , componentView{externalDependencies} , edit3DView{externalDependencies} + , effectMakerView{externalDependencies} , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} @@ -74,6 +76,7 @@ public: ContentLibraryView contentLibraryView; ComponentView componentView; Edit3DView edit3DView; + EffectMakerView effectMakerView; FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; @@ -206,6 +209,9 @@ QList ViewManager::standardViews() const .toBool()) list.append(&d->debugView); + if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) + list.append(&d->effectMakerView); + #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) list.append(&d->contentLibraryView); @@ -381,6 +387,9 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); widgetInfoList.append(d->statesEditorView.widgetInfo()); + if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) + widgetInfoList.append(d->effectMakerView.widgetInfo()); + #ifdef CHECK_LICENSE if (checkLicense() == FoundLicense::enterprise) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp new file mode 100644 index 00000000000..85c9e9942e3 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -0,0 +1,121 @@ +// 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 +#include +#include + +#include + +#include + +namespace QmlDesigner { + +EffectMakerModel::EffectMakerModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerModel::roleNames() const +{ + QHash roles; + roles[CategoryRole] = "categoryName"; + roles[EffectsRole] = "effectNames"; + return roles; +} + +int EffectMakerModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_categories.count(); +} + +QVariant EffectMakerModel::data(const QModelIndex &index, int role) const +{ + // TODO: to be updated + + if (index.row() < 0 || index.row() >= m_categories.count()) + return {}; + + const EffectsCategory *category = m_categories[index.row()]; + if (role == CategoryRole) + return category->name(); + + if (role == EffectsRole) { + QStringList effectsNames; + const QList effects = category->effects(); + for (const EffectNode *effect : effects) + effectsNames << effect->name(); + + return effectsNames; + } + + return {}; +} + +// static +Utils::FilePath EffectMakerModel::getQmlEffectsPath() +{ + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + qWarning() << __FUNCTION__ << "No project open"; + return ""; + } + + const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + return baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker/defaultnodes"); +} + +void EffectMakerModel::loadModel() +{ + const Utils::FilePath effectsPath = getQmlEffectsPath(); + + if (!effectsPath.exists()) { + qWarning() << __FUNCTION__ << "Effects are not found."; + return; + } + QDirIterator itCategories(effectsPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); + while (itCategories.hasNext()) { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + itCategories.next(); + if (itCategories.fileName() == "images") + continue; + QList effects = {}; + Utils::FilePath categoryPath = effectsPath.resolvePath(itCategories.fileName()); + QDirIterator itEffects(categoryPath.toString(), QDir::Files | QDir::NoDotAndDotDot); + while (itEffects.hasNext()) { + itEffects.next(); + effects.push_back(new EffectNode(QFileInfo(itEffects.fileName()).baseName())); + } + EffectsCategory *category = new EffectsCategory(itCategories.fileName(), effects); + m_categories.push_back(category); + endInsertRows(); + } +} + +void EffectMakerModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void EffectMakerModel::selectEffect(int idx, bool force) +{ + Q_UNUSED(idx) + Q_UNUSED(force) + + // TODO +} + +void EffectMakerModel::applyToSelected(qint64 internalId, bool add) +{ + Q_UNUSED(internalId) + Q_UNUSED(add) + + // TODO: remove? +} + +} // 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..e7ed32aa64f --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -0,0 +1,61 @@ +// 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 "effectscategory.h" + +namespace Utils { +class FilePath; +} + +namespace QmlDesigner { + +class EffectMakerModel : public QAbstractListModel +{ + Q_OBJECT + + enum Roles { + CategoryRole = Qt::UserRole + 1, + EffectsRole + }; + + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + +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 isEmpty() const { return m_isEmpty; } + + void loadModel(); + void resetModel(); + + QList categories() { return m_categories; } + + Q_INVOKABLE void selectEffect(int idx, bool force = false); + Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false); + +signals: + void isEmptyChanged(); + void selectedIndexChanged(int idx); + void hasModelSelectionChanged(); + +private: + bool isValidIndex(int idx) const; + static Utils::FilePath getQmlEffectsPath(); + + QList m_categories; + + int m_selectedIndex = -1; + bool m_isEmpty = true; + bool m_hasModelSelection = false; +}; + +} // 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..7460fb0bd66 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp @@ -0,0 +1,68 @@ +// 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 "effectmakermodel.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->effectMakerModel()->loadModel(); +} + +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..8a07cc10e7b --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -0,0 +1,115 @@ +// 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 "effectmakermodel.h" +#include "effectmakerview.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.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_effectMakerView(view) + , m_effectMakerWidget{new StudioQuickWidget(this)} +{ + setWindowTitle(tr("Effect Maker", "Title of effect maker widget")); + setMinimumWidth(250); + + m_effectMakerWidget->quickWidget()->installEventFilter(this); + + // create the inner widget + m_effectMakerWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_EFFECT_MAKER); + m_effectMakerWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + Theme::setupTheme(m_effectMakerWidget->engine()); + m_effectMakerWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_effectMakerWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + + auto layout = new QHBoxLayout(this); + layout->setContentsMargins({}); + layout->setSpacing(0); + layout->addWidget(m_effectMakerWidget.data()); + + setStyleSheet(Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + + QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_EFFECTMAKER_TIME); + + auto map = m_effectMakerWidget->registerPropertyMap("EffectMakerBackend"); + map->setProperties({{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, + {"rootView", QVariant::fromValue(this)}}); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +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_effectMakerWidget.data(); +} + +QPointer EffectMakerWidget::effectMakerModel() const +{ + return m_effectMakerModel; +} + +void EffectMakerWidget::focusSection(int section) +{ + Q_UNUSED(section) +} + +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::reloadQmlSource() +{ + const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml"; + QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return); + m_effectMakerWidget->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..61e75df9885 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -0,0 +1,50 @@ +// 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 EffectMakerView; +class EffectMakerModel; + +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(); + + StudioQuickWidget *quickWidget() const; + QPointer effectMakerModel() const; + + Q_INVOKABLE void focusSection(int section); + + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void reloadQmlSource(); + + QPointer m_effectMakerModel; + QPointer m_effectMakerView; + QPointer m_effectMakerWidget; +}; + +} // 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..777fe28ec82 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -0,0 +1,16 @@ +// 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" + +namespace QmlDesigner { + +EffectNode::EffectNode(const QString &name) + : m_name(name) {} + +QString EffectNode::name() const +{ + return m_name; +} + +} // 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..74571193f7c --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -0,0 +1,21 @@ +// 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 EffectNode +{ +public: + EffectNode(const QString &name); + + QString name() const; + +private: + QString m_name; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp b/src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp new file mode 100644 index 00000000000..a5b4843fc53 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectscategory.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 "effectscategory.h" + +namespace QmlDesigner { + +EffectsCategory::EffectsCategory(const QString &name, const QList &subcategories) + : m_name(name), + m_effects(subcategories) {} + +QString EffectsCategory::name() const +{ + return m_name; +} + +QList EffectsCategory::effects() const +{ + return m_effects; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectscategory.h b/src/plugins/qmldesigner/components/effectmaker/effectscategory.h new file mode 100644 index 00000000000..aa4fc22eb92 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectscategory.h @@ -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 + +#pragma once + +#include "effectnode.h" + +#include + +namespace QmlDesigner { + +class EffectsCategory +{ +public: + EffectsCategory(const QString &name, const QList &subcategories); + + QString name() const; + QList effects() const; + +private: + QString m_name; + QList m_effects; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index ca1b927c3a6..fade9e9a953 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -5,6 +5,7 @@ #include "assetslibrarywidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" +#include "effectmakerwidget.h" #include "formeditorwidget.h" #include "materialbrowserwidget.h" #include "navigatorwidget.h" @@ -98,6 +99,18 @@ 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); +} + } } diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index fab7fe0ea30..0829b2a8222 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -74,5 +74,14 @@ 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; +}; + } } diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 1e16848c6b5..660c9b2d5ca 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -14,6 +14,7 @@ const char C_DUPLICATE[] = "QmlDesigner.Duplicate"; 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"; @@ -122,6 +123,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,6 +154,7 @@ 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"; From eb13b7274d08d09d132b9189db1bf6dedcc10f40 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 8 Aug 2023 13:32:25 +0200 Subject: [PATCH 023/266] QmlProjectManager: Fix a crash while accessing an uninitialized aspect Task-number: QDS-8145 Change-Id: I1ee9688a2ae860291878c64305cb5e6b2e0df277 Reviewed-by: hjk --- src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp index db0001ac70f..13c2bab9bbb 100644 --- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp @@ -68,8 +68,10 @@ QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target) connect(this, &BoolAspect::changed, this, [this] { for (ProjectExplorer::RunControl *runControl : ProjectExplorer::ProjectExplorerPlugin::allRunControls()) { - if (runControl->aspect()->origin == this) - runControl->initiateStop(); + if (auto aspect = runControl->aspect()) { + if (auto origin = aspect->origin; origin == this) + runControl->initiateStop(); + } } }); } From 380648e142282c974d36c10aa5a18347406ee665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Mon, 7 Aug 2023 10:24:17 +0300 Subject: [PATCH 024/266] Doc: Rewrite the QDS on MCUs intro page in ToC format Rewrote the QDS on MCUs intro page into ToC format. Moved all the sections into separate topics and added links to the topics. Sections rewritten as new topics: - Qt for MCUs Framework - Developing Applications for MCUs - Connecting MCUs with Qt Creator Existing topics polished & edited to suit the new structure: - Qt Design Studio Version Compatibility with Qt for MCUs SDKs - Creating Projects for MCUs - Creating UIs for MCUs - All Topics - Help Task-number: QDS-10398 Change-Id: Iaba9c699d99c13f5f3b8d0fbf7b3ee97c674e9c4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa Reviewed-by: Yasser Grimes --- ...ignstudio-compatibility-with-mcu-sdks.qdoc | 2 +- ...gnstudio-connecting-mcus-with-creator.qdoc | 30 +++++ ...signstudio-creating-projects-for-mcus.qdoc | 10 +- .../qtdesignstudio-creating-uis-for-mcus.qdoc | 6 +- ...udio-developing-applications-for-mcus.qdoc | 28 +++++ .../mcus/qtdesignstudio-mcu-framework.qdoc | 27 +++++ .../src/mcus/qtdesignstudio-on-mcus.qdoc | 106 ++++++------------ .../src/qtdesignstudio-help-overview.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 3 + 9 files changed, 131 insertions(+), 83 deletions(-) create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc create mode 100644 doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc 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 index 2371222c394..40995ec84a9 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc @@ -8,14 +8,14 @@ \title Creating Projects for MCUs - Use the \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. + 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 \QMCU preset creates an application that uses a subset of the - default components that you can deploy, run, and debug on MCU boards. + 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}. diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc index 78ddf12f1a3..dbcb90269ba 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc @@ -4,7 +4,7 @@ /*! \page studio-creating-uis-for-mcus.html \previouspage studio-projects-for-mcus.html - \nextpage studio-help.html + \nextpage studio-developing-apps-for-mcus.html \title Creating UIs for MCUs @@ -20,9 +20,9 @@ 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 \QDS to create application UIs for MCU devices + \section1 Using MCU Components - With \l{\QDS}, you can use subsets of components to create UIs for + 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. 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-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/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 38d20e06883..cacd95b80e0 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-creating-uis-for-mcus.html + \previouspage studio-connecting-mcus-with-creator.html \nextpage creator-help.html \title Help diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 960226a9917..e6fc6e4a602 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -254,10 +254,13 @@ \li \l{Using 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 From fdb560019478fe1379d186b75f9e43b500c97893 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Mon, 7 Aug 2023 10:38:17 +0200 Subject: [PATCH 025/266] Docs: Add docs for removing properties through Qt Bridge plugin Improve the properties docs across all Qt Bridge plugins as well Task-number: QDS-8602 Change-Id: Ia48000053f793b08648e0b092195de8a443142b8 Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Vikas Pachdha Reviewed-by: Qt CI Patch Build Bot --- .../src/qtbridge/qtbridge-figma-using.qdoc | 10 ++++++++-- .../src/qtbridge/qtbridge-ps-using.qdoc | 18 ++++++++++++++++-- .../src/qtbridge/qtbridge-sketch-using.qdoc | 18 ++++++++++++++++-- .../src/qtbridge/qtbridge-xd-using.qdoc | 10 ++++++++-- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index 8874186c6f4..5dd5f6d8bdf 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -172,15 +172,21 @@ an \e alias. \row \li \uicontrol {Properties} - \li Specify new properties or assign value to the existing properties of + \li Specify new properties or assign 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. \row \li \uicontrol {Snippet} \li Specify component to be added as child under this component. 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-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-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index 0f16df676d3..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: From e7fa015a4c33e6f1de6ddefc46265318f511f186 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 7 Aug 2023 17:02:40 +0200 Subject: [PATCH 026/266] QmlProjectManager: Fix for absolute import path problem Task-number: QDS-10145 Change-Id: I11998b474a25351901ca0eb45716e1d3e309c572 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- .../qmldesignerexternaldependencies.cpp | 9 ++------- .../buildsystem/qmlbuildsystem.cpp | 14 ++++++++++++-- .../qmlprojectmanager/buildsystem/qmlbuildsystem.h | 8 +++++++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 1bd58991657..a703f838663 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -236,9 +236,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; } @@ -250,10 +248,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/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index df52b599aa8..6b6ef441bfe 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -200,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); } @@ -649,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; From 63dd551b06224872a98c36a3155141db79aa325a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 9 Aug 2023 18:15:56 +0300 Subject: [PATCH 027/266] QmlDesigner: Add effect maker nodes popup window Barebone combobox custom popup implementation. This allows the popup to extend outside the Qml limits. Window content to be implemented separately. Task-number: QDS-10403 Change-Id: Ic59d6a8436630d1dcd76063425ce311e5bdff190 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../effectMakerQmlSources/EffectMaker.qml | 53 ++++++++++++++++++- .../effectmaker/effectmakerwidget.cpp | 22 ++++---- .../effectmaker/effectmakerwidget.h | 2 +- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 82a5ab44193..8eee99d9b27 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -39,7 +39,58 @@ Item { height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground - // TODO + StudioControls.ComboBox { + id: effectNodesComboBox + + actionIndicatorVisible: false + x: 5 + width: parent.width - 50 + anchors.verticalCenter: parent.verticalCenter + + model: [qsTr("+ Add Effect")] + + // hide default popup + popup.width: 0 + popup.height: 0 + + Connections { + target: effectNodesComboBox.popup + + function onAboutToShow() { + var a = root.mapToGlobal(0, 0) + var b = effectNodesComboBox.mapToItem(root, 0, 0) + + effectNodesWindow.x = a.x + b.x + effectNodesComboBox.width - effectNodesWindow.width + effectNodesWindow.y = a.y + b.y + effectNodesComboBox.height - 1 + effectNodesWindow.show() + effectNodesWindow.requestActivate() + } + + function onAboutToHide() { + effectNodesWindow.hide() + } + } + + Window { + id: effectNodesWindow + + width: 600 + height: Math.min(400, Screen.height - y - 40) // TODO: window sizing will be refined + flags: Qt.Popup | Qt.FramelessWindowHint + + onActiveChanged: { + if (!active) + effectNodesComboBox.popup.close() + } + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePopupBackground + border.color: StudioTheme.Values.themeInteraction + border.width: 1 + } + } + } } Image { diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index 8a07cc10e7b..31ad0d5a2a1 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -34,31 +34,31 @@ static QString propertyEditorResourcesPath() EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) : m_effectMakerModel{new EffectMakerModel(this)} , m_effectMakerView(view) - , m_effectMakerWidget{new StudioQuickWidget(this)} + , m_quickWidget{new StudioQuickWidget(this)} { setWindowTitle(tr("Effect Maker", "Title of effect maker widget")); setMinimumWidth(250); - m_effectMakerWidget->quickWidget()->installEventFilter(this); + m_quickWidget->quickWidget()->installEventFilter(this); // create the inner widget - m_effectMakerWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_EFFECT_MAKER); - m_effectMakerWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - Theme::setupTheme(m_effectMakerWidget->engine()); - m_effectMakerWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_effectMakerWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + 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_effectMakerWidget.data()); + 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_effectMakerWidget->registerPropertyMap("EffectMakerBackend"); + auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); map->setProperties({{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, {"rootView", QVariant::fromValue(this)}}); @@ -83,7 +83,7 @@ void EffectMakerWidget::contextHelp(const Core::IContext::HelpCallback &callback StudioQuickWidget *EffectMakerWidget::quickWidget() const { - return m_effectMakerWidget.data(); + return m_quickWidget.data(); } QPointer EffectMakerWidget::effectMakerModel() const @@ -109,7 +109,7 @@ void EffectMakerWidget::reloadQmlSource() { const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml"; QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return); - m_effectMakerWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath)); + 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 index 61e75df9885..243c8aad14e 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -44,7 +44,7 @@ private: QPointer m_effectMakerModel; QPointer m_effectMakerView; - QPointer m_effectMakerWidget; + QPointer m_quickWidget; }; } // namespace QmlDesigner From 6e2c62776bdbcd4d0a72c24a03b05b1200a50121 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 11 Aug 2023 12:53:28 +0300 Subject: [PATCH 028/266] QmlDesigner: Implement effect maker nodes popup and load data in it Change-Id: I95625f2eaf8aac71679b2f816dd20a9167849830 Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectMaker.qml | 87 +++++++++++++++-- src/plugins/qmldesigner/CMakeLists.txt | 3 +- .../effectmaker/effectmakermodel.cpp | 6 +- .../components/effectmaker/effectmakermodel.h | 6 +- .../effectmaker/effectmakernodesmodel.cpp | 94 +++++++++++++++++++ .../effectmaker/effectmakernodesmodel.h | 43 +++++++++ .../effectmaker/effectmakerview.cpp | 4 +- .../effectmaker/effectmakerwidget.cpp | 10 +- .../effectmaker/effectmakerwidget.h | 4 +- .../components/effectmaker/effectnode.h | 6 +- .../effectmaker/effectnodescategory.cpp | 22 +++++ .../effectmaker/effectnodescategory.h | 30 ++++++ .../effectmaker/effectscategory.cpp | 22 ----- .../components/effectmaker/effectscategory.h | 25 ----- 14 files changed, 296 insertions(+), 66 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectscategory.h diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 8eee99d9b27..f4cdddeaca1 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -12,9 +12,6 @@ import EffectMakerBackend Item { id: root - property var effectMakerModel: EffectMakerBackend.effectMakerModel - property var rootView: EffectMakerBackend.rootView - Column { id: col anchors.fill: parent @@ -28,7 +25,7 @@ Item { color: StudioTheme.Values.themeToolbarBackground Row { - // TODO: Filter row + // TODO } } @@ -62,6 +59,7 @@ Item { effectNodesWindow.x = a.x + b.x + effectNodesComboBox.width - effectNodesWindow.width effectNodesWindow.y = a.y + b.y + effectNodesComboBox.height - 1 + effectNodesWindow.show() effectNodesWindow.requestActivate() } @@ -74,7 +72,7 @@ Item { Window { id: effectNodesWindow - width: 600 + width: row.width height: Math.min(400, Screen.height - y - 40) // TODO: window sizing will be refined flags: Qt.Popup | Qt.FramelessWindowHint @@ -85,9 +83,84 @@ Item { Rectangle { anchors.fill: parent - color: StudioTheme.Values.themePopupBackground + color: StudioTheme.Values.themePanelBackground border.color: StudioTheme.Values.themeInteraction border.width: 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 = root.mapToGlobal(0, 0) + var b = effectNodesComboBox.mapToItem(root, 0, 0) + + effectNodesWindow.x = a.x + b.x + effectNodesComboBox.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: 10 } // spacer + + Repeater { + model: categoryNodes + + Rectangle { + width: 180 + height: 30 + + color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction + : "transparent" + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + } + + Row { + spacing: 5 + + Image { + id: nodeIcon + + width: 30 + height: 30 + + Rectangle { // TODO: placeholder until setting image source + anchors.fill: parent + color: "gray" + } + } + + Text { + text: modelData.nodeName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: nodeIcon.verticalCenter + } + } + } + } + } + } + } } } } @@ -123,7 +196,7 @@ Item { Repeater { id: categories width: root.width - model: effectMakerModel + model: EffectMakerBackend.effectMakerModel delegate: HelperWidgets.Section { id: effectsSection diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 78895e0a340..409bf10c3fd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -712,8 +712,9 @@ extend_qtc_plugin(QmlDesigner effectmakerwidget.cpp effectmakerwidget.h effectmakerview.cpp effectmakerview.h effectmakermodel.cpp effectmakermodel.h + effectmakernodesmodel.cpp effectmakernodesmodel.h effectnode.cpp effectnode.h - effectscategory.cpp effectscategory.h + effectnodescategory.cpp effectnodescategory.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 85c9e9942e3..5adae133b5c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -40,13 +40,13 @@ QVariant EffectMakerModel::data(const QModelIndex &index, int role) const if (index.row() < 0 || index.row() >= m_categories.count()) return {}; - const EffectsCategory *category = m_categories[index.row()]; + const EffectNodesCategory *category = m_categories.at(index.row()); if (role == CategoryRole) return category->name(); if (role == EffectsRole) { QStringList effectsNames; - const QList effects = category->effects(); + const QList effects = category->nodes(); for (const EffectNode *effect : effects) effectsNames << effect->name(); @@ -90,7 +90,7 @@ void EffectMakerModel::loadModel() itEffects.next(); effects.push_back(new EffectNode(QFileInfo(itEffects.fileName()).baseName())); } - EffectsCategory *category = new EffectsCategory(itCategories.fileName(), effects); + EffectNodesCategory *category = new EffectNodesCategory(itCategories.fileName(), effects); m_categories.push_back(category); endInsertRows(); } diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index e7ed32aa64f..1278a1c5af6 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -5,7 +5,7 @@ #include -#include "effectscategory.h" +#include "effectnodescategory.h" namespace Utils { class FilePath; @@ -37,7 +37,7 @@ public: void loadModel(); void resetModel(); - QList categories() { return m_categories; } + QList categories() { return m_categories; } Q_INVOKABLE void selectEffect(int idx, bool force = false); Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false); @@ -51,7 +51,7 @@ private: bool isValidIndex(int idx) const; static Utils::FilePath getQmlEffectsPath(); - QList m_categories; + QList m_categories; int m_selectedIndex = -1; bool m_isEmpty = true; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp new file mode 100644 index 00000000000..1c7ff72de1c --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -0,0 +1,94 @@ +// 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 +#include + +#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)); +} + +// static +Utils::FilePath EffectMakerNodesModel::getQmlEffectNodesPath() +{ + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + qWarning() << __FUNCTION__ << "No project open"; + return ""; + } + + const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); + return baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker/defaultnodes"); +} + +void EffectMakerNodesModel::loadModel() +{ + const Utils::FilePath effectsPath = getQmlEffectNodesPath(); + + if (!effectsPath.exists()) { + qWarning() << __FUNCTION__ << "Effects not found."; + return; + } + + QDirIterator itCategories(effectsPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); + while (itCategories.hasNext()) { + itCategories.next(); + + if (itCategories.fileName() == "images") + continue; + + QList effects = {}; + Utils::FilePath categoryPath = effectsPath.resolvePath(itCategories.fileName()); + QDirIterator itEffects(categoryPath.toString(), QDir::Files | QDir::NoDotAndDotDot); + while (itEffects.hasNext()) { + itEffects.next(); + effects.push_back(new EffectNode(QFileInfo(itEffects.fileName()).baseName())); + } + EffectNodesCategory *category = new EffectNodesCategory(itCategories.fileName(), 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..022111ea2c0 --- /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 + +#include "effectnodescategory.h" + +namespace Utils { +class FilePath; +} + +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: + static Utils::FilePath getQmlEffectNodesPath(); + + QList m_categories; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp index 7460fb0bd66..8ac65ed4558 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp @@ -4,7 +4,7 @@ #include "effectmakerview.h" #include "effectmakerwidget.h" -#include "effectmakermodel.h" +#include "effectmakernodesmodel.h" #include "designmodecontext.h" #include "nodeinstanceview.h" @@ -57,7 +57,7 @@ void EffectMakerView::modelAttached(Model *model) // Add some dummy effects data //m_widget->effectMakerModel()->setEffects({"Drop Shadow", "Colorize", "Fast Blue"}); // TODO - m_widget->effectMakerModel()->loadModel(); + m_widget->effectMakerNodesModel()->loadModel(); } void EffectMakerView::modelAboutToBeDetached(Model *model) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index 31ad0d5a2a1..bf85a77d115 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -4,6 +4,7 @@ #include "effectmakerwidget.h" #include "effectmakermodel.h" +#include "effectmakernodesmodel.h" #include "effectmakerview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" @@ -33,6 +34,7 @@ static QString propertyEditorResourcesPath() EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) : m_effectMakerModel{new EffectMakerModel(this)} + , m_effectMakerNodesModel{new EffectMakerNodesModel(this)} , m_effectMakerView(view) , m_quickWidget{new StudioQuickWidget(this)} { @@ -59,7 +61,8 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_EFFECTMAKER_TIME); auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); - map->setProperties({{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, + map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, + {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, {"rootView", QVariant::fromValue(this)}}); // init the first load of the QML UI elements @@ -91,6 +94,11 @@ QPointer EffectMakerWidget::effectMakerModel() const return m_effectMakerModel; } +QPointer EffectMakerWidget::effectMakerNodesModel() const +{ + return m_effectMakerNodesModel; +} + void EffectMakerWidget::focusSection(int section) { Q_UNUSED(section) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h index 243c8aad14e..74df8d8eec1 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -13,6 +13,7 @@ namespace QmlDesigner { class EffectMakerView; class EffectMakerModel; +class EffectMakerNodesModel; class EffectMakerWidget : public QFrame { @@ -32,10 +33,10 @@ public: StudioQuickWidget *quickWidget() const; QPointer effectMakerModel() const; + QPointer effectMakerNodesModel() const; Q_INVOKABLE void focusSection(int section); - protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -43,6 +44,7 @@ private: void reloadQmlSource(); QPointer m_effectMakerModel; + QPointer m_effectMakerNodesModel; QPointer m_effectMakerView; QPointer m_quickWidget; }; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h index 74571193f7c..b9a51c7f044 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -7,8 +7,12 @@ namespace QmlDesigner { -class EffectNode +class EffectNode : public QObject { + Q_OBJECT + + Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + public: EffectNode(const QString &name); 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/effectscategory.cpp b/src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp deleted file mode 100644 index a5b4843fc53..00000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectscategory.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// 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 "effectscategory.h" - -namespace QmlDesigner { - -EffectsCategory::EffectsCategory(const QString &name, const QList &subcategories) - : m_name(name), - m_effects(subcategories) {} - -QString EffectsCategory::name() const -{ - return m_name; -} - -QList EffectsCategory::effects() const -{ - return m_effects; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectscategory.h b/src/plugins/qmldesigner/components/effectmaker/effectscategory.h deleted file mode 100644 index aa4fc22eb92..00000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectscategory.h +++ /dev/null @@ -1,25 +0,0 @@ -// 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 EffectsCategory -{ -public: - EffectsCategory(const QString &name, const QList &subcategories); - - QString name() const; - QList effects() const; - -private: - QString m_name; - QList m_effects; -}; - -} // namespace QmlDesigner From dcbab3b5ddb2ed3e603d9a59e210ed60aa8856f1 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 11 Aug 2023 14:30:16 +0300 Subject: [PATCH 029/266] QmlDesigner: Load effect maker nodes from path ...instead of from Qt SDK Change-Id: I10d3104477b7cd7c729121175c66a7402ea49651 Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen --- .../effectmaker/effectmakernodesmodel.cpp | 45 +++++++++++-------- .../effectmaker/effectmakernodesmodel.h | 12 ++--- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp index 1c7ff72de1c..e86a7eaf8e6 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -3,13 +3,9 @@ #include "effectmakernodesmodel.h" -#include -#include -#include +#include -#include - -#include +#include namespace QmlDesigner { @@ -42,29 +38,42 @@ QVariant EffectMakerNodesModel::data(const QModelIndex &index, int role) const return m_categories.at(index.row())->property(roleNames().value(role)); } -// static -Utils::FilePath EffectMakerNodesModel::getQmlEffectNodesPath() +void EffectMakerNodesModel::findNodesPath() { - const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); - if (!target) { - qWarning() << __FUNCTION__ << "No project open"; - return ""; + 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; } - const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); - return baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker/defaultnodes"); + m_nodesPath = Utils::FilePath::fromString(nodesDir.path()); } void EffectMakerNodesModel::loadModel() { - const Utils::FilePath effectsPath = getQmlEffectNodesPath(); + findNodesPath(); - if (!effectsPath.exists()) { + if (!m_nodesPath.exists()) { qWarning() << __FUNCTION__ << "Effects not found."; return; } - QDirIterator itCategories(effectsPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); + QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); while (itCategories.hasNext()) { itCategories.next(); @@ -72,7 +81,7 @@ void EffectMakerNodesModel::loadModel() continue; QList effects = {}; - Utils::FilePath categoryPath = effectsPath.resolvePath(itCategories.fileName()); + Utils::FilePath categoryPath = m_nodesPath.resolvePath(itCategories.fileName()); QDirIterator itEffects(categoryPath.toString(), QDir::Files | QDir::NoDotAndDotDot); while (itEffects.hasNext()) { itEffects.next(); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h index 022111ea2c0..5ed702f84be 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h @@ -3,13 +3,11 @@ #pragma once -#include - #include "effectnodescategory.h" -namespace Utils { -class FilePath; -} +#include + +#include namespace QmlDesigner { @@ -35,9 +33,11 @@ public: QList categories() const { return m_categories; } private: - static Utils::FilePath getQmlEffectNodesPath(); + void findNodesPath(); QList m_categories; + Utils::FilePath m_nodesPath; + bool m_probeNodesDir = false; }; } // namespace QmlDesigner From f90755c6dc57fe50b14e2966360aee77de93223f Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 11 Aug 2023 16:07:19 +0300 Subject: [PATCH 030/266] Only add .qen to the effect maker nodes model Also capitalize first letter of category name. Change-Id: I1a86a27d98a4cf666c0b748193eb093061ad2a3c Reviewed-by: Miikka Heikkinen --- .../components/effectmaker/effectmakernodesmodel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp index e86a7eaf8e6..3d201b9e9c4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -82,12 +82,15 @@ void EffectMakerNodesModel::loadModel() QList effects = {}; Utils::FilePath categoryPath = m_nodesPath.resolvePath(itCategories.fileName()); - QDirIterator itEffects(categoryPath.toString(), QDir::Files | QDir::NoDotAndDotDot); + QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files); while (itEffects.hasNext()) { itEffects.next(); effects.push_back(new EffectNode(QFileInfo(itEffects.fileName()).baseName())); } - EffectNodesCategory *category = new EffectNodesCategory(itCategories.fileName(), effects); + + QString catName = itCategories.fileName(); + catName[0] = catName[0].toUpper(); // capitalize first letter + EffectNodesCategory *category = new EffectNodesCategory(catName, effects); m_categories.push_back(category); } From b4db8a2d84c5b71c81b7feb3bc1d1328ea2247c6 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 8 Aug 2023 13:49:40 +0300 Subject: [PATCH 031/266] QmlDesigner: Enable shader caching in puppet When shaders are cached in addition to pipeline caching, further speedup of another 20% or so is achieved on resetting puppets in complex 3D projects. Task-number: QTBUG-103802 Change-Id: I55c950b14c7d854ca3b4de40558ecd26889aa134 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../instances/qt5nodeinstanceserver.cpp | 52 +++++++++++++++++-- .../instances/qt5nodeinstanceserver.h | 3 ++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index e520079a5d1..6dab9421243 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -47,6 +47,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 { @@ -170,7 +177,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 +188,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 +224,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 +250,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 +275,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,6 +287,23 @@ 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 } @@ -281,7 +323,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 diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h index b76e639897a..29f8e253efc 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -81,10 +81,13 @@ protected: 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; }; From 46800c6f977ebf17af55715a6af047ea6bea5e1d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 11 Aug 2023 15:46:30 +0300 Subject: [PATCH 032/266] QmlDesigner: Improve helper grid handling in 3D edit view Changed the grid spacing to depend on view distance rather than zoom factor, making it more understandable. Also it now takes into account the distance of the lookat point from the grid plane to improve spacing when focusing objects far away from the grid plane. Simplified the logic on how subdivision alpha is calculated, making it more flexible in case we want to allow configurable grid spacing. Also, now the grid steps are bit more sensible numbers, i.e. multiples of 50 instead of multiples of 55. Change-Id: I37bcdb9469273b3ac8981519182096aa789d5b77 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 4 +- .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 67 +++++++++++++++---- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 05bc0f5e2c7..de93e1cd1c8 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -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(); diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 9f3e25d44a2..e06140199eb 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,19 +15,62 @@ 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) + // This is step of the main line of the grid, between those is always one subdiv line + property int gridStep: 100 + + property int minGridStep: 50 + readonly property int maxGridStep: 32 * minGridStep + + readonly property int gridArea: minGridStep * 128 + + // 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 + + // 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]); + function calcRad() + { + return Math.atan(gridStep / cameraDistance) + } + + onCameraDistanceChanged: { + if (cameraDistance === 0) + return + + // Calculate new grid step + let gridRad = calcRad() + while (gridRad < minGridRad && gridStep < maxGridStep) { + gridStep *= 2 + if (gridStep > maxGridStep) + gridStep = maxGridStep + gridRad = calcRad() + } + while (gridRad > minGridRad * 2 && gridStep > minGridStep) { + gridStep /= 2 + if (gridStep < minGridStep) + gridStep = minGridStep + gridRad = calcRad() + } + + // Calculate alpha for subgrid. Smaller the perceived spacing, more transparent subgrid is. + helperGrid.subdivAlpha = 2 * (1 - (minGridRad / gridRad)) } environment: sceneEnv @@ -41,9 +85,8 @@ 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; + lines: gridArea / gridStep + step: gridStep } PointLight { From 56303d62a6215498e21e5177128a531b33810f3e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 11 Aug 2023 17:18:01 +0300 Subject: [PATCH 033/266] QmlDesigner: Load effect maker nodes' icons Also remove some unused code. Fixes: QDS-10426 Change-Id: I71c4fde339261e2856472c15bde56ee8850ed236 Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../effectMakerQmlSources/EffectMaker.qml | 8 +-- .../effectmaker/effectmakermodel.cpp | 65 +------------------ .../components/effectmaker/effectmakermodel.h | 6 -- .../effectmaker/effectmakernodesmodel.cpp | 9 ++- .../components/effectmaker/effectnode.cpp | 10 +-- .../components/effectmaker/effectnode.h | 7 +- 6 files changed, 19 insertions(+), 86 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index f4cdddeaca1..c4ddd616ed0 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -137,16 +137,14 @@ Item { Row { spacing: 5 - Image { + IconImage { id: nodeIcon width: 30 height: 30 - Rectangle { // TODO: placeholder until setting image source - anchors.fill: parent - color: "gray" - } + color: StudioTheme.Values.themeTextColor + source: modelData.nodeIcon } Text { diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 5adae133b5c..0d38ea446e8 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -3,14 +3,6 @@ #include "effectmakermodel.h" -#include -#include -#include - -#include - -#include - namespace QmlDesigner { EffectMakerModel::EffectMakerModel(QObject *parent) @@ -33,69 +25,16 @@ int EffectMakerModel::rowCount(const QModelIndex &parent) const return m_categories.count(); } -QVariant EffectMakerModel::data(const QModelIndex &index, int role) const +QVariant EffectMakerModel::data(const QModelIndex &index, int /*role*/) const { - // TODO: to be updated - if (index.row() < 0 || index.row() >= m_categories.count()) return {}; - const EffectNodesCategory *category = m_categories.at(index.row()); - if (role == CategoryRole) - return category->name(); - - if (role == EffectsRole) { - QStringList effectsNames; - const QList effects = category->nodes(); - for (const EffectNode *effect : effects) - effectsNames << effect->name(); - - return effectsNames; - } + // TODO return {}; } -// static -Utils::FilePath EffectMakerModel::getQmlEffectsPath() -{ - const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); - if (!target) { - qWarning() << __FUNCTION__ << "No project open"; - return ""; - } - - const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); - return baseQtVersion->qmlPath().pathAppended("QtQuickEffectMaker/defaultnodes"); -} - -void EffectMakerModel::loadModel() -{ - const Utils::FilePath effectsPath = getQmlEffectsPath(); - - if (!effectsPath.exists()) { - qWarning() << __FUNCTION__ << "Effects are not found."; - return; - } - QDirIterator itCategories(effectsPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); - while (itCategories.hasNext()) { - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - itCategories.next(); - if (itCategories.fileName() == "images") - continue; - QList effects = {}; - Utils::FilePath categoryPath = effectsPath.resolvePath(itCategories.fileName()); - QDirIterator itEffects(categoryPath.toString(), QDir::Files | QDir::NoDotAndDotDot); - while (itEffects.hasNext()) { - itEffects.next(); - effects.push_back(new EffectNode(QFileInfo(itEffects.fileName()).baseName())); - } - EffectNodesCategory *category = new EffectNodesCategory(itCategories.fileName(), effects); - m_categories.push_back(category); - endInsertRows(); - } -} - void EffectMakerModel::resetModel() { beginResetModel(); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 1278a1c5af6..f56c2ed9b91 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -7,10 +7,6 @@ #include "effectnodescategory.h" -namespace Utils { -class FilePath; -} - namespace QmlDesigner { class EffectMakerModel : public QAbstractListModel @@ -34,7 +30,6 @@ public: bool isEmpty() const { return m_isEmpty; } - void loadModel(); void resetModel(); QList categories() { return m_categories; } @@ -49,7 +44,6 @@ signals: private: bool isValidIndex(int idx) const; - static Utils::FilePath getQmlEffectsPath(); QList m_categories; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp index 3d201b9e9c4..8b7b5c3deb1 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -80,15 +80,20 @@ void EffectMakerNodesModel::loadModel() if (itCategories.fileName() == "images") 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(QFileInfo(itEffects.fileName()).baseName())); + QString fileName = QFileInfo(itEffects.fileName()).baseName(); + QString iconPath = m_nodesPath.path() + '/' + catName + "/icon/" + fileName + ".svg"; + if (!QFileInfo::exists(iconPath)) + iconPath = m_nodesPath.path() + "/placeholder.svg"; + effects.push_back(new EffectNode(fileName, iconPath)); } - QString catName = itCategories.fileName(); catName[0] = catName[0].toUpper(); // capitalize first letter EffectNodesCategory *category = new EffectNodesCategory(catName, effects); m_categories.push_back(category); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp index 777fe28ec82..5b554bc6364 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -5,12 +5,8 @@ namespace QmlDesigner { -EffectNode::EffectNode(const QString &name) - : m_name(name) {} - -QString EffectNode::name() const -{ - return m_name; -} +EffectNode::EffectNode(const QString &name, const QString &iconPath) + : m_name(name) + , m_iconPath(QUrl::fromLocalFile(iconPath)) {} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h index b9a51c7f044..4b1216d3539 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -4,6 +4,7 @@ #pragma once #include +#include namespace QmlDesigner { @@ -12,14 +13,14 @@ class EffectNode : public QObject Q_OBJECT Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT) public: - EffectNode(const QString &name); - - QString name() const; + EffectNode(const QString &name, const QString &iconPath); private: QString m_name; + QUrl m_iconPath; }; } // namespace QmlDesigner From a4fbbb24637b12f08a6dcfef702c34d35d25e03f Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 14 Aug 2023 11:12:04 +0300 Subject: [PATCH 034/266] QmlDesigner: Create effect node entity members The EffectNode entity members and initial implementation for loading effect resources Task-number: QDS-10402 Change-Id: I36913731f15fdc89e7bbaa1a7b40088a817e3086 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../effectmaker/effectmakernodesmodel.cpp | 6 +- .../components/effectmaker/effectnode.cpp | 67 ++++++++++++++++++- .../components/effectmaker/effectnode.h | 28 +++++++- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp index 8b7b5c3deb1..420c3f741bf 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -87,11 +87,7 @@ void EffectMakerNodesModel::loadModel() QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files); while (itEffects.hasNext()) { itEffects.next(); - QString fileName = QFileInfo(itEffects.fileName()).baseName(); - QString iconPath = m_nodesPath.path() + '/' + catName + "/icon/" + fileName + ".svg"; - if (!QFileInfo::exists(iconPath)) - iconPath = m_nodesPath.path() + "/placeholder.svg"; - effects.push_back(new EffectNode(fileName, iconPath)); + effects.push_back(new EffectNode(itEffects.filePath())); } catName[0] = catName[0].toUpper(); // capitalize first letter diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp index 5b554bc6364..fef05c3ed00 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -3,10 +3,71 @@ #include "effectnode.h" +#include +#include + namespace QmlDesigner { -EffectNode::EffectNode(const QString &name, const QString &iconPath) - : m_name(name) - , m_iconPath(QUrl::fromLocalFile(iconPath)) {} +EffectNode::EffectNode(const QString &qenPath) + : m_qenPath(qenPath) +{ + parse(qenPath); +} + +QString EffectNode::qenPath() const +{ + return m_qenPath; +} + +QString EffectNode::name() const +{ + return m_name; +} + +int EffectNode::nodeId() const +{ + return m_nodeId; +} + +QString EffectNode::fragmentCode() const +{ + return m_fragmentCode; +} + +QString EffectNode::vertexCode() const +{ + return m_vertexCode; +} + +QString EffectNode::qmlCode() const +{ + return m_qmlCode; +} + +QString EffectNode::description() const +{ + return m_description; +} + +void EffectNode::parse(const QString &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); + + // TODO: QDS-10467 + // Parse the effect from QEN file + // The process from the older implementation has the concept of `project` + // and it contains source & dest nodes that we don't need +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h index 4b1216d3539..a82ef4ccac3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -16,10 +16,36 @@ class EffectNode : public QObject Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT) public: - EffectNode(const QString &name, const QString &iconPath); + EffectNode(const QString &qenPath); + + QString qenPath() const; + + QString name() const; + int nodeId() const; + QString fragmentCode() const; + QString vertexCode() const; + QString qmlCode() const; + QString description() const; + + bool operator==(const EffectNode &rhs) const noexcept + { + return this->m_nodeId == rhs.m_nodeId; + } + bool operator!=(const EffectNode &rhs) const noexcept + { + return !operator==(rhs); + } private: + void parse(const QString &qenPath); + + QString m_qenPath; QString m_name; + int m_nodeId = -1; + QString m_fragmentCode; + QString m_vertexCode; + QString m_qmlCode; + QString m_description; QUrl m_iconPath; }; From bdd076efcdb5ac762631389df128b29cedefc784 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 4 Aug 2023 20:16:43 +0200 Subject: [PATCH 035/266] QmlDesigner: Improve Model Make node collection a little bit more explicit. Before we created, copied and destroyed node lists. Now we send a list around where we add nodes. Use raw pointer for properties. Properties are used as internal pointer plus the property name. The repesentation as an shared pointer is a implementation detail and could be removed in the future. Use raw pointer for internal nodes if we don't handle ownership but only access member. Remove double checks for properties. One lookup is better than two or three. Change-Id: If162f365db3e8869cbc19913abfc44d57c2d8324 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Vikas Pachdha --- .../designercore/include/abstractproperty.h | 12 +- .../designercore/include/nodelistproperty.h | 6 +- .../designercore/model/abstractproperty.cpp | 66 +++--- .../designercore/model/bindingproperty.cpp | 13 +- .../designercore/model/internalnode.cpp | 105 ++++++---- .../designercore/model/internalnode_p.h | 70 ++++--- .../model/internalnodeabstractproperty.h | 5 - .../model/internalnodelistproperty.cpp | 20 +- .../model/internalnodelistproperty.h | 8 +- .../model/internalnodeproperty.cpp | 25 +-- .../designercore/model/internalnodeproperty.h | 6 +- .../designercore/model/internalproperty.cpp | 11 - .../designercore/model/internalproperty.h | 70 ++++++- .../qmldesigner/designercore/model/model.cpp | 196 +++++++++++------- .../qmldesigner/designercore/model/model_p.h | 46 ++-- .../designercore/model/modelnode.cpp | 6 +- .../model/nodeabstractproperty.cpp | 86 ++++++-- .../designercore/model/nodelistproperty.cpp | 28 ++- .../designercore/model/nodeproperty.cpp | 9 +- .../model/signalhandlerproperty.cpp | 18 +- .../designercore/model/variantproperty.cpp | 6 +- 21 files changed, 475 insertions(+), 337 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/abstractproperty.h b/src/plugins/qmldesigner/designercore/include/abstractproperty.h index 6074858265a..7c1dab63916 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/abstractproperty.h @@ -54,7 +54,7 @@ public: ~AbstractProperty(); AbstractProperty(const AbstractProperty &property, AbstractView *view); - PropertyName name() const; + const PropertyName &name() const; bool isValid() const; explicit operator bool() const { return isValid(); } @@ -125,8 +125,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/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/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/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index bf1dc59e155..5369884a841 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -22,7 +22,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 +58,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(); } @@ -351,7 +352,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/internalnode.cpp b/src/plugins/qmldesigner/designercore/model/internalnode.cpp index 0c81d2ab28f..356c0a8be44 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnode.cpp @@ -18,10 +18,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(); @@ -116,8 +112,7 @@ AuxiliaryDatasForType InternalNode::auxiliaryData(AuxiliaryDataType type) const void InternalNode::removeProperty(const PropertyName &name) { - InternalProperty::Pointer property = m_namePropertyHash.take(name); - Q_ASSERT(property); + m_namePropertyHash.remove(name); } PropertyNameList InternalNode::propertyNameList() const @@ -125,53 +120,73 @@ 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; -} - 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 InternalProperty::Pointer &property : m_namePropertyHash) + addSubNodes(nodes, property.get()); +} + +void InternalNode::addDirectSubNodes(QList &nodes) const +{ + for (const InternalProperty::Pointer &property : m_namePropertyHash) + addDirectSubNodes(nodes, property.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..84165b3cb8c 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -52,7 +52,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,18 +66,25 @@ public: AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); } template - typename Type::Pointer property(const PropertyName &name) const + Type *property(const PropertyName &name) const { - auto property = m_namePropertyHash.value(name); - if (property && property->propertyType() == Type::type) - return std::static_pointer_cast(property); + auto propertyIter = m_namePropertyHash.find(name); + + if (propertyIter != m_namePropertyHash.end()) { + if (auto property = propertyIter->get(); property && property->propertyType() == Type::type) + return static_cast(property); + } return {}; } - InternalProperty::Pointer property(const PropertyName &name) const + InternalProperty *property(const PropertyName &name) const { - return m_namePropertyHash.value(name); + auto propertyIter = m_namePropertyHash.find(name); + if (propertyIter != m_namePropertyHash.end()) + return propertyIter->get(); + + return nullptr; } auto bindingProperty(const PropertyName &name) const @@ -116,60 +123,62 @@ public: return {}; } - InternalNodeProperty::Pointer nodeProperty(const PropertyName &name) const + auto nodeProperty(const PropertyName &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_namePropertyHash.insert(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,9 +194,10 @@ public: friend size_t qHash(const InternalNodePointer &node) { return ::qHash(node.get()); } -protected: void removeProperty(const PropertyName &name); +protected: + public: TypeName typeName; QString id; 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..efc3a09a74e 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -426,7 +426,7 @@ void ModelPrivate::removeNode(const InternalNodePointer &node) } if (oldParentProperty && oldParentProperty->isEmpty()) { - removePropertyWithoutNotification(oldParentProperty); + removePropertyWithoutNotification(oldParentProperty.get()); propertyChangeFlags |= AbstractView::EmptyPropertiesRemoved; } @@ -753,8 +753,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 +761,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 +779,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 +794,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 +902,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 +916,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 +932,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 +948,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 +987,7 @@ void ModelPrivate::notifyVariantPropertiesChanged(const InternalNodePointer &nod } void ModelPrivate::notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodeAbstractProperty *newPropertyParent, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange) @@ -998,8 +999,12 @@ void ModelPrivate::notifyNodeAboutToBeReparent(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->nodeAboutToBeReparented(modelNode, newProperty, oldProperty, propertyChange); @@ -1007,7 +1012,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 +1024,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 +1134,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 +1149,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 +1211,48 @@ 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()) { + removeNodeFromModel({nodeProperty->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 +1261,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 +1272,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 +1304,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 +1319,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 +1334,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 +1353,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 +1371,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,11 +1391,15 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, const TypeName &dynamicTypeName) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; - if (!parentNode->hasProperty(name)) { + InternalNodeAbstractProperty *newParentProperty = nullptr; + if (auto property = parentNode->property(name)) { + newParentProperty = property->to(); + } else { if (list) - parentNode->addNodeListProperty(name); + newParentProperty = parentNode->addNodeListProperty(name); else - parentNode->addNodeProperty(name, dynamicTypeName); + newParentProperty = parentNode->addNodeProperty(name, dynamicTypeName); + propertyChange |= AbstractView::PropertiesAdded; } @@ -1374,16 +1411,21 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, oldParentPropertyName = childNode->parentProperty()->name(); } - InternalNodeAbstractPropertyPointer newParentProperty(parentNode->nodeAbstractProperty(name)); Q_ASSERT(newParentProperty); - notifyNodeAboutToBeReparent(childNode, newParentProperty, oldParentNode, oldParentPropertyName, propertyChange); + 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 +1444,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 +1477,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); @@ -1742,7 +1788,7 @@ 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); } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 324df37ddb2..88f3891b505 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; @@ -156,12 +148,12 @@ public: void notifyNodeCreated(const InternalNodePointer &newNode); void notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractPropertyPointer &newPropertyParent, + const InternalNodeAbstractProperty *newPropertyParent, 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 +166,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 +246,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, @@ -300,16 +298,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..0c289a7d579 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -607,8 +607,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 @@ -800,7 +800,7 @@ PropertyNameList ModelNode::propertyNames() const */ bool ModelNode::hasProperty(const PropertyName &name) const { - return isValid() && m_internalNode->hasProperty(name); + return isValid() && m_internalNode->property(name); } bool ModelNode::hasVariantProperty(const PropertyName &name) const 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 0822786467e..18100e5298f 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..88983f6b4bc 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp @@ -30,10 +30,13 @@ void NodeProperty::setModelNode(const ModelNode &modelNode) return; } - if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) - privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); + if (auto property = internalNode()->property(name()); !property->isNodeProperty()) + privateModel()->removePropertyAndRelatedResources(property); - 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/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/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) From 1b6f9db8e643b2cdbf311897f527118dd737d771 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 14 Aug 2023 12:43:31 +0200 Subject: [PATCH 036/266] QmlDesigner: Use view instead of values In the case of the has and remove functions we don't need the value. If we use views we can use const expressions. We have to change from QHash to std::map because QHash can not handle different types for the find function. The patch is removing a double lookup too. Change-Id: I89bb942d7948127344d230ee61ac166102354ccb Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Vikas Pachdha --- .../designercore/include/modelfwd.h | 1 + .../designercore/include/modelnode.h | 18 +++--- .../designercore/model/internalnode.cpp | 18 +++--- .../designercore/model/internalnode_p.h | 57 ++++++++++--------- .../qmldesigner/designercore/model/model.cpp | 2 +- .../qmldesigner/designercore/model/model_p.h | 2 +- .../designercore/model/modelnode.cpp | 54 +++++++++++------- 7 files changed, 85 insertions(+), 67 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 2483893d9a8..59c5c467691 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -10,6 +10,7 @@ namespace QmlDesigner { using PropertyName = QByteArray; +using PropertyNameView = QByteArrayView; using PropertyNameList = QList; using TypeName = QByteArray; using PropertyTypeList = QList; diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 9c4b8fb4efe..b082ca538a3 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; diff --git a/src/plugins/qmldesigner/designercore/model/internalnode.cpp b/src/plugins/qmldesigner/designercore/model/internalnode.cpp index 356c0a8be44..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 @@ -110,14 +112,10 @@ AuxiliaryDatasForType InternalNode::auxiliaryData(AuxiliaryDataType type) const return data; } -void InternalNode::removeProperty(const PropertyName &name) -{ - m_namePropertyHash.remove(name); -} - PropertyNameList InternalNode::propertyNameList() const { - return m_namePropertyHash.keys(); + return Utils::transform(m_nameProperties, + [](const auto &entry) { return entry.first; }); } QList InternalNode::allSubNodes() const @@ -150,14 +148,14 @@ void InternalNode::addSubNodes(QList &nodes, const Internal void InternalNode::addSubNodes(QList &nodes) const { - for (const InternalProperty::Pointer &property : m_namePropertyHash) - addSubNodes(nodes, property.get()); + for (const auto &entry : m_nameProperties) + addSubNodes(nodes, entry.second.get()); } void InternalNode::addDirectSubNodes(QList &nodes) const { - for (const InternalProperty::Pointer &property : m_namePropertyHash) - addDirectSubNodes(nodes, property.get()); + for (const auto &entry : m_nameProperties) + addDirectSubNodes(nodes, entry.second.get()); } void InternalNode::addDirectSubNodes(QList &nodes, diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index 84165b3cb8c..a8e8eaf2536 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 @@ -66,74 +67,74 @@ public: AuxiliaryDatasView auxiliaryData() const { return std::as_const(m_auxiliaryDatas); } template - Type *property(const PropertyName &name) const + Type *property(PropertyNameView name) const { - auto propertyIter = m_namePropertyHash.find(name); + auto propertyIter = m_nameProperties.find(name); - if (propertyIter != m_namePropertyHash.end()) { - if (auto property = propertyIter->get(); property && property->propertyType() == Type::type) + if (propertyIter != m_nameProperties.end()) { + if (auto property = propertyIter->second.get(); + property && property->propertyType() == Type::type) return static_cast(property); } return {}; } - InternalProperty *property(const PropertyName &name) const + InternalProperty *property(PropertyNameView name) const { - auto propertyIter = m_namePropertyHash.find(name); - if (propertyIter != m_namePropertyHash.end()) - return propertyIter->get(); + 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; + if (property->propertyType() == PropertyType::NodeList + || property->propertyType() == PropertyType::Node) { + return std::static_pointer_cast(property); + } } return {}; } - auto nodeProperty(const PropertyName &name) const - { - return property(name); - } + auto nodeProperty(PropertyNameView name) const { return property(name); } template Type *addProperty(const PropertyName &name) { auto newProperty = std::make_shared(name, shared_from_this()); auto pointer = newProperty.get(); - m_namePropertyHash.insert(name, std::move(newProperty)); + m_nameProperties.try_emplace(name, std::move(newProperty)); return pointer; } @@ -194,7 +195,11 @@ public: friend size_t qHash(const InternalNodePointer &node) { return ::qHash(node.get()); } - 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) + } protected: @@ -216,7 +221,7 @@ public: private: AuxiliaryDatas m_auxiliaryDatas; InternalNodeAbstractProperty::WeakPointer m_parentProperty; - QHash m_namePropertyHash; + std::map> m_nameProperties; }; } // Internal diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index efc3a09a74e..7652f1f378f 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -465,7 +465,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; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 88f3891b505..9b0fdf72b2d 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -262,7 +262,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); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 0c289a7d579..2811267a32f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -599,7 +599,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; @@ -798,60 +798,74 @@ PropertyNameList ModelNode::propertyNames() const /*! \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->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) From aa39093a3fc7b2ebd60dc3780d8bd7f4fca7c6f7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 9 Aug 2023 13:01:04 +0200 Subject: [PATCH 037/266] QmlDesigner: Simplify and optimize property getter So far we create all AbtractProperties and then filter and cast them. So we throw most of them away. Now we filter before we create them and we create directly the type we want. Change-Id: Ic1b32e11f2b934edfcf890e27de323af38051784 Reviewed-by: Vikas Pachdha Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../designercore/include/abstractproperty.h | 5 +- .../designercore/include/bindingproperty.h | 1 - .../designercore/include/modelnode.h | 2 + .../include/nodeabstractproperty.h | 6 +- .../designercore/include/nodeproperty.h | 6 +- .../include/signalhandlerproperty.h | 3 - .../designercore/include/variantproperty.h | 1 - .../designercore/model/internalnode_p.h | 12 ++- .../designercore/model/modelnode.cpp | 84 ++++++++----------- 9 files changed, 59 insertions(+), 61 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/abstractproperty.h b/src/plugins/qmldesigner/designercore/include/abstractproperty.h index 7c1dab63916..811be48870f 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/abstractproperty.h @@ -53,6 +53,10 @@ 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); const PropertyName &name() const; @@ -124,7 +128,6 @@ public: } protected: - AbstractProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); AbstractProperty(const Internal::InternalPropertyPointer &property, Model *model, AbstractView *view); 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/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index b082ca538a3..e95b4f47e3e 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -270,6 +270,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/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/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/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/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index a8e8eaf2536..af5b43eec28 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -119,8 +119,8 @@ public: auto found = m_nameProperties.find(name); if (found != m_nameProperties.end()) { auto property = found->second; - if (property->propertyType() == PropertyType::NodeList - || property->propertyType() == PropertyType::Node) { + auto propertyType = property->propertyType(); + if (propertyType == PropertyType::NodeList || propertyType == PropertyType::Node) { return std::static_pointer_cast(property); } } @@ -201,7 +201,11 @@ public: m_nameProperties.erase(found); // C++ 23 -> m_nameProperties.erase(name) } -protected: + 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; @@ -221,7 +225,7 @@ public: private: AuxiliaryDatas m_auxiliaryDatas; InternalNodeAbstractProperty::WeakPointer m_parentProperty; - std::map> m_nameProperties; + PropertyDict m_nameProperties; }; } // Internal diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 2811267a32f..e0a15ca6c60 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -509,46 +509,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 +535,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; } /*! @@ -795,6 +764,25 @@ 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(); + if (((propertyType == type) || ...)) + properties.emplace_back(propertyName, m_internalNode, model(), view()); + } + + 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 */ From 0c6bc8382fc5d88b9f59e9f8e95b4fdc57fcaf2b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 14 Aug 2023 16:46:47 +0300 Subject: [PATCH 038/266] QmlDesigner: Add a ScrollView to the effect maker nodes popup Also make nodes size smaller and small tweaks to margins. Change-Id: I044684d1a57d1da2ed8b5768df80fb0948f94882 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectMaker.qml | 107 +++++++++--------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index c4ddd616ed0..cf57e43ca41 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -72,8 +72,8 @@ Item { Window { id: effectNodesWindow - width: row.width - height: Math.min(400, Screen.height - y - 40) // TODO: window sizing will be refined + 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: { @@ -87,71 +87,76 @@ Item { border.color: StudioTheme.Values.themeInteraction border.width: 1 - Row { - id: row + HelperWidgets.ScrollView { + anchors.fill: parent + anchors.margins: 1 - 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 + Row { + id: row - var a = root.mapToGlobal(0, 0) - var b = effectNodesComboBox.mapToItem(root, 0, 0) + 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 - effectNodesWindow.x = a.x + b.x + effectNodesComboBox.width - row.width - } + var a = root.mapToGlobal(0, 0) + var b = effectNodesComboBox.mapToItem(root, 0, 0) - padding: 10 - spacing: 10 + effectNodesWindow.x = a.x + b.x + effectNodesComboBox.width - row.width + } - Repeater { - model: EffectMakerBackend.effectMakerNodesModel + padding: 10 + spacing: 10 - Column { - spacing: 10 + Repeater { + model: EffectMakerBackend.effectMakerNodesModel - Text { - text: categoryName - color: StudioTheme.Values.themeTextColor - font.pointSize: StudioTheme.Values.baseFontSize - } + Column { + spacing: 10 - Item { width: 1; height: 10 } // spacer + Text { + text: categoryName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.baseFontSize + } - Repeater { - model: categoryNodes + Item { width: 1; height: 5 } // spacer - Rectangle { - width: 180 - height: 30 + Repeater { + model: categoryNodes - color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction - : "transparent" + Rectangle { + width: 140 + height: 22 - MouseArea { - id: mouseArea + color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction + : "transparent" - anchors.fill: parent - hoverEnabled: true - } + MouseArea { + id: mouseArea - Row { - spacing: 5 - - IconImage { - id: nodeIcon - - width: 30 - height: 30 - - color: StudioTheme.Values.themeTextColor - source: modelData.nodeIcon + anchors.fill: parent + hoverEnabled: true } - Text { - text: modelData.nodeName - color: StudioTheme.Values.themeTextColor - font.pointSize: StudioTheme.Values.baseFontSize - anchors.verticalCenter: nodeIcon.verticalCenter + Row { + spacing: 5 + + IconImage { + id: nodeIcon + + width: 22 + height: 22 + + color: StudioTheme.Values.themeTextColor + source: modelData.nodeIcon + } + + Text { + text: modelData.nodeName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.smallFontSize + anchors.verticalCenter: nodeIcon.verticalCenter + } } } } From c7d2b9bec1a61d36e0c386bc76746c1d1ac4898d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 15 Aug 2023 10:42:00 +0200 Subject: [PATCH 039/266] ADS: Fix potential crash Base repository commit 7a362b7ee3048ecee521ad4ba51647bc8a98193e Change-Id: Id888b92b3738136a534ae7758f585f915777b07d Reviewed-by: Miikka Heikkinen --- src/libs/advanceddockingsystem/dockareatabbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp index c7dab3c2297..57e7c52da78 100644 --- a/src/libs/advanceddockingsystem/dockareatabbar.cpp +++ b/src/libs/advanceddockingsystem/dockareatabbar.cpp @@ -280,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()); From a7987380cde25662be6ff8671e3d7935d8891623 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 15 Aug 2023 13:30:25 +0200 Subject: [PATCH 040/266] QmlDesigner: Silence bogus warning by Clang The parens are needed by the fold expression. Change-Id: Ib53596f2be2f3bcdb6040c1690f5f6dfe6249583 Reviewed-by: Vikas Pachdha --- src/plugins/qmldesigner/designercore/model/modelnode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index e0a15ca6c60..2bc778e44cb 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -776,8 +776,11 @@ QList ModelNode::properties(PropertyType... type) const 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; From bf6ea8496a499c3885d2889c0249ed9623607b53 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 15 Aug 2023 15:40:46 +0300 Subject: [PATCH 041/266] QmlDesigner: Refactor Effect maker nodes view Also add node click handler Change-Id: I4b4e327f5c69f27d64ef2f2c3d13628aea46f0af Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectMaker.qml | 134 +----------------- .../effectMakerQmlSources/EffectNode.qml | 65 +++------ .../EffectNodesComboBox.qml | 109 ++++++++++++++ .../effectmaker/effectmakerwidget.cpp | 9 ++ .../effectmaker/effectmakerwidget.h | 1 + 5 files changed, 144 insertions(+), 174 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index cf57e43ca41..4f751883ac9 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Layouts -import QtQuick.Controls import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme @@ -36,136 +34,8 @@ Item { height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground - StudioControls.ComboBox { - id: effectNodesComboBox - - actionIndicatorVisible: false - x: 5 - width: parent.width - 50 - anchors.verticalCenter: parent.verticalCenter - - model: [qsTr("+ Add Effect")] - - // hide default popup - popup.width: 0 - popup.height: 0 - - Connections { - target: effectNodesComboBox.popup - - function onAboutToShow() { - var a = root.mapToGlobal(0, 0) - var b = effectNodesComboBox.mapToItem(root, 0, 0) - - effectNodesWindow.x = a.x + b.x + effectNodesComboBox.width - effectNodesWindow.width - effectNodesWindow.y = a.y + b.y + effectNodesComboBox.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) - effectNodesComboBox.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 = root.mapToGlobal(0, 0) - var b = effectNodesComboBox.mapToItem(root, 0, 0) - - effectNodesWindow.x = a.x + b.x + effectNodesComboBox.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 - - Rectangle { - width: 140 - height: 22 - - color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction - : "transparent" - - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: true - } - - Row { - spacing: 5 - - IconImage { - id: nodeIcon - - width: 22 - height: 22 - - color: StudioTheme.Values.themeTextColor - source: modelData.nodeIcon - } - - Text { - text: modelData.nodeName - color: StudioTheme.Values.themeTextColor - font.pointSize: StudioTheme.Values.smallFontSize - anchors.verticalCenter: nodeIcon.verticalCenter - } - } - } - } - } - } - } - } - } - } + EffectNodesComboBox { + mainRoot: root } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml index 808abf89abe..1e9db9ebcd0 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml @@ -3,70 +3,51 @@ import QtQuick import QtQuick.Controls -import QtQuick.Layouts import QtQuickDesignerTheme import HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend -// TODO: this will be redone based on new specs - Rectangle { - property real margin: StudioTheme.Values.marginTopBottom - id: root - height: col.height + margin * 2 - signal showContextMenu() + width: 140 + height: 22 - border.width: EffectMakerBackend.effectMakerModel.selectedIndex === index ? 3 : 0 - border.color: EffectMakerBackend.effectMakerModel.selectedIndex === index - ? StudioTheme.Values.themeControlOutlineInteraction - : "transparent" - color: "transparent" - visible: true // TODO: from rolename -> effectVisible + color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction + : "transparent" MouseArea { id: mouseArea anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + acceptedButtons: Qt.LeftButton - onPressed: (mouse) => { - EffectMakerBackend.effectMakerModel.selectEffect(index) - EffectMakerBackend.rootView.focusSection(0) // TODO: Set the correct section based on current effect - - if (mouse.button === Qt.LeftButton) - // TODO: Start dragging here - ; - else if (mouse.button === Qt.RightButton) - root.showContextMenu() + onClicked: { + EffectMakerBackend.rootView.addEffectNode(modelData.nodeName) } } - ColumnLayout { - id: col - Layout.topMargin: margin - Layout.bottomMargin: margin - Row { + Row { + spacing: 5 - width: parent.width + IconImage { + id: nodeIcon - Text { - anchors.verticalCenter: parent.verticalCenter - rightPadding: margin * 3 - text: '⋮⋮' - color: StudioTheme.Values.themeTextColorDisabled - font.letterSpacing: -10 - font.bold: true - font.pointSize: StudioTheme.Values.mediumIconFontSize - } + width: 22 + height: 22 - StudioControls.CheckBox { - text: modelData - actionIndicatorVisible: false - } + 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..c29449fcf13 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -0,0 +1,109 @@ +// 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 + anchors.verticalCenter: parent.verticalCenter + + 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.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 {} + } + } + } + } + } + } + } +} diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index bf85a77d115..2de6e25c9d1 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -99,6 +99,15 @@ QPointer EffectMakerWidget::effectMakerNodesModel() const return m_effectMakerNodesModel; } +void EffectMakerWidget::addEffectNode(const QString &nodeName) +{ + Q_UNUSED(nodeName) + + // TODO: implement adding a node to the composition + // TODO: nodeName might be changed to nodeId? +// m_effectMakerModel.addNode(nodeName); +} + void EffectMakerWidget::focusSection(int section) { Q_UNUSED(section) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h index 74df8d8eec1..0eee5ef7ff4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -35,6 +35,7 @@ public: QPointer effectMakerModel() const; QPointer effectMakerNodesModel() const; + Q_INVOKABLE void addEffectNode(const QString &nodeName); Q_INVOKABLE void focusSection(int section); protected: From e2f5b37fce8270a2b35f3eced86c299913e5d3d9 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 14 Aug 2023 15:01:46 +0300 Subject: [PATCH 042/266] QmlDesigner: Add 3 icons to the icon font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added icons: help, drag marks, and code. Fixes: QDS-10459 Change-Id: I87c7e28d8858e49c98a4e9e2d991ee5d87df5a23 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 537 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 58192 -> 60932 bytes .../components/componentcore/theme.h | 3 + 3 files changed, 273 insertions(+), 267 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 364b80a4856..c9ee8a38803 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -74,273 +74,276 @@ 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 keyframe: "\u00BB" + readonly property string languageList_medium: "\u00BC" + readonly property string layouts_small: "\u00BD" + readonly property string lights_small: "\u00BE" + readonly property string linear_medium: "\u00BF" + readonly property string linkTriangle: "\u00C0" + readonly property string linked: "\u00C1" + readonly property string listView: "\u00C2" + readonly property string list_medium: "\u00C3" + readonly property string localOrient_medium: "\u00C4" + readonly property string lockOff: "\u00C5" + readonly property string lockOn: "\u00C6" + readonly property string loopPlayback_medium: "\u00C7" + readonly property string materialBrowser_medium: "\u00C8" + readonly property string materialPreviewEnvironment: "\u00C9" + readonly property string materialPreviewModel: "\u00CA" + readonly property string material_medium: "\u00CB" + readonly property string maxBar_small: "\u00CC" + readonly property string mergeCells: "\u00CD" + readonly property string merge_small: "\u00CE" + readonly property string minus: "\u00CF" + readonly property string mirror: "\u00D0" + readonly property string more_medium: "\u00D1" + readonly property string mouseArea_small: "\u00D2" + readonly property string moveDown_medium: "\u00D3" + readonly property string moveInwards_medium: "\u00D4" + readonly property string moveUp_medium: "\u00D5" + readonly property string moveUpwards_medium: "\u00D6" + readonly property string move_medium: "\u00D7" + readonly property string newMaterial: "\u00D8" + readonly property string nextFile_large: "\u00D9" + readonly property string normalBar_small: "\u00DA" + readonly property string openLink: "\u00DB" + readonly property string openMaterialBrowser: "\u00DC" + readonly property string orientation: "\u00DD" + readonly property string orthCam_medium: "\u00DE" + readonly property string orthCam_small: "\u00DF" + readonly property string paddingEdge: "\u00E0" + readonly property string paddingFrame: "\u00E1" + readonly property string particleAnimation_medium: "\u00E2" + readonly property string pasteStyle: "\u00E3" + readonly property string paste_small: "\u00E4" + readonly property string pause: "\u00E5" + readonly property string perspectiveCam_medium: "\u00E6" + readonly property string perspectiveCam_small: "\u00E7" + readonly property string pin: "\u00E8" + readonly property string plane_medium: "\u00E9" + readonly property string plane_small: "\u00EA" + readonly property string play: "\u00EB" + readonly property string playFill_medium: "\u00EC" + readonly property string playOutline_medium: "\u00ED" + readonly property string plus: "\u00EE" + readonly property string pointLight_small: "\u00EF" + readonly property string positioners_small: "\u00F0" + readonly property string previewEnv_medium: "\u00F1" + readonly property string previousFile_large: "\u00F2" + readonly property string promote: "\u00F3" + readonly property string properties_medium: "\u00F4" + readonly property string readOnly: "\u00F5" + readonly property string recordFill_medium: "\u00F6" + readonly property string recordOutline_medium: "\u00F7" + readonly property string redo: "\u00F8" + readonly property string reload_medium: "\u00F9" + readonly property string remove_medium: "\u00FA" + readonly property string remove_small: "\u00FB" + readonly property string rename_small: "\u00FC" + readonly property string replace_small: "\u00FD" + readonly property string resetView_small: "\u00FE" + readonly property string restartParticles_medium: "\u00FF" + readonly property string reverseOrder_medium: "\u0100" + readonly property string roatate_medium: "\u0101" + readonly property string rotationFill: "\u0102" + readonly property string rotationOutline: "\u0103" + readonly property string runProjFill_large: "\u0104" + readonly property string runProjOutline_large: "\u0105" + readonly property string s_anchors: "\u0106" + readonly property string s_annotations: "\u0107" + readonly property string s_arrange: "\u0108" + readonly property string s_boundingBox: "\u0109" + readonly property string s_component: "\u010A" + readonly property string s_connections: "\u010B" + readonly property string s_edit: "\u010C" + readonly property string s_enterComponent: "\u010D" + readonly property string s_eventList: "\u010E" + readonly property string s_group: "\u010F" + readonly property string s_layouts: "\u0110" + readonly property string s_merging: "\u0111" + readonly property string s_mouseArea: "\u0112" + readonly property string s_positioners: "\u0113" + readonly property string s_selection: "\u0114" + readonly property string s_snapping: "\u0115" + readonly property string s_timeline: "\u0116" + readonly property string s_visibility: "\u0117" + readonly property string saveLogs_medium: "\u0118" + readonly property string scale_medium: "\u0119" + readonly property string search: "\u011A" + readonly property string search_small: "\u011B" + readonly property string sectionToggle: "\u011C" + readonly property string selectFill_medium: "\u011D" + readonly property string selectOutline_medium: "\u011E" + readonly property string selectParent_small: "\u011F" + readonly property string selection_small: "\u0120" + readonly property string settings_medium: "\u0121" + readonly property string signal_small: "\u0122" + readonly property string snapping_small: "\u0123" + readonly property string sphere_medium: "\u0124" + readonly property string sphere_small: "\u0125" + readonly property string splitColumns: "\u0126" + readonly property string splitRows: "\u0127" + readonly property string spotLight_small: "\u0128" + readonly property string stackedContainer_small: "\u0129" + readonly property string startNode: "\u012A" + readonly property string step_medium: "\u012B" + readonly property string stop_medium: "\u012C" + readonly property string testIcon: "\u012D" + readonly property string textAlignBottom: "\u012E" + readonly property string textAlignCenter: "\u012F" + readonly property string textAlignJustified: "\u0130" + readonly property string textAlignLeft: "\u0131" + readonly property string textAlignMiddle: "\u0132" + readonly property string textAlignRight: "\u0133" + readonly property string textAlignTop: "\u0134" + readonly property string textBulletList: "\u0135" + readonly property string textFullJustification: "\u0136" + readonly property string textNumberedList: "\u0137" + readonly property string textures_medium: "\u0138" + readonly property string tickIcon: "\u0139" + readonly property string tickMark_small: "\u013A" + readonly property string timeline_small: "\u013B" + readonly property string toEndFrame_medium: "\u013C" + readonly property string toNextFrame_medium: "\u013D" + readonly property string toPrevFrame_medium: "\u013E" + readonly property string toStartFrame_medium: "\u013F" + readonly property string topToolbar_annotations: "\u0140" + readonly property string topToolbar_closeFile: "\u0141" + readonly property string topToolbar_designMode: "\u0142" + readonly property string topToolbar_enterComponent: "\u0143" + readonly property string topToolbar_home: "\u0144" + readonly property string topToolbar_makeComponent: "\u0145" + readonly property string topToolbar_navFile: "\u0146" + readonly property string topToolbar_runProject: "\u0147" + readonly property string translationCreateFiles: "\u0148" + readonly property string translationCreateReport: "\u0149" + readonly property string translationExport: "\u014A" + readonly property string translationImport: "\u014B" + readonly property string translationSelectLanguages: "\u014C" + readonly property string translationTest: "\u014D" + readonly property string transparent: "\u014E" + readonly property string triState: "\u014F" + readonly property string triangleArcA: "\u0150" + readonly property string triangleArcB: "\u0151" + readonly property string triangleCornerA: "\u0152" + readonly property string triangleCornerB: "\u0153" + readonly property string unLinked: "\u0154" + readonly property string undo: "\u0155" + readonly property string unify_medium: "\u0156" + readonly property string unpin: "\u0157" + readonly property string upDownIcon: "\u0158" + readonly property string upDownSquare2: "\u0159" + readonly property string updateAvailable_medium: "\u015A" + readonly property string updateContent_medium: "\u015B" + readonly property string visibilityOff: "\u015C" + readonly property string visibilityOn: "\u015D" + readonly property string visible_medium: "\u015E" + readonly property string visible_small: "\u015F" + readonly property string wildcard: "\u0160" + readonly property string wizardsAutomotive: "\u0161" + readonly property string wizardsDesktop: "\u0162" + readonly property string wizardsGeneric: "\u0163" + readonly property string wizardsMcuEmpty: "\u0164" + readonly property string wizardsMcuGraph: "\u0165" + readonly property string wizardsMobile: "\u0166" + readonly property string wizardsUnknown: "\u0167" + readonly property string zoomAll: "\u0168" + readonly property string zoomIn: "\u0169" + readonly property string zoomIn_medium: "\u016A" + readonly property string zoomOut: "\u016B" + readonly property string zoomOut_medium: "\u016C" + readonly property string zoomSelection: "\u016D" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 312554382ce6f286058aede9c3ce645b27c847f1..ac572cde027c833d0a7d587716fce3bcd4ecebf1 100644 GIT binary patch delta 11989 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 Q-1B)0S_F=?nEMDH0Cez#-T(jq delta 9042 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 diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index fba2f25a05c..0c87fcd3ad4 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, From 86d4fbab79a0efe9ca0ada6ee46256f26a2fdf8e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 7 Jun 2023 15:52:58 +0300 Subject: [PATCH 043/266] QmlDesigner: Import only mandatory libraries or directories Only mandatory files are imported by the newly created component. In the case that the import data is empty, All parent imports would be included. Task-number: QDS-9829 Change-Id: Ie96e2bc04a10e00b15ae12c5e58b5dc2392886ae Reviewed-by: Thomas Hartmann Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Bot --- .../include/basetexteditmodifier.h | 2 +- .../include/componenttextmodifier.h | 2 +- .../designercore/include/nodemetainfo.h | 1 + .../include/plaintexteditmodifier.h | 2 +- .../designercore/include/textmodifier.h | 2 +- .../designercore/metainfo/nodemetainfo.cpp | 50 +++++++++++++++++++ .../model/basetexteditmodifier.cpp | 5 +- .../designercore/model/rewriterview.cpp | 17 ++++++- .../qmljscomponentfromobjectdef.cpp | 20 +++----- .../qmljseditor/qmljscomponentfromobjectdef.h | 3 +- src/plugins/qmljseditor/qmljsquickfix.h | 4 +- src/plugins/qmljseditor/qmljsquickfixes.cpp | 6 ++- src/plugins/qmljseditor/qmljswrapinloader.cpp | 3 +- .../qmljstools/qmljsrefactoringchanges.cpp | 12 +++++ .../qmljstools/qmljsrefactoringchanges.h | 1 + 15 files changed, 106 insertions(+), 24 deletions(-) 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/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/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 0ee4e19fe37..3736079cdc2 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -194,6 +194,7 @@ public: bool isEnumeration() const; QString importDirectoryPath() const; + QString requiredImportString() const; friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second) { diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index 4172e8921ee..32a9480d55c 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 QStringList(); } - bool moveToComponent(int /* nodeOffset */) override + bool moveToComponent(int /* nodeOffset */, const QString & /* importData */) override { return false; } private: 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/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 5208428ca42..016117ab9cd 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); @@ -1747,6 +1785,18 @@ QString NodeMetaInfo::importDirectoryPath() const return {}; } +QString NodeMetaInfo::requiredImportString() const +{ + if (!isValid()) + return {}; + + Import imp = m_privateData->requiredImport(); + if (!imp.isEmpty()) + return imp.toImportString(); + + return {}; +} + const Storage::Info::Type &NodeMetaInfo::typeData() const { if (!m_typeData) diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp index 69c74ec6cf8..eb7f0604184 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/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 60067a6bc91..1f80d6fe490 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -1031,11 +1032,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/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 From 0f9a6a2cccd438c893e89085690940c5e8603ec5 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 12 Jun 2023 13:46:31 +0300 Subject: [PATCH 044/266] QmlDesigner: Use Designer icons for 3d view toolbar Designer Icons are updated for the toolbar usage. Disabled colors are changed for the View 3D toolbar. Task-number: QDS-10044 Change-Id: I98e7e43d39af4d57a1527fd1a9600c964e49114c Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../qtcreator/qmldesigner/designericons.json | 444 +++++++++++------- .../componentcore/designeractionmanager.cpp | 5 + .../componentcore/designeractionmanager.h | 1 + .../componentcore/designericons.cpp | 85 +++- .../components/componentcore/designericons.h | 15 +- .../components/edit3d/edit3dview.cpp | 259 +++++----- 6 files changed, 486 insertions(+), 323 deletions(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index ddbfcf5a850..1e70226ff00 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -1,186 +1,278 @@ -{ - "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" + }, + "ToggleGroupIcon": { + "Off": { + "iconName": "selectOutline_medium" + }, + "On": { + "iconName": "selectFill_medium" + } + }, + "VisibilityIcon": { + "Off": { + "iconName": "invisible_medium" + }, + "On": { + "iconName": "visible_medium" + } } } -} +] diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 7cac4c49349..c3af27c455e 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); 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..e2fc812b880 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,11 +87,17 @@ public: ModelCylinderIcon, ModelPlaneIcon, ModelSphereIcon, + MoveToolIcon, ParentIcon, + ParticlesAnimationIcon, + ParticlesPlayIcon, + ParticlesRestartIcon, PasteIcon, PositionsersIcon, PrimitivesIcon, ResetViewIcon, + RotateToolIcon, + ScaleToolIcon, SelecionIcon, ShowBoundsIcon, SimpleCheckIcon, @@ -98,10 +108,7 @@ public: }; Q_ENUM(IconId) - enum Area { - TopToolbarArea, - ContextMenuArea - }; + enum Area { ContextMenuArea, ToolbarArea, TopToolbarArea }; Q_ENUM(Area) enum Mode { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index a96ca59cae7..106890f78e7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -21,7 +21,6 @@ #include "qmldesignerplugin.h" #include "qmlvisualnode.h" #include "seekerslider.h" -#include "theme.h" #include @@ -42,27 +41,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) @@ -554,15 +540,16 @@ Edit3DAction *Edit3DView::createSeekerSliderAction() 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 + = new Edit3DAction(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, @@ -571,7 +558,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_W), true, true, - toolbarIcon(Theme::move_medium), + toolbarIcon(DesignerIcons::MoveToolIcon), this); m_rotateToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ROTATE_TOOL, @@ -581,7 +568,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_E), true, false, - toolbarIcon(Theme::roatate_medium), + toolbarIcon(DesignerIcons::RotateToolIcon), this); m_scaleToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_SCALE_TOOL, @@ -591,7 +578,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_R), true, false, - toolbarIcon(Theme::scale_medium), + toolbarIcon(DesignerIcons::ScaleToolIcon), this); m_fitAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_FIT_SELECTED, @@ -601,30 +588,28 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_F), false, false, - toolbarIcon(Theme::fitToView_medium), + toolbarIcon(DesignerIcons::FitToViewIcon), this); - m_alignCamerasAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_ALIGN_CAMERAS, - View3DActionType::AlignCamerasToView, - QCoreApplication::translate("AlignCamerasToViewAction", "Align Cameras to View"), - QKeySequence(), - false, - false, - toolbarIcon(Theme::alignToCam_medium), - this); - m_alignCamerasAction->action()->setEnabled(false); + m_alignCamerasAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ALIGN_CAMERAS, + View3DActionType::AlignCamerasToView, + QCoreApplication::translate("AlignCamerasToViewAction", + "Align Cameras to View"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::AlignCameraToViewIcon), + this); - m_alignViewAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_ALIGN_VIEW, - View3DActionType::AlignViewToCamera, - QCoreApplication::translate("AlignCamerasToViewAction", "Align View to Camera"), - QKeySequence(), - false, - false, - toolbarIcon(Theme::alignToView_medium), - this); - m_alignViewAction->action()->setEnabled(false); + m_alignViewAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ALIGN_VIEW, + View3DActionType::AlignViewToCamera, + QCoreApplication::translate("AlignViewToCameraAction", + "Align View to Camera"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::AlignViewToCameraIcon), + this); m_cameraModeAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_CAMERA, @@ -634,29 +619,29 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_T), true, false, - toolbarIcon(Theme::orthCam_small, Theme::perspectiveCam_small), + toolbarIcon(DesignerIcons::CameraIcon), this); - m_orientationModeAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_ORIENTATION, - View3DActionType::OrientationToggle, - QCoreApplication::translate("OrientationToggleAction", "Toggle Global/Local Orientation"), - QKeySequence(Qt::Key_Y), - true, - false, - toolbarIcon(Theme::localOrient_medium), - this); + m_orientationModeAction + = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ORIENTATION, + View3DActionType::OrientationToggle, + QCoreApplication::translate("OrientationToggleAction", + "Toggle Global/Local Orientation"), + QKeySequence(Qt::Key_Y), + true, + false, + 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 = new Edit3DAction(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( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_GRID, @@ -764,49 +749,50 @@ 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 + = new Edit3DAction(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 = new Edit3DAction(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 + = new Edit3DAction(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 = new Edit3DAction(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()) @@ -825,16 +811,17 @@ void Edit3DView::createEdit3DActions() pos); }; - m_visibilityTogglesAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_VISIBILITY_TOGGLES, - View3DActionType::Empty, - QCoreApplication::translate("VisibilityTogglesAction", "Visibility Toggles"), - QKeySequence(), - false, - false, - toolbarIcon(Theme::invisible_medium, Theme::visible_medium), - this, - visibilityTogglesTrigger); + m_visibilityTogglesAction + = new Edit3DAction(QmlDesigner::Constants::EDIT3D_VISIBILITY_TOGGLES, + View3DActionType::Empty, + QCoreApplication::translate("VisibilityTogglesAction", + "Visibility Toggles"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::VisibilityIcon), + this, + visibilityTogglesTrigger); SelectionContextOperation backgroundColorActionsTrigger = [this](const SelectionContext &) { if (!edit3DWidget()->backgroundColorMenu()) @@ -853,23 +840,25 @@ void Edit3DView::createEdit3DActions() pos); }; - m_backgrondColorMenuAction = new Edit3DAction( - QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, - View3DActionType::Empty, - QCoreApplication::translate("BackgroundColorMenuActions", "Background Color Actions"), - QKeySequence(), - false, - false, - toolbarIcon(Theme::colorSelection_medium), - this, - backgroundColorActionsTrigger); + m_backgrondColorMenuAction + = new Edit3DAction(QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, + View3DActionType::Empty, + QCoreApplication::translate("BackgroundColorMenuActions", + "Background Color Actions"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::EditColorIcon), + this, + backgroundColorActionsTrigger); m_seekerAction = createSeekerSliderAction(); - m_bakeLightsAction = new Edit3DBakeLightsAction( - toolbarIcon(Theme::editLightOn_medium), //: TODO placeholder icon - this, - bakeLightsTrigger); + m_bakeLightsAction + = new Edit3DBakeLightsAction(toolbarIcon( + DesignerIcons::EditLightIcon), //: TODO placeholder icon + this, + bakeLightsTrigger); m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator From c24ae6441129f8fec37f5c18a1b37d02425b645b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Jul 2023 18:15:03 +0200 Subject: [PATCH 045/266] QmlDesigner: Remove useless semicolon Change-Id: I3b829ac7a050dfaf1f0ea75e9bef68155930bf60 Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/projectstorageupdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 2b8a9c58456..25430bb4ebd 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -505,7 +505,7 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); } - }; + } } } From dadb9074edb6653b7d96dcd880e28817fa5c0a65 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 25 Jul 2023 15:59:14 +0200 Subject: [PATCH 046/266] UnitTests: Improve matcher It should work for pointer and non pointer. Change-Id: Ib80a58874282191b0cbaa576e8846f43d674ce9d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- tests/unit/tests/matchers/unittest-matchers.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/unit/tests/matchers/unittest-matchers.h b/tests/unit/tests/matchers/unittest-matchers.h index 315252dd454..42b3ec76009 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"; } From f4492f567b16d4d47ba03170ca17d47ef3edba9f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Jul 2023 18:15:41 +0200 Subject: [PATCH 047/266] QmlDesigner: Fix qml file name filter Change-Id: I40b40a4c6235566830510b244752ec120759f500 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/projectstorage/filesystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 007467898b34c0eb85bb119624dc1ab46c5c6889 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 15 Aug 2023 16:54:06 +0200 Subject: [PATCH 048/266] QmlDesigner: Fix crash for missing null pointer check Change-Id: I49e971ee9037847dcaa591e98fa8933cff162658 Reviewed-by: Thomas Hartmann --- .../designercore/model/nodeproperty.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp index 88983f6b4bc..330b7e75e93 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp @@ -24,14 +24,14 @@ 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 (auto property = internalNode()->property(name()); !property->isNodeProperty()) - privateModel()->removePropertyAndRelatedResources(property); + if (!nodeProperty) + privateModel()->removePropertyAndRelatedResources(property); + } privateModel()->reparentNode(internalNodeSharedPointer(), name(), From 0f7462ad24fbeac755b3cc893138f9cbf5be1f99 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 15 Aug 2023 17:40:41 +0200 Subject: [PATCH 049/266] QmlDesigner: Fix dependency Change-Id: Iccf3f80e9dd5c9a9dc49c363f9099352732bab60 Reviewed-by: Tim Jenssen --- tests/unit/tests/matchers/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/tests/matchers/CMakeLists.txt b/tests/unit/tests/matchers/CMakeLists.txt index bea752e670c..f050cb83c85 100644 --- a/tests/unit/tests/matchers/CMakeLists.txt +++ b/tests/unit/tests/matchers/CMakeLists.txt @@ -3,7 +3,7 @@ add_qtc_library(TestMatchers OBJECT PROPERTIES SKIP_AUTOGEN ON PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} DEPENDS - Googletest Utils QmlDesigner + Googletest Utils TestDesignerCore SOURCES info_exportedtypenames-matcher.h import-matcher.h From 91a931dd218fe18ab77bd2b17127c3a52d5f4db1 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 15 Aug 2023 20:55:38 +0200 Subject: [PATCH 050/266] QmlDesigner: build it only if Qt is GREATER_EQUAL 6.4.3 Change-Id: Iccd05a7d4f4ace47fd9398bdc78584e3919ec595 Reviewed-by: Tim Jenssen --- CMakeLists.txt | 10 ++++++++-- src/plugins/qmldesigner/CMakeLists.txt | 4 ++-- src/plugins/qmlpreview/CMakeLists.txt | 18 +----------------- .../qmlpreview/qmldebugtranslationclient.cpp | 7 ------- tests/unit/tools/CMakeLists.txt | 4 +--- 5 files changed, 12 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55bf2bfca85..c82f017c8e5 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_LESS_EQUAL 6.4.3) + option(WITH_QMLDESIGNER "Build QmlDesigner" OFF) +else() + option(WITH_QMLDESIGNER "Build QmlDesigner" ${ENV_QTC_WITH_QMLDESIGNER}) +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/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 409bf10c3fd..2320b4d76fd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -37,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 @@ -428,7 +428,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 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/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) From 98a28d30bc3fbdb67890ddccbc6ad2d38be991bd Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 16 Aug 2023 11:47:15 +0200 Subject: [PATCH 051/266] QmlDesigner: fix LESS_EQUAL -> GREATER_EQUAL Qt 6.4.3 check Change-Id: I4718e1afa555164d1b41e8f577a915d915187e30 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c82f017c8e5..acab23d9641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,10 +72,10 @@ find_package(Qt6 REQUIRED ) -if (Qt6_VERSION VERSION_LESS_EQUAL 6.4.3) - option(WITH_QMLDESIGNER "Build QmlDesigner" OFF) -else() +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} "") From c78e0965c043e8be320c458747b29927efa7b46f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 3 May 2023 19:42:51 +0200 Subject: [PATCH 052/266] QmlDesigner: Implement transient scrollbar for widgets in studiostyle Task-number: QDS-9556 Task-number: QDS-10368 Task-number: QDS-10385 Change-Id: Idcbc70db3075f7741a754376580f48d7df40e67a Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/libs/advanceddockingsystem/dockwidget.cpp | 17 + src/libs/advanceddockingsystem/dockwidget.h | 12 + src/libs/utils/styleanimator.cpp | 189 ++++++ src/libs/utils/styleanimator.h | 78 ++- src/libs/utils/transientscroll.cpp | 398 +++++++++-- src/libs/utils/transientscroll.h | 25 +- .../find/highlightscrollbarcontroller.cpp | 21 +- .../connectioneditor/connectionviewwidget.cpp | 1 - .../curveeditor/detail/graphicsview.cpp | 5 - .../components/edit3d/edit3dwidget.cpp | 1 - .../formeditor/formeditorwidget.cpp | 1 - .../navigator/navigatortreeview.cpp | 5 +- .../components/navigator/navigatorwidget.cpp | 1 - .../texteditor/texteditorwidget.cpp | 5 - .../timelineeditor/timelinewidget.cpp | 159 ++++- .../timelineeditor/timelinewidget.h | 31 +- .../transitioneditorwidget.cpp | 9 +- .../transitioneditor/transitioneditorwidget.h | 4 +- src/plugins/qmldesigner/designmodewidget.cpp | 6 +- src/plugins/qmldesignerbase/CMakeLists.txt | 1 + .../qmldesignerbase/studio/studiostyle.cpp | 627 +++++++++++------- .../qmldesignerbase/studio/studiostyle.h | 75 +-- .../qmldesignerbase/studio/studiostyle_p.cpp | 240 +++++++ .../qmldesignerbase/studio/studiostyle_p.h | 84 +++ 24 files changed, 1631 insertions(+), 364 deletions(-) create mode 100644 src/plugins/qmldesignerbase/studio/studiostyle_p.cpp create mode 100644 src/plugins/qmldesignerbase/studio/studiostyle_p.h diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index 229995b9322..c9ffcc1bef9 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -50,6 +50,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; @@ -219,6 +220,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 +441,21 @@ 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); +} + +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..72ac4789cb6 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) + 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. 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 22ab0745b30..1cb0f67cc11 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 { @@ -76,4 +78,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/transientscroll.cpp b/src/libs/utils/transientscroll.cpp index 844d48de3ad..1d3df1259d8 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,25 +363,6 @@ 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)) { @@ -197,14 +379,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 +438,7 @@ bool ScrollBar::event(QEvent *event) } killTimer(d->flashTimer); d->flashTimer = 0; + return true; } break; default: @@ -220,3 +446,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 c48880caa78..c63c89d2e1b 100644 --- a/src/libs/utils/transientscroll.h +++ b/src/libs/utils/transientscroll.h @@ -21,6 +21,7 @@ class QTCREATOR_UTILS_EXPORT TransientScrollAreaSupport : public QObject Q_OBJECT public: static void support(QAbstractScrollArea *scrollArea); + static void supportWidget(QWidget *widget); virtual ~TransientScrollAreaSupport(); protected: @@ -35,20 +36,38 @@ private: class QTCREATOR_UTILS_EXPORT ScrollBar : public QScrollBar { Q_OBJECT + + 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/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/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index 8a8845b47fd..655cc1a3914 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -80,7 +80,6 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : 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, diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp index b7548020723..5127f206042 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() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index be0afacfaba..ddf1bff6d29 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -74,7 +74,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); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index ef7ed1d52d4..ceb0356c1d2 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))); } 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/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/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 64fce948489..3bccd598b41 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -97,7 +98,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 +113,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 +125,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 +132,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 +280,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() @@ -541,11 +537,11 @@ 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->setRange(min, max); m_scrollbar->setValue(current); m_scrollbar->setSingleStep((max - min) / 10); m_scrollbar->blockSignals(b); + m_scrollbar->flash(); } void TimelineWidget::setTimelineId(const QString &id) @@ -634,4 +630,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/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp index adbe164201b..6cb9c285be7 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); 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/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/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/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index 74dd5bfe495..cec8a323686 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,295 @@ 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 = hovered ? "#D9D9D9" : "#9B9B9B"; + 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("#D9D9D9"); + brushColor.setAlpha(0.3 * 255); + painter->setBrush(QBrush(brushColor)); + painter->drawRoundedRect(scrollBarGroove, 4, 4); + } else { + painter->setBrush(QBrush("#773E3E")); + 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 +904,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 +960,7 @@ QSize StudioStyle::sizeFromContents( break; } } - break; - + } break; default: newSize = Super::sizeFromContents(type, option, size, widget); break; @@ -860,26 +984,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 +1047,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 +1106,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..b13310f27ad --- /dev/null +++ b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp @@ -0,0 +1,240 @@ +// 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 + +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 + ); + } +} + +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 From ce8423a42bafdd2cefdcb399d6de06a4780fda5f Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 16 Aug 2023 11:35:10 +0300 Subject: [PATCH 053/266] QmlDesigner: Create the CompositionNode class This one is used to represent an effect with its attributes while editing the current composition. Also some refactoring to remove the composition properties from EffectNode. Change-Id: Idd0137db98cb608f3ead49ba3208e4480a185e88 Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 63 +++++++++++++++++++ .../components/effectmaker/compositionnode.h | 31 +++++++++ .../components/effectmaker/effectnode.cpp | 53 ++++------------ .../components/effectmaker/effectnode.h | 20 ------ 5 files changed, 106 insertions(+), 62 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/compositionnode.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 2320b4d76fd..ec233e40955 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -715,6 +715,7 @@ extend_qtc_plugin(QmlDesigner effectmakernodesmodel.cpp effectmakernodesmodel.h effectnode.cpp effectnode.h effectnodescategory.cpp effectnodescategory.h + compositionnode.cpp compositionnode.h ) extend_qtc_plugin(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..ce686bc3fd6 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -0,0 +1,63 @@ +// 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 +#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; +} + +void CompositionNode::parse(const QString &qenPath) +{ + + QFile loadFile(qenPath); + + if (!loadFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect file."); + return; + } + + QByteArray loadData = loadFile.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(); + QFileInfo fi(loadFile); + + // TODO: QDS-10467 + // Parse the effect from QEN file + // The process from the older implementation has the concept of `project` + // and it contains source & dest nodes that we don't need +} + +} diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h new file mode 100644 index 00000000000..0d16dbc5dc7 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -0,0 +1,31 @@ +// 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 CompositionNode : public QObject +{ + Q_OBJECT + +public: + CompositionNode(const QString &qenPath); + + QString fragmentCode() const; + QString vertexCode() const; + QString description() const; + +private: + void parse(const QString &qenPath); + + QString m_name; + QString m_fragmentCode; + QString m_vertexCode; + QString m_description; +}; + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp index fef05c3ed00..d8774ae8cd3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -11,7 +11,17 @@ namespace QmlDesigner { EffectNode::EffectNode(const QString &qenPath) : m_qenPath(qenPath) { - parse(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::qenPath() const @@ -24,50 +34,9 @@ QString EffectNode::name() const return m_name; } -int EffectNode::nodeId() const -{ - return m_nodeId; -} - -QString EffectNode::fragmentCode() const -{ - return m_fragmentCode; -} - -QString EffectNode::vertexCode() const -{ - return m_vertexCode; -} - -QString EffectNode::qmlCode() const -{ - return m_qmlCode; -} - QString EffectNode::description() const { return m_description; } -void EffectNode::parse(const QString &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); - - // TODO: QDS-10467 - // Parse the effect from QEN file - // The process from the older implementation has the concept of `project` - // and it contains source & dest nodes that we don't need -} - - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h index a82ef4ccac3..3c6563e80f2 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -19,32 +19,12 @@ public: EffectNode(const QString &qenPath); QString qenPath() const; - QString name() const; - int nodeId() const; - QString fragmentCode() const; - QString vertexCode() const; - QString qmlCode() const; QString description() const; - bool operator==(const EffectNode &rhs) const noexcept - { - return this->m_nodeId == rhs.m_nodeId; - } - bool operator!=(const EffectNode &rhs) const noexcept - { - return !operator==(rhs); - } - private: - void parse(const QString &qenPath); - QString m_qenPath; QString m_name; - int m_nodeId = -1; - QString m_fragmentCode; - QString m_vertexCode; - QString m_qmlCode; QString m_description; QUrl m_iconPath; }; From 585ffb92c26cacc25fd161c6f77c80365bdf74f9 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 16 Aug 2023 18:36:23 +0200 Subject: [PATCH 054/266] QmlDesigner: Block Simulink from MCU ComponentView Task-number: QDS-10361 Change-Id: I95b9e1808ab1b2c6671fcc3b7bb5c1acd63ebae4 Reviewed-by: Aleksei German --- share/qtcreator/qmldesigner/qt4mcu/qul-14.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-17.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-18.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-19.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-20.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-21.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-22.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-23.qml | 5 ++++- share/qtcreator/qmldesigner/qt4mcu/qul-24.qml | 5 ++++- 9 files changed, 36 insertions(+), 9 deletions(-) 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 { From b4d153562b1b7f0dec1e81dd0b3b8fc360f07abf Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 16 Aug 2023 12:57:41 +0300 Subject: [PATCH 055/266] QmlDesigner: Add effect maker main UI buttons and toolbars Also refactor some components into separate files. Fixes: QDS-10409 Change-Id: Ifb6380214b85d9f1d67fbb99269d135234abf1d5 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../effectMakerQmlSources/EffectMaker.qml | 35 ++++--- .../EffectMakerPreview.qml | 98 ++++++++++++++++++ .../EffectMakerTopBar.qml | 43 ++++++++ .../EffectNodesComboBox.qml | 3 +- .../effectMakerQmlSources/images/qt_logo.png | Bin 0 -> 3663 bytes 5 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerTopBar.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/images/qt_logo.png diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 4f751883ac9..299f8672329 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -13,37 +13,40 @@ Item { Column { id: col anchors.fill: parent - spacing: 5 + spacing: 1 - Rectangle { - id: topHeader + EffectMakerTopBar { - width: parent.width - height: StudioTheme.Values.toolbarHeight - color: StudioTheme.Values.themeToolbarBackground + } + + EffectMakerPreview { - Row { - // TODO - } } Rectangle { - id: previewHeader - 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 } } - Image { - id: previewImage - - // TODO - } HelperWidgets.ScrollView { id: scrollView diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml new file mode 100644 index 00000000000..a5bc05ed590 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -0,0 +1,98 @@ +// 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 + +Column { + id: root + + width: parent.width + + Rectangle { // toolbar + width: parent.width + height: StudioTheme.Values.toolbarHeight + color: StudioTheme.Values.themeToolbarBackground + + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 5 + spacing: 5 + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.zoomOut_medium + tooltip: qsTr("Zoom out") + + onClicked: {} // TODO + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.zoomIn_medium + tooltip: qsTr("Zoom In") + + onClicked: {} // TODO + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.cornersAll + tooltip: qsTr("Zoom Fit") + + onClicked: {} // TODO + } + + 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: previewImage + + color: "#dddddd" + width: parent.width + height: 200 + + Image { + anchors.margins: 5 + anchors.fill: parent + fillMode: Image.PreserveAspectFit + + source: "images/qt_logo.png" // TODO: update image + } + } +} 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/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml index c29449fcf13..3e5a252fb06 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -14,7 +14,6 @@ StudioControls.ComboBox { actionIndicatorVisible: false x: 5 width: parent.width - 50 - anchors.verticalCenter: parent.verticalCenter model: [qsTr("+ Add Effect")] @@ -51,7 +50,7 @@ StudioControls.ComboBox { flags: Qt.Popup | Qt.FramelessWindowHint onActiveChanged: { - if (!active) + if (!active && !root.hover) root.popup.close() } 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 0000000000000000000000000000000000000000..5181f1b5ab43f8536b1c828b2a470a0036389a72 GIT binary patch literal 3663 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 literal 0 HcmV?d00001 From d11f3202a2b84010dc9e34732951268799390f19 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 17 Aug 2023 13:41:27 +0300 Subject: [PATCH 056/266] QmlDesigner: Prepare composition model for adding a node Change-Id: If4d7fda3956ce4e7f6060e827329b1e8698d5b2f Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../effectMakerQmlSources/EffectNode.qml | 4 +++- .../EffectNodesComboBox.qml | 7 ++++++- .../effectmaker/effectmakermodel.cpp | 18 +++++++++++++---- .../components/effectmaker/effectmakermodel.h | 20 +++++++++---------- .../effectmaker/effectmakerwidget.cpp | 8 ++------ .../effectmaker/effectmakerwidget.h | 2 +- .../components/effectmaker/effectnode.cpp | 10 +++++----- .../components/effectmaker/effectnode.h | 6 ++++-- 8 files changed, 45 insertions(+), 30 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml index 1e9db9ebcd0..324304e4e45 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml @@ -18,6 +18,8 @@ Rectangle { color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction : "transparent" + signal addEffectNode(var nodeQenPath) + MouseArea { id: mouseArea @@ -26,7 +28,7 @@ Rectangle { acceptedButtons: Qt.LeftButton onClicked: { - EffectMakerBackend.rootView.addEffectNode(modelData.nodeName) + root.addEffectNode(modelData.nodeQenPath) } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml index 3e5a252fb06..9c52f687aac 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNodesComboBox.qml @@ -97,7 +97,12 @@ StudioControls.ComboBox { Repeater { model: categoryNodes - EffectNode {} + EffectNode { + onAddEffectNode: (nodeQenPath) => { + EffectMakerBackend.rootView.addEffectNode(modelData.nodeQenPath) + root.popup.close() + } + } } } } diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 0d38ea446e8..cc34926f027 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -3,6 +3,8 @@ #include "effectmakermodel.h" +#include "compositionnode.h" + namespace QmlDesigner { EffectMakerModel::EffectMakerModel(QObject *parent) @@ -13,8 +15,7 @@ EffectMakerModel::EffectMakerModel(QObject *parent) QHash EffectMakerModel::roleNames() const { QHash roles; - roles[CategoryRole] = "categoryName"; - roles[EffectsRole] = "effectNames"; + roles[NameRole] = "nodeName"; return roles; } @@ -22,12 +23,12 @@ int EffectMakerModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) - return m_categories.count(); + return m_nodes.count(); } QVariant EffectMakerModel::data(const QModelIndex &index, int /*role*/) const { - if (index.row() < 0 || index.row() >= m_categories.count()) + if (index.row() < 0 || index.row() >= m_nodes.count()) return {}; // TODO @@ -41,6 +42,15 @@ void EffectMakerModel::resetModel() endResetModel(); } +void EffectMakerModel::addNode(const QString &nodeQenPath) +{ + static int id = 0; + + auto *node = new CompositionNode(nodeQenPath); + m_nodes.insert(id++, node); +// TODO: update model +} + void EffectMakerModel::selectEffect(int idx, bool force) { Q_UNUSED(idx) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index f56c2ed9b91..969267a7b2d 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -3,21 +3,17 @@ #pragma once +#include #include -#include "effectnodescategory.h" - namespace QmlDesigner { +class CompositionNode; + class EffectMakerModel : public QAbstractListModel { Q_OBJECT - enum Roles { - CategoryRole = Qt::UserRole + 1, - EffectsRole - }; - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) @@ -32,7 +28,7 @@ public: void resetModel(); - QList categories() { return m_categories; } + void addNode(const QString &nodeQenPath); Q_INVOKABLE void selectEffect(int idx, bool force = false); Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false); @@ -43,13 +39,17 @@ signals: void hasModelSelectionChanged(); private: + enum Roles { + NameRole = Qt::UserRole + 1, +// TODO + }; + bool isValidIndex(int idx) const; - QList m_categories; + QMap m_nodes; int m_selectedIndex = -1; bool m_isEmpty = true; - bool m_hasModelSelection = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index 2de6e25c9d1..02fe24e344c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -99,13 +99,9 @@ QPointer EffectMakerWidget::effectMakerNodesModel() const return m_effectMakerNodesModel; } -void EffectMakerWidget::addEffectNode(const QString &nodeName) +void EffectMakerWidget::addEffectNode(const QString &nodeQenPath) { - Q_UNUSED(nodeName) - - // TODO: implement adding a node to the composition - // TODO: nodeName might be changed to nodeId? -// m_effectMakerModel.addNode(nodeName); + m_effectMakerModel->addNode(nodeQenPath); } void EffectMakerWidget::focusSection(int section) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h index 0eee5ef7ff4..e42edd04400 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -35,7 +35,7 @@ public: QPointer effectMakerModel() const; QPointer effectMakerNodesModel() const; - Q_INVOKABLE void addEffectNode(const QString &nodeName); + Q_INVOKABLE void addEffectNode(const QString &nodeQenPath); Q_INVOKABLE void focusSection(int section); protected: diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp index d8774ae8cd3..08d11925f56 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp @@ -24,11 +24,6 @@ EffectNode::EffectNode(const QString &qenPath) m_iconPath = QUrl::fromLocalFile(iconPath); } -QString EffectNode::qenPath() const -{ - return m_qenPath; -} - QString EffectNode::name() const { return m_name; @@ -39,4 +34,9 @@ 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 index 3c6563e80f2..823fe092db0 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectnode.h @@ -13,19 +13,21 @@ 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 qenPath() const; QString name() const; QString description() const; + QString qenPath() const; private: - QString m_qenPath; QString m_name; QString m_description; + QString m_qenPath; QUrl m_iconPath; }; From e010bab8eaa7eba5d311c380cd3406413250bccf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 10 Aug 2023 10:31:13 +0200 Subject: [PATCH 057/266] QmlDesigner: Refactor PropertyEditorView::setupQmlBackend() Change-Id: I535785659cdb7ceb108c34c5135769ba01dc78f8 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../propertyeditorqmlbackend.cpp | 11 +- .../propertyeditor/propertyeditorqmlbackend.h | 2 +- .../propertyeditor/propertyeditorview.cpp | 142 ++++++++++++------ .../designercore/include/nodemetainfo.h | 5 + 4 files changed, 109 insertions(+), 51 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index eaa5ef95738..045a622f2c8 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -955,19 +955,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..2b5e324ff2c 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); 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/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 3736079cdc2..c782c47bc4c 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -204,6 +204,11 @@ public: return first.m_privateData == second.m_privateData; } + friend bool operator!=(const NodeMetaInfo &first, const NodeMetaInfo &second) + { + return !(first == second); + } + private: const Storage::Info::Type &typeData() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; From 344c33c24787aa4f4263300d1dbae81c64b803d5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 13 Jul 2023 12:10:14 +0200 Subject: [PATCH 058/266] QmlDesigner: Remove NodeMetaInfo::typeName() usage The project storage is using the imports of a document to get the type. So it is not using qualified types and gets the version from the imports. After we switch all three methods will be removed. Task-number: QDS-10266 Change-Id: Ic127a3bb6bd6f559457b3ce6a8631774359259a5 Reviewed-by: Thomas Hartmann Reviewed-by: Miikka Heikkinen --- .../componentcore/designeractionmanager.cpp | 18 +- .../componentcore/modelnodeoperations.cpp | 50 ++-- .../connectioneditor/backendmodel.cpp | 6 +- .../contentlibrarybundleimporter.cpp | 4 + .../contentlibrarybundleimporter.h | 4 + .../contentlibraryeffectsmodel.cpp | 16 +- .../contentlibraryeffectsmodel.h | 4 + .../contentlibrarymaterialsmodel.cpp | 16 +- .../contentlibrarymaterialsmodel.h | 4 + .../contentlibrary/contentlibraryview.cpp | 215 ++++++++++++++---- .../contentlibrary/contentlibraryview.h | 12 +- .../components/eventlist/eventlist.cpp | 5 +- .../components/eventlist/eventlistview.cpp | 4 + .../materialeditorcontextobject.cpp | 12 +- .../materialeditor/materialeditorview.cpp | 12 +- .../propertyeditorcontextobject.cpp | 8 +- .../textureeditor/textureeditorview.cpp | 12 +- .../designercore/model/abstractview.cpp | 14 +- .../designercore/model/texttomodelmerger.cpp | 2 +- 19 files changed, 314 insertions(+), 104 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index c3af27c455e..228934b2f83 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -592,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 { @@ -891,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/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index d791b04672b..fa533d14de4 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/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/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 370713c7a50..6b1de2d2a73 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -92,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) { 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 e5ee371002e..800d399df2f 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 241c8bdd500..d22a3d91f3f 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 @@ -230,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) @@ -248,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 { @@ -256,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; @@ -288,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) { @@ -337,6 +439,7 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle } }); } +#endif ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { @@ -344,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; @@ -363,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]*)"); @@ -388,6 +516,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) return newMatNode; } +#endif void ContentLibraryView::updateBundleMaterialsImportedState() { @@ -432,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/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp index a0d7cc89815..24adad74192 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/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..3192269b3ca 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) { diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 8c9b19d62ee..52323a3615e 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/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 73c3c65f30e..7c6ba5fc497 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) { 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/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index ccfffbc867b..0d6c756ba43 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1221,7 +1221,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); From c2b3016d33af3c7cfbca5e0388412933d74b8932 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 20 Jul 2023 18:01:46 +0200 Subject: [PATCH 059/266] QmlDesigner: Workaround broken qmltypes files Change-Id: I3382113a2b11268a7418268d6966bf7e67ce0a00 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../qmldesigner/projectstorage/fake.qmltypes | 47 --------- .../qmldesigner/qmldesignerprojectmanager.cpp | 96 ++++++++++--------- 2 files changed, 53 insertions(+), 90 deletions(-) diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes index b757c79a4e6..420ccf001c3 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,8 @@ Component { name: "QOpcUaLocalizedText" } -Component { - name: "QDate" -} - Component { name: "Qt::InputMethodHints" } -Component { - name: "QChar" -} - -Component { - name: "QVector3D" -} } diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index a45c015f481..ae1d26b6ce7 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"QtMultimedia", + u"QtPositioning", + u"MaterialEditor", + u"QtTextToSpeech", + u"QtWebEngine", + u"Qt/labs") + || 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 {}; @@ -561,7 +571,7 @@ void QmlDesignerProjectManager::update() if (!m_projectData || !m_projectData->projectStorageData) return; - m_projectData->projectStorageData->updater.update(qmlDirs(m_projectData->activeTarget), + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), qmlTypes(m_projectData->activeTarget)); } From 3e14b64508d0434562994188bff9ba18113d7f4a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 26 Jul 2023 18:14:38 +0200 Subject: [PATCH 060/266] QmlDesigner: More workarounds and exception improvements Change-Id: I0c02334dc8891e16ac52cc8a6b716d1d3e52603f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../qmldesigner/projectstorage/fake.qmltypes | 27 +++++++ .../projectstorage/projectstorage.h | 29 +++++--- .../projectstorageexceptions.cpp | 30 +++++--- .../projectstorage/projectstorageexceptions.h | 71 +++++++++++-------- .../qmldesigner/qmldesignerprojectmanager.cpp | 4 +- 5 files changed, 111 insertions(+), 50 deletions(-) diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes index 420ccf001c3..f6f21596899 100644 --- a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes +++ b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes @@ -291,4 +291,31 @@ Component { name: "Qt::InputMethodHints" } +Component { + name: "QRgb" +} + +Component { + name: "ulong" +} + +Component { + name: "QQuickTapHandler::ExclusiveSignals" +} + +Component { + name: "QColorDialogOptions::ColorDialogOptions" +} + +Component { + name: "QPolygonF" +} + +Component { + name: "QScreen" +} + +Component { + name: "QScxmlError" +} } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 78064fedab0..60b9ea5e44b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -739,7 +739,7 @@ private: SourceIds exportedSourceIds; exportedSourceIds.reserve(types.size()); - for (auto &&type : types) { + for (auto &type : types) { if (!type.sourceId) throw TypeHasInvalidSourceId{}; @@ -1056,7 +1056,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 +1084,7 @@ private: TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, propertyTypeId); @@ -1108,7 +1108,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 +1178,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 +1290,7 @@ private: type.typeId); } } catch (const Sqlite::ConstraintPreventsModification &) { - throw QmlDesigner::ExportedTypeCannotBeInserted{}; + throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; } }; @@ -1344,7 +1346,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 +1388,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) @@ -2038,7 +2040,7 @@ private: typeId = fetchTypeId(typeNameId); if (!typeId) - throw TypeNameDoesNotExists{}; + throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)}; } return {typeId, typeNameId}; @@ -2146,6 +2148,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) { @@ -3109,6 +3116,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 " 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/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index ae1d26b6ce7..e97fbab2d3f 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -362,12 +362,12 @@ bool skipPath(const QString &directoryPath) u"Qt5Compat", u"QtCharts", u"QtLocation", - u"QtMultimedia", u"QtPositioning", u"MaterialEditor", u"QtTextToSpeech", u"QtWebEngine", - u"Qt/labs") + u"Qt/labs", + u"QtDataVisualization") || skipDirectoriesEndsWith(directoryPath, u"designer"); } From 9074fb28e214d57d15647ddf3cab881e505d8a7b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 31 Jul 2023 17:32:30 +0200 Subject: [PATCH 061/266] QmlDesigner: Cpp module id can be invalid for qml documents Qml documents have no cpp module id. Change-Id: Ib0f9f23473e3f0f680784416e30365f7be51866b Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../designercore/projectstorage/projectstorage.h | 8 ++------ .../projectstorage/projectstoragetypes.h | 3 ++- .../projectstorage/projectstorage-test.cpp | 16 ++++++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 60b9ea5e44b..1b0de855226 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -806,8 +806,7 @@ private: throw ProjectDataHasInvalidProjectSourceId{}; if (!projectData.sourceId) throw ProjectDataHasInvalidSourceId{}; - if (!projectData.moduleId) - throw ProjectDataHasInvalidModuleId{}; + insertProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId, @@ -817,11 +816,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, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index e2e4c1bb4b1..c6e309b5e7c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -735,7 +735,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: diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index fbf07ed9a54..0c83794429c 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5067,18 +5067,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 +5089,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) From b14daaadcbcb4cee92772b75094808633ccd0cf7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 1 Aug 2023 12:56:08 +0200 Subject: [PATCH 062/266] QmlDesigner: Use NodeMetaInfo instead of string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is already a methode in NodeMetaInfo to inspect the type instead of using a the string type name. Change-Id: I604076519cfb068674d8fd4461ee29be523ac919 Reviewed-by: Henning Gründl Reviewed-by: --- .../propertyeditor/propertyeditorqmlbackend.cpp | 8 +++----- .../components/propertyeditor/propertyeditorqmlbackend.h | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 045a622f2c8..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( diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 2b5e324ff2c..b369a417e20 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -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); From c87fc7e085a286ce4387fc2dcb18c066d4d1a179 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 1 Aug 2023 12:57:11 +0200 Subject: [PATCH 063/266] QmlDesigner: Create a model which uses the project storage Change-Id: Iec729e3f31b3cea2f66b5d76c57ba5359bb71060 Reviewed-by: Thomas Hartmann Reviewed-by: --- .../components/integration/designdocument.cpp | 14 +++++++++++--- .../qmldesigner/designercore/include/model.h | 19 +++++++++++++------ .../qmldesigner/designercore/model/model.cpp | 15 +++++++++++---- .../qmldesigner/designercore/model/model_p.h | 3 ++- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 7bbbac5b253..01e37671a01 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/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/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 7652f1f378f..05737629ad4 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), {}); @@ -1643,9 +1645,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, diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 9b0fdf72b2d..20b83a564b6 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -100,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, From cf54c2ffcd3aacdd099f544643b863c06283c824 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 1 Aug 2023 12:57:29 +0200 Subject: [PATCH 064/266] QmlDesigner: Remove empty line Change-Id: Ifeeff642569ecd21810b2627490a2581d68bd79f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/projectstorage/projectstorage.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 1b0de855226..262abfea295 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -807,7 +807,6 @@ private: if (!projectData.sourceId) throw ProjectDataHasInvalidSourceId{}; - insertProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId, projectData.moduleId, From 85bf537fc0f73b3ab02d1ebdedeccb8da5d13968 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 17 Aug 2023 15:59:46 +0300 Subject: [PATCH 065/266] QmlDesigner: Implement basic composition node delegate Task-number: QDS-10404 Change-Id: Ia456fb96c157d5e8d6206732c90d761c59b27fab Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectCompositionNode.qml | 37 +++++++++++++++++ .../effectMakerQmlSources/EffectMaker.qml | 40 +++++-------------- .../effectmaker/compositionnode.cpp | 40 ++++++++++++++----- .../components/effectmaker/compositionnode.h | 3 +- .../effectmaker/effectmakermodel.cpp | 15 +++---- 5 files changed, 85 insertions(+), 50 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml new file mode 100644 index 00000000000..0cb84e8c89c --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -0,0 +1,37 @@ +// 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 + + caption: model.nodeName + category: "EffectMaker" + +// TODO: implement effect properties +// property var propList: model.props + + Column { + anchors.fill: parent + spacing: 2 + + // Repeater { + // id: effects + // model: effectList + // width: parent.width + // height: parent.height + // delegate: Text { + // width: parent.width + // //height: StudioTheme.Values.checkIndicatorHeight * 2 // TODO: update or remove + // } + // } + } +} + diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 299f8672329..ee3992cac51 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -64,40 +64,18 @@ Item { } Column { - Item { - width: scrollView.width - height: categories.height + width: scrollView.width + height: compositionRepeater.height + spacing: 1 - Column { - Repeater { - id: categories - width: root.width - model: EffectMakerBackend.effectMakerModel + Repeater { + id: compositionRepeater - delegate: HelperWidgets.Section { - id: effectsSection - width: root.width - caption: model.categoryName - category: "EffectMaker" + width: root.width + model: EffectMakerBackend.effectMakerModel - property var effectList: model.effectNames - - onExpandedChanged: { - effects.visible = expanded // TODO: update - } - - Repeater { - id: effects - model: effectList - width: parent.width - height: parent.height - delegate: EffectNode { - width: parent.width - //height: StudioTheme.Values.checkIndicatorHeight * 2 // TODO: update or remove - } - } - } - } + delegate: EffectCompositionNode { + width: root.width } } } diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index ce686bc3fd6..7fc4694b718 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -4,9 +4,9 @@ #include "compositionnode.h" #include +#include #include #include -#include namespace QmlDesigner { @@ -33,14 +33,14 @@ QString CompositionNode::description() const void CompositionNode::parse(const QString &qenPath) { - QFile loadFile(qenPath); + QFile qenFile(qenPath); - if (!loadFile.open(QIODevice::ReadOnly)) { + if (!qenFile.open(QIODevice::ReadOnly)) { qWarning("Couldn't open effect file."); return; } - QByteArray loadData = loadFile.readAll(); + QByteArray loadData = qenFile.readAll(); QJsonParseError parseError; QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); if (parseError.error != QJsonParseError::NoError) { @@ -51,13 +51,31 @@ void CompositionNode::parse(const QString &qenPath) return; } - QJsonObject json = jsonDoc.object(); - QFileInfo fi(loadFile); + QJsonObject json = jsonDoc.object().value("QEN").toObject(); - // TODO: QDS-10467 - // Parse the effect from QEN file - // The process from the older implementation has the concept of `project` - // and it contains source & dest nodes that we don't need + m_name = json.value("name").toString(); + + // parse properties + QJsonArray properties = json.value("properties").toArray(); + for (const auto /*QJsonValueRef*/ &prop : properties) { + QJsonObject propObj = prop.toObject(); + propObj.value("name"); + propObj.value("type"); + propObj.value("defaultValue"); + propObj.value("description"); + // TODO + } + + // parse shaders + QJsonArray vertexCode = json.value("vertexCode").toArray(); + if (!vertexCode.isEmpty()) { + // TODO + } + + QJsonArray fragmentCode = json.value("fragmentCode").toArray(); + if (!fragmentCode.isEmpty()) { + // TODO + } } -} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index 0d16dbc5dc7..cc878a428c9 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -11,6 +11,8 @@ class CompositionNode : public QObject { Q_OBJECT + Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + public: CompositionNode(const QString &qenPath); @@ -28,4 +30,3 @@ private: }; } // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index cc34926f027..db0754bed4d 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -5,6 +5,8 @@ #include "compositionnode.h" +#include + namespace QmlDesigner { EffectMakerModel::EffectMakerModel(QObject *parent) @@ -26,14 +28,12 @@ int EffectMakerModel::rowCount(const QModelIndex &parent) const return m_nodes.count(); } -QVariant EffectMakerModel::data(const QModelIndex &index, int /*role*/) const +QVariant EffectMakerModel::data(const QModelIndex &index, int role) const { - if (index.row() < 0 || index.row() >= m_nodes.count()) - return {}; + QTC_ASSERT(index.isValid() && index.row() < m_nodes.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); - // TODO - - return {}; + return m_nodes.values().at(index.row())->property(roleNames().value(role)); } void EffectMakerModel::resetModel() @@ -46,9 +46,10 @@ void EffectMakerModel::addNode(const QString &nodeQenPath) { static int id = 0; + beginInsertRows({}, m_nodes.size(), m_nodes.size()); auto *node = new CompositionNode(nodeQenPath); m_nodes.insert(id++, node); -// TODO: update model + endInsertRows(); } void EffectMakerModel::selectEffect(int idx, bool force) From a26275a545fcd9af25a0d507833d48b0f3ec8b3d Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Thu, 17 Aug 2023 18:38:40 +0300 Subject: [PATCH 066/266] QmlDesigner: Add some qen parsing functionality Added Uniform class to store composition node uniforms for shader and ui usage Implement some shader code parsing create unique id for composition nodes (basic implementation) Change-Id: I07ece2058e158b01590bd9b995c179275f489a1c Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 45 +++++++++++----- .../components/effectmaker/compositionnode.h | 1 + .../components/effectmaker/uniform.cpp | 15 ++++++ .../components/effectmaker/uniform.h | 53 +++++++++++++++++++ 5 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/uniform.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/uniform.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index ec233e40955..268e40fc8a6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -716,6 +716,7 @@ extend_qtc_plugin(QmlDesigner effectnode.cpp effectnode.h effectnodescategory.cpp effectnodescategory.h compositionnode.cpp compositionnode.h + uniform.cpp uniform.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index 7fc4694b718..7f1f84f8d8b 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -3,6 +3,8 @@ #include "compositionnode.h" +#include "uniform.h" + #include #include #include @@ -53,29 +55,46 @@ void CompositionNode::parse(const QString &qenPath) QJsonObject json = jsonDoc.object().value("QEN").toObject(); - m_name = json.value("name").toString(); + 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(); //TODO: there is a difference between name and type + m_description = json.value("description").toString(); + m_fragmentCode = codeFromJsonArray(json.value("fragmentCode").toArray()); + m_vertexCode = codeFromJsonArray(json.value("vertexCode").toArray()); // parse properties QJsonArray properties = json.value("properties").toArray(); for (const auto /*QJsonValueRef*/ &prop : properties) { QJsonObject propObj = prop.toObject(); + Uniform *u = new Uniform(propObj); + Q_UNUSED(u) + + // TODO propObj.value("name"); propObj.value("type"); propObj.value("defaultValue"); propObj.value("description"); - // TODO - } - - // parse shaders - QJsonArray vertexCode = json.value("vertexCode").toArray(); - if (!vertexCode.isEmpty()) { - // TODO - } - - QJsonArray fragmentCode = json.value("fragmentCode").toArray(); - if (!fragmentCode.isEmpty()) { - // TODO } } +QString CompositionNode::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/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index cc878a428c9..9cb8ae85506 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -22,6 +22,7 @@ public: private: void parse(const QString &qenPath); + QString codeFromJsonArray(const QJsonArray &codeArray); QString m_name; QString m_fragmentCode; diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp new file mode 100644 index 00000000000..e4989b8550b --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -0,0 +1,15 @@ +// 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 + +namespace QmlDesigner { + +Uniform::Uniform(const QJsonObject &props) +{ + Q_UNUSED(props) +} + +} // 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..e0b48778bd9 --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -0,0 +1,53 @@ +// 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 Uniform : public QObject +{ + Q_OBJECT + +public: + enum class Type + { + Bool, + Int, + Float, + Vec2, + Vec3, + Vec4, + Color, + Sampler, + Define + }; + + Uniform(const QJsonObject &props); + + // TODO: setters & getters + +private: + Type m_type; + QVariant m_value; + QVariant m_defaultValue; + QVariant m_minValue; + QVariant m_maxValue; + QString m_name; + QString m_description; + QString m_customValue; + bool m_useCustomValue = false; + bool m_visible = true; + bool m_enableMipmap = false; + + bool operator==(const Uniform& rhs) const noexcept + { + return this->m_name == rhs.m_name; + } +}; + +} // namespace QmlDesigner + From 6b62b56903ade17a6e879a6cceb106a83f3f83a8 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 18 Aug 2023 11:47:16 +0300 Subject: [PATCH 067/266] QmlDesginer: Add Uniform getters and setters Change-Id: Id2e75db32bed9577cca061b2c5971171dca984ad Reviewed-by: Mahmoud Badri --- .../components/effectmaker/uniform.cpp | 75 +++++++++++++++++++ .../components/effectmaker/uniform.h | 28 ++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index e4989b8550b..a5d1be5b600 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -12,4 +12,79 @@ Uniform::Uniform(const QJsonObject &props) Q_UNUSED(props) } +Uniform::Type Uniform::type() const +{ + return m_type; +} + +QVariant Uniform::value() const +{ + return m_value; +} + +void Uniform::setValue(const QVariant &newValue) +{ + m_value = newValue; +} + +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; +} + +void Uniform::setName(const QString &newName) +{ + m_name = newName; +} + +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; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index e0b48778bd9..652b4d5f1ca 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -28,7 +28,31 @@ public: Uniform(const QJsonObject &props); - // TODO: setters & getters + Type type() const; + + QVariant value() const; + void setValue(const QVariant &newValue); + + QVariant defaultValue() const; + + QVariant minValue() const; + + QVariant maxValue() const; + + QString name() const; + void setName(const QString &newName); + + QString description() const; + + QString customValue() const; + void setCustomValue(const QString &newCustomValue); + + bool useCustomValue() const; + + bool enabled() const; + void setEnabled(bool newEnabled); + + bool enableMipmap() const; private: Type m_type; @@ -40,7 +64,7 @@ private: QString m_description; QString m_customValue; bool m_useCustomValue = false; - bool m_visible = true; + bool m_enabled = true; bool m_enableMipmap = false; bool operator==(const Uniform& rhs) const noexcept From a3ee70dfd0bb5c68474a7705aa014dbd11498800 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 18 Aug 2023 15:39:40 +0300 Subject: [PATCH 068/266] QmlDesigner: Set Uniform properties Setting properties with some support functionality ready for UI binding Change-Id: I9636435c3ddeac74b2dbeec826571abeb4247350 Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 20 +--- .../components/effectmaker/compositionnode.h | 1 - .../components/effectmaker/effectutils.cpp | 23 +++++ .../components/effectmaker/effectutils.h | 20 ++++ .../components/effectmaker/uniform.cpp | 98 +++++++++++++++++-- .../components/effectmaker/uniform.h | 12 ++- 7 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectutils.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectutils.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 268e40fc8a6..59f15ef159b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -717,6 +717,7 @@ extend_qtc_plugin(QmlDesigner effectnodescategory.cpp effectnodescategory.h compositionnode.cpp compositionnode.h uniform.cpp uniform.h + effectutils.cpp effectutils.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index 7f1f84f8d8b..0fdf105407f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -3,6 +3,7 @@ #include "compositionnode.h" +#include "effectutils.h" #include "uniform.h" #include @@ -64,10 +65,10 @@ void CompositionNode::parse(const QString &qenPath) return; } - m_name = json.value("name").toString(); //TODO: there is a difference between name and type + m_name = json.value("name").toString(); m_description = json.value("description").toString(); - m_fragmentCode = codeFromJsonArray(json.value("fragmentCode").toArray()); - m_vertexCode = codeFromJsonArray(json.value("vertexCode").toArray()); + m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()); + m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); // parse properties QJsonArray properties = json.value("properties").toArray(); @@ -84,17 +85,4 @@ void CompositionNode::parse(const QString &qenPath) } } -QString CompositionNode::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/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index 9cb8ae85506..cc878a428c9 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -22,7 +22,6 @@ public: private: void parse(const QString &qenPath); - QString codeFromJsonArray(const QJsonArray &codeArray); QString m_name; QString m_fragmentCode; 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/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index a5d1be5b600..df1436d5c4f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -7,9 +7,40 @@ namespace QmlDesigner { -Uniform::Uniform(const QJsonObject &props) +Uniform::Uniform(const QJsonObject &propObj) { - Q_UNUSED(props) + //TODO: some cases such as missing values or default values not yet implemented + + QString value, defaultValue; + + m_name = propObj.value("name").toString(); + m_description = propObj.value("description").toString(); + m_type = typeFromString(propObj.value("type").toString()); + defaultValue = propObj.value("defaultValue").toString(); + + 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; + } + m_customValue = propObj.value("customValue").toString(); + m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false); + m_minValue = propObj.value("minValue").toString(); + m_maxValue = propObj.value("maxValue").toString(); + //TODO: set uniform value data after validating, for now just set to current value + m_defaultValue = defaultValue; + m_value = value; } Uniform::Type Uniform::type() const @@ -47,11 +78,6 @@ QString Uniform::name() const return m_name; } -void Uniform::setName(const QString &newName) -{ - m_name = newName; -} - QString Uniform::description() const { return m_description; @@ -87,4 +113,62 @@ 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; +} + +Uniform::Type Uniform::typeFromString(const QString &typeString) const +{ + if (typeString == "bool") + return Type::Bool; + if (typeString == "int") + return Type::Int; + if (typeString == "float") + return Type::Float; + if (typeString == "vec2") + return Type::Vec2; + if (typeString == "vec3") + return Type::Vec3; + if (typeString == "vec4") + return Type::Vec4; + if (typeString == "color") + return Type::Color; + if (typeString == "image") + return Type::Sampler; + if (typeString == "define") + return Type::Define; + + qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1(); + return Type::Float; +} + +// 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 {}; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 652b4d5f1ca..c3e4256e431 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -6,6 +6,8 @@ #include #include +QT_FORWARD_DECLARE_CLASS(QJsonObject) + namespace QmlDesigner { class Uniform : public QObject @@ -36,17 +38,13 @@ public: QVariant defaultValue() const; QVariant minValue() const; - QVariant maxValue() const; QString name() const; - void setName(const QString &newName); - QString description() const; QString customValue() const; void setCustomValue(const QString &newCustomValue); - bool useCustomValue() const; bool enabled() const; @@ -55,6 +53,12 @@ public: bool enableMipmap() const; private: + QString mipmapPropertyName(const QString &name) const; + + Uniform::Type typeFromString(const QString &typeString) const; + bool getBoolValue(const QJsonValue &jsonValue, bool defaultValue); + QString getResourcePath(const QString &value) const; + Type m_type; QVariant m_value; QVariant m_defaultValue; From 16bbccf2eaac5e6bf1250c15e6b59aa8513fe0d1 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 27 Jul 2023 12:00:45 +0200 Subject: [PATCH 069/266] QmlDesigner: Connection Editor design cleanup Cleanup of the QML connection editor only. Binding and property will be done afterwards as it is just copy and paste of the style in the end. Task-number: QDS-10211 Change-Id: I83fb93bc25813571ea7f645feeeaeb274751b6de Reviewed-by: Thomas Hartmann --- .../assetsLibraryQmlSources/Assets.qml | 10 +- .../connectionseditor/BindingsListView.qml | 7 +- .../connectionseditor/ConnectionsDialog.qml | 23 +- .../ConnectionsDialogForm.qml | 404 +++++++++--------- .../connectionseditor/ConnectionsListView.qml | 149 ++++--- .../qmldesigner/connectionseditor/Main.qml | 197 +++++---- .../connectionseditor/PopupDialog.qml | 102 +++-- .../connectionseditor/PropertiesListView.qml | 7 +- .../connectionseditor/TabCheckButton.qml | 181 +++++--- .../MaterialBrowser.qml | 59 +-- .../imports/StudioTheme/Values.qml | 7 + .../connectioneditor/connectionview.cpp | 14 +- 12 files changed, 669 insertions(+), 491 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml index 502b67f6c65..6520a5f4092 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 diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 1e8e123aa3a..4c73809c780 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuick.Controls -import ConnectionsEditor import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -22,9 +21,9 @@ ListView { property int modelCurrentIndex: listView.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 } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index a23ca40c9da..be45e30679b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -2,9 +2,28 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme PopupDialog { - ConnectionsDialogForm { - y: 32 + titleBar: Row { + spacing: 30 // TODO + anchors.fill: parent + + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Target") + font.pixelSize: StudioTheme.Values.myFontSize + anchors.verticalCenter: parent.verticalCenter + } + + StudioControls.TopLevelComboBox { + id: target + width: 210 + anchors.verticalCenter: parent.verticalCenter + model: ["mySpinbox", "foo", "backendObject"] + } } + + ConnectionsDialogForm {} } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index d39aefdba3d..c9c9030897b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -1,242 +1,238 @@ - // 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 StudioControls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme -Rectangle { - width: 400 - height: 800 - color: "#1b1b1b" +Column { + id: root - Text { - id: text1 - x: 10 - y: 25 - color: "#ffffff" - text: qsTr("Target:") - font.pixelSize: 15 + readonly property real horizontalSpacing: 10 + readonly property real verticalSpacing: 10 + readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 + + component PopupLabel : Text { + width: root.columnWidth + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.myFontSize } - TopLevelComboBox { - id: target - x: 95 - width: 210 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -367 - model: ["mySpinbox", "foo", "backendObject"] + enum ActionType { + CallFunction, + Assign, + ChangeState, + SetProperty, + PrintMessage, + Custom } - Text { - id: text2 - x: 10 - y: 131 - color: "#ffffff" - text: qsTr("Signal") - font.pixelSize: 15 + y: StudioTheme.Values.popupMargin + width: parent.width + spacing: root.verticalSpacing + + Row { + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Signal") } + PopupLabel { text: qsTr("Action") } } - TopLevelComboBox { - id: signal - x: 10 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -207 - model: ["onClicked", "onPressed", "onReleased"] - } + Row { + spacing: root.horizontalSpacing - TopLevelComboBox { - id: action - x: 207 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -207 - model: ["Call Function", "Assign", "ChnageState"] - } - - Text { - id: text3 - x: 207 - y: 131 - color: "#ffffff" - text: qsTr("Action") - font.pixelSize: 15 - } - - Item { - id: functionGroup - x: 0 - y: 276 - width: 400 - height: 176 - - Text { - id: text4 - x: 17 - y: -11 - color: "#ffffff" - text: qsTr("Target") - font.pixelSize: 15 + StudioControls.TopLevelComboBox { + id: signal + width: root.columnWidth + model: ["Clicked", "Pressed", "Released"] } - TopLevelComboBox { - id: functionTarget - x: 10 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -48 - model: ["mySpinBox", "backendObject", "someButton"] - } + StudioControls.TopLevelComboBox { + id: action + width: root.columnWidth + textRole: "text" + valueRole: "value" - TopLevelComboBox { - id: functionName - x: 203 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -48 - model: ["start", "trigger", "stop"] - } - - Text { - id: text5 - x: 203 - y: -11 - color: "#ffffff" - text: qsTr("Function") - font.pixelSize: 15 + model: [ + { value: ConnectionsDialogForm.CallFunction, text: qsTr("Call Function") }, + { value: ConnectionsDialogForm.Assign, text: qsTr("Assign") }, + { value: ConnectionsDialogForm.ChangeState, text: qsTr("Change State") }, + { value: ConnectionsDialogForm.SetProperty, text: qsTr("Set Property") }, + { value: ConnectionsDialogForm.PrintMessage, text: qsTr("Print Message") }, + { value: ConnectionsDialogForm.Custom, text: qsTr("Custom") } + ] } } - Item { - id: statesGroup - x: 0 - y: 383 - width: 400 - height: 106 - Text { - id: text6 - x: 17 - y: -11 - color: "#ffffff" - text: qsTr("State Group") - font.pixelSize: 15 + // Call Function + Row { + visible: action.currentValue === ConnectionsDialogForm.CallFunction + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Item") } + PopupLabel { text: qsTr("Method") } + } + + Row { + visible: action.currentValue === ConnectionsDialogForm.CallFunction + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["mySpinBox", "myAnimation", "myCustomComponent"] } - TopLevelComboBox { - id: stateGroup - x: 10 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -12 - model: ["default", "State Group 01", "State Group 02"] - } - - TopLevelComboBox { - id: stateName - x: 209 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -12 - model: ["State 01", "State 02", "State 03"] - } - - Text { - id: text7 - x: 209 - y: -11 - color: "#ffffff" - text: qsTr("State") - font.pixelSize: 15 + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["start", "stop", "reset"] } } - Item { - id: assignment - x: 10 - y: 505 - width: 400 - height: 106 - Text { - id: text8 - x: 17 - y: -11 - color: "#ffffff" - text: qsTr("target") - font.pixelSize: 15 + // Assign + Row { + visible: action.currentValue === ConnectionsDialogForm.Assign + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("From") } + PopupLabel { text: qsTr("To") } + } + + Row { + visible: action.currentValue === ConnectionsDialogForm.Assign + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["value", "foo", "bar"] } - TopLevelComboBox { - id: valueTarget - x: 10 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -12 - model: ["mySpinBox", "myButton", "backendObject"] + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["myValue", "yourValue", "ourValue"] + } + } + + // Change State + Row { + visible: action.currentValue === ConnectionsDialogForm.ChangeState + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("State Group") } + PopupLabel { text: qsTr("State") } + } + + Row { + visible: action.currentValue === ConnectionsDialogForm.ChangeState + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["main", "group1", "group2"] } - TopLevelComboBox { - id: valueSource - x: 209 - y: 7 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -12 - model: ["mySpinBox", "myButton", "backendObject"] + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["state1", "state2", "state3", "state4"] } + } + + // Set Property + Row { + visible: action.currentValue === ConnectionsDialogForm.SetProperty + spacing: root.horizontalSpacing + + PopupLabel { text: qsTr("Item") } + PopupLabel { text: qsTr("Property") } + } + + Row { + visible: action.currentValue === ConnectionsDialogForm.SetProperty + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["value", "foo", "bar"] + } + + StudioControls.TopLevelComboBox { + width: root.columnWidth + model: ["myValue", "yourValue", "ourValue"] + } + } + + PopupLabel { + visible: action.currentValue === ConnectionsDialogForm.SetProperty + text: qsTr("Value") + } + + StudioControls.TextField { + visible: action.currentValue === ConnectionsDialogForm.SetProperty + width: root.width + text: "This is a test" + actionIndicatorVisible: false + translationIndicatorVisible: false + } + + // Print Message + PopupLabel { + visible: action.currentValue === ConnectionsDialogForm.PrintMessage + text: qsTr("Message") + } + + StudioControls.TextField { + visible: action.currentValue === ConnectionsDialogForm.PrintMessage + width: root.width + text: "my value is" + actionIndicatorVisible: false + translationIndicatorVisible: false + } + + // Custom + PopupLabel { + visible: action.currentValue === ConnectionsDialogForm.Custom + text: qsTr("Custom Connections can only be edited with the binding editor") + width: root.width + horizontalAlignment: Text.AlignHCenter + } + + HelperWidgets.AbstractButton { + width: 200 + buttonIcon: qsTr("Add Condition") + iconSize: StudioTheme.Values.baseFontSize + iconFont: StudioTheme.Constants.font + anchors.horizontalCenter: parent.horizontalCenter + visible: action.currentValue !== ConnectionsDialogForm.Custom + + onClicked: console.log("ADD CONDITION") + } + + // Editor + Rectangle { + id: editor + width: parent.width + height: 200 + color: "#333333" Text { - id: text9 - x: 209 - y: -11 - color: "#ffffff" - text: qsTr("source") - font.pixelSize: 15 + anchors.centerIn: parent + text: "backend.myValue = mySpinbox.value" + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.myFontSize } - Text { - id: text10 - x: 17 - y: 76 - color: "#ffffff" - text: qsTr("value") - font.pixelSize: 15 - } + HelperWidgets.AbstractButton { + id: editorButton - TopLevelComboBox { - id: valueOut - x: 10 - y: -2 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 84 - model: ["width", "height", "opacity"] - } + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 4 - TopLevelComboBox { - id: valueIn - x: 209 - y: -2 - width: 156 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 84 - model: ["width", "height", "x", "y"] - } - - Text { - id: text11 - x: 209 - y: 76 - color: "#ffffff" - text: qsTr("value") - font.pixelSize: 15 + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.edit_medium + tooltip: qsTr("Add something.") + onClicked: console.log("OPEN EDITOR") } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 3238b6cd9b4..2c83ec167bd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -3,35 +3,40 @@ 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 + + clip: true + interactive: true highlightMoveDuration: 0 + ScrollBar.vertical: ScrollBar { + id: comboBoxPopupScrollBar + visible: root.height < root.contentHeight + } + 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 + root.currentIndex = root.model.currentIndex } data: [ @@ -41,75 +46,113 @@ ListView { } ] - delegate: Item { + // Proper row width calculation + readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin + readonly property int rowSpace: root.width - (root.rowSpacing * 4) + - root.style.squareControlSize.width + property int rowWidth: root.rowSpace / 3 + property int rowRest: root.rowSpace % 3 - width: 600 - height: 18 + delegate: Rectangle { + id: itemDelegate + + required property int index + + required property string signal + required property string target + required property string action + + 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 + hoverEnabled: true + onClicked: { - listView.model.currentIndex = index - listView.currentIndex = index + root.model.currentIndex = index + root.currentIndex = index dialog.popup(mouseArea) } - - property int currentIndex: listView.currentIndex } Row { - id: row1 - x: 0 - y: 0 - width: 600 - height: 16 - spacing: 10 + id: row + + property color textColor: itemDelegate.ListView.isCurrentItem ? root.style.text.selectedText + : root.style.icon.idle + + height: itemDelegate.height + spacing: root.rowSpacing 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 + leftPadding: root.rowSpacing + } + + Text { + width: root.rowWidth + height: itemDelegate.height + text: itemDelegate.signal + color: row.textColor + verticalAlignment: Text.AlignVCenter font.bold: false } Text { - width: 120 - text: signal - color: "#ffffff" - anchors.verticalCenter: parent.verticalCenter + width: root.rowWidth + height: itemDelegate.height + text: itemDelegate.action + verticalAlignment: Text.AlignVCenter + color: row.textColor font.bold: false } - Text { - width: 120 + Rectangle { + width: root.style.squareControlSize.width + height: root.style.squareControlSize.height - text: action - anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" - font.bold: false - } + color: toolTipArea.containsMouse ? + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover + : "transparent" - Text { - width: 120 - - text: "-" - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignRight - font.pointSize: 14 - color: "#ffffff" - font.bold: true - MouseArea { + 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(index) } } } } highlight: Rectangle { - color: "#2a5593" - width: 600 + color: root.style.interaction } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 628a00a548a..a124228b3ea 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -4,114 +4,125 @@ 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 + + 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.connections_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: { + print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate) + print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type) + print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type.model) + + 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 + } - 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 + } - 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 + } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 3aad2785ce2..5d0663e2a35 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -3,70 +3,88 @@ import QtQuick import QtQuick.Controls +import HelperWidgets 2.0 as HelperWidgets +import StudioTheme 1.0 as StudioTheme +import ConnectionsEditorEditorBackend Window { id: window + + property alias titleBar: titleBarContent.children + default property alias content: mainContent.children + width: 400 - height: 800 + height: column.implicitHeight visible: true - flags: Qt.FramelessWindowHint || Qt.Dialog - - color: Qt.transparent - - property int bw: 5 + flags: Qt.FramelessWindowHint | Qt.Dialog + color: "#060606" 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() + window.x = p.x - window.width - padding + if (window.x < 0) + window.x = p.x + item.width + padding + window.y = p.y + window.show() + window.raise() } - Rectangle { - id: rectangle1 - color: "#d7d7d7" - border.color: "#232323" + 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 + 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/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 9e0822dc6c3..f39b26ee7b6 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuick.Controls -import ConnectionsEditor import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme @@ -22,9 +21,9 @@ ListView { property int modelCurrentIndex: listView.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 } diff --git a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml index c49bc539157..8165a69a1b4 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml @@ -2,79 +2,162 @@ // 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 + 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/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/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index ed47402de7c..41f09f45a4d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -218,11 +218,18 @@ QtObject { property real colorEditorPopupCmoboBoxWidth: 110 property real colorEditorPopupSpinBoxWidth: 54 + // Popup Window + property real titleBarHeight: values.height + 4 + 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 diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 9da3fcc7be4..221400dd0f0 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -68,13 +68,9 @@ public: "ConnectionsEditorEditorBackend", 1); map->setProperties( - {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}}); - - map->setProperties( - {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}}); - - map->setProperties( - {{"dynamicPropertiesModel", + {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}, + {"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}, + {"dynamicPropertiesModel", QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); Theme::setupTheme(engine()); @@ -85,6 +81,7 @@ public: // init the first load of the QML UI elements reloadQmlSource(); } + ~ConnectionViewQuickWidget() = default; static QString qmlSourcesPath() @@ -297,7 +294,7 @@ void ConnectionView::currentStateChanged(const ModelNode &) WidgetInfo ConnectionView::widgetInfo() { /* Enable new connection editor here */ - const bool newEditor = false; + const bool newEditor = true; QWidget *widget = m_connectionViewWidget.data(); if (newEditor) @@ -381,7 +378,6 @@ void ConnectionView::setCurrentIndex(int i) ConnectionView *ConnectionView::instance() { - static ConnectionView *s_instance = nullptr; if (s_instance) From 87c9e64a74bdbfe7552eb41d86efa2e7d65a50fc Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Aug 2023 11:56:37 +0300 Subject: [PATCH 070/266] QmlDesigner: Create alias for spinBoxInput.horizontalAlignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic012aaeedd4f4b27e6045cbd46a0d7e22b58393d Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../imports/StudioControls/RealSpinBox.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 2f219a411da..2e16d46c17c 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 From 82ee90b8752b71967b904801d76e53d47cd83ab8 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Aug 2023 11:52:11 +0300 Subject: [PATCH 071/266] QmlDesigner: Add effect maker uniforms model Also some initial relevant UI part. Change-Id: I79a4a060d0e2af0aeff86e27ebe3c70faf5681c2 Task-number: QDS-10404 Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNode.qml | 25 +++--- .../EffectCompositionNodeUniform.qml | 81 +++++++++++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 19 ++--- .../components/effectmaker/compositionnode.h | 12 +++ .../effectmaker/effectmakermodel.cpp | 1 + .../components/effectmaker/effectmakermodel.h | 2 +- .../effectmaker/effectmakeruniformsmodel.cpp | 58 +++++++++++++ .../effectmaker/effectmakeruniformsmodel.h | 41 ++++++++++ .../components/effectmaker/uniform.h | 7 +- 10 files changed, 217 insertions(+), 30 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml index 0cb84e8c89c..d1bcd4fb454 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -12,26 +12,19 @@ import EffectMakerBackend HelperWidgets.Section { id: root - caption: model.nodeName + caption: nodeName category: "EffectMaker" -// TODO: implement effect properties -// property var propList: model.props - Column { - anchors.fill: parent - spacing: 2 + spacing: 10 - // Repeater { - // id: effects - // model: effectList - // width: parent.width - // height: parent.height - // delegate: Text { - // width: parent.width - // //height: StudioTheme.Values.checkIndicatorHeight * 2 // TODO: update or remove - // } - // } + Repeater { + model: 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..8e12395c937 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -0,0 +1,81 @@ +// 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 + +Item { + id: root + + height: 22 + + RowLayout { + spacing: 10 + anchors.fill: parent + + Text { + text: uniformName + color: StudioTheme.Values.themeTextColor + font.pointSize: StudioTheme.Values.smallFontSize + horizontalAlignment: Text.AlignRight + Layout.preferredWidth: 80 + } + + Loader { + sourceComponent: floatValue // TODO: set component based on prop type + Layout.fillWidth: true + } + } + + Component { + id: floatValue + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: spinBox + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + from: 21 + to: 78 + stepSize: 1 + onValueChanged: slider.value = realValue + } + + StudioControls.Slider { + id: slider + + width: parent.width - 60 + labels: false + actionIndicatorVisible: false + from: 21 + to: 78 + stepSize: 1 + onValueChanged: spinBox.realValue = value + } + } + } + + Component { // TODO + id: colorValue + + Row { + width: parent.width + spacing: 5 + + HelperWidgets.ColorEditor { + backendValue: "#ffff00" + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 59f15ef159b..f8bae184ee4 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -713,6 +713,7 @@ extend_qtc_plugin(QmlDesigner 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 diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index 0fdf105407f..cd88c224fe3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -4,6 +4,7 @@ #include "compositionnode.h" #include "effectutils.h" +#include "effectmakeruniformsmodel.h" #include "uniform.h" #include @@ -33,6 +34,11 @@ QString CompositionNode::description() const return m_description; } +QObject *CompositionNode::uniformsModel() +{ + return &m_unifomrsModel; +} + void CompositionNode::parse(const QString &qenPath) { @@ -72,17 +78,8 @@ void CompositionNode::parse(const QString &qenPath) // parse properties QJsonArray properties = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : properties) { - QJsonObject propObj = prop.toObject(); - Uniform *u = new Uniform(propObj); - Q_UNUSED(u) - - // TODO - propObj.value("name"); - propObj.value("type"); - propObj.value("defaultValue"); - propObj.value("description"); - } + for (const auto /*QJsonValueRef*/ &prop : properties) + m_unifomrsModel.addUniform(new Uniform(prop.toObject())); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index cc878a428c9..f50c09a9a44 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -3,15 +3,20 @@ #pragma once +#include "effectmakeruniformsmodel.h" + #include namespace QmlDesigner { +class Uniform; + class CompositionNode : public QObject { Q_OBJECT Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) public: CompositionNode(const QString &qenPath); @@ -20,6 +25,11 @@ public: QString vertexCode() const; QString description() const; + QObject *uniformsModel(); + +signals: + void uniformsModelChanged(); + private: void parse(const QString &qenPath); @@ -27,6 +37,8 @@ private: QString m_fragmentCode; QString m_vertexCode; QString m_description; + + EffectMakerUniformsModel m_unifomrsModel; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index db0754bed4d..50879021938 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -18,6 +18,7 @@ QHash EffectMakerModel::roleNames() const { QHash roles; roles[NameRole] = "nodeName"; + roles[UniformsRole] = "nodeUniformsModel"; return roles; } diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 969267a7b2d..bad64a6cddc 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -41,7 +41,7 @@ signals: private: enum Roles { NameRole = Qt::UserRole + 1, -// TODO + UniformsRole }; bool isValidIndex(int idx) const; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp new file mode 100644 index 00000000000..6e2831d2afd --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -0,0 +1,58 @@ +// 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[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)); +} + +void EffectMakerUniformsModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void EffectMakerUniformsModel::addUniform(Uniform *uniform) +{ + beginInsertRows({}, m_uniforms.size(), m_uniforms.size()); + m_uniforms.append(uniform); + endInsertRows(); +} + +} // 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..fe64efcf42e --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -0,0 +1,41 @@ +// 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; + + void resetModel(); + + void addUniform(Uniform *uniform); + +private: + enum Roles { + NameRole = Qt::UserRole + 1, + DescriptionRole, + ValueRole, + DefaultValueRole, + MaxValueRole, + MinValueRole, + TypeRole, + }; + + QList m_uniforms; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index c3e4256e431..c4669692872 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -14,6 +14,10 @@ class Uniform : public QObject { Q_OBJECT + Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) + Q_PROPERTY(Type uniformType MEMBER m_type CONSTANT) + Q_PROPERTY(QVariant uniformValue MEMBER m_value CONSTANT) + public: enum class Type { @@ -71,11 +75,10 @@ private: bool m_enabled = true; bool m_enableMipmap = false; - bool operator==(const Uniform& rhs) const noexcept + bool operator==(const Uniform &rhs) const noexcept { return this->m_name == rhs.m_name; } }; } // namespace QmlDesigner - From 1ac682d02d34a54dd1b693e6c892f55300bf1f49 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Mon, 14 Aug 2023 11:03:02 +0200 Subject: [PATCH 072/266] QmlDesigner: Add style to Connection View popout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib6d8d9f11337a319bdaf8208880be45756d87e9b Reviewed-by: Henning Gründl --- .../connectionseditor/ConnectionsDialog.qml | 3 +- .../ConnectionsDialogForm.qml | 24 +++++++++--- .../connectionseditor/ConnectionsListView.qml | 1 + .../qmldesigner/connectionseditor/Main.qml | 2 +- .../connectionseditor/PopupDialog.qml | 5 ++- .../ConnectionPopupButtonStyle.qml | 34 ++++++++++++++++ .../ConnectionPopupControlStyle.qml | 39 +++++++++++++++++++ .../imports/StudioTheme/Values.qml | 28 ++++++++++++- .../imports/StudioTheme/qmldir | 2 + .../themes/design-light.creatortheme | 30 +++++++++++++- share/qtcreator/themes/design.creatortheme | 30 +++++++++++++- src/libs/utils/theme/theme.h | 19 +++++++++ 12 files changed, 203 insertions(+), 14 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupButtonStyle.qml create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index be45e30679b..b982e3424eb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -19,7 +19,8 @@ PopupDialog { StudioControls.TopLevelComboBox { id: target - width: 210 + style: StudioTheme.Values.connectionPopupControlStyle + width: 180 anchors.verticalCenter: parent.verticalCenter model: ["mySpinbox", "foo", "backendObject"] } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index c9c9030897b..a54baa55c31 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -11,7 +11,7 @@ Column { id: root readonly property real horizontalSpacing: 10 - readonly property real verticalSpacing: 10 + readonly property real verticalSpacing: 16 readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 component PopupLabel : Text { @@ -45,12 +45,14 @@ Column { StudioControls.TopLevelComboBox { id: signal + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["Clicked", "Pressed", "Released"] } StudioControls.TopLevelComboBox { id: action + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth textRole: "text" valueRole: "value" @@ -80,11 +82,13 @@ Column { spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["mySpinBox", "myAnimation", "myCustomComponent"] } StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["start", "stop", "reset"] } @@ -104,11 +108,13 @@ Column { spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["value", "foo", "bar"] } StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["myValue", "yourValue", "ourValue"] } @@ -128,11 +134,13 @@ Column { spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["main", "group1", "group2"] } StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["state1", "state2", "state3", "state4"] } @@ -152,11 +160,13 @@ Column { spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["value", "foo", "bar"] } StudioControls.TopLevelComboBox { + style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth model: ["myValue", "yourValue", "ourValue"] } @@ -193,12 +203,16 @@ Column { PopupLabel { visible: action.currentValue === ConnectionsDialogForm.Custom text: qsTr("Custom Connections can only be edited with the binding editor") - width: root.width + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 30 horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap } HelperWidgets.AbstractButton { - width: 200 + style: StudioTheme.Values.connectionPopupButtonStyle + width: 160 buttonIcon: qsTr("Add Condition") iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font @@ -212,8 +226,8 @@ Column { Rectangle { id: editor width: parent.width - height: 200 - color: "#333333" + height: 150 + color: StudioTheme.Values.themeToolbarBackground Text { anchors.centerIn: parent diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 2c83ec167bd..e1fa90265bc 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -16,6 +16,7 @@ ListView { clip: true interactive: true highlightMoveDuration: 0 + highlightResizeDuration: 0 ScrollBar.vertical: ScrollBar { id: comboBoxPopupScrollBar diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index a124228b3ea..6e47848b12e 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -68,7 +68,7 @@ Rectangle { TabCheckButton { id: properties - buttonIcon: StudioTheme.Constants.connections_medium + buttonIcon: StudioTheme.Constants.properties_medium text: qsTr("Properties") tooltip: qsTr("This is a tooltip.") autoExclusive: true diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 5d0663e2a35..2119f56e88c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -13,11 +13,11 @@ Window { property alias titleBar: titleBarContent.children default property alias content: mainContent.children - width: 400 + width: 320 height: column.implicitHeight visible: true flags: Qt.FramelessWindowHint | Qt.Dialog - color: "#060606" + color: StudioTheme.Values.themePopoutBackground function popup(item) { print("popup " + item) @@ -55,6 +55,7 @@ Window { id: row anchors.fill: parent anchors.leftMargin: StudioTheme.Values.popupMargin + anchors.rightMargin: StudioTheme.Values.popupMargin spacing: 0 Item { 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..9aaea8dae2c --- /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.themeInteraction + 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/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 41f09f45a4d..03a1d078fcc 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -219,7 +219,7 @@ QtObject { property real colorEditorPopupSpinBoxWidth: 54 // Popup Window - property real titleBarHeight: values.height + 4 + property real titleBarHeight: values.height + 10 property real popupMargin: 10 // Toolbar @@ -275,6 +275,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) @@ -409,6 +433,8 @@ QtObject { // 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/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/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index eeac285a5fe..b0f34fcfbdd 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 diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index 5e8294efa55..9a7065311ac 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 diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 2604780a2d5..7a24c7fbd03 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -323,6 +323,25 @@ 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, /*Legacy QtDS*/ DSiconColor, From e6529d3d0e38a0576079c92cf33656363bf6ee05 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Aug 2023 14:38:50 +0300 Subject: [PATCH 073/266] QmlDesigner: Wire effect maker composition node value to UI Wire the value, min/max values to the UI. Also small relevant UI tweaks. Change-Id: Id067cabd07bdc3ec02682a0a78c01a31e65ecdf6 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectCompositionNodeUniform.qml | 21 +++++++++++-------- .../effectMakerQmlSources/EffectMaker.qml | 1 - .../components/effectmaker/uniform.h | 2 ++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 8e12395c937..c4e06a4239a 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -23,7 +23,9 @@ Item { color: StudioTheme.Values.themeTextColor font.pointSize: StudioTheme.Values.smallFontSize horizontalAlignment: Text.AlignRight - Layout.preferredWidth: 80 + Layout.maximumWidth: 140 + Layout.minimumWidth: 140 + Layout.preferredWidth: 140 } Loader { @@ -46,21 +48,22 @@ Item { actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - from: 21 - to: 78 - stepSize: 1 - onValueChanged: slider.value = realValue + realFrom: uniformMinValue + realTo: uniformMaxValue + realValue: uniformValue + realStepSize: .01 + decimals: 2 + onRealValueChanged: slider.value = realValue } StudioControls.Slider { id: slider - width: parent.width - 60 + width: parent.width - 80 labels: false actionIndicatorVisible: false - from: 21 - to: 78 - stepSize: 1 + from: uniformMinValue + to: uniformMaxValue onValueChanged: spinBox.realValue = value } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index ee3992cac51..3e41d32c633 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -65,7 +65,6 @@ Item { Column { width: scrollView.width - height: compositionRepeater.height spacing: 1 Repeater { diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index c4669692872..6c203cfec3b 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -17,6 +17,8 @@ class Uniform : public QObject Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) Q_PROPERTY(Type uniformType MEMBER m_type CONSTANT) Q_PROPERTY(QVariant uniformValue MEMBER m_value CONSTANT) + Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) + Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) public: enum class Type From 42309ae64a8d50a4fc59aaf503f08ca02c5f99e8 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 21 Aug 2023 15:26:32 +0300 Subject: [PATCH 074/266] QmlDesigner: Add values validation for shader uniforms Uniform values could be empty, so this validates and cast to the right uniform type. If a uniform value is empty, a default value is assigned. Change-Id: Icb2760bca5ca9377e389ee5a65f7c15ad19455ce Reviewed-by: Mahmoud Badri --- .../components/effectmaker/uniform.cpp | 99 +++++++++++++++++-- .../components/effectmaker/uniform.h | 6 ++ 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index df1436d5c4f..322c0815a56 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -3,15 +3,15 @@ #include "uniform.h" +#include #include +#include namespace QmlDesigner { Uniform::Uniform(const QJsonObject &propObj) { - //TODO: some cases such as missing values or default values not yet implemented - - QString value, defaultValue; + QString value, defaultValue, minValue, maxValue; m_name = propObj.value("name").toString(); m_description = propObj.value("description").toString(); @@ -34,13 +34,10 @@ Uniform::Uniform(const QJsonObject &propObj) // QEN files don't store the current value, so with those use default value value = defaultValue; } - m_customValue = propObj.value("customValue").toString(); - m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false); - m_minValue = propObj.value("minValue").toString(); - m_maxValue = propObj.value("maxValue").toString(); - //TODO: set uniform value data after validating, for now just set to current value - m_defaultValue = defaultValue; - m_value = value; + minValue = propObj.value("minValue").toString(); + maxValue = propObj.value("maxValue").toString(); + + setValueData(value, defaultValue, minValue, maxValue); } Uniform::Type Uniform::type() const @@ -171,4 +168,86 @@ QString Uniform::getResourcePath(const QString &value) const 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(m_type, false) : valueStringToVariant(m_type, value); + m_defaultValue = defaultValue.isEmpty() ? getInitializedVariant(m_type, false) + : valueStringToVariant(m_type, defaultValue); + m_minValue = minValue.isEmpty() ? getInitializedVariant(m_type, false) : valueStringToVariant(m_type, minValue); + m_maxValue = maxValue.isEmpty() ? getInitializedVariant(m_type, true) : valueStringToVariant(m_type, maxValue); +} + +// Initialize the value variant with correct type +QVariant Uniform::getInitializedVariant(Uniform::Type type, bool maxValue) +{ + switch (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 Uniform::Type type, const QString &value) +{ + QVariant variant; + switch (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; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 6c203cfec3b..f9634b48829 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -6,7 +6,9 @@ #include #include +QT_FORWARD_DECLARE_CLASS(QColor) QT_FORWARD_DECLARE_CLASS(QJsonObject) +QT_FORWARD_DECLARE_CLASS(QVector2D) namespace QmlDesigner { @@ -64,6 +66,10 @@ private: Uniform::Type typeFromString(const QString &typeString) 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(Uniform::Type type, bool maxValue); + QVariant valueStringToVariant(const Uniform::Type type, const QString &value); Type m_type; QVariant m_value; From 9018a7bc2052733885966adc6d53ff89cb9281f0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Aug 2023 22:18:16 +0300 Subject: [PATCH 075/266] QmlDesigner: Update composition node model's value from UI Task-number: QDS-10404 Change-Id: I6292c9371a748d6305bdf068cc0cf8f3ca907fd9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed --- .../EffectCompositionNodeUniform.qml | 9 +++++++-- .../effectmaker/effectmakeruniformsmodel.cpp | 11 +++++++++++ .../components/effectmaker/effectmakeruniformsmodel.h | 1 + .../qmldesigner/components/effectmaker/uniform.cpp | 5 ++++- .../qmldesigner/components/effectmaker/uniform.h | 5 ++++- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index c4e06a4239a..fac1b649df1 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -53,7 +53,7 @@ Item { realValue: uniformValue realStepSize: .01 decimals: 2 - onRealValueChanged: slider.value = realValue + onRealValueModified: uniformValue = realValue } StudioControls.Slider { @@ -61,10 +61,15 @@ Item { width: parent.width - 80 labels: false + decimals: 2 actionIndicatorVisible: false from: uniformMinValue to: uniformMaxValue - onValueChanged: spinBox.realValue = value + value: uniformValue + onMoved: { + uniformValue = value + spinBox.realValue = value // binding isn't working for this property so update it + } } } } diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp index 6e2831d2afd..b1bdc12331a 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -42,6 +42,17 @@ QVariant EffectMakerUniformsModel::data(const QModelIndex &index, int role) cons 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(); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h index fe64efcf42e..a5f2ae6eea4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -19,6 +19,7 @@ public: 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(); diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index 322c0815a56..2c0b85a3b42 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -52,7 +52,10 @@ QVariant Uniform::value() const void Uniform::setValue(const QVariant &newValue) { - m_value = newValue; + if (m_value != newValue) { + m_value = newValue; + emit uniformValueChanged(); + } } QVariant Uniform::defaultValue() const diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index f9634b48829..73de5558db3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -18,7 +18,7 @@ class Uniform : public QObject Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) Q_PROPERTY(Type uniformType MEMBER m_type CONSTANT) - Q_PROPERTY(QVariant uniformValue MEMBER m_value CONSTANT) + Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) @@ -60,6 +60,9 @@ public: bool enableMipmap() const; +signals: + void uniformValueChanged(); + private: QString mipmapPropertyName(const QString &name) const; From 6cb3e6ec9d79ad27e374ee0614940a6635bd5edb Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 22 Aug 2023 11:05:50 +0200 Subject: [PATCH 076/266] QmlDesigner: Fix top toolbar workspace selection Task-number: QDS-10494 Change-Id: I1f055f7bed7a76df76d733ff8d17e190485ab76d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/toolbar/Main.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index ebed433e724..816e849143d 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 { From f018ea6d8bb4810b4919f953b5c82a61e0529fb4 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 10 Jul 2023 17:14:13 +0300 Subject: [PATCH 077/266] QmlDesigner: Implement Connection Editor Evaluator and Parser An AST Visitor checks the correct structure for the connection editor Statements are parsed as the result Javascript statements could be resurrected Some tests are added Task-number: QDS-10257 Task-number: QDS-10288 Change-Id: Ia6e4f9c0f48678b6610941c977b7d7bed296ced0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../connectioneditorevaluator.cpp | 1096 +++++++++++++++++ .../connectioneditorevaluator.h | 55 + .../connectioneditorstatements.cpp | 353 ++++++ .../connectioneditorstatements.h | 124 ++ tests/auto/qml/CMakeLists.txt | 1 + .../auto/qml/connectioneditor/CMakeLists.txt | 8 + .../connectioneditor/tst_connectioneditor.cpp | 353 ++++++ 8 files changed, 1992 insertions(+) create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h create mode 100644 tests/auto/qml/connectioneditor/CMakeLists.txt create mode 100644 tests/auto/qml/connectioneditor/tst_connectioneditor.cpp diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index f8bae184ee4..10ec06fa11c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -935,6 +935,8 @@ extend_qtc_plugin(QmlDesigner backendmodel.cpp backendmodel.h bindingmodel.cpp bindingmodel.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 diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp new file mode 100644 index 00000000000..ba69f7c1f34 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -0,0 +1,1096 @@ +// 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 (!couldBeLHS()) + return {}; + + return std::get(m_rhs); + } + + ConnectionEditorStatements::Variable variable() const + { + if (!isValid()) + return {}; + + if (std::holds_alternative(m_rhs)) + return std::get(m_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.compare("state") == 0 + && 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 QString identifierName = identifier->name.toString(); + if (identifierName.compare("console") != 0) { + m_failed = true; + return false; + } + return true; + } + + bool visit(QmlJS::AST::FieldMemberExpression *fieldExpression) override + { + if (m_completed) + return true; + + const QString fieldName = fieldExpression->name.toString(); + if (fieldName.compare("log") != 0) { + 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 (std::holds_alternative(m_handler)) + return &std::get(m_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() +{ + delete d.release(); +} + +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::StatementList *statementList) +{ + return d->checkValidityAndReturn(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([[maybe_unused]] QmlJS::AST::ExpressionStatement *expressionStatement) +{ + 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) + if (fieldExpression->name.toString().compare("log") == 0) + 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 (std::holds_alternative(rhs)) + *currentStatement = std::get(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.toString().compare("log") == 0) { + 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::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..c4465fcc32f --- /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::StatementList *statementList) override; + bool visit(QmlJS::AST::IfStatement *ifStatement) override; + bool visit(QmlJS::AST::IdentifierExpression *identifier) override; + bool visit(QmlJS::AST::ExpressionStatement *expressionStatement) 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 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..5a9f0665f74 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -0,0 +1,353 @@ +// 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) + { + 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 {}; + }; + } + + 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) + ";\n"; + } + + 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 += "} else {\n"; + value += JSOverload()(conditional.ko); + } + value += "}"; + + 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(const ConnectionEditorStatements::Handler &handler) +{ + return std::visit(Overload{[](const ConnectionEditorStatements::MatchedStatement &var) { return var; }, + [](const ConnectionEditorStatements::ConditionalStatement &statement) { + return statement.ok; + }}, + handler); +} + +MatchedStatement ConnectionEditorStatements::koStatement(const ConnectionEditorStatements::Handler &handler) +{ + return std::visit(Overload{[](const ConnectionEditorStatements::ConditionalStatement &statement) { + return statement.ko; + }, + [](const auto &) -> ConnectionEditorStatements::MatchedStatement { return {}; }}, + handler); +} diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h new file mode 100644 index 00000000000..c76a494014e --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -0,0 +1,124 @@ +// 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 { 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; +}; + +QMLDESIGNERCORE_EXPORT bool isEmptyStatement(const MatchedStatement &stat); +QMLDESIGNERCORE_EXPORT QString toString(const ComparativeStatement &stat); +QMLDESIGNERCORE_EXPORT QString toString(const RightHandSide &rhs); +QMLDESIGNERCORE_EXPORT QString toString(const Literal &literal); +QMLDESIGNERCORE_EXPORT QString toString(const MatchedStatement &statement); + +QMLDESIGNERCORE_EXPORT bool isConsoleLog(const MatchedStatement &curState); +QMLDESIGNERCORE_EXPORT bool isLiteralType(const RightHandSide &var); + +QMLDESIGNERCORE_EXPORT QString toString(const Handler &handler); +QMLDESIGNERCORE_EXPORT QString toJavascript(const Handler &handler); +QMLDESIGNERCORE_EXPORT QString toDisplayName(const MatchedStatement &statement); +QMLDESIGNERCORE_EXPORT QString toDisplayName(const Handler &handler); + +QMLDESIGNERCORE_EXPORT MatchedStatement okStatement(const ConnectionEditorStatements::Handler &handler); +QMLDESIGNERCORE_EXPORT MatchedStatement koStatement(const ConnectionEditorStatements::Handler &handler); + +} // namespace ConnectionEditorStatements + +} // namespace QmlDesigner 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..1deb5ac22c8 --- /dev/null +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -0,0 +1,353 @@ +// 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(); + 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(countOne()) + << "if (object.atr == 3 && kmt == 43) {kMtr.lptr = 3;} else {kMtr.ptr = 4;}" << true; + + QTest::newRow(countOne()) << "{}" << true; + QTest::newRow(countOne()) << "{console.log(\"test\")}" << true; + QTest::newRow(countOne()) << "{someItem.functionCall()}" << true; + QTest::newRow(countOne()) << "{someItem.width.kkk = 10}" << true; + QTest::newRow(countOne()) << "{someItem.color = \"red\"}" << true; + QTest::newRow(countOne()) << "{someItem.state = \"state\"}" << true; + QTest::newRow(countOne()) << "{someItem.text = \"some string\"}" << true; + QTest::newRow(countOne()) << "{someItem.speed = 1.0}" << true; + QTest::newRow(countOne()) << "{someItem.speed = someOtherItem.speed}" << true; + QTest::newRow(countOne()) << "{if (someItem.bool) { someItem.speed = someOtherItem.speed }}" + << true; + QTest::newRow(countOne()) << "{if (someItem.bool) { someItem.functionCall() }}" << true; + QTest::newRow(countOne()) << "{if (someItem.bool) { console.log(\"ok\") }}" << true; + QTest::newRow(countOne()) + << "{if (someItem.bool) { console.log(\"ok\") } else {console.log(\"ko\")}}" << true; + QTest::newRow(countOne()) << "{if (someItem.bool && someItem.someBool2) { } else { } }" + << true; + QTest::newRow(countOne()) << "{if (someItem.width > 10) { }}" << true; + QTest::newRow(countOne()) << "{if (someItem.width > 10 && someItem.someBool) { }}" << true; + QTest::newRow(countOne()) << "{if (someItem.width > 10 || someItem.someBool) { }}" << true; + QTest::newRow(countOne()) << "{if (someItem.width === someItem.height) { }}" << true; + QTest::newRow(countOne()) << "{if (someItem.width === someItem.height) {} }" << true; + + // False Conditions + QTest::newRow(countOne()) << "{someItem.complexCall(blah)}" << false; + QTest::newRow(countOne()) << "{someItem.width = someItem.Height + 10}" << false; + QTest::newRow(countOne()) + << "if (someItem.bool) {console.log(\"ok\") } else { someItem.functionCall() }" << false; + QTest::newRow(countOne()) << "{if (someItem.width == someItem.height) {} }" << false; + QTest::newRow(countOne()) << "{if (someItem.width = someItem.height) {} }" << false; + QTest::newRow(countOne()) << "{if (someItem.width > someItem.height + 10) {} }" << false; + QTest::newRow(countOne()) << "{if (someItem.width > someItem.height) ak = 2; }" << false; +} + +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; +} + +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.state"); // 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::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" From 3ca4b37e3c29281a7c9cf17fb4f628df018bed7b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 22 Aug 2023 15:12:43 +0300 Subject: [PATCH 078/266] QmlDesigner: Hide selection box for 3D scene root nodes Fixes: QDS-10501 Change-Id: I31477f6ef241aab8d400bae174e3928c47c5c189 Reviewed-by: Mahmoud Badri --- .../qml2puppet/instances/qt5informationnodeinstanceserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 268e694bc30..0492eb56ec5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -2239,7 +2239,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; } } From 733c276a4d94f395cf0da691b1ac91e14cc67492 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 22 Aug 2023 17:05:28 +0300 Subject: [PATCH 079/266] QmlDesigner: Add some effect maker uniform UI value types Change-Id: I13516f5bcbf6e15591fc2ca81e0aa9c7eb9e9c64 Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNodeUniform.qml | 79 +++------ .../effectMakerQmlSources/ValueBool.qml | 15 ++ .../effectMakerQmlSources/ValueColor.qml | 16 ++ .../effectMakerQmlSources/ValueFloat.qml | 45 +++++ .../effectMakerQmlSources/ValueImage.qml | 16 ++ .../effectMakerQmlSources/ValueVec2.qml | 86 ++++++++++ .../effectMakerQmlSources/ValueVec3.qml | 123 ++++++++++++++ .../effectMakerQmlSources/ValueVec4.qml | 159 ++++++++++++++++++ .../components/effectmaker/uniform.cpp | 24 ++- .../components/effectmaker/uniform.h | 4 +- 10 files changed, 507 insertions(+), 60 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index fac1b649df1..249cb763d86 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -2,6 +2,7 @@ // 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 @@ -12,10 +13,29 @@ import EffectMakerBackend Item { id: root - height: 22 + height: layout.implicitHeight + + Component.onCompleted: { + 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") // TODO +// valueLoader.sourceComponent = colorValue +// else if (uniformType === "image") // TODO +// valueLoader.sourceComponent = imageValue + else + valueLoader.source = "ValueFloat.qml" + } RowLayout { - spacing: 10 + id: layout + + spacing: 20 anchors.fill: parent Text { @@ -29,61 +49,8 @@ Item { } Loader { - sourceComponent: floatValue // TODO: set component based on prop type + id: valueLoader Layout.fillWidth: true } } - - Component { - id: floatValue - - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: spinBox - - width: 40 - 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 - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue - to: uniformMaxValue - value: uniformValue - onMoved: { - uniformValue = value - spinBox.realValue = value // binding isn't working for this property so update it - } - } - } - } - - Component { // TODO - id: colorValue - - Row { - width: parent.width - spacing: 5 - - HelperWidgets.ColorEditor { - backendValue: "#ffff00" - } - } - } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml new file mode 100644 index 00000000000..d75df978ae9 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml @@ -0,0 +1,15 @@ +// 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.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..5c920bdacc2 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml @@ -0,0 +1,16 @@ +// 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 + +Row { + width: parent.width + spacing: 5 + + // TODO +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml new file mode 100644 index 00000000000..577f90f2a11 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml @@ -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 + +import QtQuick +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme 1.0 as StudioTheme +import EffectMakerBackend + +Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: spinBox + + width: 40 + 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 - 80 + labels: false + decimals: 2 + actionIndicatorVisible: 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..5c920bdacc2 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -0,0 +1,16 @@ +// 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 + +Row { + width: parent.width + spacing: 5 + + // TODO +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml new file mode 100644 index 00000000000..939838f7338 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml @@ -0,0 +1,86 @@ +// 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 + +Column { + width: parent.width + spacing: 1 + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vX + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + StudioControls.Slider { + id: sliderX + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.x + to: uniformMaxValue.x + value: uniformValue.x + onMoved: { + uniformValue.x = value + vX.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vY + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + StudioControls.Slider { + id: sliderY + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.y + to: uniformMaxValue.y + value: uniformValue.y + onMoved: { + uniformValue.y = value + vY.realValue = value // binding isn't working for this property so update it + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml new file mode 100644 index 00000000000..4328408bdc4 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml @@ -0,0 +1,123 @@ +// 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 + +Column { + width: parent.width + + spacing: 1 + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vX + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + StudioControls.Slider { + id: sliderX + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.x + to: uniformMaxValue.x + value: uniformValue.x + onMoved: { + uniformValue.x = value + vX.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vY + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + StudioControls.Slider { + id: sliderY + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.y + to: uniformMaxValue.y + value: uniformValue.y + onMoved: { + uniformValue.y = value + vY.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vZ + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.z + realTo: uniformMaxValue.z + realValue: uniformValue.z + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.z = realValue + } + + StudioControls.Slider { + id: sliderZ + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.z + to: uniformMaxValue.z + value: uniformValue.z + onMoved: { + uniformValue.z = value + vZ.realValue = value // binding isn't working for this property so update it + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml new file mode 100644 index 00000000000..40a71014413 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml @@ -0,0 +1,159 @@ +// 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 + +Column { + width: parent.width + + spacing: 1 + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vX + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue + } + + StudioControls.Slider { + id: sliderX + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.x + to: uniformMaxValue.x + value: uniformValue.x + onMoved: { + uniformValue.x = value + vX.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vY + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.y + realTo: uniformMaxValue.y + realValue: uniformValue.y + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.y = realValue + } + + StudioControls.Slider { + id: sliderY + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.y + to: uniformMaxValue.y + value: uniformValue.y + onMoved: { + uniformValue.y = value + vY.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vZ + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.z + realTo: uniformMaxValue.z + realValue: uniformValue.z + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.z = realValue + } + + StudioControls.Slider { + id: sliderZ + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.z + to: uniformMaxValue.z + value: uniformValue.z + onMoved: { + uniformValue.z = value + vZ.realValue = value // binding isn't working for this property so update it + } + } + } + + Row { + width: parent.width + spacing: 5 + + StudioControls.RealSpinBox { + id: vW + + width: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.w + realTo: uniformMaxValue.w + realValue: uniformValue.w + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.w = realValue + } + + StudioControls.Slider { + id: sliderW + + width: parent.width - 80 + labels: false + decimals: 2 + actionIndicatorVisible: false + from: uniformMinValue.w + to: uniformMaxValue.w + value: uniformValue.w + onMoved: { + uniformValue.w = value + vW.realValue = value // binding isn't working for this property so update it + } + } + } +} diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index 2c0b85a3b42..1c0ba83aed0 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -40,9 +40,29 @@ Uniform::Uniform(const QJsonObject &propObj) setValueData(value, defaultValue, minValue, maxValue); } -Uniform::Type Uniform::type() const +QString Uniform::type() const { - return m_type; + if (m_type == Type::Bool) + return "bool"; + if (m_type == Type::Int) + return "int"; + if (m_type == Type::Float) + return "float"; + if (m_type == Type::Vec2) + return "vec2"; + if (m_type == Type::Vec3) + return "vec3"; + if (m_type == Type::Vec4) + return "vec4"; + if (m_type == Type::Color) + return "color"; + if (m_type == Type::Sampler) + return "image"; + if (m_type == Type::Define) + return "define"; + + qWarning() << "Unknown type"; + return "float"; } QVariant Uniform::value() const diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 73de5558db3..321ba8a7d9c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -17,7 +17,7 @@ class Uniform : public QObject Q_OBJECT Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) - Q_PROPERTY(Type uniformType MEMBER m_type CONSTANT) + Q_PROPERTY(QString uniformType READ type CONSTANT) Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) @@ -38,7 +38,7 @@ public: Uniform(const QJsonObject &props); - Type type() const; + QString type() const; QVariant value() const; void setValue(const QVariant &newValue); From 0f05d7adb3a8705da445d183e76fc29808a5c370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Thu, 17 Aug 2023 22:12:52 +0200 Subject: [PATCH 080/266] cleanup QMessageBox includes Change-Id: I358a216c48b8fbf549af38360a93a073f03e3a98 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/advanceddockingsystem/dockmanager.cpp | 1 - src/plugins/android/avddialog.cpp | 1 - src/plugins/baremetal/debugservers/uvsc/uvtargetdriverviewer.cpp | 1 - src/plugins/coreplugin/themechooser.cpp | 1 - src/plugins/projectexplorer/targetsetuppage.cpp | 1 - src/plugins/projectexplorer/userfileaccessor.h | 1 - .../qmldesigner/assetexporterplugin/assetexportdialog.cpp | 1 - .../designercore/instances/nodeinstanceserverproxy.cpp | 1 - src/plugins/qmlprojectmanager/qmlproject.cpp | 1 - src/plugins/texteditor/highlightersettingspage.cpp | 1 - tests/manual/fakevim/main.cpp | 1 - 11 files changed, 11 deletions(-) diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 20b9a71a730..20321c9b818 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index a45423cc2c1..8e7f0b0fd0e 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/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/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index fd1e8a78c64..61b23b2b78e 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/projectexplorer/userfileaccessor.h b/src/plugins/projectexplorer/userfileaccessor.h index 287dbcc6699..778e42855ad 100644 --- a/src/plugins/projectexplorer/userfileaccessor.h +++ b/src/plugins/projectexplorer/userfileaccessor.h @@ -8,7 +8,6 @@ #include #include -#include namespace ProjectExplorer { 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/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index 8b80a9c4af2..c7f13c7c09c 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/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 12e1b9f7cbe..619b590c112 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/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/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 From 7f323ee1b3609a9d81c742c18dd513e07e6b01dc Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 23 Aug 2023 10:47:37 +0300 Subject: [PATCH 081/266] QmlDesigner: Move pruneChildren to ModelUtils Change-Id: I77a799d351b7f95e901b32a4b5f7e39b9450c8c6 Reviewed-by: Marco Bubke --- .../componentcore/groupitemaction.cpp | 5 ++-- .../designercore/include/modelnode.h | 1 - .../designercore/model/modelnode.cpp | 25 ------------------- .../designercore/model/modelutils.cpp | 25 +++++++++++++++++++ .../designercore/model/modelutils.h | 3 +++ 5 files changed, 31 insertions(+), 28 deletions(-) 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/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index e95b4f47e3e..676a211cd61 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -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; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 2bc778e44cb..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 @@ -1333,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/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index efbe8f09783..387b9262b11 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -129,4 +130,28 @@ 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; +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index 946882c2df7..dd36cefa23e 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -33,4 +33,7 @@ QMLDESIGNERCORE_EXPORT QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo); QMLDESIGNERCORE_EXPORT QString componentFilePath(const ModelNode &node); + +QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &nodes); + } // namespace QmlDesigner::ModelUtils From e0314c8204e8b4b6696a1fa36a5ec5b32c42ad8c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Aug 2023 23:27:16 +0200 Subject: [PATCH 082/266] QmlDesigner: Prefer std::get_if to std::get for variants It cannot not throw, so no exceptions code is generated. And it is quite often shorter too. Change-Id: Icd555cedb880a1547b0ddcbcdd74fc21feb83ccb Reviewed-by: Ali Kianian Reviewed-by: Thomas Hartmann --- .../connectioneditorevaluator.cpp | 19 ++++--- .../imagecache/imagecachefontcollector.cpp | 54 ++++++++++--------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index ba69f7c1f34..0a52deb7af5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -344,10 +344,13 @@ public: ConnectionEditorStatements::Variable lhs() const { - if (!couldBeLHS()) + if (!isValid()) return {}; - return std::get(m_rhs); + if (auto rhs = std::get_if(&m_rhs)) + return *rhs; + + return {}; } ConnectionEditorStatements::Variable variable() const @@ -355,8 +358,8 @@ public: if (!isValid()) return {}; - if (std::holds_alternative(m_rhs)) - return std::get(m_rhs); + if (auto rhs = std::get_if(&m_rhs)) + return *rhs; return {}; } @@ -702,8 +705,8 @@ public: MatchedCondition *currentCondition() { - if (std::holds_alternative(m_handler)) - return &std::get(m_handler).condition; + if (auto handler = std::get_if(&m_handler)) + return &handler->condition; return nullptr; } @@ -997,8 +1000,8 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::CallExpression *callExpression callExpression->accept(&callVisitor); if (callVisitor.isValid()) { ConnectionEditorStatements::RightHandSide rhs = callVisitor.rhs(); - if (std::holds_alternative(rhs)) - *currentStatement = std::get(rhs); + if (auto rhs_ = std::get_if(&rhs)) + *currentStatement = *rhs_; else return d->checkValidityAndReturn(false, "Invalid Matched Function type."); } else { 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)); + } } } From 22166404f3cd6293bf05d4e1892e056c6c3e5cd5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Aug 2023 10:21:59 +0200 Subject: [PATCH 083/266] QmlDesigner: Use system includes for google test System includes are silencing warnings. Change-Id: I7d7954e01a28f94aa5983cfc44eba976d97b0311 Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Cristian Adam --- cmake/QtCreatorAPIInternal.cmake | 9 +++++---- src/libs/googletest/CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index ecb6d17f360..39735a80c7a 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -246,13 +246,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 $ $ ) @@ -465,7 +465,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;PUBLIC_INCLUDES;PUBLIC_SYSTEM_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES;SOURCES_PROPERTIES" ${ARGN} ) @@ -507,7 +507,8 @@ function(extend_qtc_target target_name) ) target_include_directories(${target_name} PRIVATE ${_arg_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/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 From 3edbdc92903e54c24af0a66ee40c49a0fb64c70b Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Wed, 23 Aug 2023 17:02:19 +0300 Subject: [PATCH 084/266] McuSupport: Allow the Text.textFormat property in QtMCUs 2.6 Task-number: QDS-10506 Change-Id: Id72641b1c309b99feab5edb51a31f2a487e82b79 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Aleksei German --- share/qtcreator/qmldesigner/qt4mcu/qul-26.qml | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-26.qml 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"] + } +} From ddde6b1ea54c947f6c94a37ed3a0aaba7a077a02 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Aug 2023 10:22:59 +0200 Subject: [PATCH 085/266] UnitTests: Silence padding warning in tests It is not very useful here and adds a warning for every test. Change-Id: Iaf75385f90ebbe3d0ac9a306328ba56c489ea6cf Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- tests/unit/tests/utils/googletest.h | 2 ++ 1 file changed, 2 insertions(+) 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") From 90fea3ebd6ee839dc91a87c03dcf9508c0475b28 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 3 Aug 2023 09:14:15 +0200 Subject: [PATCH 086/266] QmlDesigner: Collect panes and specifics in updater The update is walking a path which is provided by the QmlDesignerProjectManager paths which ends to Pane.qml or Specifics.qml. From that paths the module and type name is extracted. That data is synchronized with the ProjectStorage. If no directory was changed it will skip that directory. It will to collect the directory id to update the storage per directory. Task-number: QDS-10390 Change-Id: I3aaf5528133cf5e5c15f38a9720fbbf540d24eca Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../projectstorage/projectstoragetypes.h | 22 ++ .../projectstorage/projectstorageupdater.cpp | 65 +++- .../projectstorage/projectstorageupdater.h | 13 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 13 +- tests/unit/tests/matchers/unittest-matchers.h | 4 +- .../tests/printers/gtest-creator-printing.cpp | 10 +- .../tests/printers/gtest-creator-printing.h | 2 + .../projectstorageupdater-test.cpp | 280 ++++++++++++------ .../tests/utils/google-using-declarations.h | 1 - 9 files changed, 307 insertions(+), 103 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index c6e309b5e7c..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: @@ -800,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 25430bb4ebd..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) 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/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index e97fbab2d3f..aee98cd5a82 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -438,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) @@ -572,7 +582,8 @@ void QmlDesignerProjectManager::update() return; m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), - qmlTypes(m_projectData->activeTarget)); + qmlTypes(m_projectData->activeTarget), + propertyEditorResourcesPath()); } } // namespace QmlDesigner diff --git a/tests/unit/tests/matchers/unittest-matchers.h b/tests/unit/tests/matchers/unittest-matchers.h index 42b3ec76009..7c52d973b88 100644 --- a/tests/unit/tests/matchers/unittest-matchers.h +++ b/tests/unit/tests/matchers/unittest-matchers.h @@ -147,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/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 25bb4d18d90..64ca07cb6b3 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -697,7 +697,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) @@ -796,6 +799,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..8d30684e9bb 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -197,6 +197,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 +216,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/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/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; From 7f9d3b302f7a61b3892c059c75461a906e8b2ef4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 15 Aug 2023 16:46:39 +0200 Subject: [PATCH 087/266] QmlDesigner: Add path for property editor in project storage A simple store for editor path in the project storage. So we can inquire the path for every type. The storage is synchronized per directory. So only entries with the updated directory id are affected. Task-number: QDS-10391 Change-Id: Ic4d0ce17eedd374ade56abd7cab08f2b7a9830c6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../designercore/include/nodemetainfo.h | 2 + .../designercore/metainfo/nodemetainfo.cpp | 10 ++ .../projectstorage/projectstorage.h | 125 ++++++++++++++++++ .../projectstorage/projectstorageinterface.h | 1 + tests/unit/tests/mocks/projectstoragemock.h | 4 + .../unittests/metainfo/nodemetainfo-test.cpp | 21 +++ .../projectstorage/projectstorage-test.cpp | 113 ++++++++++++++++ 7 files changed, 276 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index c782c47bc4c..ea73a215688 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -209,6 +209,8 @@ public: return !(first == second); } + SourceId propertyEditorPathId() const; + private: const Storage::Info::Type &typeData() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 016117ab9cd..7d0deab9e6c 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1797,6 +1797,16 @@ QString NodeMetaInfo::requiredImportString() const 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) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 262abfea295..1156673d6ce 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -83,6 +83,8 @@ public: relinkablePrototypes, relinkableExtensions, package.updatedSourceIds); + synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, + package.updatedPropertyEditorQmlPathSourceIds); deleteNotUpdatedTypes(updatedTypeIds, package.updatedSourceIds, @@ -523,6 +525,20 @@ public: Storage::Synchronization::ProjectData>(64, 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: class ModuleStorageAdapter { @@ -1660,6 +1676,76 @@ private: return json; } + TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const + { + 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) { @@ -2313,6 +2399,7 @@ private: createDocumentImportsTable(database, moduleIdColumn); createFileStatusesTable(database); createProjectDatasTable(database); + createPropertyEditorPathsTable(database); } database.setIsInitialized(true); } @@ -2646,6 +2733,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: @@ -3331,6 +3434,28 @@ public: " 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/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index adbbfcac4c3..d7da40ae140 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -59,6 +59,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 diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 7d0dfd9924f..ad6bcbfab1b 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -259,6 +259,10 @@ public: MOCK_METHOD(std::vector, fetchAllSourceContexts, (), ()); MOCK_METHOD(std::vector, fetchAllSources, (), ()); + MOCK_METHOD(QmlDesigner::SourceId, + propertyEditorPathId, + (QmlDesigner::TypeId typeId), + (const, override)); QmlDesigner::Storage::Info::CommonTypeCache typeCache{*this}; }; diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index 4d645ec1681..deffe2bd87b 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -2345,4 +2345,25 @@ 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()); +} + } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 0c83794429c..f0c591225d1 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -965,6 +965,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 +1069,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")}; @@ -7011,4 +7055,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 From 681468051f8efbbee9169fd58797077b47408f44 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 10 Aug 2023 16:04:30 +0200 Subject: [PATCH 088/266] QmlDesigner: Add type to NodeMetaInfo If the project storage works we can access if a type is a value, reference, sequence or non of the above. We can even test of values has properties like QFont etc.. That is removing quite some heuristic code. Change-Id: Ie3447d99931c6784db178e4b48f7d2f9a4f646fc Reviewed-by: Reviewed-by: Vikas Pachdha Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/nodemetainfo.h | 4 ++ .../designercore/metainfo/nodemetainfo.cpp | 20 +++++++++ .../unittests/metainfo/nodemetainfo-test.cpp | 45 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index ea73a215688..ebf7556f7c5 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; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 7d0deab9e6c..0aade8c08d7 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1456,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()) diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index deffe2bd87b..31236bfc53e 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -2366,4 +2366,49 @@ TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty) 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 From 7d948b26d7590df425721743501b5b758dc0de28 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 15 Aug 2023 15:37:18 +0200 Subject: [PATCH 089/266] QmlDesigner: Support dot properties in NodeMetaInfo::hasProperty Dot and dotdot properties are supported but not three or more dot properties. Task-number: QDS-10472 Change-Id: Ib5e40bfca470fe7520c9caacf0f469892bb73f7c Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/metainfo/nodemetainfo.cpp | 32 ++++++++- .../unittests/metainfo/nodemetainfo-test.cpp | 68 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 0aade8c08d7..e75e6348a7a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1502,10 +1502,40 @@ bool NodeMetaInfo::isInProjectModule() const return false; } +namespace { +[[maybe_unused]] bool hasPropertyForTypeId(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 bool(projectStorage.propertyDeclarationId(propertyTypeId, {begin, end})); + } + } + } + + return bool(propertyId); +} + +} // namespace + bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { if constexpr (useProjectStorage()) - return isValid() && bool(m_projectStorage->propertyDeclarationId(m_typeId, propertyName)); + return isValid() && hasPropertyForTypeId(*m_projectStorage, m_typeId, propertyName); else return isValid() && m_privateData->properties().contains(propertyName); } diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index 31236bfc53e..b6ccdd8d4bd 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -307,6 +307,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(); From 1bdbe55c289e7c333316d0cb5cc1252fdf09a9e0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 16 Aug 2023 18:48:02 +0200 Subject: [PATCH 090/266] QmlDesigner: Add NodeMetaInfo::property dot getter support Dot and dotdot properties can be gotten but three dots or more are not supported. Task-number: QDS-10472 Change-Id: I5e05405cd8ce80bc321dd78b2f74158832fed0bb Reviewed-by: Tim Jenssen --- .../designercore/metainfo/nodemetainfo.cpp | 19 +++++---- .../unittests/metainfo/nodemetainfo-test.cpp | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index e75e6348a7a..e9b5edb834e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1503,9 +1503,10 @@ bool NodeMetaInfo::isInProjectModule() const } namespace { -[[maybe_unused]] bool hasPropertyForTypeId(const ProjectStorageType &projectStorage, - TypeId typeId, - Utils::SmallStringView propertyName) + +[[maybe_unused]] auto propertyId(const ProjectStorageType &projectStorage, + TypeId typeId, + Utils::SmallStringView propertyName) { auto begin = propertyName.begin(); const auto end = propertyName.end(); @@ -1522,12 +1523,12 @@ namespace { if (propertyId && found != end) { begin = std::next(found); - return bool(projectStorage.propertyDeclarationId(propertyTypeId, {begin, end})); + return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); } } } - return bool(propertyId); + return propertyId; } } // namespace @@ -1535,7 +1536,7 @@ namespace { bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { if constexpr (useProjectStorage()) - return isValid() && hasPropertyForTypeId(*m_projectStorage, m_typeId, propertyName); + return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName)); else return isValid() && m_privateData->properties().contains(propertyName); } @@ -1594,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}; diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index b6ccdd8d4bd..07c9957a530 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -413,6 +413,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(); From 1be5475f6213dd5dd339af7f98b68ffd106d9c86 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 16 Aug 2023 12:28:20 +0200 Subject: [PATCH 091/266] QmlDesgner: Add inflate property functions Sometimes we want to have the dot properties instead of the value(readonly) properties. CompoundPropertyMetaInfos inflateValueProperties(PropertyMetaInfos properties); CompoundPropertyMetaInfos inflateValueAndReadOnlyProperties(PropertyMetaInfos properties); are removing the value proeprties and adding the dot properties if the property type has properties. Task-number: QDS-10472 Change-Id: Ia2291f77f1b38bbe62d71319bfa28cf545cbef18 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/nodemetainfo.h | 2 + .../designercore/include/propertymetainfo.h | 35 ++++++++ .../designercore/metainfo/nodemetainfo.cpp | 89 +++++++++++++++++++ .../projectstorage/commontypecache.h | 85 +++++++++++++++--- .../projectstorage/projectstorage.h | 7 +- .../projectstorage/projectstorageinfotypes.h | 3 + .../projectstorage/projectstorageinterface.h | 11 +++ tests/unit/tests/mocks/projectstoragemock.cpp | 50 ++++++++++- tests/unit/tests/mocks/projectstoragemock.h | 13 +++ .../tests/printers/gtest-creator-printing.cpp | 11 +++ .../tests/printers/gtest-creator-printing.h | 4 + .../unittests/metainfo/nodemetainfo-test.cpp | 86 ++++++++++++++++++ .../metainfo/propertymetainfo-test.cpp | 20 +++++ .../projectstorage/projectstorage-test.cpp | 78 ++++++++++++++-- 14 files changed, 471 insertions(+), 23 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index ebf7556f7c5..f8a864ab815 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -215,6 +215,8 @@ public: SourceId propertyEditorPathId() const; + const ProjectStorageType &projectStorage() const { return *m_projectStorage; } + private: const Storage::Info::Type &typeData() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; 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/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index e9b5edb834e..d2bf00c9b67 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -3107,6 +3107,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()) { @@ -3127,6 +3137,11 @@ bool PropertyMetaInfo::isWritable() const return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); } +bool PropertyMetaInfo::isReadOnly() const +{ + return !isWritable(); +} + bool PropertyMetaInfo::isListProperty() const { if constexpr (useProjectStorage()) @@ -3327,4 +3342,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/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/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 1156673d6ce..f3977ac8c41 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; @@ -961,7 +963,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); @@ -1676,7 +1678,8 @@ private: return json; } - TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const + TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, + Utils::SmallStringView name) const override { return selectTypeIdByModuleIdAndExportedNameStatement.template value(moduleId, name); } 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 d7da40ae140..b90b6efbad3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -14,7 +14,14 @@ 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; @@ -81,7 +88,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/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 ad6bcbfab1b..08f2f8e6b9d 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, @@ -263,6 +267,15 @@ public: 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 64ca07cb6b3..81572a7b359 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -472,6 +472,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()) diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index 8d30684e9bb..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; diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index 07c9957a530..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: @@ -483,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(); diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp index 43620baeb91..923743799e3 100644 --- a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -89,6 +89,26 @@ TEST_F(PropertyMetaInfo, default_hads_invalid_property_type) 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()); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index f0c591225d1..45660302627 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -391,10 +391,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 +425,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{}, @@ -1087,6 +1141,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; @@ -6625,6 +6680,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()}; @@ -6650,14 +6714,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) From 4c52e5ef954aab240ae170e1f15ab3300366278d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 20 Aug 2023 14:26:58 +0200 Subject: [PATCH 092/266] Utils: remove a copy from the SmallString default constructor StringDataLayout() is now constexpr too. Not that is useful because we have to wait for constexpr new. Change-Id: I34982c8b17e1935de5c65ec6fc1b01b572170966 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 12 ++++-------- src/libs/utils/smallstringlayout.h | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index bd4f708fed1..4a8e7a65907 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -50,15 +50,11 @@ 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() = default; constexpr BasicSmallString(const BasicSmallStringLiteral &stringReference) diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 6235995d944..7ec48f6b759 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -95,7 +95,7 @@ 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} @@ -147,7 +147,7 @@ struct alignas(16) StringDataLayout Date: Sun, 20 Aug 2023 19:57:49 +0200 Subject: [PATCH 093/266] Utils: Make SmallStringLiteral constexpr The whole small string has to be intialized for const expressions. But because we don't want to pay for that all the time we use a C++ feature to only do that at compile time. So now at runtime only the control block and the string content code is generated but at compile time everything is filled up with zeros. So you can now write: void foo(std::vector &v) { static constexpr Utils::SmallStringLiteral foo{"Some short string"}; v.push_back(foo); } The advantage compared to a string_view is that it is only memory copying the literal because it know it has to be a constant. Change-Id: Ia0e4bac293c363ae08341d18d47ddaf78e896e6a Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstringlayout.h | 46 +++++++++++++++++++++++++++-- src/libs/utils/smallstringliteral.h | 2 ++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 7ec48f6b759..0e278c61861 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -109,8 +109,20 @@ struct alignas(16) StringDataLayout control = {Size - 1, 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}; +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = 0; i < sizeof(dummy); ++i) + dummy[i] = 0; + } +#endif reference = {{string}, Size - 1, 0}; } } @@ -120,7 +132,16 @@ struct alignas(16) StringDataLayout return MaximumShortStringDataAreaSize; } - constexpr void reset() { control = ControlBlock(); } + constexpr void reset() + { + 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) @@ -161,8 +182,20 @@ struct alignas(16) StringDataLayout= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = Size; i < MaximumShortStringDataAreaSize; ++i) + shortString[i] = 0; + } +#endif } else { control = {0, true, true}; +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L + if (std::is_constant_evaluated()) { + for (size_type i = 0; i < sizeof(dummy); ++i) + dummy[i] = 0; + } +#endif reference = {{string}, Size - 1, 0}; } } @@ -184,7 +217,16 @@ struct alignas(16) StringDataLayout(); } + constexpr void reset() + { + 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) diff --git a/src/libs/utils/smallstringliteral.h b/src/libs/utils/smallstringliteral.h index c7f410d79ae..b2e17105336 100644 --- a/src/libs/utils/smallstringliteral.h +++ b/src/libs/utils/smallstringliteral.h @@ -20,6 +20,8 @@ public: using const_reverse_iterator = std::reverse_iterator; using size_type = std::size_t; + constexpr BasicSmallStringLiteral() = default; + template constexpr BasicSmallStringLiteral(const char(&string)[ArraySize]) noexcept From 8c0402390c4c120abe37452a0af16109ee991601 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 20 Aug 2023 20:12:39 +0200 Subject: [PATCH 094/266] Utils: Add some more noexcepts The code cannot throw any exceptions. Change-Id: I735269e92458793df10f887b18a5d808070bac5b Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 182 ++++++++++++---------------- src/libs/utils/smallstringlayout.h | 38 ++---- src/libs/utils/smallstringliteral.h | 11 +- 3 files changed, 90 insertions(+), 141 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 4a8e7a65907..c64e46f6f87 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -54,21 +54,19 @@ public: : sizeof(Internal::StringDataLayout) == Size + 2, "Size is wrong"); - constexpr BasicSmallString() = default; + BasicSmallString() noexcept = default; - constexpr - BasicSmallString(const BasicSmallStringLiteral &stringReference) + 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); @@ -80,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)) {} @@ -135,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; @@ -143,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(); @@ -176,7 +174,7 @@ public: } BasicSmallString take() noexcept { return std::move(*this); } - BasicSmallString clone() const + BasicSmallString clone() const noexcept { BasicSmallString clonedString(m_data); @@ -200,26 +198,13 @@ public: return QByteArray(data(), int(size())); } - QString toQString() const - { - return QString::fromUtf8(data(), int(size())); - } + QString toQString() const noexcept { return QString::fromUtf8(data(), int(size())); } - SmallStringView toStringView() const - { - return SmallStringView(data(), size()); - } + SmallStringView toStringView() const noexcept { return SmallStringView(data(), size()); } - operator SmallStringView() const - { - return SmallStringView(data(), size()); - } + operator SmallStringView() const noexcept { return SmallStringView(data(), size()); } - explicit - operator QString() const - { - return toQString(); - } + explicit operator QString() const noexcept { return toQString(); } explicit operator std::string() const @@ -227,13 +212,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())) { @@ -246,7 +230,7 @@ public: } } - void resize(size_type newSize) + void resize(size_type newSize) noexcept { reserve(newSize); setSize(newSize); @@ -318,7 +302,7 @@ public: return end(); } - static BasicSmallString fromQString(const QString &qString) + static BasicSmallString fromQString(const QString &qString) noexcept { BasicSmallString string; string.append(qString); @@ -326,7 +310,7 @@ public: return string; } - static BasicSmallString fromQStringView(QStringView qStringView) + static BasicSmallString fromQStringView(QStringView qStringView) noexcept { BasicSmallString string; string.append(qStringView); @@ -334,18 +318,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); @@ -426,7 +410,7 @@ 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(); @@ -436,7 +420,7 @@ public: setSize(newSize); } - void append(QStringView string) + void append(QStringView string) noexcept { QStringEncoder encoder{QStringEncoder::Utf8}; @@ -468,28 +452,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); @@ -499,14 +483,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(); @@ -524,7 +508,7 @@ public: setSize(newSize); } - BasicSmallString toCarriageReturnsStripped() const + BasicSmallString toCarriageReturnsStripped() const noexcept { BasicSmallString text = *this; @@ -547,10 +531,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) @@ -580,8 +566,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 @@ -595,23 +580,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()); @@ -622,7 +598,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()); @@ -633,7 +609,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()); @@ -645,14 +621,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); } @@ -682,8 +660,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; @@ -692,7 +669,7 @@ unittest_public: return (cacheLineBlocks + 1) * cacheLineSize; } - size_type countOccurrence(SmallStringView text) + size_type countOccurrence(SmallStringView text) noexcept { auto found = begin(); @@ -719,7 +696,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(); @@ -738,7 +716,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; @@ -746,17 +724,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()); @@ -779,7 +751,7 @@ private: } } - void replaceSmallerSized(SmallStringView fromText, SmallStringView toText) + void replaceSmallerSized(SmallStringView fromText, SmallStringView toText) noexcept { auto found = std::search(begin(), end(), @@ -823,7 +795,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(), @@ -860,7 +832,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; @@ -876,7 +848,7 @@ private: } } - void setSize(size_type size) + void setSize(size_type size) noexcept { if (isShortString()) m_data.control.setShortStringSize(size); @@ -918,8 +890,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()); @@ -931,8 +902,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 0e278c61861..705bdce4221 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -31,45 +31,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; @@ -132,7 +116,7 @@ struct alignas(16) StringDataLayout return MaximumShortStringDataAreaSize; } - constexpr void reset() + constexpr void reset() noexcept { control = ControlBlock(); #if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L @@ -217,7 +201,7 @@ struct alignas(16) StringDataLayout(); #if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L diff --git a/src/libs/utils/smallstringliteral.h b/src/libs/utils/smallstringliteral.h index b2e17105336..3ca6420c379 100644 --- a/src/libs/utils/smallstringliteral.h +++ b/src/libs/utils/smallstringliteral.h @@ -20,11 +20,10 @@ public: using const_reverse_iterator = std::reverse_iterator; using size_type = std::size_t; - constexpr BasicSmallStringLiteral() = default; + 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!"); @@ -82,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 From bf5490f325cf23112463bf85d0b9caf9117b973b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 20 Aug 2023 20:27:39 +0200 Subject: [PATCH 095/266] Utils: Use long small string optimization for move constructor Change-Id: I3531c4302c7b73a006618bb8b1b3968ecc581239 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstringlayout.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 705bdce4221..6efcad449bb 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -184,14 +184,19 @@ struct alignas(16) StringDataLayout); 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; } From 55b9d156fd91b3e8cc1a2149a6907eba6c3093d9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 21 Aug 2023 15:44:06 +0200 Subject: [PATCH 096/266] QmlDesigner: Cleanup code Auto is here much more secure because it prevent an accidental creation of a shared pointer from a raw pointer. Change-Id: I16abb623fbfaf2d4e205a302e5bbd667efa1bb2c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/model/model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 05737629ad4..11bafc8c0e7 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -415,7 +415,7 @@ void ModelPrivate::removeNode(const InternalNodePointer &node) notifyNodeAboutToBeRemoved(node); - InternalNodeAbstractPropertyPointer oldParentProperty(node->parentProperty()); + auto oldParentProperty = node->parentProperty(); removeAllSubNodes(node); removeNodeFromModel(node); @@ -1220,7 +1220,7 @@ void ModelPrivate::removePropertyWithoutNotification(InternalProperty *property) for (const InternalNodePointer &node : allSubNodes) removeNodeFromModel(node); } else if (auto nodeProperty = property->to()) { - removeNodeFromModel({nodeProperty->node()}); + removeNodeFromModel(nodeProperty->node()); } auto propertyOwner = property->propertyOwner(); From 91d2c2e0197e6f7237b74efb7e93741419bfc6ae Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 21 Aug 2023 16:51:55 +0200 Subject: [PATCH 097/266] QmlDesigner: Don't remove already removed node We call for the removal of the parent property of an already removed node. So we test here if the node is already removed. Change-Id: Ica0aab76e6bbd1bfb364ead6096d818ce2e32aeb Reviewed-by: Tim Jenssen Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/designercore/model/model.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 11bafc8c0e7..79556bc1715 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1220,7 +1220,8 @@ void ModelPrivate::removePropertyWithoutNotification(InternalProperty *property) for (const InternalNodePointer &node : allSubNodes) removeNodeFromModel(node); } else if (auto nodeProperty = property->to()) { - removeNodeFromModel(nodeProperty->node()); + if (auto node = nodeProperty->node()) + removeNodeFromModel(node); } auto propertyOwner = property->propertyOwner(); From 23525fcde033ad73d1154e3e85f6078bfe8a9eb5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Aug 2023 14:34:10 +0200 Subject: [PATCH 098/266] QmlDesigner: Use QMLDESIGNER_EXPORT instead for QMLDESIGNERCORE_EXPORT for non core QMLDESIGNERCORE_EXPORT should be only used for core exports. It is still a little bit messy and that is why it worked but in the long run we want to clean it up. Change-Id: I20e898344731fa7b36226b4c3418b336fb98dabb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../componentcore/modelnodeoperations.h | 9 ++++-- .../connectioneditorstatements.h | 28 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) 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/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h index c76a494014e..1596ce2bfe6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace QmlDesigner { @@ -102,22 +102,22 @@ struct ConditionalStatement MatchedCondition condition; }; -QMLDESIGNERCORE_EXPORT bool isEmptyStatement(const MatchedStatement &stat); -QMLDESIGNERCORE_EXPORT QString toString(const ComparativeStatement &stat); -QMLDESIGNERCORE_EXPORT QString toString(const RightHandSide &rhs); -QMLDESIGNERCORE_EXPORT QString toString(const Literal &literal); -QMLDESIGNERCORE_EXPORT QString toString(const MatchedStatement &statement); +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); -QMLDESIGNERCORE_EXPORT bool isConsoleLog(const MatchedStatement &curState); -QMLDESIGNERCORE_EXPORT bool isLiteralType(const RightHandSide &var); +QMLDESIGNER_EXPORT bool isConsoleLog(const MatchedStatement &curState); +QMLDESIGNER_EXPORT bool isLiteralType(const RightHandSide &var); -QMLDESIGNERCORE_EXPORT QString toString(const Handler &handler); -QMLDESIGNERCORE_EXPORT QString toJavascript(const Handler &handler); -QMLDESIGNERCORE_EXPORT QString toDisplayName(const MatchedStatement &statement); -QMLDESIGNERCORE_EXPORT QString toDisplayName(const Handler &handler); +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); -QMLDESIGNERCORE_EXPORT MatchedStatement okStatement(const ConnectionEditorStatements::Handler &handler); -QMLDESIGNERCORE_EXPORT MatchedStatement koStatement(const ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement okStatement(const ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement koStatement(const ConnectionEditorStatements::Handler &handler); } // namespace ConnectionEditorStatements From 28a288d1ac5b425cfd9a7000250699f5c1dd3f14 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Aug 2023 01:28:51 +0200 Subject: [PATCH 099/266] QmlDesigner: Use QStringView directly We don't need to convert to QString. Removed some ';' too. Change-Id: I30923f73427062d70e28a5431f9a81f202e4b6a8 Reviewed-by: Tim Jenssen Reviewed-by: Ali Kianian Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../connectioneditorevaluator.cpp | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index 0a52deb7af5..e77e270304f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -55,7 +55,7 @@ inline ConditionToken operator2ConditionToken(const int &op) return ConditionToken::Not; default: return ConditionToken::Unknown; - }; + } } inline bool isStatementNode(const int &kind) @@ -111,7 +111,7 @@ public: bool operator!=(Kind kind) const { return m_kind != kind; } - int increaseChildNo() { return m_children++; }; + int increaseChildNo() { return m_children++; } bool isStatementNode() const { return m_isStatementNode; } @@ -255,7 +255,7 @@ protected: { checkValidityAndReturn(false, "Recursion depth problem"); qDebug() << Q_FUNC_INFO << this; - }; + } void checkAndResetVariable() { @@ -478,7 +478,7 @@ protected: { setFailed(); qDebug() << Q_FUNC_INFO << this; - }; + } void checkAndResetCal() { @@ -520,8 +520,7 @@ MatchedStatement checkForStateSet(const MatchedStatement ¤tState) using namespace ConnectionEditorStatements; return std::visit( Overload{[](const PropertySet &propertySet) -> MatchedStatement { - if (propertySet.lhs.nodeId.size() - && propertySet.lhs.propertyName.compare("state") == 0 + if (propertySet.lhs.nodeId.size() && propertySet.lhs.propertyName == u"state" && std::holds_alternative(propertySet.rhs)) return StateSet{propertySet.lhs.nodeId, ConnectionEditorStatements::toString(propertySet.rhs)}; @@ -539,7 +538,7 @@ public: ConnectionEditorStatements::ConsoleLog expression() { return ConnectionEditorStatements::ConsoleLog{m_arg}; - }; + } protected: bool preVisit(QmlJS::AST::Node *node) override @@ -578,8 +577,8 @@ protected: if (m_completed) return true; - const QString identifierName = identifier->name.toString(); - if (identifierName.compare("console") != 0) { + const QStringView identifierName = identifier->name; + if (identifierName != u"console") { m_failed = true; return false; } @@ -591,8 +590,8 @@ protected: if (m_completed) return true; - const QString fieldName = fieldExpression->name.toString(); - if (fieldName.compare("log") != 0) { + const QStringView fieldName = fieldExpression->name; + if (fieldName != u"log") { m_failed = true; return false; } @@ -619,7 +618,7 @@ protected: { m_failed = true; qDebug() << Q_FUNC_INFO << this; - }; + } private: bool m_failed = false; @@ -970,9 +969,8 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpres bool ConnectionEditorEvaluator::visit(QmlJS::AST::FieldMemberExpression *fieldExpression) { - if (d->parentNodeStatus() == Kind::Kind_CallExpression) - if (fieldExpression->name.toString().compare("log") == 0) - d->m_consoleLogCount++; + if (d->parentNodeStatus() == Kind::Kind_CallExpression && fieldExpression->name == u"log") + d->m_consoleLogCount++; d->addVariableCondition(fieldExpression); @@ -1056,7 +1054,7 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::FieldMemberExpression *fiel if (status() != UnFinished) return; - if (fieldExpression->name.toString().compare("log") == 0) { + if (fieldExpression->name == u"log") { if (d->m_consoleIdentifierCount != d->m_consoleLogCount) { d->m_acceptLogArgument = false; } else { From 76a798992aad321e390aee0ac6b7baaddb33047c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Aug 2023 15:53:04 +0200 Subject: [PATCH 100/266] UnitTests: Merge MockListModelEditorView into AbstractViewMock It was an AbstractView mock too. Change-Id: Ida971a3468df1b14b1f8bd900817ff9fe74e0767 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- tests/unit/tests/mocks/CMakeLists.txt | 1 - tests/unit/tests/mocks/abstractviewmock.h | 42 ++++++++++++++ .../tests/mocks/mocklistmodeleditorview.h | 56 ------------------- .../listmodeleditor/listmodeleditor-test.cpp | 6 +- .../unit/tests/unittests/model/model-test.cpp | 4 +- .../model/modelresourcemanagement-test.cpp | 4 +- 6 files changed, 49 insertions(+), 64 deletions(-) delete mode 100644 tests/unit/tests/mocks/mocklistmodeleditorview.h 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/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/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/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; From 3d77155ceaa93af4d19bbc6e0e1f03061bab4201 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 17 Aug 2023 12:15:11 +0300 Subject: [PATCH 101/266] QmlDesigner: Implement snapping to increment When ctrl is pressed during move gizmo drag, the move is snapped to configurable increment. If the drag axes are aligned to the global axes, the snapping can be done either to absolute grid or increment to start position, depending on snap options selected. If drag axes are not aligned, then drag is simply snapped to configured increment length along the drag vector regardless of configuration. All snapping settings are persistent and shared by all 3D scenes. Fixes: QDS-10463 Change-Id: I9407e558aae0ad8b72d975fb6dfa15a4e78f6ee8 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qtcreator/qmldesigner/designericons.json | 3 + .../SnapConfigurationDialog.qml | 104 ++++++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 3 +- ...ytogglesmenu.cpp => edit3dtoolbarmenu.cpp} | 14 +- ...ilitytogglesmenu.h => edit3dtoolbarmenu.h} | 6 +- .../components/edit3d/edit3dview.cpp | 113 ++++++++++++++- .../components/edit3d/edit3dview.h | 12 +- .../components/edit3d/edit3dviewconfig.h | 20 ++- .../components/edit3d/edit3dwidget.cpp | 33 ++++- .../components/edit3d/edit3dwidget.h | 3 + .../components/edit3d/snapconfiguration.cpp | 136 ++++++++++++++++++ .../components/edit3d/snapconfiguration.h | 57 ++++++++ .../qmldesigner/qmldesignerconstants.h | 4 + .../utils/designersettings.cpp | 3 + .../qmldesignerbase/utils/designersettings.h | 3 + src/tools/qml2puppet/mockfiles/qt6/Arrow.qml | 6 + .../qml2puppet/mockfiles/qt6/MoveGizmo.qml | 21 +++ .../mockfiles/qt6/PlanarMoveHandle.qml | 6 + .../qml2puppet/editor3d/generalhelper.cpp | 114 +++++++++++++++ .../qml2puppet/editor3d/generalhelper.h | 13 ++ .../qt5informationnodeinstanceserver.cpp | 19 +++ .../qt5informationnodeinstanceserver.h | 1 + 22 files changed, 672 insertions(+), 22 deletions(-) create mode 100644 share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml rename src/plugins/qmldesigner/components/edit3d/{edit3dvisibilitytogglesmenu.cpp => edit3dtoolbarmenu.cpp} (54%) rename src/plugins/qmldesigner/components/edit3d/{edit3dvisibilitytogglesmenu.h => edit3dtoolbarmenu.h} (63%) create mode 100644 src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/snapconfiguration.h diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 1e70226ff00..bad8531177b 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -258,6 +258,9 @@ "ScaleToolIcon": { "iconName": "scale_medium" }, + "SnappingIcon": { + "iconName": "snapping_small" + }, "ToggleGroupIcon": { "Off": { "iconName": "selectOutline_medium" diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml new file mode 100644 index 00000000000..a4903997e77 --- /dev/null +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -0,0 +1,104 @@ +// 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 QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property int toolTipDelay: 1000 + + color: StudioTheme.Values.themePanelBackground + + Column { + id: col + padding: 8 + spacing: 4 + + Rectangle { + id: ctrlRect + width: root.width - 16 + height: posIntValue.height + 16 + + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + Row { + x: 8 + y: 8 + width: posIntLabel.width + posIntValue.width + StudioTheme.Values.sectionRowSpacing + spacing: StudioTheme.Values.sectionRowSpacing + + Text { + id: posIntLabel + text: qsTr("Position Snap Interval:") + color: enabled ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + height: posIntValue.height + } + + StudioControls.RealSpinBox { + id: posIntValue + realFrom: 1 + realTo: 100000 + realValue: rootView.posInt + realStepSize: 1 + width: ctrlRect.width - 24 - posIntLabel.width + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap interval for move gizmo.") + ToolTip.delay: root.toolTipDelay + + onRealValueChanged: rootView.posInt = realValue + } + } + } + + Item { + id: spacer + width: 1 + height: Math.max(root.height - buttons.height - ctrlRect.height - 16, 2) + } + + Item { + id: buttons + height: cancelButton.height + 8 + width: ctrlRect.width + + Row { + spacing: StudioTheme.Values.dialogButtonSpacing + height: cancelButton.height + anchors.right: parent.right + + HelperWidgets.Button { + id: cancelButton + text: qsTr("Cancel") + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding + onClicked: rootView.cancel() + } + + HelperWidgets.Button { + id: applyButton + text: qsTr("Ok") + leftPadding: StudioTheme.Values.dialogButtonPadding + rightPadding: StudioTheme.Values.dialogButtonPadding + onClicked: { + rootView.apply() + rootView.cancel() + } + } + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 10ec06fa11c..08b9647cdf5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -597,9 +597,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 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 106890f78e7..5acaac9da88 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -21,6 +21,7 @@ #include "qmldesignerplugin.h" #include "qmlvisualnode.h" #include "seekerslider.h" +#include "snapconfiguration.h" #include @@ -200,6 +201,11 @@ void Edit3DView::modelAttached(Model *model) { AbstractView::modelAttached(model); + rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); + rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); + rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, + Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL)); + checkImports(); auto cachedImage = m_canvasCache.take(model); if (cachedImage) { @@ -297,6 +303,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()); @@ -860,6 +869,90 @@ void Edit3DView::createEdit3DActions() this, bakeLightsTrigger); + SelectionContextOperation snapMenuTrigger = [this](const SelectionContext &) { + if (!edit3DWidget()->snapMenu()) + return; + + QPoint pos; + const auto &actionWidgets = m_snapMenuAction->action()->associatedWidgets(); + for (auto actionWidget : actionWidgets) { + if (auto button = qobject_cast(actionWidget)) { + pos = button->mapToGlobal(QPoint(0, 0)); + break; + } + } + + edit3DWidget()->showSnapMenu(!edit3DWidget()->snapMenu()->isVisible(), pos); + }; + + m_snapMenuAction + = new Edit3DAction(QmlDesigner::Constants::EDIT3D_SNAP_MENU, + View3DActionType::Empty, + QCoreApplication::translate("Snapping", + "Snapping"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::SnappingIcon), + this, + snapMenuTrigger); + + SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { + QPoint pos; + pos = m_edit3DWidget->mapToGlobal(QPoint(m_edit3DWidget->width() / 2, + m_edit3DWidget->height() / 2)); + if (!m_snapConfiguration) + m_snapConfiguration = new SnapConfiguration(this); + m_snapConfiguration->showConfigDialog(pos); + }; + + m_snapConfigAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_SNAP_CONFIG, + View3DActionType::Empty, + QCoreApplication::translate("SnapConfigAction", "Snap Configuration"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::SnappingIcon), + this, + snapConfigTrigger, + QCoreApplication::translate("SnapConfigAction", "Open snap configuration dialog.")); + + SelectionContextOperation snapPositionTrigger = [this](const SelectionContext &) { + if (model()) + rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); + }; + + + m_snapPositionAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_SNAP_POSITION, + View3DActionType::Empty, + QCoreApplication::translate("SnapPositionAction", "Snap Position"), + QKeySequence(Qt::SHIFT | Qt::Key_Tab), + true, + Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_POSITION), false).toBool(), + {}, + this, + snapPositionTrigger, + QCoreApplication::translate("SnapPositionAction", "Toggle position snapping during node drag.")); + + SelectionContextOperation snapAbsoluteTrigger = [this](const SelectionContext &) { + if (model()) + rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); + }; + + m_snapAbsoluteAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE, + View3DActionType::Empty, + QCoreApplication::translate("SnapAbsoluteAction", "Absolute Snap"), + QKeySequence(Qt::SHIFT | Qt::Key_W), + true, + Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE), true).toBool(), + {}, + this, + snapAbsoluteTrigger, + QCoreApplication::translate("SnapAbsoluteAction", "If enabled, snapping uses scene origin as origin point.\nOtherwise snapping uses drag start point as origin point.")); + m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -877,8 +970,8 @@ void Edit3DView::createEdit3DActions() m_leftActions << m_alignViewAction; m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction; - m_leftActions << nullptr; m_leftActions << m_backgrondColorMenuAction; + m_leftActions << m_snapMenuAction; m_rightActions << m_particleViewModeAction; m_rightActions << m_particlesPlayAction; @@ -900,6 +993,10 @@ void Edit3DView::createEdit3DActions() m_backgroundColorActions << createGridColorSelectionAction(); m_backgroundColorActions << syncBackgroundColorAction; m_backgroundColorActions << createResetColorAction(syncBackgroundColorAction->action()); + + m_snapActions << m_snapConfigAction; + m_snapActions << m_snapPositionAction; + m_snapActions << m_snapAbsoluteAction; } QVector Edit3DView::leftActions() const @@ -922,6 +1019,11 @@ QVector Edit3DView::backgroundColorActions() const return m_backgroundColorActions; } +QVector Edit3DView::snapActions() const +{ + return m_snapActions; +} + Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const { return m_edit3DActions.value(type, nullptr).data(); @@ -1003,4 +1105,13 @@ bool Edit3DView::isBakingLightsSupported() const return m_isBakingLightsSupported; } +const char *Edit3DView::settingKeyForAction(const QByteArray &actionId) +{ + if (actionId == Constants::EDIT3D_SNAP_POSITION) + return DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION; + if (actionId == Constants::EDIT3D_SNAP_ABSOLUTE) + return DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE; + return ""; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 6e6b82948e5..6f55581f036 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -23,9 +23,10 @@ QT_END_NAMESPACE namespace QmlDesigner { class BakeLights; -class Edit3DWidget; class Edit3DAction; class Edit3DBakeLightsAction; +class Edit3DWidget; +class SnapConfiguration; class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView { @@ -62,6 +63,7 @@ public: QVector rightActions() const; QVector visibilityToggleActions() const; QVector backgroundColorActions() const; + QVector snapActions() const; Edit3DAction *edit3DAction(View3DActionType type) const; Edit3DBakeLightsAction *bakeLightsAction() const; @@ -76,6 +78,8 @@ public: bool isBakingLightsSupported() const; + const char *settingKeyForAction(const QByteArray &actionId); + private slots: void onEntriesChanged(); @@ -110,6 +114,7 @@ private: QVector m_rightActions; QVector m_visibilityToggleActions; QVector m_backgroundColorActions; + QVector m_snapActions; QMap> m_edit3DActions; Edit3DAction *m_selectionModeAction = nullptr; @@ -133,6 +138,10 @@ private: Edit3DAction *m_particlesRestartAction = nullptr; Edit3DAction *m_visibilityTogglesAction = nullptr; Edit3DAction *m_backgrondColorMenuAction = nullptr; + Edit3DAction *m_snapMenuAction = nullptr; + Edit3DAction *m_snapConfigAction = nullptr; + Edit3DAction *m_snapPositionAction = nullptr; + Edit3DAction *m_snapAbsoluteAction = nullptr; Edit3DAction *m_seekerAction = nullptr; Edit3DBakeLightsAction *m_bakeLightsAction = nullptr; int particlemode; @@ -145,6 +154,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..b077fed6a46 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h @@ -28,6 +28,16 @@ public: }); } + static QVariant load(const QByteArray &key, const QVariant &defaultValue = {}) + { + 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, View3DActionType type, const QList &colorConfig) { setVariant(view, type, QVariant::fromValue(colorConfig)); @@ -45,21 +55,17 @@ 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, View3DActionType type, const QVariant &value) { - view->emitView3DAction(type, colorConfig); + view->emitView3DAction(type, value); } - static void saveVariant(const QByteArray &key, const QVariant &colorConfig) - { - QmlDesignerPlugin::settings().insert(key, colorConfig); - } }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index ddf1bff6d29..ee5800641d4 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" @@ -157,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); @@ -165,6 +166,19 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); + m_snapMenu = new Edit3DToolbarMenu(this); + handleActions(view->snapActions(), m_snapMenu, false); + connect(m_snapMenu, &QMenu::aboutToHide, this, [view]() { + // Persist the checkable settings of the menu + const auto actions = view->snapActions(); + for (auto &action : actions) { + if (action->action()->isCheckable()) { + Edit3DViewConfig::save(view->settingKeyForAction(action->menuId()), + action->action()->isChecked()); + } + } + }); + createContextMenu(); m_mcuLabel = new QLabel(this); @@ -453,6 +467,21 @@ void Edit3DWidget::showVisibilityTogglesMenu(bool show, const QPoint &pos) m_visibilityTogglesMenu->close(); } +QMenu *Edit3DWidget::snapMenu() const +{ + return m_snapMenu.data(); +} + +void Edit3DWidget::showSnapMenu(bool show, const QPoint &pos) +{ + if (m_snapMenu.isNull()) + return; + if (show) + m_snapMenu->popup(pos); + else + m_snapMenu->close(); +} + QMenu *Edit3DWidget::backgroundColorMenu() const { return m_backgroundColorMenu.data(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index f764f068bf4..1e60c75f88a 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -45,6 +45,8 @@ public: void showCanvas(bool show); QMenu *visibilityTogglesMenu() const; void showVisibilityTogglesMenu(bool show, const QPoint &pos); + QMenu *snapMenu() const; + void showSnapMenu(bool show, const QPoint &pos); QMenu *backgroundColorMenu() const; void showBackgroundColorMenu(bool show, const QPoint &pos); @@ -76,6 +78,7 @@ private: QPointer m_visibilityTogglesMenu; QPointer m_backgroundColorMenu; QPointer m_contextMenu; + QPointer m_snapMenu; QPointer m_bakeLightsAction; QPointer m_editComponentAction; QPointer m_editMaterialAction; diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp new file mode 100644 index 00000000000..0e3d90d007d --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -0,0 +1,136 @@ +// 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 "abstractview.h" +#include "designersettings.h" +#include "edit3dviewconfig.h" +#include "modelnode.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(AbstractView *view) + : QObject(view) + , m_view(view) +{ +} + +SnapConfiguration::~SnapConfiguration() +{ + cleanup(); +} + +void SnapConfiguration::apply() +{ + Edit3DViewConfig::save(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, + m_positionInterval); + m_view->rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, m_positionInterval); +} + +void SnapConfiguration::showConfigDialog(const QPoint &pos) +{ + double posInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, 10.).toDouble(); + setPosInt(posInt); + + if (!m_configDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/SnapConfigurationDialog.qml"; + + m_configDialog = new QQuickView; + m_configDialog->setTitle(tr("3D Snap Configuration")); + m_configDialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_configDialog->setFlags(Qt::Dialog); + m_configDialog->setModality(Qt::ApplicationModal); + m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_configDialog->setMinimumSize({250, 100}); + + 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->size().height() / 2); + m_configDialog->setPosition(finalPos); + } + + m_configDialog->show(); +} + +void SnapConfiguration::setPosInt(double value) +{ + if (value != m_positionInterval) { + m_positionInterval = value; + emit posIntChanged(); + } +} + +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) +{ + if (obj == m_configDialog) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) + cancel(); + if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + // Apply asynchronously to allow the final value to be set by dialog before apply + QTimer::singleShot(0, this, [this]() { + apply(); + cancel(); + }); + } + } else if (event->type() == QEvent::Close) { + cancel(); + } + } + + 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..792d38ecb6e --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -0,0 +1,57 @@ +// 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 AbstractView; + +inline constexpr AuxiliaryDataKeyView edit3dSnapPosProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapPos3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapPosIntProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapPosInt3d"}; +inline constexpr AuxiliaryDataKeyView edit3dSnapAbsProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "snapAbs3d"}; + +class SnapConfiguration : public QObject +{ + Q_OBJECT + Q_PROPERTY(double posInt READ posInt WRITE setPosInt NOTIFY posIntChanged) + +public: + SnapConfiguration(AbstractView *view); + ~SnapConfiguration(); + + Q_INVOKABLE void cancel(); + Q_INVOKABLE void apply(); + + void showConfigDialog(const QPoint &pos); + + void setPosInt(double value); + double posInt() const { return m_positionInterval; } + +signals: + void posIntChanged(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void cleanup(); + + QPointer m_configDialog; + QPointer m_view; + double m_positionInterval = 11.; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 660c9b2d5ca..bbc66b75fe8 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -64,6 +64,10 @@ 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_MENU[] = "QmlDesigner.Editor3D.SnapMenu"; +const char EDIT3D_SNAP_POSITION[] = "QmlDesigner.Editor3D.SnapPosition"; +const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; +const char EDIT3D_SNAP_ABSOLUTE[] = "QmlDesigner.Editor3D.SnapToGrid"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index 7071cfffbd1..32f1c0f87bb 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -83,6 +83,9 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QStringList{"#222222", "#999999"}); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa"); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, false); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_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..758715c0fc6 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -32,6 +32,9 @@ 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_POSITION[] = "Edit3DViewSnapPosition"; +inline constexpr char EDIT3DVIEW_SNAP_POSITION_INTERVAL[] = "Edit3DViewSnapPositionInterval"; 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/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/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/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index b057b6e1f96..dfccf96a724 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 @@ -770,6 +771,119 @@ bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const return m_rotationBlockedNodes.contains(node); } +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 || increment == 0. || snapAxes.isNull() || qFuzzyIsNull((newPos - startPos).length())) + return newPos; + + // 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 && !snapPos) || (ctrlMod && snapPos)) + return newPos; + + if (shiftMod) + increment *= 0.1; + + // 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; +} + void GeneralHelper::handlePendingToolStateUpdate() { m_toolStateUpdateTimer.stop(); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 089ebf14919..5313e730d25 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -101,6 +101,15 @@ 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); + void setSnapPosition(bool enable) { m_snapPosition = enable; } + void setSnapAbsolute(bool enable) { m_snapAbsolute = enable; } + void setSnapPositionInterval(double interval) { m_snapPositionInterval = interval; } + signals: void overlayUpdateNeeded(); void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); @@ -134,6 +143,10 @@ private: QQuick3DNode *m_multiSelectRootNode = nullptr; QList m_multiSelectConnections; bool m_blockMultiSelectionNodePositioning = false; + + bool m_snapAbsolute = true; + bool m_snapPosition = false; + double m_snapPositionInterval = 10.; }; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 0492eb56ec5..867e79ffe04 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -368,6 +368,23 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks( #endif } +void Qt5InformationNodeInstanceServer::updateSnapSettings(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() == "snapAbs3d") + helper->setSnapAbsolute(container.value().toBool()); + else if (container.name() == "snapPosInt3d") + helper->setSnapPositionInterval(container.value().toDouble()); + } + } +#endif +} + void Qt5InformationNodeInstanceServer::removeRotationBlocks( [[maybe_unused]] const QVector &instanceIds) { @@ -2113,6 +2130,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com setup3DEditView(instanceList, command); updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); + updateSnapSettings(command.auxiliaryChanges); } QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, @@ -2553,6 +2571,7 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia { updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); + updateSnapSettings(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 8d02b586c20..3f0e68f854d 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -125,6 +125,7 @@ private: void resolveImportSupport(); void updateMaterialPreviewData(const QVector &valueChanges); void updateRotationBlocks(const QVector &valueChanges); + void updateSnapSettings(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); void getNodeAtPos(const QPointF &pos); From ef7f3224878469ff9d1ae86a541fbc551f6eec48 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 24 Aug 2023 13:41:55 +0300 Subject: [PATCH 102/266] QmlDesigner: Set a sensible initial size for various views Fixes: QDS-10493 Change-Id: Idfb49daa60e73a5ee12da0d418d5122b1b5f6e62 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../materialEditorQmlSources/EmptyMaterialEditorPane.qml | 3 +++ .../materialEditorQmlSources/MaterialEditorPane.qml | 3 +++ .../textureEditorQmlSource/EmptyTextureEditorPane.qml | 3 +++ .../qmldesigner/textureEditorQmlSource/TextureEditorPane.qml | 3 +++ .../components/contentlibrary/contentlibrarywidget.cpp | 5 +++++ .../components/contentlibrary/contentlibrarywidget.h | 2 ++ .../qmldesigner/components/effectmaker/effectmakerwidget.cpp | 5 +++++ .../qmldesigner/components/effectmaker/effectmakerwidget.h | 2 ++ .../components/materialbrowser/materialbrowserwidget.cpp | 5 +++++ .../components/materialbrowser/materialbrowserwidget.h | 2 ++ 10 files changed, 33 insertions(+) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index 355f71f7591..c8b2ab7ef6c 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml @@ -10,6 +10,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..0816d01ec4c 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -8,6 +8,9 @@ import HelperWidgets 2.0 PropertyEditorPane { id: itemPane + width: 420 + height: 420 + signal toolBarAction(int action) signal previewEnvChanged(string env) signal previewModelChanged(string model) diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml index 9623232146b..70fa841461a 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml @@ -10,6 +10,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..2098eda5165 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml @@ -8,6 +8,9 @@ import HelperWidgets 2.0 PropertyEditorPane { id: itemPane + width: 420 + height: 420 + signal toolBarAction(int action) // invoked from C++ to refresh material preview image diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 1b5bdb8c768..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 {}; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index a5baa66dd1c..5e41f289141 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -71,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); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index 02fe24e344c..25df13603b9 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -109,6 +109,11 @@ void EffectMakerWidget::focusSection(int section) Q_UNUSED(section) } +QSize EffectMakerWidget::sizeHint() const +{ + return {420, 420}; +} + QString EffectMakerWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h index e42edd04400..74780425aef 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -38,6 +38,8 @@ public: 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; 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(); From 144ec097b1c0b0ae34fa2e465c07854ea7f725f1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 24 Aug 2023 11:12:22 +0300 Subject: [PATCH 103/266] QmlDesigner: Properly delete 3D view actions Make all actions unique pointers and members of the 3D view, so they will get deleted once view gets deleted. Change-Id: I20d576480a2e553d9164a51e72f9f35d9715180e Reviewed-by: Marco Bubke Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/edit3dview.cpp | 552 +++++++++--------- .../components/edit3d/edit3dview.h | 76 +-- 2 files changed, 324 insertions(+), 304 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 5acaac9da88..a3f33120f52 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -9,7 +9,6 @@ #include "designericons.h" #include "designersettings.h" #include "designmodecontext.h" -#include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dviewconfig.h" #include "edit3dwidget.h" @@ -234,7 +233,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() @@ -415,7 +414,7 @@ QSize Edit3DView::canvasSize() const return {}; } -Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction) +void Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction) { QString description = QCoreApplication::translate("SelectBackgroundColorAction", "Select Background Color"); @@ -436,19 +435,20 @@ Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgro }); }; - 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::SelectBackgroundColor, + 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", @@ -462,19 +462,20 @@ Edit3DAction *Edit3DView::createGridColorSelectionAction() View3DActionType::SelectGridColor); }; - 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::SelectGridColor, + 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", @@ -496,19 +497,20 @@ Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAct } }; - 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::ResetBackgroundColor, + description, + QKeySequence(), + false, + false, + QIcon(), + this, + operation, + tooltip); } -Edit3DAction *Edit3DView::createSyncBackgroundColorAction() +void Edit3DView::createSyncBackgroundColorAction() { QString description = QCoreApplication::translate("SyncEdit3DColorAction", "Use Scene Environment Color"); @@ -516,111 +518,116 @@ 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; } 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(DesignerIcons::ToggleGroupIcon), - 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(DesignerIcons::MoveToolIcon), - 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(DesignerIcons::RotateToolIcon), - 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(DesignerIcons::ScaleToolIcon), - 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(DesignerIcons::FitToViewIcon), - 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(QmlDesigner::Constants::EDIT3D_ALIGN_CAMERAS, - View3DActionType::AlignCamerasToView, - QCoreApplication::translate("AlignCamerasToViewAction", - "Align Cameras to View"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::AlignCameraToViewIcon), - this); + m_alignCamerasAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_ALIGN_CAMERAS, + View3DActionType::AlignCamerasToView, + QCoreApplication::translate("AlignCamerasToViewAction", + "Align Cameras to View"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::AlignCameraToViewIcon), + this); - m_alignViewAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ALIGN_VIEW, - View3DActionType::AlignViewToCamera, - QCoreApplication::translate("AlignViewToCameraAction", - "Align View to Camera"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::AlignViewToCameraIcon), - this); + m_alignViewAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_ALIGN_VIEW, + View3DActionType::AlignViewToCamera, + QCoreApplication::translate("AlignViewToCameraAction", + "Align View to Camera"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::AlignViewToCameraIcon), + this); - m_cameraModeAction = new Edit3DAction( + m_cameraModeAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_CAMERA, View3DActionType::CameraToggle, QCoreApplication::translate("CameraToggleAction", @@ -631,74 +638,75 @@ void Edit3DView::createEdit3DActions() toolbarIcon(DesignerIcons::CameraIcon), this); - m_orientationModeAction - = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ORIENTATION, - View3DActionType::OrientationToggle, - QCoreApplication::translate("OrientationToggleAction", - "Toggle Global/Local Orientation"), - QKeySequence(Qt::Key_Y), - true, - false, - toolbarIcon(DesignerIcons::LocalOrientIcon), - this); + m_orientationModeAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_ORIENTATION, + View3DActionType::OrientationToggle, + QCoreApplication::translate("OrientationToggleAction", + "Toggle Global/Local Orientation"), + QKeySequence(Qt::Key_Y), + true, + false, + 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(DesignerIcons::EditLightIcon), - 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( @@ -706,7 +714,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", @@ -714,7 +722,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_M), true, false, - {}, + QIcon(), this, nullptr, QCoreApplication::translate( @@ -758,50 +766,55 @@ 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(DesignerIcons::ParticlesAnimationIcon), - 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(DesignerIcons::ParticlesPlayIcon), - 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(DesignerIcons::ParticlesRestartIcon), - 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(DesignerIcons::ResetViewIcon), - 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()) @@ -820,17 +833,17 @@ void Edit3DView::createEdit3DActions() pos); }; - m_visibilityTogglesAction - = new Edit3DAction(QmlDesigner::Constants::EDIT3D_VISIBILITY_TOGGLES, - View3DActionType::Empty, - QCoreApplication::translate("VisibilityTogglesAction", - "Visibility Toggles"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::VisibilityIcon), - this, - visibilityTogglesTrigger); + m_visibilityTogglesAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_VISIBILITY_TOGGLES, + View3DActionType::Empty, + QCoreApplication::translate("VisibilityTogglesAction", + "Visibility Toggles"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::VisibilityIcon), + this, + visibilityTogglesTrigger); SelectionContextOperation backgroundColorActionsTrigger = [this](const SelectionContext &) { if (!edit3DWidget()->backgroundColorMenu()) @@ -849,25 +862,24 @@ void Edit3DView::createEdit3DActions() pos); }; - m_backgrondColorMenuAction - = new Edit3DAction(QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, - View3DActionType::Empty, - QCoreApplication::translate("BackgroundColorMenuActions", - "Background Color Actions"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::EditColorIcon), - this, - backgroundColorActionsTrigger); + m_backgrondColorMenuAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, + View3DActionType::Empty, + QCoreApplication::translate("BackgroundColorMenuActions", + "Background Color Actions"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::EditColorIcon), + this, + backgroundColorActionsTrigger); - m_seekerAction = createSeekerSliderAction(); + createSeekerSliderAction(); - m_bakeLightsAction - = new Edit3DBakeLightsAction(toolbarIcon( - DesignerIcons::EditLightIcon), //: TODO placeholder icon - this, - bakeLightsTrigger); + m_bakeLightsAction = std::make_unique( + toolbarIcon(DesignerIcons::EditLightIcon), //: TODO placeholder icon + this, + bakeLightsTrigger); SelectionContextOperation snapMenuTrigger = [this](const SelectionContext &) { if (!edit3DWidget()->snapMenu()) @@ -885,17 +897,16 @@ void Edit3DView::createEdit3DActions() edit3DWidget()->showSnapMenu(!edit3DWidget()->snapMenu()->isVisible(), pos); }; - m_snapMenuAction - = new Edit3DAction(QmlDesigner::Constants::EDIT3D_SNAP_MENU, - View3DActionType::Empty, - QCoreApplication::translate("Snapping", - "Snapping"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::SnappingIcon), - this, - snapMenuTrigger); + m_snapMenuAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SNAP_MENU, + View3DActionType::Empty, + QCoreApplication::translate("Snapping", "Snapping"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::SnappingIcon), + this, + snapMenuTrigger); SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { QPoint pos; @@ -906,7 +917,7 @@ void Edit3DView::createEdit3DActions() m_snapConfiguration->showConfigDialog(pos); }; - m_snapConfigAction = new Edit3DAction( + m_snapConfigAction = std::make_unique( QmlDesigner::Constants::EDIT3D_SNAP_CONFIG, View3DActionType::Empty, QCoreApplication::translate("SnapConfigAction", "Snap Configuration"), @@ -923,15 +934,14 @@ void Edit3DView::createEdit3DActions() rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); }; - - m_snapPositionAction = new Edit3DAction( + m_snapPositionAction = std::make_unique( QmlDesigner::Constants::EDIT3D_SNAP_POSITION, View3DActionType::Empty, QCoreApplication::translate("SnapPositionAction", "Snap Position"), QKeySequence(Qt::SHIFT | Qt::Key_Tab), true, Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_POSITION), false).toBool(), - {}, + QIcon(), this, snapPositionTrigger, QCoreApplication::translate("SnapPositionAction", "Toggle position snapping during node drag.")); @@ -941,62 +951,66 @@ void Edit3DView::createEdit3DActions() rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); }; - m_snapAbsoluteAction = new Edit3DAction( + m_snapAbsoluteAction = std::make_unique( QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE, View3DActionType::Empty, QCoreApplication::translate("SnapAbsoluteAction", "Absolute Snap"), QKeySequence(Qt::SHIFT | Qt::Key_W), true, Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE), true).toBool(), - {}, + QIcon(), this, snapAbsoluteTrigger, QCoreApplication::translate("SnapAbsoluteAction", "If enabled, snapping uses scene origin as origin point.\nOtherwise snapping uses drag start point as origin point.")); - m_leftActions << m_selectionModeAction; + 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_alignCamerasAction.get(); + m_leftActions << m_alignViewAction.get(); m_leftActions << nullptr; - m_leftActions << m_visibilityTogglesAction; - m_leftActions << m_backgrondColorMenuAction; - m_leftActions << m_snapMenuAction; + m_leftActions << m_visibilityTogglesAction.get(); + m_leftActions << m_backgrondColorMenuAction.get(); + m_leftActions << m_snapMenuAction.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_snapActions << m_snapConfigAction; - m_snapActions << m_snapPositionAction; - m_snapActions << m_snapAbsoluteAction; + m_backgroundColorActions << m_selectBackgroundColorAction.get(); + m_backgroundColorActions << m_selectGridColorAction.get(); + m_backgroundColorActions << m_syncBackgroundColorAction.get(); + m_backgroundColorActions << m_resetColorAction.get(); + + m_snapActions << m_snapConfigAction.get(); + m_snapActions << m_snapPositionAction.get(); + m_snapActions << m_snapAbsoluteAction.get(); } QVector Edit3DView::leftActions() const @@ -1026,12 +1040,12 @@ QVector Edit3DView::snapActions() const 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 6f55581f036..20967af87d0 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 @@ -23,8 +24,6 @@ QT_END_NAMESPACE namespace QmlDesigner { class BakeLights; -class Edit3DAction; -class Edit3DBakeLightsAction; class Edit3DWidget; class SnapConfiguration; @@ -103,11 +102,11 @@ 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(); QPointer m_edit3DWidget; QVector m_leftActions; @@ -116,34 +115,41 @@ private: QVector m_backgroundColorActions; QVector m_snapActions; - 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_snapMenuAction = nullptr; - Edit3DAction *m_snapConfigAction = nullptr; - Edit3DAction *m_snapPositionAction = nullptr; - Edit3DAction *m_snapAbsoluteAction = 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_backgrondColorMenuAction; + std::unique_ptr m_snapMenuAction; + std::unique_ptr m_snapConfigAction; + std::unique_ptr m_snapPositionAction; + std::unique_ptr m_snapAbsoluteAction; + std::unique_ptr m_bakeLightsAction; + int particlemode; ModelCache m_canvasCache; ModelNode m_droppedModelNode; From c496434ad8f1b05cee68e700d2952bb0adbfbddb Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 24 Aug 2023 16:16:14 +0200 Subject: [PATCH 104/266] QmlDesigner: Fix for SpinBoxes decimals updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10514 Change-Id: I958106ecdb1124483d76fe069e1000c53b562906 Reviewed-by: Henning Gründl --- .../imports/StudioControls/RealSpinBox.qml | 1 + .../propertyEditorQmlSources/imports/StudioControls/SpinBox.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 2e16d46c17c..19496da346d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -307,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/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml index a0c7244c79c..fcf2b5c58dd 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml @@ -274,6 +274,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) { From 29a207c59fb43d4d055a84d5ec334d8e8124902c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 24 Aug 2023 15:21:52 +0300 Subject: [PATCH 105/266] QmlDesigner: Add effect maker composition node color value Since no StudioControls ColorEditor exists, using the HelperWidgets one and adding dummy context and backend to get it to work. Task-number: QDS-10404 Change-Id: Ifc1506b4b1f761b6abf4144791f5b0397a90cdf0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNodeUniform.qml | 6 +- .../effectMakerQmlSources/ValueColor.qml | 9 +- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/coloreditorcontextobject.cpp | 185 ++++++++++++++++++ .../effectmaker/coloreditorcontextobject.h | 101 ++++++++++ .../effectmaker/effectmakeruniformsmodel.cpp | 1 + .../effectmaker/effectmakeruniformsmodel.h | 1 + .../effectmaker/effectmakerwidget.cpp | 6 + .../components/effectmaker/uniform.cpp | 10 + .../components/effectmaker/uniform.h | 7 + 10 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 249cb763d86..baa8b01f928 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -24,10 +24,10 @@ Item { valueLoader.source = "ValueVec4.qml" else if (uniformType === "bool") valueLoader.source = "ValueBool.qml" -// else if (uniformType === "color") // TODO -// valueLoader.sourceComponent = colorValue + else if (uniformType === "color") + valueLoader.source = "ValueColor.qml" // else if (uniformType === "image") // TODO -// valueLoader.sourceComponent = imageValue +// valueLoader.source = valueImage else valueLoader.source = "ValueFloat.qml" } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml index 5c920bdacc2..d8e679b41fc 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml @@ -4,13 +4,18 @@ import QtQuick import QtQuickDesignerTheme import HelperWidgets as HelperWidgets -import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend Row { + id: itemPane + width: parent.width spacing: 5 - // TODO + HelperWidgets.ColorEditor { + backendValue: uniformBackendValue + + onValueChanged: uniformValue = convertColorToString(color) + } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 08b9647cdf5..84cda5bddb2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -720,6 +720,7 @@ extend_qtc_plugin(QmlDesigner compositionnode.cpp compositionnode.h uniform.cpp uniform.h effectutils.cpp effectutils.h + coloreditorcontextobject.cpp coloreditorcontextobject.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp b/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp new file mode 100644 index 00000000000..a5d89ae286a --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.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 "coloreditorcontextobject.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +ColorEditorContextObject::ColorEditorContextObject(QQmlContext *context, QObject *parent) + : QObject(parent) + , m_qmlContext(context) +{ +} + +QString ColorEditorContextObject::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 ColorEditorContextObject::colorFromString(const QString &colorString) +{ + return colorString; +} + +int ColorEditorContextObject::majorVersion() const +{ + return m_majorVersion; +} + +void ColorEditorContextObject::setMajorVersion(int majorVersion) +{ + if (m_majorVersion == majorVersion) + return; + + m_majorVersion = majorVersion; + + emit majorVersionChanged(); +} + +void ColorEditorContextObject::setStateName(const QString &newStateName) +{ + if (newStateName == m_stateName) + return; + + m_stateName = newStateName; + emit stateNameChanged(); +} + +void ColorEditorContextObject::setAllStateNames(const QStringList &allStates) +{ + if (allStates == m_allStateNames) + return; + + m_allStateNames = allStates; + emit allStateNamesChanged(); +} + +void ColorEditorContextObject::setIsBaseState(bool newIsBaseState) +{ + if (newIsBaseState == m_isBaseState) + return; + + m_isBaseState = newIsBaseState; + emit isBaseStateChanged(); +} + +void ColorEditorContextObject::setSelectionChanged(bool newSelectionChanged) +{ + if (newSelectionChanged == m_selectionChanged) + return; + + m_selectionChanged = newSelectionChanged; + emit selectionChangedChanged(); +} + +void ColorEditorContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) +{ + if (newBackendValues == m_backendValues) + return; + + m_backendValues = newBackendValues; + emit backendValuesChanged(); +} + +void ColorEditorContextObject::setModel(Model *model) +{ + m_model = model; +} + +void ColorEditorContextObject::triggerSelectionChanged() +{ + setSelectionChanged(!m_selectionChanged); +} + +void ColorEditorContextObject::hideCursor() +{ + if (QApplication::overrideCursor()) + return; + + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWidget *w = QApplication::activeWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void ColorEditorContextObject::restoreCursor() +{ + if (!QApplication::overrideCursor()) + return; + + QApplication::restoreOverrideCursor(); + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void ColorEditorContextObject::holdCursorInPlace() +{ + if (!QApplication::overrideCursor()) + return; + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int ColorEditorContextObject::devicePixelRatio() +{ + if (QWidget *w = QApplication::activeWindow()) + return w->devicePixelRatio(); + + return 1; +} + +QStringList ColorEditorContextObject::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 ColorEditorContextObject::isBlocked(const QString &) const +{ + return false; +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h b/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h new file mode 100644 index 00000000000..cb3b11bb73f --- /dev/null +++ b/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.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 ColorEditorContextObject : 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: + ColorEditorContextObject(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/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp index b1bdc12331a..8bd3926ef1c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -20,6 +20,7 @@ QHash EffectMakerUniformsModel::roleNames() const roles[NameRole] = "uniformName"; roles[DescriptionRole] = "uniformDescription"; roles[ValueRole] = "uniformValue"; + roles[BackendValueRole] = "uniformBackendValue"; roles[DefaultValueRole] = "uniformDefaultValue"; roles[MinValueRole] = "uniformMinValue"; roles[MaxValueRole] = "uniformMaxValue"; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h index a5f2ae6eea4..ecaa8752d65 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -30,6 +30,7 @@ private: NameRole = Qt::UserRole + 1, DescriptionRole, ValueRole, + BackendValueRole, DefaultValueRole, MaxValueRole, MinValueRole, diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index 25df13603b9..c592b787019 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -3,11 +3,13 @@ #include "effectmakerwidget.h" +#include "coloreditorcontextobject.h" #include "effectmakermodel.h" #include "effectmakernodesmodel.h" #include "effectmakerview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "qqmlcontext.h" #include "theme.h" #include @@ -65,6 +67,10 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, {"rootView", QVariant::fromValue(this)}}); + auto colorEditorCtx = new ColorEditorContextObject(m_quickWidget->rootContext()); + m_quickWidget->rootContext()->setContextObject(colorEditorCtx); + m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", {}); + // init the first load of the QML UI elements reloadQmlSource(); } diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index 1c0ba83aed0..1288dec8f75 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -3,6 +3,8 @@ #include "uniform.h" +#include "propertyeditorvalue.h" + #include #include #include @@ -38,6 +40,9 @@ Uniform::Uniform(const QJsonObject &propObj) maxValue = propObj.value("maxValue").toString(); setValueData(value, defaultValue, minValue, maxValue); + + m_backendValue = new PropertyEditorValue(this); + m_backendValue->setValue(value); } QString Uniform::type() const @@ -70,6 +75,11 @@ 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) { diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 321ba8a7d9c..4e11dbc2cfa 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -12,6 +12,8 @@ QT_FORWARD_DECLARE_CLASS(QVector2D) namespace QmlDesigner { +class PropertyEditorValue; + class Uniform : public QObject { Q_OBJECT @@ -19,6 +21,7 @@ class Uniform : public QObject Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) Q_PROPERTY(QString uniformType READ type 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) @@ -43,6 +46,8 @@ public: QVariant value() const; void setValue(const QVariant &newValue); + QVariant backendValue() const; + QVariant defaultValue() const; QVariant minValue() const; @@ -62,6 +67,7 @@ public: signals: void uniformValueChanged(); + void uniformBackendValueChanged(); private: QString mipmapPropertyName(const QString &name) const; @@ -85,6 +91,7 @@ private: bool m_useCustomValue = false; bool m_enabled = true; bool m_enableMipmap = false; + PropertyEditorValue *m_backendValue = nullptr; bool operator==(const Uniform &rhs) const noexcept { From c71ebda677925068997fbb6d5ab46583040a95db Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Thu, 24 Aug 2023 17:09:29 +0200 Subject: [PATCH 106/266] QmlProject: Fix incorrect conversion of MCU property Task-number: QDS-10376 Change-Id: Ic16d03355419281a0bf5c8bd04ba5fea317a70d4 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmlprojectmanager/buildsystem/projectitem/converters.cpp | 2 +- .../data/converter/test-set-1/testfile.qmlproject | 2 ++ .../data/converter/test-set-1/testfile.qmltojson | 1 + .../data/converter/test-set-2/testfile.jsontoqml | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 7f01e33f435..8487c00cd7e 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/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: "" From 8537d96270d89cee29453f437e36f4eea76eb902 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 21 Aug 2023 10:22:21 +0300 Subject: [PATCH 107/266] Doc: Update Git documentation Task-number: QDS-10080 Change-Id: I4122232049620294672cfd0f5a0d7c9a48e27633 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- doc/qtcreator/src/vcs/creator-vcs-git.qdoc | 26 ++++++++++------------ 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index 8d65462a2bf..cc59f24c917 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -25,10 +25,7 @@ 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 @@ -47,6 +44,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 @@ -182,7 +189,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. @@ -464,15 +471,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 > From 185ce525612d3944bb7866de3d32f1287fd6e71f Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Mon, 26 Jun 2023 15:57:45 +0200 Subject: [PATCH 108/266] Disable unsupported properties if qtForMCUs is set When qtForMCUs is enable in the project, disables the action to open the easing curve editor in the timeline and the actions to set an interpolation to anything else than linear in the animation curve editor. Shows the disabled reason in the tooltip and paint non linear curve segments in the error color and without handles. Fixes: QDS-10061 Change-Id: I71b2af0dd6f6c10714fceb75ff3ac16c88504adf Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/curveeditor/animationcurve.cpp | 2 +- .../components/curveeditor/curveeditor.cpp | 9 +++++ .../components/curveeditor/curveeditor.h | 1 + .../curveeditor/curveeditortoolbar.cpp | 38 ++++++++++++++----- .../curveeditor/curveeditortoolbar.h | 9 +++++ .../components/curveeditor/curvesegment.cpp | 9 ++++- .../components/curveeditor/curvesegment.h | 2 + .../curveeditor/detail/curveitem.cpp | 19 +++++++--- .../components/curveeditor/detail/curveitem.h | 12 ++++-- .../curveeditor/detail/graphicsscene.cpp | 31 +++++++++++---- .../curveeditor/detail/graphicsscene.h | 16 +++++--- .../curveeditor/detail/graphicsview.cpp | 5 +++ .../curveeditor/detail/graphicsview.h | 2 + .../timelineeditor/timelinetoolbar.cpp | 21 +++++++--- .../timelineeditor/timelinetoolbar.h | 4 ++ .../timelineeditor/timelinewidget.cpp | 2 + 16 files changed, 142 insertions(+), 40 deletions(-) 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 5127f206042..d41a25c43bb 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp @@ -189,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/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 3bccd598b41..f6e9a3ad27c 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -478,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); } From f1bf33289ecd3667f55599db2b25d22beddd1446 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 25 Aug 2023 16:27:01 +0300 Subject: [PATCH 109/266] QmlDesigner: Add effect maker composition node image value Change-Id: I67b88a6085e4cf6c6fcb4a9e7f78420ab126dff6 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectCompositionNodeUniform.qml | 4 +- .../effectMakerQmlSources/ValueImage.qml | 9 ++++- src/plugins/qmldesigner/CMakeLists.txt | 2 +- ...bject.cpp => effectmakercontextobject.cpp} | 38 +++++++++---------- ...extobject.h => effectmakercontextobject.h} | 4 +- .../effectmaker/effectmakernodesmodel.cpp | 2 +- .../effectmaker/effectmakerview.cpp | 1 + .../effectmaker/effectmakerwidget.cpp | 22 +++++++---- .../effectmaker/effectmakerwidget.h | 4 ++ 9 files changed, 51 insertions(+), 35 deletions(-) rename src/plugins/qmldesigner/components/effectmaker/{coloreditorcontextobject.cpp => effectmakercontextobject.cpp} (75%) rename src/plugins/qmldesigner/components/effectmaker/{coloreditorcontextobject.h => effectmakercontextobject.h} (96%) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index baa8b01f928..0f0d05c38c1 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -26,8 +26,8 @@ Item { valueLoader.source = "ValueBool.qml" else if (uniformType === "color") valueLoader.source = "ValueColor.qml" -// else if (uniformType === "image") // TODO -// valueLoader.source = valueImage + else if (uniformType === "image") + valueLoader.source = "ValueImage.qml" else valueLoader.source = "ValueFloat.qml" } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml index 5c920bdacc2..05bcdfa6943 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -4,13 +4,18 @@ import QtQuick import QtQuickDesignerTheme import HelperWidgets as HelperWidgets -import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend Row { + id: itemPane + width: parent.width spacing: 5 - // TODO + HelperWidgets.UrlChooser { + backendValue: uniformBackendValue + + onAbsoluteFilePathChanged: uniformValue = absoluteFilePath + } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 84cda5bddb2..8939fa7f174 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -720,7 +720,7 @@ extend_qtc_plugin(QmlDesigner compositionnode.cpp compositionnode.h uniform.cpp uniform.h effectutils.cpp effectutils.h - coloreditorcontextobject.cpp coloreditorcontextobject.h + effectmakercontextobject.cpp effectmakercontextobject.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp similarity index 75% rename from src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp rename to src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp index a5d89ae286a..7ee8399c2cf 100644 --- a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp @@ -1,7 +1,7 @@ // 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 "coloreditorcontextobject.h" +#include "effectmakercontextobject.h" #include #include @@ -29,13 +29,13 @@ namespace QmlDesigner { -ColorEditorContextObject::ColorEditorContextObject(QQmlContext *context, QObject *parent) +EffectMakerContextObject::EffectMakerContextObject(QQmlContext *context, QObject *parent) : QObject(parent) , m_qmlContext(context) { } -QString ColorEditorContextObject::convertColorToString(const QVariant &color) +QString EffectMakerContextObject::convertColorToString(const QVariant &color) { QString colorString; QColor theColor; @@ -52,17 +52,17 @@ QString ColorEditorContextObject::convertColorToString(const QVariant &color) } // TODO: this method is used by the ColorEditor helper widget, check if at all needed? -QColor ColorEditorContextObject::colorFromString(const QString &colorString) +QColor EffectMakerContextObject::colorFromString(const QString &colorString) { return colorString; } -int ColorEditorContextObject::majorVersion() const +int EffectMakerContextObject::majorVersion() const { return m_majorVersion; } -void ColorEditorContextObject::setMajorVersion(int majorVersion) +void EffectMakerContextObject::setMajorVersion(int majorVersion) { if (m_majorVersion == majorVersion) return; @@ -72,7 +72,7 @@ void ColorEditorContextObject::setMajorVersion(int majorVersion) emit majorVersionChanged(); } -void ColorEditorContextObject::setStateName(const QString &newStateName) +void EffectMakerContextObject::setStateName(const QString &newStateName) { if (newStateName == m_stateName) return; @@ -81,7 +81,7 @@ void ColorEditorContextObject::setStateName(const QString &newStateName) emit stateNameChanged(); } -void ColorEditorContextObject::setAllStateNames(const QStringList &allStates) +void EffectMakerContextObject::setAllStateNames(const QStringList &allStates) { if (allStates == m_allStateNames) return; @@ -90,7 +90,7 @@ void ColorEditorContextObject::setAllStateNames(const QStringList &allStates) emit allStateNamesChanged(); } -void ColorEditorContextObject::setIsBaseState(bool newIsBaseState) +void EffectMakerContextObject::setIsBaseState(bool newIsBaseState) { if (newIsBaseState == m_isBaseState) return; @@ -99,7 +99,7 @@ void ColorEditorContextObject::setIsBaseState(bool newIsBaseState) emit isBaseStateChanged(); } -void ColorEditorContextObject::setSelectionChanged(bool newSelectionChanged) +void EffectMakerContextObject::setSelectionChanged(bool newSelectionChanged) { if (newSelectionChanged == m_selectionChanged) return; @@ -108,7 +108,7 @@ void ColorEditorContextObject::setSelectionChanged(bool newSelectionChanged) emit selectionChangedChanged(); } -void ColorEditorContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) +void EffectMakerContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) { if (newBackendValues == m_backendValues) return; @@ -117,17 +117,17 @@ void ColorEditorContextObject::setBackendValues(QQmlPropertyMap *newBackendValue emit backendValuesChanged(); } -void ColorEditorContextObject::setModel(Model *model) +void EffectMakerContextObject::setModel(Model *model) { m_model = model; } -void ColorEditorContextObject::triggerSelectionChanged() +void EffectMakerContextObject::triggerSelectionChanged() { setSelectionChanged(!m_selectionChanged); } -void ColorEditorContextObject::hideCursor() +void EffectMakerContextObject::hideCursor() { if (QApplication::overrideCursor()) return; @@ -138,7 +138,7 @@ void ColorEditorContextObject::hideCursor() m_lastPos = QCursor::pos(w->screen()); } -void ColorEditorContextObject::restoreCursor() +void EffectMakerContextObject::restoreCursor() { if (!QApplication::overrideCursor()) return; @@ -149,7 +149,7 @@ void ColorEditorContextObject::restoreCursor() QCursor::setPos(w->screen(), m_lastPos); } -void ColorEditorContextObject::holdCursorInPlace() +void EffectMakerContextObject::holdCursorInPlace() { if (!QApplication::overrideCursor()) return; @@ -158,7 +158,7 @@ void ColorEditorContextObject::holdCursorInPlace() QCursor::setPos(w->screen(), m_lastPos); } -int ColorEditorContextObject::devicePixelRatio() +int EffectMakerContextObject::devicePixelRatio() { if (QWidget *w = QApplication::activeWindow()) return w->devicePixelRatio(); @@ -166,7 +166,7 @@ int ColorEditorContextObject::devicePixelRatio() return 1; } -QStringList ColorEditorContextObject::allStatesForId(const QString &id) +QStringList EffectMakerContextObject::allStatesForId(const QString &id) { if (m_model && m_model->rewriterView()) { const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); @@ -177,7 +177,7 @@ QStringList ColorEditorContextObject::allStatesForId(const QString &id) return {}; } -bool ColorEditorContextObject::isBlocked(const QString &) const +bool EffectMakerContextObject::isBlocked(const QString &) const { return false; } diff --git a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h similarity index 96% rename from src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h rename to src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h index cb3b11bb73f..e27957f4ece 100644 --- a/src/plugins/qmldesigner/components/effectmaker/coloreditorcontextobject.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h @@ -16,7 +16,7 @@ namespace QmlDesigner { -class ColorEditorContextObject : public QObject +class EffectMakerContextObject : public QObject { Q_OBJECT @@ -32,7 +32,7 @@ class ColorEditorContextObject : public QObject Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) public: - ColorEditorContextObject(QQmlContext *context, QObject *parent = nullptr); + EffectMakerContextObject(QQmlContext *context, QObject *parent = nullptr); QString stateName() const { return m_stateName; } QStringList allStateNames() const { return m_allStateNames; } diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp index 420c3f741bf..521e3e7ce21 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp @@ -77,7 +77,7 @@ void EffectMakerNodesModel::loadModel() while (itCategories.hasNext()) { itCategories.next(); - if (itCategories.fileName() == "images") + if (itCategories.fileName() == "images" || itCategories.fileName() == "common") continue; QString catName = itCategories.fileName(); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp index 8ac65ed4558..641b41a8ce2 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp @@ -58,6 +58,7 @@ void EffectMakerView::modelAttached(Model *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) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp index c592b787019..f6f96bc886c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp @@ -3,7 +3,7 @@ #include "effectmakerwidget.h" -#include "coloreditorcontextobject.h" +#include "effectmakercontextobject.h" #include "effectmakermodel.h" #include "effectmakernodesmodel.h" #include "effectmakerview.h" @@ -66,15 +66,9 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, {"rootView", QVariant::fromValue(this)}}); - - auto colorEditorCtx = new ColorEditorContextObject(m_quickWidget->rootContext()); - m_quickWidget->rootContext()->setContextObject(colorEditorCtx); - m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", {}); - - // init the first load of the QML UI elements - reloadQmlSource(); } + bool EffectMakerWidget::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj) @@ -129,6 +123,18 @@ QString EffectMakerWidget::qmlSourcesPath() 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"; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h index 74780425aef..d59318eb459 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h @@ -3,6 +3,8 @@ #pragma once +#include "qmlmodelnodeproxy.h" + #include #include @@ -30,6 +32,7 @@ public: void delayedUpdateModel(); void updateModel(); + void initView(); StudioQuickWidget *quickWidget() const; QPointer effectMakerModel() const; @@ -50,6 +53,7 @@ private: QPointer m_effectMakerNodesModel; QPointer m_effectMakerView; QPointer m_quickWidget; + QmlModelNodeProxy m_backendModelNode; }; } // namespace QmlDesigner From bf0511af2f2e6ebae149decd93c6c76234d70632 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 24 Aug 2023 17:04:06 +0200 Subject: [PATCH 110/266] QmlDesigner: Fix typo in StudioTheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0108b740186324b4724bada976849afd4d245a6f Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../imports/HelperWidgets/ColorEditorPopup.qml | 2 +- .../propertyEditorQmlSources/imports/StudioTheme/Values.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 7a53153c6d8..aa902e7bd1b 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" diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 03a1d078fcc..092c6f2a724 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -215,7 +215,7 @@ QtObject { property real hueSliderHeight: 20 property real hueSliderHandleWidth: 10 - property real colorEditorPopupCmoboBoxWidth: 110 + property real colorEditorPopupComboBoxWidth: 110 property real colorEditorPopupSpinBoxWidth: 54 // Popup Window From 1fe4685dce6254a501776cae4a134f6b204ba679 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 28 Aug 2023 15:02:54 +0300 Subject: [PATCH 111/266] QmlDesigner: Add effect maker composition node int value Also remove HelperWidger import from value components that don't use it Task-number: QDS-10404 Change-Id: I14e4b102f874a73aedd65cc7cc1d5838c73ef960 Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed Reviewed-by: Qt CI Patch Build Bot --- .../EffectCompositionNodeUniform.qml | 4 +- .../effectMakerQmlSources/ValueBool.qml | 1 - .../effectMakerQmlSources/ValueFloat.qml | 1 - .../effectMakerQmlSources/ValueInt.qml | 41 +++++++++++++++++++ .../effectMakerQmlSources/ValueVec2.qml | 1 - .../effectMakerQmlSources/ValueVec3.qml | 1 - .../effectMakerQmlSources/ValueVec4.qml | 1 - .../imports/StudioControls/SpinBox.qml | 1 + 8 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 0f0d05c38c1..79f18db0475 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -16,7 +16,9 @@ Item { height: layout.implicitHeight Component.onCompleted: { - if (uniformType === "vec2") + if (uniformType === "int") + valueLoader.source = "ValueInt.qml" + else if (uniformType === "vec2") valueLoader.source = "ValueVec2.qml" else if (uniformType === "vec3") valueLoader.source = "ValueVec3.qml" diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml index d75df978ae9..201f9976989 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueBool.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml index 577f90f2a11..78e13f23a6b 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml new file mode 100644 index 00000000000..dfcb1ebf234 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml @@ -0,0 +1,41 @@ +// 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: 40 + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + from: uniformMinValue + to: uniformMaxValue + value: uniformValue + onValueModified: uniformValue = value + } + + StudioControls.Slider { + id: slider + + width: parent.width - 80 + labels: false + actionIndicatorVisible: 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 index 939838f7338..4d27d1f8a4c 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml index 4328408bdc4..6902907e767 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml index 40a71014413..ae7d13c89f5 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml @@ -3,7 +3,6 @@ import QtQuick import QtQuickDesignerTheme -import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme 1.0 as StudioTheme import EffectMakerBackend diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml index fcf2b5c58dd..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 From 674d753262779e5f4bb949f1e34a7e9b481426b9 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 28 Aug 2023 15:49:27 +0300 Subject: [PATCH 112/266] QmlDesigner: Add effect maker composition node define value Task-number: QDS-10404 Change-Id: Ib12e9abfc42b30a2a352304b2ec4f977e70038d6 Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNodeUniform.qml | 2 ++ .../effectMakerQmlSources/ValueDefine.qml | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 share/qtcreator/qmldesigner/effectMakerQmlSources/ValueDefine.qml diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 79f18db0475..fbd36ef6cf7 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -30,6 +30,8 @@ Item { 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" } 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 + } +} From b2fff59b8c4f670cd49a76f354b556b6deb82dc9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 26 Aug 2023 02:32:34 +0200 Subject: [PATCH 113/266] QmlDesigner: Timeout thread The thread will be stopped after 10min. And then started if needed. Change-Id: I66f64081353fa4f24c7927139d1676ee8de85679 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../asynchronousexplicitimagecache.cpp | 69 ++++++++++++----- .../imagecache/asynchronousimagecache.cpp | 76 ++++++++++++------- .../imagecache/asynchronousimagefactory.cpp | 68 ++++++++++++----- .../imagecache/asynchronousimagefactory.h | 7 +- .../imagecache/imagecachegenerator.cpp | 57 +++++++++----- .../imagecache/imagecachegenerator.h | 6 +- .../include/asynchronousexplicitimagecache.h | 7 +- .../include/asynchronousimagecache.h | 7 +- .../imagecache/imagecachegenerator-test.cpp | 2 +- 9 files changed, 198 insertions(+), 101 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index 1c46722d4c4..527ef437b53 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -12,20 +12,7 @@ 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() @@ -110,9 +97,10 @@ void AsynchronousExplicitImageCache::clean() clearEntries(); } -std::optional AsynchronousExplicitImageCache::getEntry() +std::optional AsynchronousExplicitImageCache::getEntry( + std::unique_lock lock) { - std::unique_lock lock{m_mutex}; + auto l = std::move(lock); if (m_requestEntries.empty()) return {}; @@ -131,6 +119,8 @@ void AsynchronousExplicitImageCache::addEntry(Utils::PathString &&name, { std::unique_lock lock{m_mutex}; + ensureThreadIsRunning(); + m_requestEntries.emplace_back(std::move(name), std::move(extraId), std::move(captureCallback), @@ -146,11 +136,23 @@ void AsynchronousExplicitImageCache::clearEntries() m_requestEntries.clear(); } -void AsynchronousExplicitImageCache::waitForEntries() +std::tuple, bool> AsynchronousExplicitImageCache::waitForEntries() { + using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_requestEntries.empty()) - m_condition.wait(lock, [&] { return m_requestEntries.size() || m_finishing; }); + if (m_finishing) + return {std::move(lock), true}; + if (m_requestEntries.empty()) { + auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { + return m_requestEntries.size() || m_finishing; + }); + + if (timedOutWithoutEntriesOrFinishing || m_finishing) { + m_sleeping = true; + return {std::move(lock), true}; + } + } + return {std::move(lock), false}; } void AsynchronousExplicitImageCache::stopThread() @@ -159,10 +161,35 @@ void AsynchronousExplicitImageCache::stopThread() m_finishing = true; } -bool AsynchronousExplicitImageCache::isRunning() +void AsynchronousExplicitImageCache::ensureThreadIsRunning() { - std::unique_lock lock{m_mutex}; - return !m_finishing || m_requestEntries.size(); + if (m_finishing) + return; + + if (!m_sleeping) + return; + + if (m_backgroundThread.joinable()) + m_backgroundThread.join(); + + m_sleeping = false; + + m_backgroundThread = std::thread{[this] { + while (true) { + auto [lock, abort] = waitForEntries(); + if (abort) + return; + + if (auto entry = getEntry(std::move(lock)); entry) { + request(entry->name, + entry->extraId, + entry->requestType, + std::move(entry->captureCallback), + std::move(entry->abortCallback), + m_storage); + } + } + }}; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 00b3ab722ef..e40d856e532 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -18,23 +18,6 @@ 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() @@ -169,10 +152,10 @@ void AsynchronousImageCache::clean() m_generator.clean(); } -std::optional AsynchronousImageCache::getEntry() +std::optional AsynchronousImageCache::getEntry( + std::unique_lock lock) { - std::unique_lock lock{m_mutex}; - + auto l = std::move(lock); if (m_entries.empty()) return {}; @@ -191,6 +174,8 @@ void AsynchronousImageCache::addEntry(Utils::PathString &&name, { std::unique_lock lock{m_mutex}; + ensureThreadIsRunning(); + m_entries.emplace_back(std::move(name), std::move(extraId), std::move(captureCallback), @@ -207,11 +192,23 @@ void AsynchronousImageCache::clearEntries() m_entries.clear(); } -void AsynchronousImageCache::waitForEntries() +std::tuple, bool> AsynchronousImageCache::waitForEntries() { + using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_entries.empty()) - m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; }); + if (m_finishing) + return {std::move(lock), true}; + if (m_entries.empty()) { + auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { + return m_entries.size() || m_finishing; + }); + + if (timedOutWithoutEntriesOrFinishing || m_finishing) { + m_sleeping = true; + return {std::move(lock), true}; + } + } + return {std::move(lock), false}; } void AsynchronousImageCache::stopThread() @@ -220,10 +217,37 @@ void AsynchronousImageCache::stopThread() m_finishing = true; } -bool AsynchronousImageCache::isRunning() +void AsynchronousImageCache::ensureThreadIsRunning() { - std::unique_lock lock{m_mutex}; - return !m_finishing || m_entries.size(); + if (m_finishing) + return; + + if (!m_sleeping) + return; + + if (m_backgroundThread.joinable()) + m_backgroundThread.join(); + + m_sleeping = false; + + m_backgroundThread = std::thread{[this] { + while (true) { + auto [lock, abort] = waitForEntries(); + if (abort) + return; + if (auto entry = getEntry(std::move(lock)); 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); + } + } + }}; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 5d6ced30601..03bafd18f43 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -17,20 +17,7 @@ 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() @@ -53,25 +40,64 @@ void AsynchronousImageFactory::addEntry(Utils::SmallStringView name, { std::unique_lock lock{m_mutex}; + ensureThreadIsRunning(); + m_entries.emplace_back(std::move(name), std::move(extraId), std::move(auxiliaryData)); } -bool AsynchronousImageFactory::isRunning() +void AsynchronousImageFactory::ensureThreadIsRunning() { - std::unique_lock lock{m_mutex}; - return !m_finishing || m_entries.size(); + if (m_finishing) + return; + + if (!m_sleeping) + return; + + if (m_backgroundThread.joinable()) + m_backgroundThread.join(); + + m_sleeping = false; + + m_backgroundThread = std::thread{[this] { + while (true) { + auto [lock, abort] = waitForEntries(); + if (abort) + return; + if (auto entry = getEntry(std::move(lock)); entry) { + request(entry->name, + entry->extraId, + std::move(entry->auxiliaryData), + m_storage, + m_timeStampProvider, + m_collector); + } + } + }}; } -void AsynchronousImageFactory::waitForEntries() +std::tuple, bool> AsynchronousImageFactory::waitForEntries() { + using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_entries.empty()) - m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; }); + if (m_finishing) + return {std::move(lock), true}; + if (m_entries.empty()) { + auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { + return m_entries.size() || m_finishing; + }); + + if (timedOutWithoutEntriesOrFinishing || m_finishing) { + m_sleeping = true; + return {std::move(lock), true}; + } + } + return {std::move(lock), false}; } -std::optional AsynchronousImageFactory::getEntry() +std::optional AsynchronousImageFactory::getEntry( + std::unique_lock lock) { - std::unique_lock lock{m_mutex}; + auto l = std::move(lock); if (m_entries.empty()) return {}; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index 415646c73cd..eb39740b8a4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -53,9 +53,9 @@ private: void addEntry(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData); - bool isRunning(); - void waitForEntries(); - std::optional getEntry(); + void ensureThreadIsRunning(); + [[nodiscard]] std::tuple, bool> waitForEntries(); + [[nodiscard]] std::optional getEntry(std::unique_lock lock); void request(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData, @@ -75,6 +75,7 @@ private: TimeStampProviderInterface &m_timeStampProvider; ImageCacheCollectorInterface &m_collector; bool m_finishing{false}; + bool m_sleeping{true}; }; } // namespace QmlDesigner 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/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index f9a053a9413..5fe61f4fe39 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h @@ -62,16 +62,16 @@ private: RequestType requestType = RequestType::Image; }; - std::optional getEntry(); + [[nodiscard]] std::optional getEntry(std::unique_lock lock); void addEntry(Utils::PathString &&name, Utils::SmallString &&extraId, ImageCache::CaptureImageCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback, RequestType requestType); void clearEntries(); - void waitForEntries(); + [[nodiscard]] std::tuple, bool> waitForEntries(); void stopThread(); - bool isRunning(); + void ensureThreadIsRunning(); static void request(Utils::SmallStringView name, Utils::SmallStringView extraId, AsynchronousExplicitImageCache::RequestType requestType, @@ -89,6 +89,7 @@ private: std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; bool m_finishing{false}; + bool m_sleeping{true}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index 3257f9d75c1..6435cc7eb43 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -73,7 +73,7 @@ private: RequestType requestType = RequestType::Image; }; - std::optional getEntry(); + [[nodiscard]] std::optional getEntry(std::unique_lock lock); void addEntry(Utils::PathString &&name, Utils::SmallString &&extraId, ImageCache::CaptureImageCallback &&captureCallback, @@ -81,9 +81,9 @@ private: ImageCache::AuxiliaryData &&auxiliaryData, RequestType requestType); void clearEntries(); - void waitForEntries(); + [[nodiscard]] std::tuple, bool> waitForEntries(); void stopThread(); - bool isRunning(); + void ensureThreadIsRunning(); static void request(Utils::SmallStringView name, Utils::SmallStringView extraId, AsynchronousImageCache::RequestType requestType, @@ -106,6 +106,7 @@ private: ImageCacheGeneratorInterface &m_generator; TimeStampProviderInterface &m_timeStampProvider; bool m_finishing{false}; + bool m_sleeping{true}; }; } // namespace QmlDesigner 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(); From 9462c7a3e90883abc0bcee9b3c9a8d4e80c5b15d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 26 Aug 2023 16:56:13 +0200 Subject: [PATCH 114/266] QmlDesigner: Add task queue To share the code TaskQueue is introduced. The Thread and the synchronization code is moved into TaskQueue. Only a dispatch and cean must be implemented in the callback. Change-Id: I19c85c891ef700aae85b54630714555e862200a4 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../asynchronousexplicitimagecache.cpp | 128 ++------------- .../imagecache/asynchronousimagecache.cpp | 150 +++--------------- .../imagecache/asynchronousimagefactory.cpp | 110 +------------ .../imagecache/asynchronousimagefactory.h | 53 ++++--- .../designercore/imagecache/taskqueue.h | 133 ++++++++++++++++ .../include/asynchronousexplicitimagecache.h | 39 +++-- .../include/asynchronousimagecache.h | 47 +++--- .../tests/testdesignercore/CMakeLists.txt | 1 + .../tests/unittests/imagecache/CMakeLists.txt | 1 + .../unittests/imagecache/taskqueue-test.cpp | 103 ++++++++++++ 11 files changed, 365 insertions(+), 401 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/imagecache/taskqueue.h create mode 100644 tests/unit/tests/unittests/imagecache/taskqueue-test.cpp diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 8939fa7f174..689c6bce9b0 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -176,6 +176,7 @@ extend_qtc_library(QmlDesignerCore meshimagecachecollector.cpp meshimagecachecollector.h synchronousimagecache.cpp + taskqueue.h textureimagecachecollector.cpp textureimagecachecollector.h timestampprovider.cpp diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index 527ef437b53..70c0bced0e3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -17,8 +17,6 @@ AsynchronousExplicitImageCache::AsynchronousExplicitImageCache(ImageCacheStorage AsynchronousExplicitImageCache::~AsynchronousExplicitImageCache() { - clean(); - wait(); } void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, @@ -57,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, @@ -79,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, @@ -88,108 +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) -{ - auto l = std::move(lock); - - 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}; - - ensureThreadIsRunning(); - - 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(); -} - -std::tuple, bool> AsynchronousExplicitImageCache::waitForEntries() -{ - using namespace std::literals::chrono_literals; - std::unique_lock lock{m_mutex}; - if (m_finishing) - return {std::move(lock), true}; - if (m_requestEntries.empty()) { - auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { - return m_requestEntries.size() || m_finishing; - }); - - if (timedOutWithoutEntriesOrFinishing || m_finishing) { - m_sleeping = true; - return {std::move(lock), true}; - } - } - return {std::move(lock), false}; -} - -void AsynchronousExplicitImageCache::stopThread() -{ - std::unique_lock lock{m_mutex}; - m_finishing = true; -} - -void AsynchronousExplicitImageCache::ensureThreadIsRunning() -{ - if (m_finishing) - return; - - if (!m_sleeping) - return; - - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); - - m_sleeping = false; - - m_backgroundThread = std::thread{[this] { - while (true) { - auto [lock, abort] = waitForEntries(); - if (abort) - return; - - if (auto entry = getEntry(std::move(lock)); entry) { - request(entry->name, - entry->extraId, - entry->requestType, - std::move(entry->captureCallback), - std::move(entry->abortCallback), - m_storage); - } - } - }}; + m_taskQueue.clean(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index e40d856e532..a18e051c364 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -22,8 +22,7 @@ AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &stora AsynchronousImageCache::~AsynchronousImageCache() { - clean(); - wait(); + m_generator.clean(); } void AsynchronousImageCache::request(Utils::SmallStringView name, @@ -93,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, @@ -122,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, @@ -137,117 +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) -{ - auto l = std::move(lock); - 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}; - - ensureThreadIsRunning(); - - 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(); -} - -std::tuple, bool> AsynchronousImageCache::waitForEntries() -{ - using namespace std::literals::chrono_literals; - std::unique_lock lock{m_mutex}; - if (m_finishing) - return {std::move(lock), true}; - if (m_entries.empty()) { - auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { - return m_entries.size() || m_finishing; - }); - - if (timedOutWithoutEntriesOrFinishing || m_finishing) { - m_sleeping = true; - return {std::move(lock), true}; - } - } - return {std::move(lock), false}; -} - -void AsynchronousImageCache::stopThread() -{ - std::unique_lock lock{m_mutex}; - m_finishing = true; -} - -void AsynchronousImageCache::ensureThreadIsRunning() -{ - if (m_finishing) - return; - - if (!m_sleeping) - return; - - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); - - m_sleeping = false; - - m_backgroundThread = std::thread{[this] { - while (true) { - auto [lock, abort] = waitForEntries(); - if (abort) - return; - if (auto entry = getEntry(std::move(lock)); 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); - } - } - }}; + m_taskQueue.clean(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 03bafd18f43..a711f1ad7d8 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -20,93 +20,14 @@ AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &s } -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}; - - ensureThreadIsRunning(); - - m_entries.emplace_back(std::move(name), std::move(extraId), std::move(auxiliaryData)); -} - -void AsynchronousImageFactory::ensureThreadIsRunning() -{ - if (m_finishing) - return; - - if (!m_sleeping) - return; - - if (m_backgroundThread.joinable()) - m_backgroundThread.join(); - - m_sleeping = false; - - m_backgroundThread = std::thread{[this] { - while (true) { - auto [lock, abort] = waitForEntries(); - if (abort) - return; - if (auto entry = getEntry(std::move(lock)); entry) { - request(entry->name, - entry->extraId, - std::move(entry->auxiliaryData), - m_storage, - m_timeStampProvider, - m_collector); - } - } - }}; -} - -std::tuple, bool> AsynchronousImageFactory::waitForEntries() -{ - using namespace std::literals::chrono_literals; - std::unique_lock lock{m_mutex}; - if (m_finishing) - return {std::move(lock), true}; - if (m_entries.empty()) { - auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { - return m_entries.size() || m_finishing; - }); - - if (timedOutWithoutEntriesOrFinishing || m_finishing) { - m_sleeping = true; - return {std::move(lock), true}; - } - } - return {std::move(lock), false}; -} - -std::optional AsynchronousImageFactory::getEntry( - std::unique_lock lock) -{ - auto l = std::move(lock); - - 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, @@ -125,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, @@ -138,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 eb39740b8a4..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,32 +52,41 @@ private: ImageCache::AuxiliaryData auxiliaryData; }; - void addEntry(Utils::SmallStringView name, - Utils::SmallStringView extraId, - ImageCache::AuxiliaryData &&auxiliaryData); - void ensureThreadIsRunning(); - [[nodiscard]] std::tuple, bool> waitForEntries(); - [[nodiscard]] std::optional getEntry(std::unique_lock lock); - 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}; - bool m_sleeping{true}; + TaskQueue m_taskQueue{Dispatch{m_storage, m_timeStampProvider, m_collector}, + Clean{}}; }; } // 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..e331b2f5716 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -0,0 +1,133 @@ +// 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 +{ +public: + TaskQueue(DispatchCallback dispatchCallback, ClearCallback clearCallback) + : m_dispatchCallback(std::move(dispatchCallback)) + , m_clearCallback(std::move(clearCallback)) + {} + + ~TaskQueue() + { + auto lock = clearTasks(); + stopThread(std::move(lock)); + joinThread(); + } + + 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() { clearTasks(); } + +private: + [[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()) + m_backgroundThread.join(); + + 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); + } + }}; + } + + std::unique_lock clearTasks() + { + std::unique_lock lock{m_mutex}; + for (Task &task : m_tasks) + m_clearCallback(task); + m_tasks.clear(); + + return lock; + } + + void stopThread(std::unique_lock lock) + { + { + auto l = std::move(lock); + m_finishing = true; + } + m_condition.notify_all(); + } + + void joinThread() + { + if (m_backgroundThread.joinable()) + m_backgroundThread.join(); + } + +private: + std::deque 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/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index 5fe61f4fe39..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; }; - [[nodiscard]] std::optional getEntry(std::unique_lock lock); - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - RequestType requestType); - void clearEntries(); - [[nodiscard]] std::tuple, bool> waitForEntries(); - void stopThread(); - void ensureThreadIsRunning(); 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,8 +100,7 @@ private: std::condition_variable m_condition; std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; - bool m_finishing{false}; - bool m_sleeping{true}; + 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 6435cc7eb43..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; }; - [[nodiscard]] std::optional getEntry(std::unique_lock lock); - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, - ImageCache::CaptureImageCallback &&captureCallback, - ImageCache::AbortCallback &&abortCallback, - ImageCache::AuxiliaryData &&auxiliaryData, - RequestType requestType); - void clearEntries(); - [[nodiscard]] std::tuple, bool> waitForEntries(); - void stopThread(); - void ensureThreadIsRunning(); static void request(Utils::SmallStringView name, Utils::SmallStringView extraId, AsynchronousImageCache::RequestType requestType, @@ -94,19 +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}; - bool m_sleeping{true}; + TaskQueue m_taskQueue{Dispatch{m_storage, m_generator, m_timeStampProvider}, + Clean{}}; }; } // namespace QmlDesigner 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/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 From a55efac890e5c23a1cc46ce9c0af91750b0df156 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 27 Aug 2023 16:16:23 +0200 Subject: [PATCH 115/266] Sqlite: Extent Statement::values for other container You can now use QList and QVarLenghtArray for values. The later is quite interesting because it is not allocating for a provided capacity. So you can now write: statement.valuesWithTransaction>(); And in almost all case no expensive memory is allocated. With the help of NRVO there should be no copy too. That is very useful for temporary values which are transformed into a different type. The capacity changed to an template parameter too: statement.valuesWithTransaction(); That is more clear than before: statement.valuesWithTransaction(1024); Change-Id: I6d8d10ed9db0cc37f0579353a416e36e23c30415 Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 35 +++++++++-- src/libs/sqlite/sqlitereadstatement.h | 6 +- src/libs/sqlite/sqlitereadwritestatement.h | 8 ++- src/libs/sqlite/sqlitesessions.cpp | 6 +- .../projectstorage/projectstorage.h | 62 ++++++++++--------- .../projectstorage/projectstorageinterface.h | 6 +- tests/unit/tests/mocks/projectstoragemock.h | 4 +- .../projectstorage/projectstorage-test.cpp | 4 ++ .../sqlite/sqlitealgorithms-test.cpp | 2 +- .../unittests/sqlite/sqlitedatabase-test.cpp | 2 +- .../unittests/sqlite/sqlitesessions-test.cpp | 5 +- .../unittests/sqlite/sqlitestatement-test.cpp | 48 +++++++------- 12 files changed, 114 insertions(+), 74 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 54d08260b7c..fd57b4f8f1c 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; @@ -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) { diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 3df47efc741..4f8091598b0 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -47,11 +47,11 @@ public: }); } - 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 Base::template values(queryValues...); }); } 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/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index f3977ac8c41..b1190874f75 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -160,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 @@ -197,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, @@ -240,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 @@ -280,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 @@ -383,7 +383,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); @@ -441,8 +441,8 @@ public: auto fetchAllSourceContexts() const { - return selectAllSourceContextsStatement.template valuesWithTransaction( - 128); + return selectAllSourceContextsStatement + .template valuesWithTransaction(); } SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) @@ -484,7 +484,7 @@ public: auto fetchAllSources() const { - return selectAllSourcesStatement.template valuesWithTransaction(1024); + return selectAllSourcesStatement.template valuesWithTransaction(); } SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) @@ -517,14 +517,15 @@ 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) @@ -595,7 +596,7 @@ private: auto fetchAllModules() const { - return selectAllModulesStatement.template valuesWithTransaction(128); + return selectAllModulesStatement.template valuesWithTransaction(); } class AliasPropertyDeclaration @@ -728,7 +729,7 @@ private: TypeIds fetchTypeIds(const SourceIds &sourceIds) { - return selectTypeIdsForSourceIdsStatement.template values(128, toIntegers(sourceIds)); + return selectTypeIdsForSourceIdsStatement.template values(toIntegers(sourceIds)); } void unique(SourceIds &sourceIds) @@ -2324,13 +2325,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) @@ -2341,8 +2342,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); @@ -2356,8 +2358,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); @@ -2373,8 +2376,9 @@ private: EnumerationDeclarationId enumerationDeclarationId) { enumerationDeclarations.emplace_back( name, - selectEnumeratorDeclarationStatement.template values< - Storage::Synchronization::EnumeratorDeclaration>(8, enumerationDeclarationId)); + selectEnumeratorDeclarationStatement + .template values( + enumerationDeclarationId)); }; selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index b90b6efbad3..49ae0637936 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -10,6 +10,8 @@ #include +#include + namespace QmlDesigner { class ProjectStorageInterface @@ -42,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; diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 08f2f8e6b9d..61788b46db8 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -140,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)); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 45660302627..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; }); 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(); From 2a301d41c41afb1e1fceda0b1a22fa507c668366 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 27 Aug 2023 16:28:24 +0200 Subject: [PATCH 116/266] Sqlite: Flatten calls inside functions The code is doing quite some template magic. This helps the compiler to be persistent in removing intermediate function calls. So it would look like handwritten code. Change-Id: Idbe152b9256f5ea23d5a4c42ea2cfd117346d3a7 Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 26 +++++++++++++--------- src/libs/sqlite/sqlitereadstatement.h | 10 ++++----- src/libs/sqlite/sqlitereadwritestatement.h | 16 ++++++------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index fd57b4f8f1c..397068477c1 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -27,6 +27,12 @@ using std::int64_t; +#ifdef Q_CC_MSVC +#define FLATTEN [[msvc::flatten]] +#else +#define FLATTEN [[gnu::flatten]] +#endif + namespace Sqlite { class Database; @@ -150,7 +156,7 @@ public: using BaseStatement::BaseStatement; StatementImplementation(StatementImplementation &&) = default; - void execute() + FLATTEN void execute() { Resetter resetter{this}; BaseStatement::next(); @@ -166,7 +172,7 @@ public: } template - void write(const ValueType&... values) + FLATTEN void write(const ValueType &...values) { Resetter resetter{this}; bindValues(values...); @@ -190,7 +196,7 @@ public: std::size_t capacity = 32, typename = std::enable_if_t::value>, typename... QueryTypes> - auto values(const QueryTypes &...queryValues) + FLATTEN auto values(const QueryTypes &...queryValues) { Resetter resetter{this}; Container resultValues; @@ -211,7 +217,7 @@ public: template typename Container = std::vector, typename = std::enable_if_t::value>, typename... QueryTypes> - auto values(const QueryTypes &...queryValues) + FLATTEN auto values(const QueryTypes &...queryValues) { return values, capacity>(queryValues...); } @@ -231,7 +237,7 @@ public: } template - auto optionalValue(const QueryTypes &...queryValues) + FLATTEN auto optionalValue(const QueryTypes &...queryValues) { Resetter resetter{this}; std::optional resultValue; @@ -245,7 +251,7 @@ public: } template - static auto toValue(Utils::SmallStringView sqlStatement, Database &database) + FLATTEN static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { StatementImplementation statement(sqlStatement, database); @@ -257,7 +263,7 @@ public: } template - void readCallback(Callable &&callable, const QueryTypes &...queryValues) + FLATTEN void readCallback(Callable &&callable, const QueryTypes &...queryValues) { Resetter resetter{this}; @@ -272,7 +278,7 @@ public: } template - void readTo(Container &container, const QueryTypes &...queryValues) + FLATTEN void readTo(Container &container, const QueryTypes &...queryValues) { Resetter resetter{this}; @@ -283,13 +289,13 @@ public: } template - auto range(const QueryTypes &...queryValues) + FLATTEN auto range(const QueryTypes &...queryValues) { return SqliteResultRange{*this, queryValues...}; } template - auto rangeWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto rangeWithTransaction(const QueryTypes &...queryValues) { return SqliteResultRangeWithTransaction{*this, queryValues...}; } diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 4f8091598b0..eebce41780b 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -32,7 +32,7 @@ public: using Base::values; template - auto valueWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto valueWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template value(queryValues...); @@ -40,7 +40,7 @@ public: } template - auto optionalValueWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto optionalValueWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); @@ -48,7 +48,7 @@ public: } template - auto valuesWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto valuesWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template values(queryValues...); @@ -56,7 +56,7 @@ public: } template - void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) + FLATTEN void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { withDeferredTransaction(Base::database(), [&] { Base::readCallback(std::forward(callable), queryValues...); @@ -64,7 +64,7 @@ public: } template - void readToWithTransaction(Container &container, const QueryTypes &...queryValues) + FLATTEN void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { withDeferredTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 2b3e6c32477..8e3825cb52c 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -32,7 +32,7 @@ public: using Base::write; template - auto valueWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto valueWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template value(queryValues...); @@ -40,17 +40,15 @@ public: } template - auto optionalValueWithTransaction(const QueryTypes &...queryValues) + FLATTEN auto optionalValueWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); }); } - template - auto valuesWithTransaction(const QueryTypes &...queryValues) + template + FLATTEN auto valuesWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template values(queryValues...); @@ -58,7 +56,7 @@ public: } template - void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) + FLATTEN void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { withImmediateTransaction(Base::database(), [&] { Base::readCallback(std::forward(callable), queryValues...); @@ -66,14 +64,14 @@ public: } template - void readToWithTransaction(Container &container, const QueryTypes &...queryValues) + FLATTEN void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { withImmediateTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } - void executeWithTransaction() + FLATTEN void executeWithTransaction() { withImmediateTransaction(Base::database(), [&] { Base::execute(); From 2d99b07907f2df3267133140c9a1472929c4cc22 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 28 Aug 2023 13:30:36 +0300 Subject: [PATCH 117/266] QmlDesigner: Add conversion functionality for uniforms and shaders This includes qml and shader types and properties Task-number: QDS-10499 Change-Id: I3a81ceb3a9e55280545e3ed498fb1ba0433a58fd Reviewed-by: Reviewed-by: Mahmoud Badri --- .../effectmaker/compositionnode.cpp | 9 +- .../components/effectmaker/compositionnode.h | 4 + .../components/effectmaker/uniform.cpp | 123 ++++++++++-------- .../components/effectmaker/uniform.h | 15 ++- 4 files changed, 87 insertions(+), 64 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index cd88c224fe3..f4f1eba37d5 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -39,6 +39,11 @@ QObject *CompositionNode::uniformsModel() return &m_unifomrsModel; } +QStringList CompositionNode::requiredNodes() const +{ + return m_requiredNodes; +} + void CompositionNode::parse(const QString &qenPath) { @@ -77,8 +82,8 @@ void CompositionNode::parse(const QString &qenPath) m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); // parse properties - QJsonArray properties = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : properties) + QJsonArray jsonProps = json.value("properties").toArray(); + for (const auto /*QJsonValueRef*/ &prop : jsonProps) m_unifomrsModel.addUniform(new Uniform(prop.toObject())); } diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index f50c09a9a44..cb9e41dae1d 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -6,6 +6,7 @@ #include "effectmakeruniformsmodel.h" #include +#include namespace QmlDesigner { @@ -27,6 +28,8 @@ public: QObject *uniformsModel(); + QStringList requiredNodes() const; + signals: void uniformsModelChanged(); @@ -37,6 +40,7 @@ private: QString m_fragmentCode; QString m_vertexCode; QString m_description; + QStringList m_requiredNodes; EffectMakerUniformsModel m_unifomrsModel; }; diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index 1288dec8f75..f83cf26881f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -17,7 +17,7 @@ Uniform::Uniform(const QJsonObject &propObj) m_name = propObj.value("name").toString(); m_description = propObj.value("description").toString(); - m_type = typeFromString(propObj.value("type").toString()); + m_type = Uniform::typeFromString(propObj.value("type").toString()); defaultValue = propObj.value("defaultValue").toString(); if (m_type == Type::Sampler) { @@ -45,29 +45,15 @@ Uniform::Uniform(const QJsonObject &propObj) m_backendValue->setValue(value); } -QString Uniform::type() const +Uniform::Type Uniform::type() const { - if (m_type == Type::Bool) - return "bool"; - if (m_type == Type::Int) - return "int"; - if (m_type == Type::Float) - return "float"; - if (m_type == Type::Vec2) - return "vec2"; - if (m_type == Type::Vec3) - return "vec3"; - if (m_type == Type::Vec4) - return "vec4"; - if (m_type == Type::Color) - return "color"; - if (m_type == Type::Sampler) - return "image"; - if (m_type == Type::Define) - return "define"; + return m_type; +} - qWarning() << "Unknown type"; - return "float"; +// String representation of the type for qml +QString Uniform::typeName() const +{ + return Uniform::stringFromType(m_type); } QVariant Uniform::value() const @@ -153,31 +139,6 @@ QString Uniform::mipmapPropertyName(const QString &name) const return simplifiedName; } -Uniform::Type Uniform::typeFromString(const QString &typeString) const -{ - if (typeString == "bool") - return Type::Bool; - if (typeString == "int") - return Type::Int; - if (typeString == "float") - return Type::Float; - if (typeString == "vec2") - return Type::Vec2; - if (typeString == "vec3") - return Type::Vec3; - if (typeString == "vec4") - return Type::Vec4; - if (typeString == "color") - return Type::Color; - if (typeString == "image") - return Type::Sampler; - if (typeString == "define") - return Type::Define; - - qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1(); - return Type::Float; -} - // 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. @@ -205,17 +166,17 @@ QString Uniform::getResourcePath(const QString &value) const void Uniform::setValueData(const QString &value, const QString &defaultValue, const QString &minValue, const QString &maxValue) { - m_value = value.isEmpty() ? getInitializedVariant(m_type, false) : valueStringToVariant(m_type, value); - m_defaultValue = defaultValue.isEmpty() ? getInitializedVariant(m_type, false) - : valueStringToVariant(m_type, defaultValue); - m_minValue = minValue.isEmpty() ? getInitializedVariant(m_type, false) : valueStringToVariant(m_type, minValue); - m_maxValue = maxValue.isEmpty() ? getInitializedVariant(m_type, true) : valueStringToVariant(m_type, 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(Uniform::Type type, bool maxValue) +QVariant Uniform::getInitializedVariant(bool maxValue) { - switch (type) { + switch (m_type) { case Uniform::Type::Bool: return maxValue ? true : false; case Uniform::Type::Int: @@ -235,10 +196,10 @@ QVariant Uniform::getInitializedVariant(Uniform::Type type, bool maxValue) } } -QVariant Uniform::valueStringToVariant(const Uniform::Type type, const QString &value) +QVariant Uniform::valueStringToVariant(const QString &value) { QVariant variant; - switch (type) { + switch (m_type) { case Type::Bool: variant = (value == "true"); break; @@ -283,4 +244,54 @@ QVariant Uniform::valueStringToVariant(const Uniform::Type type, const QString & 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 "image"; + 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 == "image") + return Uniform::Type::Sampler; + else if (typeString == "define") + return Uniform::Type::Define; + + qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1(); + return Uniform::Type::Float; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 4e11dbc2cfa..761a22199f5 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -19,7 +19,7 @@ class Uniform : public QObject Q_OBJECT Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) - Q_PROPERTY(QString uniformType READ type CONSTANT) + Q_PROPERTY(QString uniformType READ typeName 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) @@ -41,7 +41,8 @@ public: Uniform(const QJsonObject &props); - QString type() const; + Type type() const; + QString typeName() const; QVariant value() const; void setValue(const QVariant &newValue); @@ -65,20 +66,22 @@ public: bool enableMipmap() const; + static QString stringFromType(Uniform::Type type); + static Uniform::Type typeFromString(const QString &typeString); + signals: void uniformValueChanged(); void uniformBackendValueChanged(); private: QString mipmapPropertyName(const QString &name) const; - - Uniform::Type typeFromString(const QString &typeString) 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(Uniform::Type type, bool maxValue); - QVariant valueStringToVariant(const Uniform::Type type, const QString &value); + + QVariant getInitializedVariant(bool maxValue); + QVariant valueStringToVariant(const QString &value); Type m_type; QVariant m_value; From 9ecaa1607490b9cf984fc67d9b3286e5c0734edf Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 25 Aug 2023 12:54:10 +0300 Subject: [PATCH 118/266] QmlDesigner: Allow hiding ColorEditor's cog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ibaff58441e88c2030c272974b96b08e8b5b9dd3d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Henning Gründl --- .../qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml | 2 ++ .../imports/HelperWidgets/ColorEditor.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml index d8e679b41fc..8b381ccaa1b 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueColor.qml @@ -16,6 +16,8 @@ Row { HelperWidgets.ColorEditor { backendValue: uniformBackendValue + showExtendedFunctionButton: false + onValueChanged: uniformValue = convertColorToString(color) } } 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 From 451327ba432d6bbc8fb36c2daf51fbc6588774cd Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 29 Aug 2023 13:41:53 +0300 Subject: [PATCH 119/266] QmlDesigner: Add snapping icons to the icon font Change-Id: I6e5547ed5170b9c750efefb52407df3a4eb783e0 Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 152 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 60932 -> 59092 bytes .../components/componentcore/theme.h | 2 + 3 files changed, 79 insertions(+), 75 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index c9ee8a38803..f8f213faebf 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -269,81 +269,83 @@ QtObject { readonly property string selection_small: "\u0120" readonly property string settings_medium: "\u0121" readonly property string signal_small: "\u0122" - readonly property string snapping_small: "\u0123" - readonly property string sphere_medium: "\u0124" - readonly property string sphere_small: "\u0125" - readonly property string splitColumns: "\u0126" - readonly property string splitRows: "\u0127" - readonly property string spotLight_small: "\u0128" - readonly property string stackedContainer_small: "\u0129" - readonly property string startNode: "\u012A" - readonly property string step_medium: "\u012B" - readonly property string stop_medium: "\u012C" - readonly property string testIcon: "\u012D" - readonly property string textAlignBottom: "\u012E" - readonly property string textAlignCenter: "\u012F" - readonly property string textAlignJustified: "\u0130" - readonly property string textAlignLeft: "\u0131" - readonly property string textAlignMiddle: "\u0132" - readonly property string textAlignRight: "\u0133" - readonly property string textAlignTop: "\u0134" - readonly property string textBulletList: "\u0135" - readonly property string textFullJustification: "\u0136" - readonly property string textNumberedList: "\u0137" - readonly property string textures_medium: "\u0138" - readonly property string tickIcon: "\u0139" - readonly property string tickMark_small: "\u013A" - readonly property string timeline_small: "\u013B" - readonly property string toEndFrame_medium: "\u013C" - readonly property string toNextFrame_medium: "\u013D" - readonly property string toPrevFrame_medium: "\u013E" - readonly property string toStartFrame_medium: "\u013F" - readonly property string topToolbar_annotations: "\u0140" - readonly property string topToolbar_closeFile: "\u0141" - readonly property string topToolbar_designMode: "\u0142" - readonly property string topToolbar_enterComponent: "\u0143" - readonly property string topToolbar_home: "\u0144" - readonly property string topToolbar_makeComponent: "\u0145" - readonly property string topToolbar_navFile: "\u0146" - readonly property string topToolbar_runProject: "\u0147" - readonly property string translationCreateFiles: "\u0148" - readonly property string translationCreateReport: "\u0149" - readonly property string translationExport: "\u014A" - readonly property string translationImport: "\u014B" - readonly property string translationSelectLanguages: "\u014C" - readonly property string translationTest: "\u014D" - readonly property string transparent: "\u014E" - readonly property string triState: "\u014F" - readonly property string triangleArcA: "\u0150" - readonly property string triangleArcB: "\u0151" - readonly property string triangleCornerA: "\u0152" - readonly property string triangleCornerB: "\u0153" - readonly property string unLinked: "\u0154" - readonly property string undo: "\u0155" - readonly property string unify_medium: "\u0156" - readonly property string unpin: "\u0157" - readonly property string upDownIcon: "\u0158" - readonly property string upDownSquare2: "\u0159" - readonly property string updateAvailable_medium: "\u015A" - readonly property string updateContent_medium: "\u015B" - readonly property string visibilityOff: "\u015C" - readonly property string visibilityOn: "\u015D" - readonly property string visible_medium: "\u015E" - readonly property string visible_small: "\u015F" - readonly property string wildcard: "\u0160" - readonly property string wizardsAutomotive: "\u0161" - readonly property string wizardsDesktop: "\u0162" - readonly property string wizardsGeneric: "\u0163" - readonly property string wizardsMcuEmpty: "\u0164" - readonly property string wizardsMcuGraph: "\u0165" - readonly property string wizardsMobile: "\u0166" - readonly property string wizardsUnknown: "\u0167" - readonly property string zoomAll: "\u0168" - readonly property string zoomIn: "\u0169" - readonly property string zoomIn_medium: "\u016A" - readonly property string zoomOut: "\u016B" - readonly property string zoomOut_medium: "\u016C" - readonly property string zoomSelection: "\u016D" + readonly property string snapping_conf_medium: "\u0123" + readonly property string snapping_medium: "\u0124" + readonly property string snapping_small: "\u0125" + readonly property string sphere_medium: "\u0126" + readonly property string sphere_small: "\u0127" + readonly property string splitColumns: "\u0128" + readonly property string splitRows: "\u0129" + readonly property string spotLight_small: "\u012A" + readonly property string stackedContainer_small: "\u012B" + readonly property string startNode: "\u012C" + readonly property string step_medium: "\u012D" + readonly property string stop_medium: "\u012E" + readonly property string testIcon: "\u012F" + readonly property string textAlignBottom: "\u0130" + readonly property string textAlignCenter: "\u0131" + readonly property string textAlignJustified: "\u0132" + readonly property string textAlignLeft: "\u0133" + readonly property string textAlignMiddle: "\u0134" + readonly property string textAlignRight: "\u0135" + readonly property string textAlignTop: "\u0136" + readonly property string textBulletList: "\u0137" + readonly property string textFullJustification: "\u0138" + readonly property string textNumberedList: "\u0139" + readonly property string textures_medium: "\u013A" + readonly property string tickIcon: "\u013B" + readonly property string tickMark_small: "\u013C" + readonly property string timeline_small: "\u013D" + readonly property string toEndFrame_medium: "\u013E" + readonly property string toNextFrame_medium: "\u013F" + readonly property string toPrevFrame_medium: "\u0140" + readonly property string toStartFrame_medium: "\u0141" + readonly property string topToolbar_annotations: "\u0142" + readonly property string topToolbar_closeFile: "\u0143" + readonly property string topToolbar_designMode: "\u0144" + readonly property string topToolbar_enterComponent: "\u0145" + readonly property string topToolbar_home: "\u0146" + readonly property string topToolbar_makeComponent: "\u0147" + readonly property string topToolbar_navFile: "\u0148" + readonly property string topToolbar_runProject: "\u0149" + readonly property string translationCreateFiles: "\u014A" + readonly property string translationCreateReport: "\u014B" + readonly property string translationExport: "\u014C" + readonly property string translationImport: "\u014D" + readonly property string translationSelectLanguages: "\u014E" + readonly property string translationTest: "\u014F" + readonly property string transparent: "\u0150" + readonly property string triState: "\u0151" + readonly property string triangleArcA: "\u0152" + readonly property string triangleArcB: "\u0153" + readonly property string triangleCornerA: "\u0154" + readonly property string triangleCornerB: "\u0155" + readonly property string unLinked: "\u0156" + readonly property string undo: "\u0157" + readonly property string unify_medium: "\u0158" + readonly property string unpin: "\u0159" + readonly property string upDownIcon: "\u015A" + readonly property string upDownSquare2: "\u015B" + readonly property string updateAvailable_medium: "\u015C" + readonly property string updateContent_medium: "\u015D" + readonly property string visibilityOff: "\u015E" + readonly property string visibilityOn: "\u015F" + readonly property string visible_medium: "\u0160" + readonly property string visible_small: "\u0161" + readonly property string wildcard: "\u0162" + readonly property string wizardsAutomotive: "\u0163" + readonly property string wizardsDesktop: "\u0164" + readonly property string wizardsGeneric: "\u0165" + readonly property string wizardsMcuEmpty: "\u0166" + readonly property string wizardsMcuGraph: "\u0167" + readonly property string wizardsMobile: "\u0168" + readonly property string wizardsUnknown: "\u0169" + readonly property string zoomAll: "\u016A" + readonly property string zoomIn: "\u016B" + readonly property string zoomIn_medium: "\u016C" + readonly property string zoomOut: "\u016D" + readonly property string zoomOut_medium: "\u016E" + readonly property string zoomSelection: "\u016F" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index ac572cde027c833d0a7d587716fce3bcd4ecebf1..fe3e70b2edac9ebf6f9acd43d8cb2714d878f719 100644 GIT binary patch delta 10154 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 delta 12010 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 MmElM$ypQk!00weo00000 diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 0c87fcd3ad4..f936a98028f 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -277,6 +277,8 @@ public: selection_small, settings_medium, signal_small, + snapping_conf_medium, + snapping_medium, snapping_small, sphere_medium, sphere_small, From 51e31314c93c3ddf9ee9d65892520633f0e8b501 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 29 Aug 2023 14:06:53 +0300 Subject: [PATCH 120/266] QmlDesigner: Add the snapping icons to C++ side Change-Id: Ia09472834fcd2db876c7bb527fe23f1f59fa36bf Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/designericons.json | 5 ++++- .../qmldesigner/components/componentcore/designericons.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index bad8531177b..36a60ea3b31 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -259,7 +259,10 @@ "iconName": "scale_medium" }, "SnappingIcon": { - "iconName": "snapping_small" + "iconName": "snapping_medium" + }, + "SnappingConfIcon": { + "iconName": "snapping_conf_medium" }, "ToggleGroupIcon": { "Off": { diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index e2fc812b880..6539462f8f7 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -102,6 +102,7 @@ public: ShowBoundsIcon, SimpleCheckIcon, SnappingIcon, + SnappingConfIcon, TimelineIcon, ToggleGroupIcon, VisibilityIcon From 203f38d616c76b0a1a758773a9d362c6296a464a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 28 Aug 2023 14:15:49 +0300 Subject: [PATCH 121/266] QmlDesigner: Allow hiding UrlChooser's cog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5b9b3b3d658031c4db6f0d61627e7d5218b1afc0 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml | 2 ++ .../imports/HelperWidgets/UrlChooser.qml | 1 + 2 files changed, 3 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml index 05bcdfa6943..571fac50002 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueImage.qml @@ -16,6 +16,8 @@ Row { HelperWidgets.UrlChooser { backendValue: uniformBackendValue + actionIndicatorVisible: false + onAbsoluteFilePathChanged: uniformValue = absoluteFilePath } } 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 From 2f30ad1eb59cdb19a0dbe24dbe26f95ddfa1efde Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 27 Aug 2023 18:13:48 +0200 Subject: [PATCH 122/266] Sqlite: Disable locking test for release Change-Id: I7a54241bdddb23f9b0b3bddbf10844d9eff36cc4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 397068477c1..000e667780e 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -431,8 +431,10 @@ private: Resetter(StatementImplementation *statement) : statement(statement) { +#ifndef QT_NO_DEBUG if (statement && !statement->database().isLocked()) throw DatabaseIsNotLocked{}; +#endif } Resetter(Resetter &) = delete; From fc99f914dfcef4fd062a3d6e7e6ceab7dd79767f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 27 Aug 2023 18:14:14 +0200 Subject: [PATCH 123/266] Sqlite: Make simple getter inline Change-Id: I8bba89a45f32a6491ca3bfef4535b5183c98382a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.cpp | 5 ----- src/libs/sqlite/sqlitebasestatement.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) 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 000e667780e..afe9c7fcada 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -123,7 +123,7 @@ public: QString columnName(int column) const; - Database &database() const; + Database &database() const { return m_database; } protected: ~BaseStatement() = default; From a20839f8c3429eaa1e37ecc71ffb3e2a85d9e062 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 27 Aug 2023 19:48:30 +0200 Subject: [PATCH 124/266] Sqlite: Reset is noexcept Change-Id: I4d702619e17118681a46dbc904038031a6f98353 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index afe9c7fcada..6e26ba82471 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -444,7 +444,7 @@ private: : statement{std::exchange(other.statement, nullptr)} {} - void reset() + void reset() noexcept { if (statement) statement->reset(); From 8af0528893617602a3ffed37559359b14358c58b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 28 Aug 2023 12:29:42 +0200 Subject: [PATCH 125/266] Sqlite: Improve insertUpdateDelete Instead of a value we keep the iterator. That is saving us a useless copy because the value range cannot change. Change-Id: I2ea36b5a08e378f8e148f317c3384e4c4954a439 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/sqlite/sqlitealgorithms.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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); } From 84e537f313de5ecf969be3f7e3045a0ef8162cd3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 25 Aug 2023 14:23:28 +0300 Subject: [PATCH 126/266] QmlDesigner: Add scale and rotation snapping to 3D view Fixes: QDS-10464 Change-Id: I9b327b21a3e09313664b2b4b47772e3cb4244327 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../SnapConfigurationDialog.qml | 141 ++++++++++++++---- .../components/edit3d/edit3dview.cpp | 56 ++++++- .../components/edit3d/edit3dview.h | 2 + .../components/edit3d/snapconfiguration.cpp | 30 +++- .../components/edit3d/snapconfiguration.h | 20 ++- .../qmldesigner/qmldesignerconstants.h | 2 + .../utils/designersettings.cpp | 4 + .../qmldesignerbase/utils/designersettings.h | 4 + .../qml2puppet/mockfiles/qt6/RotateRing.qml | 1 + .../qml2puppet/mockfiles/qt6/ScaleGizmo.qml | 1 + .../qml2puppet/editor3d/generalhelper.cpp | 93 ++++++++++-- .../qml2puppet/editor3d/generalhelper.h | 14 +- .../qml2puppet/editor3d/mousearea3d.cpp | 19 ++- .../qml2puppet/editor3d/mousearea3d.h | 3 + .../qt5informationnodeinstanceserver.cpp | 13 +- 15 files changed, 350 insertions(+), 53 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml index a4903997e77..d34a210ae8c 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -23,49 +23,132 @@ Rectangle { Rectangle { id: ctrlRect width: root.width - 16 - height: posIntValue.height + 16 + height: posIntValue.height + rotIntValue.height + scaleIntValue.height + 32 color: StudioTheme.Values.themePanelBackground border.color: StudioTheme.Values.themeControlOutline border.width: StudioTheme.Values.border - Row { - x: 8 - y: 8 - width: posIntLabel.width + posIntValue.width + StudioTheme.Values.sectionRowSpacing - spacing: StudioTheme.Values.sectionRowSpacing - - Text { - id: posIntLabel - text: qsTr("Position Snap Interval:") - color: enabled ? StudioTheme.Values.themeTextColor - : StudioTheme.Values.themeTextColorDisabled - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignRight + Column { + padding: 8 + spacing: 8 + Row { height: posIntValue.height + width: parent.width - 16 + spacing: StudioTheme.Values.sectionRowSpacing + + Text { + id: posIntLabel + text: qsTr("Position Snap Interval:") + color: enabled ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + height: posIntValue.height + } + + Item { // Spacer + width: Math.max(ctrlRect.width - posIntLabel.width - posIntValue.width - 32, 1) + height: 1 + } + + StudioControls.RealSpinBox { + id: posIntValue + realFrom: 1 + realTo: 100000 + realValue: rootView.posInt + realStepSize: 1 + width: 80 + actionIndicatorVisible: false + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Snap interval for move gizmo.") + ToolTip.delay: root.toolTipDelay + + onRealValueChanged: rootView.posInt = realValue + } } - StudioControls.RealSpinBox { - id: posIntValue - realFrom: 1 - realTo: 100000 - realValue: rootView.posInt - realStepSize: 1 - width: ctrlRect.width - 24 - posIntLabel.width - actionIndicatorVisible: false + Row { + height: rotIntValue.height + width: parent.width - 16 + spacing: StudioTheme.Values.sectionRowSpacing - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Snap interval for move gizmo.") - ToolTip.delay: root.toolTipDelay + Text { + id: rotIntLabel + text: qsTr("Rotation Snap Interval:") + color: enabled ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + height: rotIntValue.height + } - onRealValueChanged: rootView.posInt = realValue + Item { // Spacer + width: Math.max(ctrlRect.width - rotIntLabel.width - rotIntValue.width - 32, 1) + height: 1 + } + + StudioControls.RealSpinBox { + id: rotIntValue + realFrom: 1 + realTo: 360 + realValue: rootView.rotInt + realStepSize: 1 + width: 80 + 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 + } + } + + Row { + height: scaleIntValue.height + width: parent.width - 16 + spacing: StudioTheme.Values.sectionRowSpacing + + Text { + id: scaleIntLabel + text: qsTr("Scale Snap Interval (%):") + color: enabled ? StudioTheme.Values.themeTextColor + : StudioTheme.Values.themeTextColorDisabled + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + height: scaleIntValue.height + } + + Item { // Spacer + width: Math.max(ctrlRect.width - scaleIntLabel.width - scaleIntValue.width - 32, 1) + height: 1 + } + + StudioControls.RealSpinBox { + id: scaleIntValue + realFrom: 1 + realTo: 100000 + realValue: rootView.scaleInt + realStepSize: 1 + width: 80 + 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 + } } } } - Item { - id: spacer + Item { // Spacer width: 1 height: Math.max(root.height - buttons.height - ctrlRect.height - 16, 2) } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index a3f33120f52..4890c031a25 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -201,9 +201,15 @@ void Edit3DView::modelAttached(Model *model) AbstractView::modelAttached(model); rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); + rootModelNode().setAuxiliaryData(edit3dSnapRotProperty, m_snapRotationAction->action()->isChecked()); + rootModelNode().setAuxiliaryData(edit3dSnapScaleProperty, m_snapScaleAction->action()->isChecked()); rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, - Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL)); + 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)); checkImports(); auto cachedImage = m_canvasCache.take(model); @@ -938,7 +944,7 @@ void Edit3DView::createEdit3DActions() QmlDesigner::Constants::EDIT3D_SNAP_POSITION, View3DActionType::Empty, QCoreApplication::translate("SnapPositionAction", "Snap Position"), - QKeySequence(Qt::SHIFT | Qt::Key_Tab), + QKeySequence(Qt::SHIFT | Qt::Key_W), true, Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_POSITION), false).toBool(), QIcon(), @@ -946,6 +952,40 @@ void Edit3DView::createEdit3DActions() snapPositionTrigger, QCoreApplication::translate("SnapPositionAction", "Toggle position snapping during node drag.")); + SelectionContextOperation snapRotationTrigger = [this](const SelectionContext &) { + if (model()) + rootModelNode().setAuxiliaryData(edit3dSnapRotProperty, m_snapRotationAction->action()->isChecked()); + }; + + m_snapRotationAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SNAP_ROTATION, + View3DActionType::Empty, + QCoreApplication::translate("SnapRotationAction", "Snap Rotation"), + QKeySequence(Qt::SHIFT | Qt::Key_E), + true, + Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ROTATION), false).toBool(), + QIcon(), + this, + snapRotationTrigger, + QCoreApplication::translate("SnapRotationAction", "Toggle rotation snapping during node drag.")); + + SelectionContextOperation snapScaleTrigger = [this](const SelectionContext &) { + if (model()) + rootModelNode().setAuxiliaryData(edit3dSnapScaleProperty, m_snapScaleAction->action()->isChecked()); + }; + + m_snapScaleAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_SNAP_SCALE, + View3DActionType::Empty, + QCoreApplication::translate("SnapScaleAction", "Snap Scale"), + QKeySequence(Qt::SHIFT | Qt::Key_R), + true, + Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_SCALE), false).toBool(), + QIcon(), + this, + snapScaleTrigger, + QCoreApplication::translate("SnapScaleAction", "Toggle scale snapping during node drag.")); + SelectionContextOperation snapAbsoluteTrigger = [this](const SelectionContext &) { if (model()) rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); @@ -954,14 +994,14 @@ void Edit3DView::createEdit3DActions() m_snapAbsoluteAction = std::make_unique( QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE, View3DActionType::Empty, - QCoreApplication::translate("SnapAbsoluteAction", "Absolute Snap"), - QKeySequence(Qt::SHIFT | Qt::Key_W), + QCoreApplication::translate("SnapAbsoluteAction", "Absolute Position Snap"), + QKeySequence(Qt::SHIFT | Qt::Key_A), true, Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE), true).toBool(), QIcon(), this, snapAbsoluteTrigger, - QCoreApplication::translate("SnapAbsoluteAction", "If enabled, snapping uses scene origin as origin point.\nOtherwise snapping uses drag start point as origin point.")); + QCoreApplication::translate("SnapAbsoluteAction", "If enabled, position snapping uses scene origin as origin point.\nOtherwise snapping uses drag start point as origin point.")); m_leftActions << m_selectionModeAction.get(); m_leftActions << nullptr; // Null indicates separator @@ -1010,6 +1050,8 @@ void Edit3DView::createEdit3DActions() m_snapActions << m_snapConfigAction.get(); m_snapActions << m_snapPositionAction.get(); + m_snapActions << m_snapRotationAction.get(); + m_snapActions << m_snapScaleAction.get(); m_snapActions << m_snapAbsoluteAction.get(); } @@ -1123,6 +1165,10 @@ const char *Edit3DView::settingKeyForAction(const QByteArray &actionId) { if (actionId == Constants::EDIT3D_SNAP_POSITION) return DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION; + if (actionId == Constants::EDIT3D_SNAP_ROTATION) + return DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION; + if (actionId == Constants::EDIT3D_SNAP_SCALE) + return DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE; if (actionId == Constants::EDIT3D_SNAP_ABSOLUTE) return DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE; return ""; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 20967af87d0..65fcdf4b0ce 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -147,6 +147,8 @@ private: std::unique_ptr m_snapMenuAction; std::unique_ptr m_snapConfigAction; std::unique_ptr m_snapPositionAction; + std::unique_ptr m_snapRotationAction; + std::unique_ptr m_snapScaleAction; std::unique_ptr m_snapAbsoluteAction; std::unique_ptr m_bakeLightsAction; diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 0e3d90d007d..0f524e88071 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -54,14 +54,26 @@ void SnapConfiguration::apply() { 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->rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, m_positionInterval); + m_view->rootModelNode().setAuxiliaryData(edit3dSnapRotIntProperty, m_rotationInterval); + m_view->rootModelNode().setAuxiliaryData(edit3dSnapScaleIntProperty, m_scaleInterval); } void SnapConfiguration::showConfigDialog(const QPoint &pos) { double posInt = Edit3DViewConfig::load( DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, 10.).toDouble(); + double rotInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, 15.).toDouble(); + double scaleInt = Edit3DViewConfig::load( + DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, 10.).toDouble(); setPosInt(posInt); + setRotInt(rotInt); + setScaleInt(scaleInt); if (!m_configDialog) { // Show non-modal progress dialog with cancel button @@ -73,7 +85,7 @@ void SnapConfiguration::showConfigDialog(const QPoint &pos) m_configDialog->setFlags(Qt::Dialog); m_configDialog->setModality(Qt::ApplicationModal); m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_configDialog->setMinimumSize({250, 100}); + m_configDialog->setMinimumSize({280, 170}); m_configDialog->rootContext()->setContextProperties({ {"rootView", QVariant::fromValue(this)} @@ -98,6 +110,22 @@ void SnapConfiguration::setPosInt(double value) } } +void SnapConfiguration::setRotInt(double value) +{ + if (value != m_rotationInterval) { + m_rotationInterval = value; + emit rotIntChanged(); + } +} + +void SnapConfiguration::setScaleInt(double value) +{ + if (value != m_scaleInterval) { + m_scaleInterval = value; + emit scaleIntChanged(); + } +} + void SnapConfiguration::cleanup() { delete m_configDialog; diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index 792d38ecb6e..e41dc7e75af 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -22,11 +22,21 @@ inline constexpr AuxiliaryDataKeyView edit3dSnapPosIntProperty{AuxiliaryDataType "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(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(AbstractView *view); @@ -39,9 +49,15 @@ public: 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; } signals: void posIntChanged(); + void rotIntChanged(); + void scaleIntChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -51,7 +67,9 @@ private: QPointer m_configDialog; QPointer m_view; - double m_positionInterval = 11.; + double m_positionInterval = 10.; + double m_rotationInterval = 15.; + double m_scaleInterval = 10.; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index bbc66b75fe8..75938f1aa7c 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -66,6 +66,8 @@ const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundC const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; const char EDIT3D_SNAP_MENU[] = "QmlDesigner.Editor3D.SnapMenu"; const char EDIT3D_SNAP_POSITION[] = "QmlDesigner.Editor3D.SnapPosition"; +const char EDIT3D_SNAP_ROTATION[] = "QmlDesigner.Editor3D.SnapRotation"; +const char EDIT3D_SNAP_SCALE[] = "QmlDesigner.Editor3D.SnapScale"; const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; const char EDIT3D_SNAP_ABSOLUTE[] = "QmlDesigner.Editor3D.SnapToGrid"; diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index 32f1c0f87bb..a19bb310a6f 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -86,6 +86,10 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, true); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, false); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, 10.); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION, false); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, 15.); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE, false); + 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 758715c0fc6..aa0b8b4b138 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -35,6 +35,10 @@ inline constexpr char EDIT3DVIEW_GRID_COLOR[] = "Edit3DViewGridLineColor"; inline constexpr char EDIT3DVIEW_SNAP_ABSOLUTE[] = "Edit3DViewSnapAbsolute"; 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/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/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index dfccf96a724..57b02f4b168 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -771,6 +771,27 @@ 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, @@ -781,19 +802,10 @@ QVector3D GeneralHelper::adjustTranslationForSnap(const QVector3D &newPos, bool snapAbs = m_snapAbsolute; double increment = m_snapPositionInterval; - if (!node || increment == 0. || snapAxes.isNull() || qFuzzyIsNull((newPos - startPos).length())) + if (!node || snapAxes.isNull() || qFuzzyIsNull((newPos - startPos).length()) + || !queryKeyboardForSnapping(snapPos, increment)) { return newPos; - - // 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 && !snapPos) || (ctrlMod && snapPos)) - return newPos; - - if (shiftMod) - increment *= 0.1; + } // 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 @@ -884,6 +896,63 @@ QVector3D GeneralHelper::adjustTranslationForSnap(const QVector3D &newPos, 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::handlePendingToolStateUpdate() { m_toolStateUpdateTimer.stop(); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 5313e730d25..c9c960950cf 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -106,9 +106,17 @@ public: const QVector3D &snapAxes, bool globalOrientation, QQuick3DNode *node); - void setSnapPosition(bool enable) { m_snapPosition = enable; } + 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.; } signals: void overlayUpdateNeeded(); @@ -146,7 +154,11 @@ private: bool m_snapAbsolute = true; bool m_snapPosition = false; + bool m_snapRotation = false; + bool m_snapScale = false; double m_snapPositionInterval = 10.; + double m_snapRotationInterval = 15.; + double m_snapScaleInterval = .1; }; } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp index 2e78d84ac82..f4f22996e1f 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -5,6 +5,8 @@ #include "mousearea3d.h" +#include "generalhelper.h" + #include #include #include @@ -16,6 +18,8 @@ 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 +636,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; } @@ -713,12 +721,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 @@ -818,6 +828,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()) { 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/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 867e79ffe04..203a2971ea5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -376,10 +376,18 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings(const QVectorsetSnapPosition(container.value().toBool()); - else if (container.name() == "snapAbs3d") - helper->setSnapAbsolute(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 @@ -482,6 +490,7 @@ 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); From e2a8f1dc512f888a324136a26df74e541a01b6fa Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 29 Aug 2023 14:46:02 +0000 Subject: [PATCH 127/266] Revert "Sqlite: Flatten calls inside functions" This reverts commit 2a301d41c41afb1e1fceda0b1a22fa507c668366. Reason for revert: msvc uses a different syntax Change-Id: Iec4ae2a65e6957c6e1368cc481a63fab0e55ce9e Reviewed-by: Marco Bubke --- src/libs/sqlite/sqlitebasestatement.h | 26 +++++++++------------- src/libs/sqlite/sqlitereadstatement.h | 10 ++++----- src/libs/sqlite/sqlitereadwritestatement.h | 16 +++++++------ 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 6e26ba82471..3c490293447 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -27,12 +27,6 @@ using std::int64_t; -#ifdef Q_CC_MSVC -#define FLATTEN [[msvc::flatten]] -#else -#define FLATTEN [[gnu::flatten]] -#endif - namespace Sqlite { class Database; @@ -156,7 +150,7 @@ public: using BaseStatement::BaseStatement; StatementImplementation(StatementImplementation &&) = default; - FLATTEN void execute() + void execute() { Resetter resetter{this}; BaseStatement::next(); @@ -172,7 +166,7 @@ public: } template - FLATTEN void write(const ValueType &...values) + void write(const ValueType&... values) { Resetter resetter{this}; bindValues(values...); @@ -196,7 +190,7 @@ public: std::size_t capacity = 32, typename = std::enable_if_t::value>, typename... QueryTypes> - FLATTEN auto values(const QueryTypes &...queryValues) + auto values(const QueryTypes &...queryValues) { Resetter resetter{this}; Container resultValues; @@ -217,7 +211,7 @@ public: template typename Container = std::vector, typename = std::enable_if_t::value>, typename... QueryTypes> - FLATTEN auto values(const QueryTypes &...queryValues) + auto values(const QueryTypes &...queryValues) { return values, capacity>(queryValues...); } @@ -237,7 +231,7 @@ public: } template - FLATTEN auto optionalValue(const QueryTypes &...queryValues) + auto optionalValue(const QueryTypes &...queryValues) { Resetter resetter{this}; std::optional resultValue; @@ -251,7 +245,7 @@ public: } template - FLATTEN static auto toValue(Utils::SmallStringView sqlStatement, Database &database) + static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { StatementImplementation statement(sqlStatement, database); @@ -263,7 +257,7 @@ public: } template - FLATTEN void readCallback(Callable &&callable, const QueryTypes &...queryValues) + void readCallback(Callable &&callable, const QueryTypes &...queryValues) { Resetter resetter{this}; @@ -278,7 +272,7 @@ public: } template - FLATTEN void readTo(Container &container, const QueryTypes &...queryValues) + void readTo(Container &container, const QueryTypes &...queryValues) { Resetter resetter{this}; @@ -289,13 +283,13 @@ public: } template - FLATTEN auto range(const QueryTypes &...queryValues) + auto range(const QueryTypes &...queryValues) { return SqliteResultRange{*this, queryValues...}; } template - FLATTEN auto rangeWithTransaction(const QueryTypes &...queryValues) + auto rangeWithTransaction(const QueryTypes &...queryValues) { return SqliteResultRangeWithTransaction{*this, queryValues...}; } diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index eebce41780b..4f8091598b0 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -32,7 +32,7 @@ public: using Base::values; template - FLATTEN auto valueWithTransaction(const QueryTypes &...queryValues) + auto valueWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template value(queryValues...); @@ -40,7 +40,7 @@ public: } template - FLATTEN auto optionalValueWithTransaction(const QueryTypes &...queryValues) + auto optionalValueWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); @@ -48,7 +48,7 @@ public: } template - FLATTEN auto valuesWithTransaction(const QueryTypes &...queryValues) + auto valuesWithTransaction(const QueryTypes &...queryValues) { return withDeferredTransaction(Base::database(), [&] { return Base::template values(queryValues...); @@ -56,7 +56,7 @@ public: } template - FLATTEN void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) + void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { withDeferredTransaction(Base::database(), [&] { Base::readCallback(std::forward(callable), queryValues...); @@ -64,7 +64,7 @@ public: } template - FLATTEN void readToWithTransaction(Container &container, const QueryTypes &...queryValues) + void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { withDeferredTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 8e3825cb52c..2b3e6c32477 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -32,7 +32,7 @@ public: using Base::write; template - FLATTEN auto valueWithTransaction(const QueryTypes &...queryValues) + auto valueWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template value(queryValues...); @@ -40,15 +40,17 @@ public: } template - FLATTEN auto optionalValueWithTransaction(const QueryTypes &...queryValues) + auto optionalValueWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); }); } - template - FLATTEN auto valuesWithTransaction(const QueryTypes &...queryValues) + template + auto valuesWithTransaction(const QueryTypes &...queryValues) { return withImmediateTransaction(Base::database(), [&] { return Base::template values(queryValues...); @@ -56,7 +58,7 @@ public: } template - FLATTEN void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) + void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) { withImmediateTransaction(Base::database(), [&] { Base::readCallback(std::forward(callable), queryValues...); @@ -64,14 +66,14 @@ public: } template - FLATTEN void readToWithTransaction(Container &container, const QueryTypes &...queryValues) + void readToWithTransaction(Container &container, const QueryTypes &...queryValues) { withImmediateTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); }); } - FLATTEN void executeWithTransaction() + void executeWithTransaction() { withImmediateTransaction(Base::database(), [&] { Base::execute(); From a37d02844398697db1552c30162983f2da31ca09 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 29 Aug 2023 13:32:17 +0300 Subject: [PATCH 128/266] QmlDesigner: Add an optional close button to the Section Change-Id: Ia74fc19a10a40290626c20e57150c444d394523d Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/Section.qml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 1cee92847fc..dd7b750ffaa 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -18,6 +18,8 @@ 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 spacing: column.spacing property int leftPadding: StudioTheme.Values.sectionLeftPadding @@ -79,6 +81,7 @@ Item { signal toggleExpand() signal expand() signal collapse() + signal closeButtonClicked() DropArea { id: dropArea @@ -157,6 +160,20 @@ Item { } } } + + IconButton { + id: closeButton + + icon: StudioTheme.Constants.close_small + buttonSize: 22 + iconScale: containsMouse ? 1.2 : 1 + transparentBg: true + anchors.right: parent.right + anchors.rightMargin: 10 + visible: false + + onClicked: root.closeButtonClicked() + } } Rectangle { From 64442a709811eb7ece02ad0ccce5ef436bdb9108 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 29 Aug 2023 16:24:43 +0300 Subject: [PATCH 129/266] QmlDesigner: Implement removing a composition node Fixes: QDS-10410 Change-Id: I4c4922c82f294d676dbf1e82f8c6dbb0f4089fdb Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNode.qml | 6 ++++ .../effectmaker/compositionnode.cpp | 3 +- .../components/effectmaker/compositionnode.h | 3 -- .../effectmaker/effectmakermodel.cpp | 29 ++++--------------- .../components/effectmaker/effectmakermodel.h | 8 ++--- 5 files changed, 15 insertions(+), 34 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml index d1bcd4fb454..6efa67919d0 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -15,6 +15,12 @@ HelperWidgets.Section { caption: nodeName category: "EffectMaker" + showCloseButton: true + closeButtonToolTip: qsTr("Remove") + onCloseButtonClicked: { + EffectMakerBackend.effectMakerModel.removeNode(index) + } + Column { spacing: 10 diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index f4f1eba37d5..0414d3ef44c 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -7,7 +7,7 @@ #include "effectmakeruniformsmodel.h" #include "uniform.h" -#include +#include #include #include #include @@ -46,7 +46,6 @@ QStringList CompositionNode::requiredNodes() const void CompositionNode::parse(const QString &qenPath) { - QFile qenFile(qenPath); if (!qenFile.open(QIODevice::ReadOnly)) { diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index cb9e41dae1d..2b2864efb97 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -6,12 +6,9 @@ #include "effectmakeruniformsmodel.h" #include -#include namespace QmlDesigner { -class Uniform; - class CompositionNode : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 50879021938..9188c43254a 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -34,39 +34,22 @@ 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.values().at(index.row())->property(roleNames().value(role)); -} - -void EffectMakerModel::resetModel() -{ - beginResetModel(); - endResetModel(); + return m_nodes.at(index.row())->property(roleNames().value(role)); } void EffectMakerModel::addNode(const QString &nodeQenPath) { - static int id = 0; - beginInsertRows({}, m_nodes.size(), m_nodes.size()); auto *node = new CompositionNode(nodeQenPath); - m_nodes.insert(id++, node); + m_nodes.append(node); endInsertRows(); } -void EffectMakerModel::selectEffect(int idx, bool force) +void EffectMakerModel::removeNode(int idx) { - Q_UNUSED(idx) - Q_UNUSED(force) - - // TODO -} - -void EffectMakerModel::applyToSelected(qint64 internalId, bool add) -{ - Q_UNUSED(internalId) - Q_UNUSED(add) - - // TODO: remove? + beginRemoveRows({}, idx, idx); + m_nodes.removeAt(idx); + endRemoveRows(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index bad64a6cddc..4193b918f22 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -26,17 +26,13 @@ public: bool isEmpty() const { return m_isEmpty; } - void resetModel(); - void addNode(const QString &nodeQenPath); - Q_INVOKABLE void selectEffect(int idx, bool force = false); - Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false); + Q_INVOKABLE void removeNode(int idx); signals: void isEmptyChanged(); void selectedIndexChanged(int idx); - void hasModelSelectionChanged(); private: enum Roles { @@ -46,7 +42,7 @@ private: bool isValidIndex(int idx) const; - QMap m_nodes; + QList m_nodes; int m_selectedIndex = -1; bool m_isEmpty = true; From 02119439091c2795bc729acaedde11f373ae91ae Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 29 Aug 2023 16:49:46 +0200 Subject: [PATCH 130/266] QmlDesigner: Fix crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If3f13e31eba0ec03177c8954a09b73ac60c59c69 Reviewed-by: Henning Gründl --- src/plugins/insight/insightmodel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp index 2d2550aac55..75ac061838c 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); + } } } From 3e0ae7951d6dd54d1fd5d5cef50230059e0ca67e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 29 Aug 2023 12:16:26 +0200 Subject: [PATCH 131/266] QmlDesigner: Add PropertyTreeModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This model represents all properties in the document in a tree model. PropertyListProxyModel allows to flatten a level into a list model. PropertyTreeModelDelegate exposed a single item to a combobox. Change-Id: I9b56f1ecc9aa57777356bc795b5a15b17559ae24 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl Reviewed-by: --- src/plugins/qmldesigner/CMakeLists.txt | 4 +- .../connectioneditor/propertytreemodel.cpp | 920 ++++++++++++++++++ .../connectioneditor/propertytreemodel.h | 186 ++++ .../designercore/model/modelutils.cpp | 8 + .../designercore/model/modelutils.h | 2 + 5 files changed, 1119 insertions(+), 1 deletion(-) create mode 100644 src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 689c6bce9b0..7f94c8ff256 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> @@ -486,6 +486,7 @@ add_qtc_plugin(QmlDesigner EXPLICIT_MOC components/propertyeditor/propertyeditorvalue.h components/connectioneditor/connectionviewwidget.h + qmldesignerplugin.h EXTRA_TRANSLATIONS "${PROJECT_SOURCE_DIR}/share/qtcreator/qmldesigner" PROPERTIES @@ -946,6 +947,7 @@ extend_qtc_plugin(QmlDesigner delegates.cpp delegates.h dynamicpropertiesmodel.cpp dynamicpropertiesmodel.h selectiondynamicpropertiesproxymodel.cpp selectiondynamicpropertiesproxymodel.h + propertytreemodel.cpp propertytreemodel.h ) extend_qtc_plugin(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..d5b5e754af3 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -0,0 +1,920 @@ +// 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", + "stateChanged", + "started", + "stopped", + "finished" + "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"}; + +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_indexCache.clear(); + m_indexHash.clear(); + m_indexCount = 0; + m_nodeList = allModelNodesWithIdsSortedByDisplayName(); + + endResetModel(); + testModel(); +} + +QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const +{ + int internalId = index.internalId(); + + if (role == InternalIdRole) + return internalId; + + if (role == RowRole) + return index.row(); + + if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole) { + if (!index.isValid()) + return {}; + + if (internalId < 0) + return {}; + + QTC_ASSERT(internalId < m_indexCount, return {"assert"}); + + DataCacheItem item = m_indexHash[index.internalId()]; + + if (item.propertyName.isEmpty()) { //node + if (role == PropertyNameRole) + return item.modelNode.displayName(); + + return true; //nodes are always shown + } + + if (role == ExpressionRole) + return QString(item.modelNode.id() + item.propertyName); + + if (role == PropertyNameRole) + return item.propertyName; + + 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 + + return false; + } + + // can be removed later since we only use the two roles above in QML + // just for testing + + if (!(role == Qt::DisplayRole || role == Qt::FontRole)) + return {}; + + if (!index.isValid()) + return {}; + + if (internalId < 0) + return {}; + + QTC_ASSERT(internalId < m_indexCount, return {"assert"}); + + DataCacheItem item = m_indexHash[index.internalId()]; + + if (item.propertyName.isEmpty()) { + const QString name = item.modelNode.displayName(); + if (role == Qt::DisplayRole) + return name; + QFont f; + f.setBold(true); + return f; + } + + if (role == Qt::DisplayRole) + return item.propertyName; + + QFont f; + auto priority = properityLists(); + if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end()) + f.setBold(true); + auto dynamic = getDynamicProperties(item.modelNode); + if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end()) + f.setBold(true); + + return f; +} + +Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const +{ + return Qt::ItemIsEnabled; +} + +QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + int internalId = parent.internalId(); + if (!m_connectionView->isAttached()) + return {}; + + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + const int rootId = -1; + + if (!parent.isValid()) + return createIndex(0, 0, rootId); + + if (internalId == rootId) { //root level model node + const ModelNode modelNode = m_nodeList[row]; + return ensureModelIndex(modelNode, row); + } + + //property + + QTC_ASSERT(internalId >= 0, 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 {}; + + int internalId = index.internalId(); + + if (internalId == -1) + 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, -1); + + 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::indexForInernalIdAndRow(int 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; + + int internalId = parent.internalId(); + + if (internalId == -1) + 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; + 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; + + return Utils::filtered(returnValue, [this](const PropertyName &name) { + return name.contains(m_filter.toUtf8()) || name == m_filter.toUtf8(); + }); +} + +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); + + const std::vector priorityListSignals = {"clicked", + "doubleClicked", + "pressed", + "released", + "toggled", + "valueModified", + "valueChanged", + "checkedChanged", + "moved", + "accepted", + "editingFinished", + "entered", + "exited", + "canceled", + "triggered", + "stateChanged", + "started", + "stopped", + "finished" + "enabledChanged", + "visibleChanged", + "opacityChanged", + "rotationChanged"}; + + auto filtered + = Utils::filtered(metaInfo.signalNames(), [&priorityListSignals](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"}}; + + return roleNames; +} + +PropertyListProxyModel::PropertyListProxyModel(PropertyTreeModel *parent) + : QAbstractListModel(), m_treeModel(parent) +{} + +void PropertyListProxyModel::setRowandInternalId(int row, int internalId) +{ + QTC_ASSERT(m_treeModel, return ); + + if (internalId == -1) + m_parentIndex = m_treeModel->index(0, 0); + else + m_parentIndex = m_treeModel->indexForInernalIdAndRow(internalId, row); + + beginResetModel(); + endResetModel(); +} + +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); +} + +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); +} + +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); + + 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(nameVector.size()); + 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 +} + +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..48a1c768b2d --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -0,0 +1,186 @@ +// 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 { + +class AbstractProperty; +class ModelNode; +class BindingProperty; +class SignalHandlerProperty; +class VariantProperty; + +class ConnectionView; + +class PropertyTreeModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum UserRoles { + PropertyNameRole = Qt::UserRole + 1, + PropertyPriorityRole, + ExpressionRole, + 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 indexForInernalIdAndRow(int 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; + int internalIndex = -1; + }; + + void setPropertyType(PropertyTypes type); + void setFilter(const QString &filter); + + QList nodeList() const; + + const std::vector getProperties(const ModelNode &modelNode) const; + ModelNode getModelNodeForId(const QString &id) const; + +protected: + 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 int m_indexCount = 0; + QList m_nodeList; + PropertyTypes m_type = AllTypes; + QString m_filter; +}; + +class PropertyListProxyModel : public QAbstractListModel +{ + Q_OBJECT +public: + PropertyListProxyModel(PropertyTreeModel *parent); + void setRowandInternalId(int row, int internalId); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + +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); + QString id() const; + QString name() 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/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index 387b9262b11..e1fe3f3cadc 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -3,6 +3,7 @@ #include "modelutils.h" +#include #include #include #include @@ -154,4 +155,11 @@ QList pruneChildren(const QList &nodes) 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 dd36cefa23e..ee6d3e41303 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -36,4 +36,6 @@ QMLDESIGNERCORE_EXPORT QString componentFilePath(const ModelNode &node); QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &nodes); +QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); + } // namespace QmlDesigner::ModelUtils From 61827510c068180059bf388bc9f3a418b3571e81 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 29 Aug 2023 16:59:25 +0200 Subject: [PATCH 132/266] QmlDesigner: Add SCXML wizard to QDS The wizard is only active if the plugin is actually enabled. Task-number: QDS-10507 Change-Id: I0a41deaa22745db90c3ec04a35109694a2fbc79c Reviewed-by: Unseon Ryu Reviewed-by: Thomas Hartmann --- .../studio_templates/files/scxml/file.scxml | 3 + .../studio_templates/files/scxml/wizard.json | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 share/qtcreator/qmldesigner/studio_templates/files/scxml/file.scxml create mode 100644 share/qtcreator/qmldesigner/studio_templates/files/scxml/wizard.json 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 + } + } + ] +} From 0af20575c18f4ea05352ca93ccf320aebb4da8c8 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 30 Aug 2023 09:46:59 +0200 Subject: [PATCH 133/266] QmlJS: Turn ErrDoNotMixTranslationFunctionsInQmlUi into a warning Task-number: QDS-10548 Task-number: QDS-7597 Change-Id: I785fbf0f47f0753b257ef5fae1c3bf4afa9b416f Reviewed-by: Tim Jenssen --- src/libs/qmljs/qmljscheck.cpp | 8 ++++---- src/libs/qmljs/qmljsstaticanalysismessage.cpp | 3 ++- src/libs/qmljs/qmljsstaticanalysismessage.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 05273c579ae..5fadd84cd3b 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -675,7 +675,7 @@ QList Check::defaultDisabledMessagesForNonQuickUi() ErrBehavioursNotSupportedInQmlUi, ErrStatesOnlyInRootItemInQmlUi, ErrReferenceToParentItemNotSupportedInQmlUi, - ErrDoNotMixTranslationFunctionsInQmlUi, + WarnDoNotMixTranslationFunctionsInQmlUi, }); return disabled; } @@ -774,7 +774,7 @@ void Check::enableQmlDesignerUiFileChecks() enableMessage(ErrBehavioursNotSupportedInQmlUi); enableMessage(ErrStatesOnlyInRootItemInQmlUi); enableMessage(ErrReferenceToParentItemNotSupportedInQmlUi); - enableMessage(ErrDoNotMixTranslationFunctionsInQmlUi); + enableMessage(WarnDoNotMixTranslationFunctionsInQmlUi); } void Check::disableQmlDesignerUiFileChecks() @@ -786,7 +786,7 @@ void Check::disableQmlDesignerUiFileChecks() disableMessage(ErrBehavioursNotSupportedInQmlUi); disableMessage(ErrStatesOnlyInRootItemInQmlUi); disableMessage(ErrReferenceToParentItemNotSupportedInQmlUi); - disableMessage(ErrDoNotMixTranslationFunctionsInQmlUi); + disableMessage(WarnDoNotMixTranslationFunctionsInQmlUi); } bool Check::preVisit(Node *ast) @@ -1937,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/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, From 34cef919830c03cfc7764afd7d9107ffd12637c0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 29 Aug 2023 18:10:37 +0200 Subject: [PATCH 134/266] QmlDesigner: Update ConnectionModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5be9117284a5eae2437b0a4fc9452694098713f3 Reviewed-by: Henning Gründl --- .../connectionseditor/ConnectionsDialog.qml | 14 +- .../ConnectionsDialogForm.qml | 217 ++- .../connectionseditor/ConnectionsListView.qml | 4 + .../connectioneditorstatements.cpp | 88 +- .../connectioneditorstatements.h | 7 +- .../connectioneditor/connectionmodel.cpp | 1276 ++++++++++++++++- .../connectioneditor/connectionmodel.h | 215 +++ .../connectioneditor/connectionview.cpp | 16 + .../connectioneditor/connectionview.h | 4 + .../designercore/include/qmlvisualnode.h | 5 +- 10 files changed, 1760 insertions(+), 86 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index b982e3424eb..aafd334046e 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -6,6 +6,9 @@ import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme PopupDialog { + + property alias backend: form.backend + titleBar: Row { spacing: 30 // TODO anchors.fill: parent @@ -22,9 +25,16 @@ PopupDialog { style: StudioTheme.Values.connectionPopupControlStyle width: 180 anchors.verticalCenter: parent.verticalCenter - model: ["mySpinbox", "foo", "backendObject"] + model: backend.signal.id.model ?? 0 + + onActivated: backend.signal.id.activateIndex(target.currentIndex) + property int currentTypeIndex: backend.signal.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex } + } - ConnectionsDialogForm {} + ConnectionsDialogForm { + id: form + } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index a54baa55c31..201790c6a67 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -14,12 +14,15 @@ Column { readonly property real verticalSpacing: 16 readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 + property var backend + component PopupLabel : Text { width: root.columnWidth color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize } + /* replaced by ConnectionModelStatementDelegate defined in C++ enum ActionType { CallFunction, Assign, @@ -27,7 +30,7 @@ Column { SetProperty, PrintMessage, Custom - } + } */ y: StudioTheme.Values.popupMargin width: parent.width @@ -47,7 +50,12 @@ Column { id: signal style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["Clicked", "Pressed", "Released"] + + model: backend.signal.name.model ?? 0 + + onActivated: backend.signal.name.activateIndex(signal.currentIndex) + property int currentTypeIndex: backend.signal.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: signal.currentIndex = signal.currentTypeIndex } StudioControls.TopLevelComboBox { @@ -56,21 +64,25 @@ Column { width: root.columnWidth textRole: "text" valueRole: "value" + ///model.getData(currentIndex, "role") + property int indexFromBackend: indexOfValue(backend.actionType) + onIndexFromBackendChanged: action.currentIndex = action.indexFromBackend + onActivated: backend.changeActionType(action.currentValue) model: [ - { value: ConnectionsDialogForm.CallFunction, text: qsTr("Call Function") }, - { value: ConnectionsDialogForm.Assign, text: qsTr("Assign") }, - { value: ConnectionsDialogForm.ChangeState, text: qsTr("Change State") }, - { value: ConnectionsDialogForm.SetProperty, text: qsTr("Set Property") }, - { value: ConnectionsDialogForm.PrintMessage, text: qsTr("Print Message") }, - { value: ConnectionsDialogForm.Custom, text: qsTr("Custom") } + { value: ConnectionModelStatementDelegate.CallFunction, text: qsTr("Call Function") }, + { value: ConnectionModelStatementDelegate.Assign, text: qsTr("Assign") }, + { value: ConnectionModelStatementDelegate.ChangeState, text: qsTr("Change State") }, + { value: ConnectionModelStatementDelegate.SetProperty, text: qsTr("Set Property") }, + { value: ConnectionModelStatementDelegate.PrintMessage, text: qsTr("Print Message") }, + { value: ConnectionModelStatementDelegate.Custom, text: qsTr("Custom") } ] } } // Call Function Row { - visible: action.currentValue === ConnectionsDialogForm.CallFunction + visible: action.currentValue === ConnectionModelStatementDelegate.CallFunction spacing: root.horizontalSpacing PopupLabel { text: qsTr("Item") } @@ -78,25 +90,36 @@ Column { } Row { - visible: action.currentValue === ConnectionsDialogForm.CallFunction + visible: action.currentValue === ConnectionModelStatementDelegate.CallFunction spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + id: functionId style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["mySpinBox", "myAnimation", "myCustomComponent"] + + model: backend.okStatement.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: ["start", "stop", "reset"] + model: backend.okStatement.function.name.model ?? 0 + + onActivated: backend.okStatement.function.name.activateIndex(functionName.currentIndex) + property int currentTypeIndex: backend.okStatement.function.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: functionName.currentIndex = functionName.currentTypeIndex } } // Assign Row { - visible: action.currentValue === ConnectionsDialogForm.Assign + visible: action.currentValue === ConnectionModelStatementDelegate.Assign spacing: root.horizontalSpacing PopupLabel { text: qsTr("From") } @@ -104,25 +127,68 @@ Column { } Row { - visible: action.currentValue === ConnectionsDialogForm.Assign + visible: action.currentValue === ConnectionModelStatementDelegate.Assign spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + id: rhsAssignmentId style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["value", "foo", "bar"] + //from - rhs - id + + model: backend.okStatement.rhsAssignment.id.model ?? 0 + + onActivated: backend.okStatement.rhsAssignment.id.activateIndex(rhsAssignmentId.currentIndex) + property int currentTypeIndex: backend.okStatement.rhsAssignment.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: rhsAssignmentId.currentIndex = rhsAssignmentId.currentTypeIndex } StudioControls.TopLevelComboBox { + id: lhsAssignmentId style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["myValue", "yourValue", "ourValue"] + //to lhs - id + model: backend.okStatement.lhs.id.model ?? 0 + + onActivated: backend.okStatement.lhs.id.activateIndex(lhsAssignmentId.currentIndex) + property int currentTypeIndex: backend.okStatement.lhs.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsAssignmentId.currentIndex = lhsAssignmentId.currentTypeIndex + } + } + + Row { + visible: action.currentValue === ConnectionModelStatementDelegate.Assign + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: rhsAssignmentName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //from - rhs - name + + model: backend.okStatement.rhsAssignment.name.model ?? 0 + + onActivated: backend.okStatement.rhsAssignment.name.activateIndex(rhsAssignmentName.currentIndex) + property int currentTypeIndex: backend.okStatement.rhsAssignment.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: rhsAssignmentName.currentIndex = rhsAssignmentName.currentTypeIndex + } + + StudioControls.TopLevelComboBox { + id: lhsAssignmentName + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + //to lhs - name + model: backend.okStatement.lhs.name.model ?? 0 + + onActivated: backend.okStatement.lhs.name.activateIndex(lhsAssignmentName.currentIndex) + property int currentTypeIndex: backend.okStatement.lhs.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsAssignmentName.currentIndex = lhsAssignmentName.currentTypeIndex } } // Change State Row { - visible: action.currentValue === ConnectionsDialogForm.ChangeState + visible: action.currentValue === ConnectionModelStatementDelegate.ChangeState spacing: root.horizontalSpacing PopupLabel { text: qsTr("State Group") } @@ -130,25 +196,36 @@ Column { } Row { - visible: action.currentValue === ConnectionsDialogForm.ChangeState + visible: action.currentValue === ConnectionModelStatementDelegate.ChangeState spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + id: stateGroups style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["main", "group1", "group2"] + model: backend.okStatement.stateTargets.model ?? 0 + + onActivated: backend.okStatement.stateTargets.activateIndex(stateGroups.currentIndex) + property int currentTypeIndex: backend.okStatement.stateTargets.currentIndex ?? 0 + onCurrentTypeIndexChanged: stateGroups.currentIndex = stateGroups.currentTypeIndex } StudioControls.TopLevelComboBox { + id: states style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["state1", "state2", "state3", "state4"] + + model: backend.okStatement.states.model ?? 0 + + onActivated: backend.okStatement.states.activateIndex(states.currentIndex) + property int currentTypeIndex: backend.okStatement.states.currentIndex ?? 0 + onCurrentTypeIndexChanged: states.currentIndex = states.currentTypeIndex } } // Set Property Row { - visible: action.currentValue === ConnectionsDialogForm.SetProperty + visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty spacing: root.horizontalSpacing PopupLabel { text: qsTr("Item") } @@ -156,52 +233,73 @@ Column { } Row { - visible: action.currentValue === ConnectionsDialogForm.SetProperty + visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty spacing: root.horizontalSpacing StudioControls.TopLevelComboBox { + id: lhsPropertyId style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["value", "foo", "bar"] + + model: backend.okStatement.lhs.id.model ?? 0 + + onActivated: backend.okStatement.lhs.id.activateIndex(lhsPropertyId.currentIndex) + property int currentTypeIndex: backend.okStatement.lhs.id.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsPropertyId.currentIndex = lhsPropertyId.currentTypeIndex + } StudioControls.TopLevelComboBox { + id: lhsPropertyName style: StudioTheme.Values.connectionPopupControlStyle width: root.columnWidth - model: ["myValue", "yourValue", "ourValue"] + model: backend.okStatement.lhs.name.model ?? 0 + + onActivated: backend.okStatement.lhs.name.activateIndex(lhsPropertyName.currentIndex) + property int currentTypeIndex: backend.okStatement.lhs.name.currentIndex ?? 0 + onCurrentTypeIndexChanged: lhsPropertyName.currentIndex = lhsPropertyName.currentTypeIndex } } PopupLabel { - visible: action.currentValue === ConnectionsDialogForm.SetProperty + visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty text: qsTr("Value") } StudioControls.TextField { - visible: action.currentValue === ConnectionsDialogForm.SetProperty + id: setPropertyArgument + visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty width: root.width - text: "This is a test" actionIndicatorVisible: false translationIndicatorVisible: false + + text: backend.okStatement.stringArgument.text ?? "" + onEditingFinished: { + backend.okStatement.stringArgument.activateText(setPropertyArgument.text) + } } // Print Message PopupLabel { - visible: action.currentValue === ConnectionsDialogForm.PrintMessage + visible: action.currentValue === ConnectionModelStatementDelegate.PrintMessage text: qsTr("Message") } StudioControls.TextField { - visible: action.currentValue === ConnectionsDialogForm.PrintMessage + id: messageString + visible: action.currentValue === ConnectionModelStatementDelegate.PrintMessage width: root.width - text: "my value is" actionIndicatorVisible: false translationIndicatorVisible: false + text: backend.okStatement.stringArgument.text ?? "" + onEditingFinished: { + backend.okStatement.stringArgument.activateText(messageString.text) + } } // Custom PopupLabel { - visible: action.currentValue === ConnectionsDialogForm.Custom + visible: action.currentValue === ConnectionModelStatementDelegate.Custom text: qsTr("Custom Connections can only be edited with the binding editor") anchors.left: parent.left anchors.right: parent.right @@ -217,9 +315,60 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionsDialogForm.Custom + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && !backend.hasCondition - onClicked: console.log("ADD CONDITION") + onClicked: backend.addCondition() + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.connectionPopupButtonStyle + width: 160 + buttonIcon: qsTr("Remove Condition") + iconSize: StudioTheme.Values.baseFontSize + iconFont: StudioTheme.Constants.font + anchors.horizontalCenter: parent.horizontalCenter + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition + + onClicked: backend.removeCondition() + } + + Flow { + spacing: root.horizontalSpacing + width: root.width + Repeater { + + model: backend.conditionListModel + Text { + text: value + color: "white" + Rectangle { + z: -1 + opacity: 0.2 + color: { + if (type === ConditionListModel.Invalid) + return "red" + if (type === ConditionListModel.Operator) + return "blue" + if (type === ConditionListModel.Literal) + return "green" + if (type === ConditionListModel.Variable) + return "yellow" + } + anchors.fill: parent + } + } + } + } + + TextInput { + id: commandInput + width: root.width + onAccepted: backend.conditionListModel.command(commandInput.text) + } + + Text { + text: "invalid " + backend.conditionListModel.error + visible: !backend.conditionListModel.valid } // Editor @@ -231,7 +380,7 @@ Column { Text { anchors.centerIn: parent - text: "backend.myValue = mySpinbox.value" + text: backend.source color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index e1fa90265bc..179c6d220e0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -38,12 +38,14 @@ ListView { onCurrentIndexChanged: { root.currentIndex = root.model.currentIndex + dialog.backend.currentRow = root.currentIndex } data: [ ConnectionsDialog { id: dialog visible: false + backend: root.model.delegate } ] @@ -79,6 +81,8 @@ ListView { onClicked: { root.model.currentIndex = index root.currentIndex = index + dialog.backend.currentRow = index + dialog.popup(mouseArea) } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp index 5a9f0665f74..d1c1b2d7115 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -187,29 +187,7 @@ struct JSOverload return "console.log(" + std::visit(JSOverload{}, consoleLog.argument) + ")"; } - QString operator()(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 {}; - }; - } + QString operator()(const ConditionToken &token) { return toJavascript(token); } QString operator()(const ConnectionEditorStatements::MatchedCondition &matched) { @@ -236,7 +214,7 @@ struct JSOverload if (isEmptyStatement(statement)) return {}; - return std::visit(JSOverload{}, statement) + ";\n"; + return std::visit(JSOverload{}, statement); } QString operator()(const ConnectionEditorStatements::ConditionalStatement &conditional) @@ -334,20 +312,60 @@ QString ConnectionEditorStatements::toDisplayName(const Handler &handler) return toDisplayName(statement); } -MatchedStatement ConnectionEditorStatements::okStatement(const ConnectionEditorStatements::Handler &handler) +MatchedStatement &ConnectionEditorStatements::okStatement( + ConnectionEditorStatements::Handler &handler) { - return std::visit(Overload{[](const ConnectionEditorStatements::MatchedStatement &var) { return var; }, - [](const ConnectionEditorStatements::ConditionalStatement &statement) { - return statement.ok; - }}, + MatchedStatement statement; + std::visit([statement](auto &test) { return statement; }, handler); + + return std::visit(Overload{[](ConnectionEditorStatements::MatchedStatement &var) + -> MatchedStatement & { return var; }, + [](ConnectionEditorStatements::ConditionalStatement &statement) + -> MatchedStatement & { return statement.ok; }}, handler); } -MatchedStatement ConnectionEditorStatements::koStatement(const ConnectionEditorStatements::Handler &handler) +MatchedStatement &ConnectionEditorStatements::koStatement( + ConnectionEditorStatements::Handler &handler) { - return std::visit(Overload{[](const ConnectionEditorStatements::ConditionalStatement &statement) { - return statement.ko; - }, - [](const auto &) -> ConnectionEditorStatements::MatchedStatement { return {}; }}, - 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; +} + +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 index 1596ce2bfe6..908b7305c69 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -115,9 +115,12 @@ 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(const ConnectionEditorStatements::Handler &handler); -QMLDESIGNER_EXPORT MatchedStatement koStatement(const ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement &okStatement(ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT MatchedStatement &koStatement(ConnectionEditorStatements::Handler &handler); + +QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ConnectionEditorStatements::Handler &handler); } // namespace ConnectionEditorStatements diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 6fe8af59eb7..ad9d463437e 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 @@ -46,8 +47,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); } @@ -264,7 +265,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 @@ -399,6 +403,11 @@ void ConnectionModel::handleException() resetModel(); } +ConnectionModelBackendDelegate *ConnectionModel::delegate() const +{ + return m_delegate; +} + void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (topLeft != bottomRight) { @@ -553,4 +562,1253 @@ 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) +{ + connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this]() { + handleTargetChanged(); + }); + + connect(&m_okStatementDelegate, + &ConnectionModelStatementDelegate::statementChanged, + this, + [this]() { handleOkStatementChanged(); }); + + 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 = "condition"; + newCondition.statements.append(variable); + + ConnectionEditorStatements::ConditionalStatement conditionalStatement; + + conditionalStatement.ok = okStatement; + conditionalStatement.condition = newCondition; + + m_handler = conditionalStatement; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", + [&]() { + signalHandlerProperty.setSource(newSource); + }); + + setSource(signalHandlerProperty.source()); + + setupHandlerAndStatements(); + setupCondition(); +} + +void ConnectionModelBackendDelegate::removeCondition() +{ + qDebug() << Q_FUNC_INFO; + + ConnectionEditorStatements::MatchedStatement okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + m_handler = okStatement; + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", + [&]() { + signalHandlerProperty.setSource(newSource); + }); + + setSource(signalHandlerProperty.source()); + + setupHandlerAndStatements(); + setupCondition(); +} + +int ConnectionModelBackendDelegate::currentRow() const +{ + return m_currentRow; +} + +QString removeOnFromSignalName(const QString &signal) +{ + 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; + + //setup + + ConnectionModel *model = qobject_cast(parent()); + + qDebug() << Q_FUNC_INFO << i << model; + + QTC_ASSERT(model, 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()); + + qDebug() << Q_FUNC_INFO + << removeOnFromSignalName(QString::fromUtf8(signalHandlerProperty.name())); + + 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; +} + +void ConnectionModelBackendDelegate::setHasCondition(bool b) +{ + if (b == m_hasCondition) + return; + + m_hasCondition = b; + emit hasConditionChanged(); +} + +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(); +} + +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()); + + 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); + } + + 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(); + + qDebug() << Q_FUNC_INFO << m_signalDelegate.id() << handlerName; + + const auto parentModelNode = signalHandlerProperty.parentModelNode(); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + const auto newId = m_signalDelegate.id(); + + 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); + }); +} + +void ConnectionModelBackendDelegate::handleOkStatementChanged() +{ + ConnectionEditorStatements::MatchedStatement &okStatement + = ConnectionEditorStatements::okStatement(m_handler); + + okStatement = m_okStatementDelegate.statement(); //TODO why? + + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + ConnectionModel *model = qobject_cast(parent()); + + QTC_ASSERT(model, return ); + + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + model->connectionView() + ->executeInTransaction("ConnectionModelBackendDelegate::handleOkStatementChanged", + [&]() { signalHandlerProperty.setSource(newSource); }); + + setSource(signalHandlerProperty.source()); +} + +void ConnectionModelBackendDelegate::handleConditionChanged() +{ + qDebug() << Q_FUNC_INFO; + + ConnectionModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return ); + QTC_ASSERT(model->connectionView()->isAttached(), return ); + + auto view = model->connectionView(); + + ConnectionEditorStatements::MatchedCondition &condition + = ConnectionEditorStatements::matchedCondition(m_handler); + condition = m_conditionListModel.condition(); //why? + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); + + qDebug() << Q_FUNC_INFO << "new source" << newSource; + + SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + + try { + RewriterTransaction transaction = view->beginRewriterTransaction( + "ConnectionModelBackendDelegate::handleConditionChanged"); + signalHandlerProperty.setSource(newSource); + transaction.commit(); + } catch (const Exception &e) { + m_conditionListModel.setInvalid(e.description()); + } + + setSource(signalHandlerProperty.source()); +} + +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(); + + 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); +} + +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)); +} + +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) +{ + m_tokens.insert(index, valueToToken(value)); + validateAndRebuildTokens(); + resetModel(); +} + +void ConditionListModel::updateToken(int index, const QString &value) +{ + m_tokens[index] = valueToToken(value); + validateAndRebuildTokens(); + resetModel(); +} + +void ConditionListModel::appendToken(const QString &value) +{ + insertToken(rowCount(), value); + validateAndRebuildTokens(); + resetModel(); +} + +void ConditionListModel::removeToken(int index) +{ + m_tokens.remove(index, 1); + validateAndRebuildTokens(); + 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) +{ + m_valid = false; + m_errorMessage = errorMessage; + emit errorChanged(); + emit validChanged(); +} + +void ConditionListModel::setValid() +{ + m_valid = true; + m_errorMessage.clear(); + emit errorChanged(); + emit validChanged(); +} + +QString ConditionListModel::error() const +{ + return m_errorMessage; +} + +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; + + 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)); + + for (const auto &token : m_tokens) + qDebug() << token.value; + + 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++; + } + return -1; +} + +void ConditionListModel::validateAndRebuildTokens() +{ + 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 %").arg(invalidToken)); + return; + } + + if (int firstError = checkOrder() != -1) { + setInvalid(tr("Invalid order at %1").arg(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::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(); + 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..499e0abea8d 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,14 @@ class SignalHandlerProperty; class VariantProperty; class ConnectionView; +class ConnectionModelBackendDelegate; class ConnectionModel : public QStandardItemModel { Q_OBJECT + + Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT) + public: enum ColumnRoles { TargetModelNodeRow = 0, @@ -71,11 +80,217 @@ 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; +}; + +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) + +public: + enum ConditionType { Invalid, Operator, Literal, Variable }; + 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); + + bool valid() const; + bool empty() const; + + //for debugging + Q_INVOKABLE void command(const QString &string); + + void setInvalid(const QString &errorMessage); + void setValid(); + + QString error() const; + +signals: + void validChanged(); + void emptyChanged(); + void conditionChanged(); + void errorChanged(); + +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; +}; + +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(); + 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(QString source READ source NOTIFY sourceChanged) + +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(); + +signals: + void currentRowChanged(); + void actionTypeChanged(); + void hasConditionChanged(); + void sourceChanged(); + +private: + int currentRow() const; + void setCurrentRow(int i); + + void handleException(); + bool hasCondition() const; + void setHasCondition(bool b); + ActionType actionType() const; + PropertyTreeModelDelegate *signal(); + ConnectionModelStatementDelegate *okStatement(); + ConnectionModelStatementDelegate *koStatement(); + ConditionListModel *conditionListModel(); + QString source() const; + void setSource(const QString &source); + void setupCondition(); + void setupHandlerAndStatements(); + + void handleTargetChanged(); + void handleOkStatementChanged(); + void handleConditionChanged(); + + 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; + QString m_source; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 221400dd0f0..5ee81b86a15 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -8,6 +8,7 @@ #include "bindingmodel.h" #include "connectionmodel.h" #include "dynamicpropertiesmodel.h" +#include "propertytreemodel.h" #include "theme.h" #include @@ -73,6 +74,21 @@ public: {"dynamicPropertiesModel", QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "DynamicPropertiesModelBackendDelegate"); + + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "ConnectionModelStatementDelegate"); + + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "ConditionListModel"); + Theme::setupTheme(engine()); setMinimumWidth(195); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 5997f230ad8..507637b4b91 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 { @@ -88,6 +90,8 @@ private: //variables 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/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; }; From 03cb6516276f9c0b2437c945315d56139dae18f1 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 29 Aug 2023 16:50:19 +0200 Subject: [PATCH 135/266] QmlDesigner: Update tests for connection editor Change-Id: I4b5e0e3cee269454089a3951e2d2d68eb162fa4b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian Reviewed-by: Thomas Hartmann --- .../connectioneditor/tst_connectioneditor.cpp | 79 +++++++++++-------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp index 1deb5ac22c8..7df48b92f37 100644 --- a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -54,41 +54,47 @@ void tst_ConnectionEditor::test01_data() QTest::addColumn("statement"); QTest::addColumn("expectedValue"); - QTest::newRow(countOne()) - << "if (object.atr == 3 && kmt == 43) {kMtr.lptr = 3;} else {kMtr.ptr = 4;}" << true; + QTest::newRow("if then else property set") + << "if (object.atr === 3 && kmt === 43) {kMtr.lptr = 3;} else {kMtr.ptr = 4;}" << true; - QTest::newRow(countOne()) << "{}" << true; - QTest::newRow(countOne()) << "{console.log(\"test\")}" << true; - QTest::newRow(countOne()) << "{someItem.functionCall()}" << true; - QTest::newRow(countOne()) << "{someItem.width.kkk = 10}" << true; - QTest::newRow(countOne()) << "{someItem.color = \"red\"}" << true; - QTest::newRow(countOne()) << "{someItem.state = \"state\"}" << true; - QTest::newRow(countOne()) << "{someItem.text = \"some string\"}" << true; - QTest::newRow(countOne()) << "{someItem.speed = 1.0}" << true; - QTest::newRow(countOne()) << "{someItem.speed = someOtherItem.speed}" << true; - QTest::newRow(countOne()) << "{if (someItem.bool) { someItem.speed = someOtherItem.speed }}" - << true; - QTest::newRow(countOne()) << "{if (someItem.bool) { someItem.functionCall() }}" << true; - QTest::newRow(countOne()) << "{if (someItem.bool) { console.log(\"ok\") }}" << true; - QTest::newRow(countOne()) + 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(countOne()) << "{if (someItem.bool && someItem.someBool2) { } else { } }" - << true; - QTest::newRow(countOne()) << "{if (someItem.width > 10) { }}" << true; - QTest::newRow(countOne()) << "{if (someItem.width > 10 && someItem.someBool) { }}" << true; - QTest::newRow(countOne()) << "{if (someItem.width > 10 || someItem.someBool) { }}" << true; - QTest::newRow(countOne()) << "{if (someItem.width === someItem.height) { }}" << true; - QTest::newRow(countOne()) << "{if (someItem.width === someItem.height) {} }" << 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(countOne()) << "{someItem.complexCall(blah)}" << false; - QTest::newRow(countOne()) << "{someItem.width = someItem.Height + 10}" << false; - QTest::newRow(countOne()) + 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(countOne()) << "{if (someItem.width == someItem.height) {} }" << false; - QTest::newRow(countOne()) << "{if (someItem.width = someItem.height) {} }" << false; - QTest::newRow(countOne()) << "{if (someItem.width > someItem.height + 10) {} }" << false; - QTest::newRow(countOne()) << "{if (someItem.width > someItem.height) ak = 2; }" << 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() @@ -159,6 +165,17 @@ void tst_ConnectionEditor::displayStrings_data() 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() @@ -262,7 +279,7 @@ void tst_ConnectionEditor::parseSetState() QVERIFY(std::holds_alternative(parsedStatement)); auto stateSet = std::get(parsedStatement); - QCOMPARE(stateSet.nodeId, "someItem.state"); // TODO should be just someItem + 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 From 9bcb31b1860909ce424128dfbaf13ba55a760267 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 30 Aug 2023 11:22:29 +0300 Subject: [PATCH 136/266] QmlDesigner: Modify the failure for two tests - Multiple statements are not allowed - Matched statement should have the same type in the case that we have both Ok and Ko statements Change-Id: I2aedc1fe7f7f2096af0293c019d0caeb9771646f Reviewed-by: Thomas Hartmann --- .../connectioneditorevaluator.cpp | 53 ++++++++++++++----- .../connectioneditorevaluator.h | 4 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index e77e270304f..fccb5b11941 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -84,6 +84,15 @@ inline bool isAcceptedIfBinaryOperator(const int &operation) } } +inline bool areOfTheSameTypeMatch(const MatchedStatement &m1, const MatchedStatement &m2) +{ + return std::visit(Overload{[](auto m1, auto m2) -> bool { + return std::is_same(); + }}, + m1, + m2); +} + class NodeStatus { public: @@ -737,10 +746,7 @@ ConnectionEditorEvaluator::ConnectionEditorEvaluator() : d(std::make_unique()) {} -ConnectionEditorEvaluator::~ConnectionEditorEvaluator() -{ - delete d.release(); -} +ConnectionEditorEvaluator::~ConnectionEditorEvaluator() {} ConnectionEditorEvaluator::Status ConnectionEditorEvaluator::status() const { @@ -872,11 +878,6 @@ bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::Program *prog return true; } -bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::StatementList *statementList) -{ - return d->checkValidityAndReturn(true); -} - bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::IfStatement *ifStatement) { if (d->m_ifStatement++) @@ -903,11 +904,6 @@ bool ConnectionEditorEvaluator::visit(QmlJS::AST::IdentifierExpression *identifi return d->checkValidityAndReturn(true); } -bool ConnectionEditorEvaluator::visit([[maybe_unused]] QmlJS::AST::ExpressionStatement *expressionStatement) -{ - return d->checkValidityAndReturn(true); -} - bool ConnectionEditorEvaluator::visit(QmlJS::AST::BinaryExpression *binaryExpression) { if (d->isInIfCondition()) { @@ -1070,6 +1066,35 @@ void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::CallExpres d->m_acceptLogArgument = false; } +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement *ifStatement) +{ + if (status() != UnFinished) + return; + + std::visit(Overload{[this](const ConnectionEditorStatements::ConditionalStatement &statement) { + if (!ConnectionEditorStatements::isEmptyStatement(statement.ok) + && !ConnectionEditorStatements::isEmptyStatement(statement.ko)) { + qDebug() << Q_FUNC_INFO + << areOfTheSameTypeMatch(statement.ok, statement.ko); + if (!areOfTheSameTypeMatch(statement.ok, statement.ko)) { + d->checkValidityAndReturn( + false, "Matched statements types are mismatched"); + } + } + }, + [](auto) {}}, + d->m_handler); +} + +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"); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h index c4465fcc32f..2cc285ed6c5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.h @@ -32,10 +32,8 @@ protected: void postVisit(QmlJS::AST::Node *node) override; bool visit(QmlJS::AST::Program *program) override; - bool visit(QmlJS::AST::StatementList *statementList) override; bool visit(QmlJS::AST::IfStatement *ifStatement) override; bool visit(QmlJS::AST::IdentifierExpression *identifier) override; - bool visit(QmlJS::AST::ExpressionStatement *expressionStatement) override; bool visit(QmlJS::AST::BinaryExpression *binaryExpression) override; bool visit(QmlJS::AST::FieldMemberExpression *fieldExpression) override; bool visit(QmlJS::AST::CallExpression *callExpression) override; @@ -45,6 +43,8 @@ protected: 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; From 6eba95d4064bfced6b4fdd1304ff077816a4a892 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 28 Aug 2023 16:59:21 +0200 Subject: [PATCH 137/266] QmlDesigner: Move MCUs qmlproject template Change-Id: I513062dac4e47e4bf8b113351ceaf040c1180675 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../app_mcu.qmlproject.tpl} | 0 .../studio_templates/projects/application-mcu/wizard.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename share/qtcreator/qmldesigner/studio_templates/projects/{app_mcu.qmlproject => application-mcu/app_mcu.qmlproject.tpl} (100%) 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..959d3f495c2 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 }, From b46a0e8e1ddc24636e4d09cb0469db9298ff6ddc Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 28 Aug 2023 17:25:01 +0200 Subject: [PATCH 138/266] QmlDesigner: Fix MCUDefaultStyle usage via wizard Task-number: QDS-10420 Change-Id: Icef96452c1f7c84accc95b67f3e0b2044e8416c4 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../application-mcu/MCUDefaultStyle/Button.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/CheckBox.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/DefaultStyle.qml | 4 ++-- .../application-mcu/MCUDefaultStyle/Dial.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/ProgressBar.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/RadioButton.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/Slider.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/SwipeView.qml | 6 +++--- .../application-mcu/MCUDefaultStyle/Switch.qml | 6 +++--- .../projects/application-mcu/MCUDefaultStyle/qmldir | 12 +++++++++++- .../projects/application-mcu/wizard.json | 2 +- 11 files changed, 38 insertions(+), 28 deletions(-) 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/application-mcu/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json index 959d3f495c2..5c27a294467 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json @@ -183,7 +183,7 @@ }, { "source": "MCUDefaultStyle", - "target": "%{ProjectDirectory}/MCUDefaultStyle" + "target": "%{ProjectDirectory}/imports/MCUDefaultStyle" }, { "isBinary": true, From 83ad97aa56db58a41ed623dddeed7161dba37969 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 30 Aug 2023 13:08:49 +0300 Subject: [PATCH 139/266] QmlDesigner: Use proper features of std::variant instead of overloads Change-Id: I6423155701b92d09011aaf913d845fa59148b207 Reviewed-by: Marco Bubke --- .../connectioneditorevaluator.cpp | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index fccb5b11941..b375ee613b2 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -84,15 +84,6 @@ inline bool isAcceptedIfBinaryOperator(const int &operation) } } -inline bool areOfTheSameTypeMatch(const MatchedStatement &m1, const MatchedStatement &m2) -{ - return std::visit(Overload{[](auto m1, auto m2) -> bool { - return std::is_same(); - }}, - m1, - m2); -} - class NodeStatus { public: @@ -1071,19 +1062,12 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement *ifStatement) if (status() != UnFinished) return; - std::visit(Overload{[this](const ConnectionEditorStatements::ConditionalStatement &statement) { - if (!ConnectionEditorStatements::isEmptyStatement(statement.ok) - && !ConnectionEditorStatements::isEmptyStatement(statement.ko)) { - qDebug() << Q_FUNC_INFO - << areOfTheSameTypeMatch(statement.ok, statement.ko); - if (!areOfTheSameTypeMatch(statement.ok, statement.ko)) { - d->checkValidityAndReturn( - false, "Matched statements types are mismatched"); - } - } - }, - [](auto) {}}, - d->m_handler); + 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) From 518977daed502baba745aad7742f69be41058b8f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 30 Aug 2023 12:59:12 +0200 Subject: [PATCH 140/266] QmlDesigner: Fix macOS build Unused variable in various cases Change-Id: I665c037b21446c4ba4eabb1b73ac5a03b22547e2 Reviewed-by: Marco Bubke --- .../components/connectioneditor/connectioneditorevaluator.cpp | 4 ++-- .../connectioneditor/connectioneditorstatements.cpp | 1 - .../components/connectioneditor/connectionmodel.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp index b375ee613b2..d44077e4ff6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorevaluator.cpp @@ -1057,7 +1057,7 @@ void ConnectionEditorEvaluator::endVisit([[maybe_unused]] QmlJS::AST::CallExpres d->m_acceptLogArgument = false; } -void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement *ifStatement) +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement * /*ifStatement*/) { if (status() != UnFinished) return; @@ -1070,7 +1070,7 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::IfStatement *ifStatement) } } -void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList *statementList) +void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statementList*/) { if (status() != UnFinished) return; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp index d1c1b2d7115..195f6f1ead6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -316,7 +316,6 @@ MatchedStatement &ConnectionEditorStatements::okStatement( ConnectionEditorStatements::Handler &handler) { MatchedStatement statement; - std::visit([statement](auto &test) { return statement; }, handler); return std::visit(Overload{[](ConnectionEditorStatements::MatchedStatement &var) -> MatchedStatement & { return var; }, diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index ad9d463437e..5311046ddc1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -1425,7 +1425,7 @@ ConditionListModel::ConditionListModel(ConnectionModel *parent) : m_connectionModel(parent), m_condition(emptyCondition) {} -int ConditionListModel::rowCount(const QModelIndex &parent) const +int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const { return m_tokens.size(); } From 011ec75e88fa2e4f06674ad20cb5933200376815 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 29 Aug 2023 18:01:19 +0200 Subject: [PATCH 141/266] ADS: fix QMetaObject::invokeMethod: No such method Change-Id: Icdd8c3de7b95c60186ea30f0c40b7feeed322e28 Reviewed-by: Tim Jenssen --- src/libs/advanceddockingsystem/dockmanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 20321c9b818..b389a88bc9e 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -713,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); From 78cfd73037245ad4c928d7ab93226df41668f375 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 30 Aug 2023 16:46:41 +0200 Subject: [PATCH 142/266] ADS: Fix crash in auto hide tab * Crash is caused by dragging an autohide tab out when it is an icon only * The crash can actually happen with non icon only tabs, but is masked by the orientation check Base repository commit 61573cba1633600dddcbf5afb7d8ed1645f82b30 Change-Id: I1aebfd6455983662050aaf82a6506a6a5a40421e Reviewed-by: Tim Jenssen --- src/libs/advanceddockingsystem/autohidetab.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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; From 315da21424665358495af19da5e71939da63b605 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 30 Aug 2023 16:50:18 +0200 Subject: [PATCH 143/266] ADS: Delete implicit ScrollArea * Delete implicit ScrollArea when content widget is deleted in hideDockWidget() Base repository commit 2178b52621ed16f9846d6f80ecb59706b9d31921 Change-Id: I2e593f3be77fc8ad988e7882707619803ef70cfc Reviewed-by: Tim Jenssen --- src/libs/advanceddockingsystem/dockwidget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index c9ffcc1bef9..75d00438322 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -162,6 +162,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; } From 939b28a0761991096355392bc86a3db8dd03ca67 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 29 Aug 2023 17:37:54 +0200 Subject: [PATCH 144/266] QmlDesigner: Fix hardcoded Controls2 config file Task-number: QDS-10545 Change-Id: I8e96ea5f4b409a8b378f94e45b413d6315ea8170 Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../qmldesigner/puppetenvironmentbuilder.cpp | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index e52c0c70581..fbf76b96630 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -83,10 +83,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(); + } } } From 2f2d8bf9e83a300c6cad127cf2d3c47a7da8dc04 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 29 Aug 2023 23:08:43 +0300 Subject: [PATCH 145/266] QmlDesigner: Implement effect maker preview zoom controls Also fix a memory leak on removing a node. Fixes: QDS-10546 Change-Id: Ifc32b6d7f6f4c6b8ce63a080b159c8ae66865a79 Reviewed-by: Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectMakerPreview.qml | 44 +++++++++++++++---- .../effectmaker/effectmakermodel.cpp | 2 + 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index a5bc05ed590..a73e1503b40 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -2,6 +2,7 @@ // 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 @@ -18,18 +19,25 @@ Column { height: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground - Row { - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: 5 + RowLayout { + anchors.fill: parent spacing: 5 + anchors.rightMargin: 5 + anchors.leftMargin: 5 + + Item { + Layout.fillWidth: true + } HelperWidgets.AbstractButton { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomOut_medium tooltip: qsTr("Zoom out") - onClicked: {} // TODO + onClicked: { + if (previewImage.scale > .4) + previewImage.scale -= .2 + } } HelperWidgets.AbstractButton { @@ -37,7 +45,10 @@ Column { buttonIcon: StudioTheme.Constants.zoomIn_medium tooltip: qsTr("Zoom In") - onClicked: {} // TODO + onClicked: { + if (previewImage.scale < 2) + previewImage.scale += .2 + } } HelperWidgets.AbstractButton { @@ -45,7 +56,13 @@ Column { buttonIcon: StudioTheme.Constants.cornersAll tooltip: qsTr("Zoom Fit") - onClicked: {} // TODO + onClicked: { + previewImage.scale = 1 + } + } + + Item { + Layout.fillWidth: true } Column { @@ -81,18 +98,29 @@ Column { } Rectangle { // preview image - id: previewImage + 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/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 9188c43254a..643eddc6b2f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -48,7 +48,9 @@ void EffectMakerModel::addNode(const QString &nodeQenPath) void EffectMakerModel::removeNode(int idx) { beginRemoveRows({}, idx, idx); + CompositionNode *node = m_nodes.at(idx); m_nodes.removeAt(idx); + delete node; endRemoveRows(); } From a5a5b00f5865d057fc9eeb9121ca84fc160ab074 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 30 Aug 2023 13:43:24 +0300 Subject: [PATCH 146/266] QmlDesigner: Add core uniforms and features for shaders Task-number: QDS-10499 Change-Id: I12b39d29133accd012ac019def0c4a34ee84d8e5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/compositionnode.cpp | 15 +++ .../effectmaker/effectmakermodel.cpp | 98 +++++++++++++++++++ .../components/effectmaker/effectmakermodel.h | 11 +++ .../effectmaker/effectmakeruniformsmodel.cpp | 5 + .../effectmaker/effectmakeruniformsmodel.h | 2 + .../components/effectmaker/shaderfeatures.cpp | 80 +++++++++++++++ .../components/effectmaker/shaderfeatures.h | 39 ++++++++ .../components/effectmaker/uniform.cpp | 4 +- .../components/effectmaker/uniform.h | 1 + 10 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7f94c8ff256..cd80c894ea6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -723,6 +723,7 @@ extend_qtc_plugin(QmlDesigner uniform.cpp uniform.h effectutils.cpp effectutils.h effectmakercontextobject.cpp effectmakercontextobject.h + shaderfeatures.cpp shaderfeatures.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index 0414d3ef44c..eb2e7b3b322 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -84,6 +84,21 @@ void CompositionNode::parse(const QString &qenPath) 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/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 643eddc6b2f..228154fc6a4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -4,6 +4,7 @@ #include "effectmakermodel.h" #include "compositionnode.h" +#include "uniform.h" #include @@ -54,4 +55,101 @@ void EffectMakerModel::removeNode(int idx) endRemoveRows(); } +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() +{ + 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; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 4193b918f22..de26b2f38f3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -3,12 +3,15 @@ #pragma once +#include "shaderfeatures.h" + #include #include namespace QmlDesigner { class CompositionNode; +class Uniform; class EffectMakerModel : public QAbstractListModel { @@ -42,10 +45,18 @@ private: bool isValidIndex(int idx) const; + const QList allUniforms(); + + const QString getBufUniform(); + const QString getVSUniforms(); + const QString getFSUniforms(); + QList m_nodes; int m_selectedIndex = -1; bool m_isEmpty = true; + + ShaderFeatures m_shaderFeatures; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp index 8bd3926ef1c..dac01905b67 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp @@ -67,4 +67,9 @@ void EffectMakerUniformsModel::addUniform(Uniform *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 index ecaa8752d65..1d69d6d1b27 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h @@ -25,6 +25,8 @@ public: void addUniform(Uniform *uniform); + QList uniforms() const; + private: enum Roles { NameRole = Qt::UserRole + 1, 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/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index f83cf26881f..e15cbb885ca 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -261,7 +261,7 @@ QString Uniform::stringFromType(Uniform::Type type) else if (type == Type::Color) return "color"; else if (type == Type::Sampler) - return "image"; + return "sampler2D"; else if (type == Type::Define) return "define"; @@ -285,7 +285,7 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Vec4; else if (typeString == "color") return Uniform::Type::Color; - else if (typeString == "image") + else if (typeString == "sampler2D") return Uniform::Type::Sampler; else if (typeString == "define") return Uniform::Type::Define; diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 761a22199f5..7117a7591de 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -68,6 +68,7 @@ public: static QString stringFromType(Uniform::Type type); static Uniform::Type typeFromString(const QString &typeString); + static QString typeToUniform(Uniform::Type type); signals: void uniformValueChanged(); From 9324f4bd4b56e4670a0344c407a3ad7b311520ee Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 31 Aug 2023 12:34:47 +0300 Subject: [PATCH 147/266] QmlDesigner: Fix sync background color to 3D scene environment The sync color setting expects an array (for potential gradient), but it was set to plain color when sync was turned on. Fixes: QDS-10527 Change-Id: Iba27aad8e2cd8c93e76a89984f739ddb6c4e2e7f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index de93e1cd1c8..220edc24476 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -225,8 +225,9 @@ Item { if ("syncBackgroundColor" in toolStates) { syncBackgroundColor = toolStates.syncBackgroundColor; if (syncBackgroundColor) { - var color = _generalHelper.sceneEnvironmentColor(sceneId); - updateViewStates({"selectBackgroundColor": color}) + var color = []; + color[0] = _generalHelper.sceneEnvironmentColor(sceneId); + updateViewStates({"selectBackgroundColor": color}); } } else if (resetToDefault) { syncBackgroundColor = false; From 96e675cd2619fc4a87f4d8e7c3ac317e8e906560 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 31 Aug 2023 13:28:02 +0300 Subject: [PATCH 148/266] QmlDesigner: Fix 3D view grid color setting Grid color was not loaded correctly on scene creation, because there was a mismatch on how it was set. Normally, it was set as a color array, but on scene creation it was set as a single color. Changed it to be always set as a single color, as there is no reason to allow array of colors for grid. Fixes: QDS-10495 Change-Id: Idbe5d52a425eed7aed6beef9e3712b26519e5843 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/edit3d/edit3dviewconfig.h | 7 ++++++- src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h index b077fed6a46..5c461cc7327 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h @@ -40,7 +40,12 @@ public: static void setColors(AbstractView *view, View3DActionType type, const QList &colorConfig) { - setVariant(view, type, QVariant::fromValue(colorConfig)); + QVariant param; + if (type == View3DActionType::SelectGridColor) + param = colorConfig.isEmpty() ? QColor() : colorConfig[0]; + else + param = QVariant::fromValue(colorConfig); + setVariant(view, type, param); } template diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 220edc24476..8a4db279628 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -204,8 +204,8 @@ Item { } } - if ("selectGridColor" in viewStates && viewStates.selectGridColor.length === 1) - viewRoot.gridColor = viewStates.selectGridColor[0] + if ("selectGridColor" in viewStates) + viewRoot.gridColor = viewStates.selectGridColor } // If resetToDefault is true, tool states not specifically set to anything will be reset to From 757c1734df6da2cf6b7e550b99df95000bc34770 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 28 Aug 2023 15:33:32 +0300 Subject: [PATCH 149/266] QmlDesigner: Overhaul 3D snapping UI Fixes: QDS-10532 Change-Id: I00dc78831aed62e0a93000938f514a04c2ab3a50 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../SnapConfigurationDialog.qml | 367 ++++++++++-------- .../components/edit3d/edit3dview.cpp | 181 +++------ .../components/edit3d/edit3dview.h | 10 +- .../components/edit3d/edit3dwidget.cpp | 28 -- .../components/edit3d/edit3dwidget.h | 3 - .../components/edit3d/snapconfiguration.cpp | 127 ++++-- .../components/edit3d/snapconfiguration.h | 44 ++- .../qmldesigner/qmldesignerconstants.h | 6 +- .../utils/designersettings.cpp | 11 +- .../qmldesignerbase/utils/designersettings.h | 1 + .../qml2puppet/editor3d/generalhelper.h | 4 +- 11 files changed, 406 insertions(+), 376 deletions(-) diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml index d34a210ae8c..b246eb62daf 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/SnapConfigurationDialog.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QtQuickDesignerTheme import HelperWidgets as HelperWidgets import StudioControls as StudioControls @@ -13,175 +14,229 @@ Rectangle { property int toolTipDelay: 1000 + width: 230 + height: 270 color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border - Column { - id: col - padding: 8 - spacing: 4 + Connections { + target: rootView - Rectangle { - id: ctrlRect - width: root.width - 16 - height: posIntValue.height + rotIntValue.height + scaleIntValue.height + 32 + // 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 + } - color: StudioTheme.Values.themePanelBackground - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border + ColumnLayout { + anchors.fill: parent + spacing: 0 - Column { - padding: 8 - spacing: 8 - Row { - height: posIntValue.height - width: parent.width - 16 - spacing: StudioTheme.Values.sectionRowSpacing + RowLayout { + height: 32 + Layout.topMargin: 8 + Layout.rightMargin: 8 + Layout.leftMargin: 8 + Layout.fillWidth: true + spacing: 16 - Text { - id: posIntLabel - text: qsTr("Position Snap Interval:") - color: enabled ? StudioTheme.Values.themeTextColor - : StudioTheme.Values.themeTextColorDisabled - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignRight - height: posIntValue.height - } + Rectangle { + width: 40 + height: 40 + radius: 5 + Layout.fillHeight: false + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border - Item { // Spacer - width: Math.max(ctrlRect.width - posIntLabel.width - posIntValue.width - 32, 1) - height: 1 - } - - StudioControls.RealSpinBox { - id: posIntValue - realFrom: 1 - realTo: 100000 - realValue: rootView.posInt - realStepSize: 1 - width: 80 - actionIndicatorVisible: false - - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Snap interval for move gizmo.") - ToolTip.delay: root.toolTipDelay - - onRealValueChanged: rootView.posInt = realValue - } - } - - Row { - height: rotIntValue.height - width: parent.width - 16 - spacing: StudioTheme.Values.sectionRowSpacing - - Text { - id: rotIntLabel - text: qsTr("Rotation Snap Interval:") - color: enabled ? StudioTheme.Values.themeTextColor - : StudioTheme.Values.themeTextColorDisabled - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignRight - height: rotIntValue.height - } - - Item { // Spacer - width: Math.max(ctrlRect.width - rotIntLabel.width - rotIntValue.width - 32, 1) - height: 1 - } - - StudioControls.RealSpinBox { - id: rotIntValue - realFrom: 1 - realTo: 360 - realValue: rootView.rotInt - realStepSize: 1 - width: 80 - 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 - } - } - - Row { - height: scaleIntValue.height - width: parent.width - 16 - spacing: StudioTheme.Values.sectionRowSpacing - - Text { - id: scaleIntLabel - text: qsTr("Scale Snap Interval (%):") - color: enabled ? StudioTheme.Values.themeTextColor - : StudioTheme.Values.themeTextColorDisabled - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignRight - height: scaleIntValue.height - } - - Item { // Spacer - width: Math.max(ctrlRect.width - scaleIntLabel.width - scaleIntValue.width - 32, 1) - height: 1 - } - - StudioControls.RealSpinBox { - id: scaleIntValue - realFrom: 1 - realTo: 100000 - realValue: rootView.scaleInt - realStepSize: 1 - width: 80 - 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 - } + 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 + } } - Item { // Spacer - width: 1 - height: Math.max(root.height - buttons.height - ctrlRect.height - 16, 2) + 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 + } } - Item { - id: buttons - height: cancelButton.height + 8 - width: ctrlRect.width - - Row { - spacing: StudioTheme.Values.dialogButtonSpacing - height: cancelButton.height - anchors.right: parent.right - - HelperWidgets.Button { - id: cancelButton - text: qsTr("Cancel") - leftPadding: StudioTheme.Values.dialogButtonPadding - rightPadding: StudioTheme.Values.dialogButtonPadding - onClicked: rootView.cancel() - } - - HelperWidgets.Button { - id: applyButton - text: qsTr("Ok") - leftPadding: StudioTheme.Values.dialogButtonPadding - rightPadding: StudioTheme.Values.dialogButtonPadding - onClicked: { - rootView.apply() - rootView.cancel() - } - } - } + HelperWidgets.Button { + text: qsTr("Reset All") + Layout.bottomMargin: 8 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + onClicked: rootView.resetDefaults() } } } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 4890c031a25..588aad00c1d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -200,16 +200,7 @@ void Edit3DView::modelAttached(Model *model) { AbstractView::modelAttached(model); - rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); - rootModelNode().setAuxiliaryData(edit3dSnapRotProperty, m_snapRotationAction->action()->isChecked()); - rootModelNode().setAuxiliaryData(edit3dSnapScaleProperty, m_snapScaleAction->action()->isChecked()); - rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); - 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)); + syncSnapAuxPropsToSettings(); checkImports(); auto cachedImage = m_canvasCache.take(model); @@ -551,7 +542,32 @@ void Edit3DView::createSeekerSliderAction() &SeekerSliderAction::valueChanged, this, [=] (int value) { this->emitView3DAction(View3DActionType::ParticlesSeek, value); - }); + }); +} + +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() @@ -887,37 +903,32 @@ void Edit3DView::createEdit3DActions() this, bakeLightsTrigger); - SelectionContextOperation snapMenuTrigger = [this](const SelectionContext &) { - if (!edit3DWidget()->snapMenu()) - return; + 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 &) { QPoint pos; - const auto &actionWidgets = m_snapMenuAction->action()->associatedWidgets(); + const auto &actionWidgets = m_snapConfigAction->action()->associatedWidgets(); for (auto actionWidget : actionWidgets) { if (auto button = qobject_cast(actionWidget)) { pos = button->mapToGlobal(QPoint(0, 0)); break; } } - - edit3DWidget()->showSnapMenu(!edit3DWidget()->snapMenu()->isVisible(), pos); - }; - - m_snapMenuAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_SNAP_MENU, - View3DActionType::Empty, - QCoreApplication::translate("Snapping", "Snapping"), - QKeySequence(), - false, - false, - toolbarIcon(DesignerIcons::SnappingIcon), - this, - snapMenuTrigger); - - SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { - QPoint pos; - pos = m_edit3DWidget->mapToGlobal(QPoint(m_edit3DWidget->width() / 2, - m_edit3DWidget->height() / 2)); if (!m_snapConfiguration) m_snapConfiguration = new SnapConfiguration(this); m_snapConfiguration->showConfigDialog(pos); @@ -926,82 +937,13 @@ void Edit3DView::createEdit3DActions() m_snapConfigAction = std::make_unique( QmlDesigner::Constants::EDIT3D_SNAP_CONFIG, View3DActionType::Empty, - QCoreApplication::translate("SnapConfigAction", "Snap Configuration"), + QCoreApplication::translate("SnapConfigAction", "Open snap configuration dialog"), QKeySequence(), false, false, - toolbarIcon(DesignerIcons::SnappingIcon), + toolbarIcon(DesignerIcons::SnappingConfIcon), this, - snapConfigTrigger, - QCoreApplication::translate("SnapConfigAction", "Open snap configuration dialog.")); - - SelectionContextOperation snapPositionTrigger = [this](const SelectionContext &) { - if (model()) - rootModelNode().setAuxiliaryData(edit3dSnapPosProperty, m_snapPositionAction->action()->isChecked()); - }; - - m_snapPositionAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_SNAP_POSITION, - View3DActionType::Empty, - QCoreApplication::translate("SnapPositionAction", "Snap Position"), - QKeySequence(Qt::SHIFT | Qt::Key_W), - true, - Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_POSITION), false).toBool(), - QIcon(), - this, - snapPositionTrigger, - QCoreApplication::translate("SnapPositionAction", "Toggle position snapping during node drag.")); - - SelectionContextOperation snapRotationTrigger = [this](const SelectionContext &) { - if (model()) - rootModelNode().setAuxiliaryData(edit3dSnapRotProperty, m_snapRotationAction->action()->isChecked()); - }; - - m_snapRotationAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_SNAP_ROTATION, - View3DActionType::Empty, - QCoreApplication::translate("SnapRotationAction", "Snap Rotation"), - QKeySequence(Qt::SHIFT | Qt::Key_E), - true, - Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ROTATION), false).toBool(), - QIcon(), - this, - snapRotationTrigger, - QCoreApplication::translate("SnapRotationAction", "Toggle rotation snapping during node drag.")); - - SelectionContextOperation snapScaleTrigger = [this](const SelectionContext &) { - if (model()) - rootModelNode().setAuxiliaryData(edit3dSnapScaleProperty, m_snapScaleAction->action()->isChecked()); - }; - - m_snapScaleAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_SNAP_SCALE, - View3DActionType::Empty, - QCoreApplication::translate("SnapScaleAction", "Snap Scale"), - QKeySequence(Qt::SHIFT | Qt::Key_R), - true, - Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_SCALE), false).toBool(), - QIcon(), - this, - snapScaleTrigger, - QCoreApplication::translate("SnapScaleAction", "Toggle scale snapping during node drag.")); - - SelectionContextOperation snapAbsoluteTrigger = [this](const SelectionContext &) { - if (model()) - rootModelNode().setAuxiliaryData(edit3dSnapAbsProperty, m_snapAbsoluteAction->action()->isChecked()); - }; - - m_snapAbsoluteAction = std::make_unique( - QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE, - View3DActionType::Empty, - QCoreApplication::translate("SnapAbsoluteAction", "Absolute Position Snap"), - QKeySequence(Qt::SHIFT | Qt::Key_A), - true, - Edit3DViewConfig::load(settingKeyForAction(QmlDesigner::Constants::EDIT3D_SNAP_ABSOLUTE), true).toBool(), - QIcon(), - this, - snapAbsoluteTrigger, - QCoreApplication::translate("SnapAbsoluteAction", "If enabled, position snapping uses scene origin as origin point.\nOtherwise snapping uses drag start point as origin point.")); + snapConfigTrigger); m_leftActions << m_selectionModeAction.get(); m_leftActions << nullptr; // Null indicates separator @@ -1016,12 +958,14 @@ void Edit3DView::createEdit3DActions() m_leftActions << m_orientationModeAction.get(); m_leftActions << m_editLightAction.get(); m_leftActions << nullptr; + m_leftActions << m_snapToggleAction.get(); + m_leftActions << m_snapConfigAction.get(); + m_leftActions << nullptr; m_leftActions << m_alignCamerasAction.get(); m_leftActions << m_alignViewAction.get(); m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction.get(); m_leftActions << m_backgrondColorMenuAction.get(); - m_leftActions << m_snapMenuAction.get(); m_rightActions << m_particleViewModeAction.get(); m_rightActions << m_particlesPlayAction.get(); @@ -1047,12 +991,6 @@ void Edit3DView::createEdit3DActions() m_backgroundColorActions << m_selectGridColorAction.get(); m_backgroundColorActions << m_syncBackgroundColorAction.get(); m_backgroundColorActions << m_resetColorAction.get(); - - m_snapActions << m_snapConfigAction.get(); - m_snapActions << m_snapPositionAction.get(); - m_snapActions << m_snapRotationAction.get(); - m_snapActions << m_snapScaleAction.get(); - m_snapActions << m_snapAbsoluteAction.get(); } QVector Edit3DView::leftActions() const @@ -1075,10 +1013,6 @@ QVector Edit3DView::backgroundColorActions() const return m_backgroundColorActions; } -QVector Edit3DView::snapActions() const -{ - return m_snapActions; -} Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const { @@ -1161,17 +1095,4 @@ bool Edit3DView::isBakingLightsSupported() const return m_isBakingLightsSupported; } -const char *Edit3DView::settingKeyForAction(const QByteArray &actionId) -{ - if (actionId == Constants::EDIT3D_SNAP_POSITION) - return DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION; - if (actionId == Constants::EDIT3D_SNAP_ROTATION) - return DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION; - if (actionId == Constants::EDIT3D_SNAP_SCALE) - return DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE; - if (actionId == Constants::EDIT3D_SNAP_ABSOLUTE) - return DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE; - return ""; -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 65fcdf4b0ce..aa3f482dac5 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -62,7 +62,6 @@ public: QVector rightActions() const; QVector visibilityToggleActions() const; QVector backgroundColorActions() const; - QVector snapActions() const; Edit3DAction *edit3DAction(View3DActionType type) const; Edit3DBakeLightsAction *bakeLightsAction() const; @@ -77,7 +76,7 @@ public: bool isBakingLightsSupported() const; - const char *settingKeyForAction(const QByteArray &actionId); + void syncSnapAuxPropsToSettings(); private slots: void onEntriesChanged(); @@ -113,7 +112,6 @@ private: QVector m_rightActions; QVector m_visibilityToggleActions; QVector m_backgroundColorActions; - QVector m_snapActions; QMap m_edit3DActions; std::unique_ptr m_selectionModeAction; @@ -144,12 +142,8 @@ private: std::unique_ptr m_resetAction; std::unique_ptr m_visibilityTogglesAction; std::unique_ptr m_backgrondColorMenuAction; - std::unique_ptr m_snapMenuAction; + std::unique_ptr m_snapToggleAction; std::unique_ptr m_snapConfigAction; - std::unique_ptr m_snapPositionAction; - std::unique_ptr m_snapRotationAction; - std::unique_ptr m_snapScaleAction; - std::unique_ptr m_snapAbsoluteAction; std::unique_ptr m_bakeLightsAction; int particlemode; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index ee5800641d4..8c3d54bb4f2 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -166,19 +166,6 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); - m_snapMenu = new Edit3DToolbarMenu(this); - handleActions(view->snapActions(), m_snapMenu, false); - connect(m_snapMenu, &QMenu::aboutToHide, this, [view]() { - // Persist the checkable settings of the menu - const auto actions = view->snapActions(); - for (auto &action : actions) { - if (action->action()->isCheckable()) { - Edit3DViewConfig::save(view->settingKeyForAction(action->menuId()), - action->action()->isChecked()); - } - } - }); - createContextMenu(); m_mcuLabel = new QLabel(this); @@ -467,21 +454,6 @@ void Edit3DWidget::showVisibilityTogglesMenu(bool show, const QPoint &pos) m_visibilityTogglesMenu->close(); } -QMenu *Edit3DWidget::snapMenu() const -{ - return m_snapMenu.data(); -} - -void Edit3DWidget::showSnapMenu(bool show, const QPoint &pos) -{ - if (m_snapMenu.isNull()) - return; - if (show) - m_snapMenu->popup(pos); - else - m_snapMenu->close(); -} - QMenu *Edit3DWidget::backgroundColorMenu() const { return m_backgroundColorMenu.data(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 1e60c75f88a..f764f068bf4 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -45,8 +45,6 @@ public: void showCanvas(bool show); QMenu *visibilityTogglesMenu() const; void showVisibilityTogglesMenu(bool show, const QPoint &pos); - QMenu *snapMenu() const; - void showSnapMenu(bool show, const QPoint &pos); QMenu *backgroundColorMenu() const; void showBackgroundColorMenu(bool show, const QPoint &pos); @@ -78,7 +76,6 @@ private: QPointer m_visibilityTogglesMenu; QPointer m_backgroundColorMenu; QPointer m_contextMenu; - QPointer m_snapMenu; QPointer m_bakeLightsAction; QPointer m_editComponentAction; QPointer m_editMaterialAction; diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 0f524e88071..44fb7ef1322 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -3,10 +3,9 @@ #include "snapconfiguration.h" -#include "abstractview.h" #include "designersettings.h" +#include "edit3dview.h" #include "edit3dviewconfig.h" -#include "modelnode.h" #include @@ -39,7 +38,7 @@ static QString qmlSourcesPath() return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString(); } -SnapConfiguration::SnapConfiguration(AbstractView *view) +SnapConfiguration::SnapConfiguration(Edit3DView *view) : QObject(view) , m_view(view) { @@ -52,40 +51,70 @@ SnapConfiguration::~SnapConfiguration() void SnapConfiguration::apply() { - 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->rootModelNode().setAuxiliaryData(edit3dSnapPosIntProperty, m_positionInterval); - m_view->rootModelNode().setAuxiliaryData(edit3dSnapRotIntProperty, m_rotationInterval); - m_view->rootModelNode().setAuxiliaryData(edit3dSnapScaleIntProperty, m_scaleInterval); + 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, 10.).toDouble(); + DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, defaultPosInt).toDouble(); double rotInt = Edit3DViewConfig::load( - DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, 15.).toDouble(); + DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, defaultRotInt).toDouble(); double scaleInt = Edit3DViewConfig::load( - DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL, 10.).toDouble(); + 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->setTitle(tr("3D Snap Configuration")); - m_configDialog->setResizeMode(QQuickView::SizeRootObjectToView); - m_configDialog->setFlags(Qt::Dialog); - m_configDialog->setModality(Qt::ApplicationModal); + 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->setMinimumSize({280, 170}); m_configDialog->rootContext()->setContextProperties({ {"rootView", QVariant::fromValue(this)} @@ -95,17 +124,54 @@ void SnapConfiguration::showConfigDialog(const QPoint &pos) QPoint finalPos = pos; finalPos.setX(pos.x() - m_configDialog->size().width() / 2); - finalPos.setY(pos.y() - m_configDialog->size().height() / 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(); } } @@ -114,6 +180,7 @@ void SnapConfiguration::setRotInt(double value) { if (value != m_rotationInterval) { m_rotationInterval = value; + m_changes = true; emit rotIntChanged(); } } @@ -122,6 +189,7 @@ void SnapConfiguration::setScaleInt(double value) { if (value != m_scaleInterval) { m_scaleInterval = value; + m_changes = true; emit scaleIntChanged(); } } @@ -141,20 +209,17 @@ void SnapConfiguration::cancel() bool SnapConfiguration::eventFilter(QObject *obj, QEvent *event) { + // Closing dialog always applies the changes + if (obj == m_configDialog) { - if (event->type() == QEvent::KeyPress) { + if (event->type() == QEvent::FocusOut) { + apply(); + } else if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) - cancel(); - if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { - // Apply asynchronously to allow the final value to be set by dialog before apply - QTimer::singleShot(0, this, [this]() { - apply(); - cancel(); - }); - } + apply(); } else if (event->type() == QEvent::Close) { - cancel(); + apply(); } } diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index e41dc7e75af..ae1a4a0d93b 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -14,7 +14,7 @@ QT_END_NAMESPACE namespace QmlDesigner { -class AbstractView; +class Edit3DView; inline constexpr AuxiliaryDataKeyView edit3dSnapPosProperty{AuxiliaryDataType::NodeInstanceAuxiliary, "snapPos3d"}; @@ -34,19 +34,34 @@ inline constexpr AuxiliaryDataKeyView edit3dSnapScaleIntProperty{AuxiliaryDataTy 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(AbstractView *view); + SnapConfiguration(Edit3DView *view); ~SnapConfiguration(); - Q_INVOKABLE void cancel(); - Q_INVOKABLE void apply(); + 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); @@ -54,7 +69,15 @@ public: 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(); @@ -66,10 +89,15 @@ private: void cleanup(); QPointer m_configDialog; - QPointer m_view; - double m_positionInterval = 10.; - double m_rotationInterval = 15.; - double m_scaleInterval = 10.; + 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/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 75938f1aa7c..3a2d591a1bf 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -64,12 +64,8 @@ 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_MENU[] = "QmlDesigner.Editor3D.SnapMenu"; -const char EDIT3D_SNAP_POSITION[] = "QmlDesigner.Editor3D.SnapPosition"; -const char EDIT3D_SNAP_ROTATION[] = "QmlDesigner.Editor3D.SnapRotation"; -const char EDIT3D_SNAP_SCALE[] = "QmlDesigner.Editor3D.SnapScale"; +const char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle"; const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; -const char EDIT3D_SNAP_ABSOLUTE[] = "QmlDesigner.Editor3D.SnapToGrid"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index a19bb310a6f..b75575c6236 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -84,11 +84,12 @@ void DesignerSettings::fromSettings(QSettings *settings) QStringList{"#222222", "#999999"}); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, "#aaaaaa"); restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ABSOLUTE, true); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION, false); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_POSITION_INTERVAL, 10.); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION, false); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_ROTATION_INTERVAL, 15.); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE, false); + 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); diff --git a/src/plugins/qmldesignerbase/utils/designersettings.h b/src/plugins/qmldesignerbase/utils/designersettings.h index aa0b8b4b138..850e2a43a06 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -33,6 +33,7 @@ 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"; diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index c9c960950cf..70605345c0c 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -156,8 +156,8 @@ private: bool m_snapPosition = false; bool m_snapRotation = false; bool m_snapScale = false; - double m_snapPositionInterval = 10.; - double m_snapRotationInterval = 15.; + double m_snapPositionInterval = 50.; + double m_snapRotationInterval = 5.; double m_snapScaleInterval = .1; }; From d9d89ef6cb78a97feae4bd7b26eec405b6159e62 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 30 Aug 2023 10:51:59 +0200 Subject: [PATCH 150/266] QmlDesigner: fix not used ": " Change-Id: I197b3dd46d0835d73c550f216a4d5bb51a8ec9f8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/designercore/model/documentmessage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From c00eb87c9686fc639db4e705662b5df48b993d80 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 16:24:18 +0200 Subject: [PATCH 151/266] QmlDesigner: Update QML for ConnectionEditor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Style PropertiesList * Style BindingsList * Add PoupLabel for reuse * Take screen height into account * Minor tweaks no forms Change-Id: I70b141732f38a9534447586fb00cd736949e56e5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../connectionseditor/BindingsDialogForm.qml | 25 ++--- .../connectionseditor/BindingsListView.qml | 99 +++++++++++++------ .../connectionseditor/ConnectionsDialog.qml | 4 + .../ConnectionsDialogForm.qml | 28 +++--- .../connectionseditor/ConnectionsListView.qml | 3 + .../connectionseditor/PopupDialog.qml | 14 +++ .../connectionseditor/PopupLabel.qml | 20 ++++ .../PropertiesDialogForm.qml | 22 ++--- .../connectionseditor/PropertiesListView.qml | 98 ++++++++++++------ 9 files changed, 206 insertions(+), 107 deletions(-) create mode 100644 share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index 932860f34cb..0d053771440 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -5,27 +5,22 @@ import QtQuick import QtQuick.Controls import StudioControls -Rectangle { +Item { width: 400 height: 800 - color: "#1b1b1b" property var backend - Text { - id: text1 + PopupLabel { x: 10 y: 25 - color: "#ffffff" text: qsTr("Target") - font.pixelSize: 15 } - Text { + PopupLabel { id: text111 x: 80 y: 25 - color: "red" text: backend.targetNode font.pixelSize: 15 } @@ -44,13 +39,10 @@ Rectangle { onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex } - Text { - id: text2 + PopupLabel { x: 13 y: 111 - color: "#ffffff" text: qsTr("Source Propety") - font.pixelSize: 15 } TopLevelComboBox { @@ -68,12 +60,10 @@ Rectangle { onCurrentTypeIndexChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex } - Text { + PopupLabel { x: 13 y: 88 - color: "#ffffff" text: qsTr("Source Node") - font.pixelSize: 15 } TopLevelComboBox { @@ -90,12 +80,9 @@ Rectangle { onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex } - Text { - id: text3 + PopupLabel { x: 10 y: 55 - color: "#ffffff" text: qsTr("Property") - font.pixelSize: 15 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 4c73809c780..8a96ae83125 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -12,13 +12,17 @@ ListView { id: listView width: 606 height: 160 - interactive: false + clip: true + interactive: true highlightMoveDuration: 0 + highlightResizeDuration: 0 onVisibleChanged: { dialog.hide() } + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + property int modelCurrentIndex: listView.model.currentIndex ?? 0 // Something weird with currentIndex happens when items are removed added. @@ -33,6 +37,12 @@ ListView { dialog.backend.currentRow = listView.currentIndex } + readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin + readonly property int rowSpace: listView.width - (listView.rowSpacing * 4) + - listView.style.squareControlSize.width + property int rowWidth: listView.rowSpace / 4 + property int rowRest: listView.rowSpace % 4 + data: [ BindingsDialog { id: dialog @@ -40,10 +50,16 @@ ListView { backend: listView.model.delegate } ] - delegate: Item { + delegate: Rectangle { - width: 600 - height: 18 + id: itemDelegate + + width: ListView.view.width + height: listView.style.squareControlSize.height + color: mouseArea.containsMouse ? + itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover + : listView.style.background.hover + : "transparent" MouseArea { id: mouseArea @@ -59,64 +75,91 @@ ListView { } Row { - id: row1 - x: 0 - y: 0 - width: 600 - height: 16 - spacing: 10 + id: row + + height: itemDelegate.height + spacing: listView.rowSpacing + + property color textColor: itemDelegate.ListView.isCurrentItem ? listView.style.text.selectedText + : listView.style.icon.idle Text { - width: 120 - color: "#ffffff" + width: listView.rowWidth + listView.rowRest + height: itemDelegate.height + color: row.textColor text: target ?? "" anchors.verticalCenter: parent.verticalCenter font.bold: false + elide: Text.ElideMiddle + leftPadding: listView.rowSpacing } Text { - width: 120 + width: listView.rowWidth + height: itemDelegate.height text: targetProperty ?? "" - color: "#ffffff" + color: row.textColor anchors.verticalCenter: parent.verticalCenter font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 + width: listView.rowWidth + height: itemDelegate.height text: source ?? "" anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" + color: row.textColor font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 + width: listView.rowWidth + height: itemDelegate.height text: sourceProperty ?? "" anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" + color: row.textColor font.bold: false + elide: Text.ElideMiddle } - Text { - width: 120 + Rectangle { + width: listView.style.squareControlSize.width + height: listView.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 ? listView.style.interactionHover + : listView.style.background.hover + : "transparent" + + Text { + anchors.fill: parent + + text: StudioTheme.Constants.remove_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: listView.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: listView.model.remove(index) } } + } } highlight: Rectangle { - color: "#2a5593" + color: listView.style.interaction width: 600 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index aafd334046e..210fcc81d3a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -18,6 +18,10 @@ PopupDialog { text: qsTr("Target") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter + ToolTipArea { + anchors.fill: parent + tooltip: qsTr("Choose the target for the signal.") + } } StudioControls.TopLevelComboBox { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 201790c6a67..7380d47aeb8 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -16,11 +16,6 @@ Column { property var backend - component PopupLabel : Text { - width: root.columnWidth - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.myFontSize - } /* replaced by ConnectionModelStatementDelegate defined in C++ enum ActionType { @@ -39,8 +34,8 @@ Column { Row { spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Signal") } - PopupLabel { text: qsTr("Action") } + PopupLabel { text: qsTr("Signal"); tooltip: qsTr("The name of the signal.") } + PopupLabel { text: qsTr("Action"); tooltip: qsTr("The type of the action.") } } Row { @@ -75,7 +70,7 @@ Column { { value: ConnectionModelStatementDelegate.ChangeState, text: qsTr("Change State") }, { value: ConnectionModelStatementDelegate.SetProperty, text: qsTr("Set Property") }, { value: ConnectionModelStatementDelegate.PrintMessage, text: qsTr("Print Message") }, - { value: ConnectionModelStatementDelegate.Custom, text: qsTr("Custom") } + { value: ConnectionModelStatementDelegate.Custom, text: qsTr("Unknown") } ] } } @@ -85,8 +80,8 @@ Column { visible: action.currentValue === ConnectionModelStatementDelegate.CallFunction spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Item") } - PopupLabel { text: qsTr("Method") } + PopupLabel { text: qsTr("Item") ; tooltip: qsTr("The target item of the function.")} + PopupLabel { text: qsTr("Method") ; tooltip: qsTr("The name of the function.")} } Row { @@ -122,8 +117,8 @@ Column { visible: action.currentValue === ConnectionModelStatementDelegate.Assign spacing: root.horizontalSpacing - PopupLabel { text: qsTr("From") } - PopupLabel { text: qsTr("To") } + PopupLabel { text: qsTr("From") ; tooltip: qsTr("The Property to assign from.")} + PopupLabel { text: qsTr("To"); tooltip: qsTr("The Property to assign to.") } } Row { @@ -191,8 +186,8 @@ Column { visible: action.currentValue === ConnectionModelStatementDelegate.ChangeState spacing: root.horizontalSpacing - PopupLabel { text: qsTr("State Group") } - PopupLabel { text: qsTr("State") } + PopupLabel { text: qsTr("State Group"); tooltip: qsTr("The State Group.") } + PopupLabel { text: qsTr("State"); tooltip: qsTr("The State .") } } Row { @@ -228,8 +223,8 @@ Column { visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty spacing: root.horizontalSpacing - PopupLabel { text: qsTr("Item") } - PopupLabel { text: qsTr("Property") } + PopupLabel { text: qsTr("Item"); tooltip: qsTr("The Item.")} + PopupLabel { text: qsTr("Property"); tooltip: qsTr("The property of the item.")} } Row { @@ -283,6 +278,7 @@ Column { PopupLabel { visible: action.currentValue === ConnectionModelStatementDelegate.PrintMessage text: qsTr("Message") + tooltip: qsTr("The message that is printed.") } StudioControls.TextField { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 179c6d220e0..1a0f3905ee9 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -104,6 +104,7 @@ ListView { verticalAlignment: Text.AlignVCenter font.bold: false leftPadding: root.rowSpacing + elide: Text.ElideMiddle } Text { @@ -113,6 +114,7 @@ ListView { color: row.textColor verticalAlignment: Text.AlignVCenter font.bold: false + elide: Text.ElideMiddle } Text { @@ -122,6 +124,7 @@ ListView { verticalAlignment: Text.AlignVCenter color: row.textColor font.bold: false + elide: Text.ElideMiddle } Rectangle { diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 2119f56e88c..8032ae84ec7 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -12,6 +12,7 @@ Window { property alias titleBar: titleBarContent.children default property alias content: mainContent.children + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle width: 320 height: column.implicitHeight @@ -19,6 +20,16 @@ Window { flags: Qt.FramelessWindowHint | Qt.Dialog color: StudioTheme.Values.themePopoutBackground + function ensureVerticalPosition() + { + if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) { + window.y = (Screen.height - window.height - window.style.dialogScreenMargin) + } + } + + onHeightChanged: window.ensureVerticalPosition() + + function popup(item) { print("popup " + item) var padding = 12 @@ -27,6 +38,9 @@ Window { if (window.x < 0) window.x = p.x + item.width + padding window.y = p.y + + window.ensureVerticalPosition() + window.show() window.raise() } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml new file mode 100644 index 00000000000..1af0950be4f --- /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 + ToolTipArea { + id: area + anchors.fill: parent + tooltip: qsTr("missing") + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml index c728027f30f..0936581c86f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -6,19 +6,18 @@ import QtQuick.Controls 2.15 import StudioControls -Rectangle { +Item { width: 400 height: 800 - color: "#1b1b1b" + property var backend - Text { - id: text1 + PopupLabel { x: 10 y: 25 - color: "#ffffff" + text: qsTr("Type:") - font.pixelSize: 15 + } TopLevelComboBox { @@ -33,20 +32,16 @@ Rectangle { onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex } - Text { - id: text2 + PopupLabel { x: 10 y: 131 - color: "#ffffff" text: qsTr("Name") - font.pixelSize: 15 } TextInput { id: name x: 70 y: 131 - color: "white" width: 156 text: backend.name.text ?? "" onEditingFinished: { @@ -54,17 +49,14 @@ Rectangle { } } - Text { + PopupLabel { x: 10 y: 81 - color: "#ffffff" text: qsTr("Value") - font.pixelSize: 15 } TextInput { id: value - color: "red" x: 70 y: 81 width: 156 diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index f39b26ee7b6..cf0194f1b48 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -12,13 +12,17 @@ ListView { id: listView width: 606 height: 160 - interactive: false + clip: true + interactive: true highlightMoveDuration: 0 + highlightResizeDuration: 0 onVisibleChanged: { dialog.hide() } + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + property int modelCurrentIndex: listView.model.currentIndex ?? 0 // Something weird with currentIndex happens when items are removed added. @@ -33,6 +37,12 @@ ListView { dialog.backend.currentRow = listView.currentIndex } + readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin + readonly property int rowSpace: listView.width - (listView.rowSpacing * 4) + - listView.style.squareControlSize.width + property int rowWidth: listView.rowSpace / 4 + property int rowRest: listView.rowSpace % 4 + data: [ PropertiesDialog { id: dialog @@ -41,10 +51,14 @@ ListView { } ] - delegate: Item { - - width: 600 - height: 18 + delegate: Rectangle { + id: itemDelegate + width: ListView.view.width + height: listView.style.squareControlSize.height + color: mouseArea.containsMouse ? + itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover + : listView.style.background.hover + : "transparent" MouseArea { id: mouseArea @@ -64,64 +78,90 @@ ListView { } Row { - id: row1 - x: 0 - y: 0 - width: 600 - height: 16 - spacing: 10 + id: row + height: itemDelegate.height + spacing: listView.rowSpacing + + property color textColor: itemDelegate.ListView.isCurrentItem ? listView.style.text.selectedText + : listView.style.icon.idle Text { - width: 120 - color: "#ffffff" + width: listView.rowWidth + listView.rowRest + height: itemDelegate.height + color: row.textColor text: target ?? "" anchors.verticalCenter: parent.verticalCenter font.bold: false + elide: Text.ElideMiddle + leftPadding: listView.rowSpacing } Text { - width: 120 + width: listView.rowWidth + height: itemDelegate.height + color: row.textColor text: name ?? "" - color: "#ffffff" anchors.verticalCenter: parent.verticalCenter font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 + width: listView.rowWidth + listView.rowRest + height: itemDelegate.height + color: row.textColor text: type ?? "" anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" font.bold: false + elide: Text.ElideMiddle } Text { - width: 120 + width: listView.rowWidth + listView.rowRest + height: itemDelegate.height + color: row.textColor text: value ?? "" anchors.verticalCenter: parent.verticalCenter - color: "#ffffff" font.bold: false + elide: Text.ElideMiddle } - Text { - width: 120 + Rectangle { + width: listView.style.squareControlSize.width + height: listView.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 ? listView.style.interactionHover + : listView.style.background.hover + : "transparent" + + Text { + anchors.fill: parent + + text: StudioTheme.Constants.remove_medium + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: listView.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: listView.model.remove(index) } } + } } highlight: Rectangle { - color: "#2a5593" + color: listView.style.interaction width: 600 } } From e3481152375bbad54625df3d185341597953a25f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 16:29:39 +0200 Subject: [PATCH 152/266] QmlDesigner: Add a few more priority properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Icee5723c670110d15828a657e8f5546a3b7580ab Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../components/connectioneditor/propertytreemodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index d5b5e754af3..14a4a9ca814 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -101,9 +101,9 @@ const std::vector priorityListSignals = {"clicked", "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"}; + = {"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", From 83fac0a5ee67616d6079f085f5fcdb076164756c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 16:24:50 +0200 Subject: [PATCH 153/266] QmlDesigner: Add maximum height and scrollbar to TopLevelComboBox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9af9b8df6ef9f307b0bb0fd6f0a673a09b1c9943 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../imports/StudioControls/TopLevelComboBox.qml | 10 ++++++++-- .../imports/StudioTheme/ControlStyle.qml | 2 ++ .../imports/StudioTheme/Values.qml | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 3210559a688..eb79352ad9c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -128,14 +128,20 @@ 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 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml index f8e959d8254..a759560117d 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 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 092c6f2a724..a1089313673 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 From 94d03d5640c118095db81df52efbc8ada2e7a443 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 18:00:38 +0200 Subject: [PATCH 154/266] QmlDesigner: Fix alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I892c26b4c50446bc297154d160ee4514f927ccf1 Reviewed-by: Henning Gründl --- .../qmldesigner/connectionseditor/BindingsListView.qml | 8 ++++---- .../qmldesigner/connectionseditor/PropertiesListView.qml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 8a96ae83125..e260c7fe606 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -88,7 +88,7 @@ ListView { height: itemDelegate.height color: row.textColor text: target ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle leftPadding: listView.rowSpacing @@ -99,7 +99,7 @@ ListView { height: itemDelegate.height text: targetProperty ?? "" color: row.textColor - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } @@ -108,7 +108,7 @@ ListView { width: listView.rowWidth height: itemDelegate.height text: source ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter color: row.textColor font.bold: false elide: Text.ElideMiddle @@ -118,7 +118,7 @@ ListView { width: listView.rowWidth height: itemDelegate.height text: sourceProperty ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter color: row.textColor font.bold: false elide: Text.ElideMiddle diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index cf0194f1b48..78aebf85bdf 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -90,7 +90,7 @@ ListView { height: itemDelegate.height color: row.textColor text: target ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle leftPadding: listView.rowSpacing @@ -101,7 +101,7 @@ ListView { height: itemDelegate.height color: row.textColor text: name ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } @@ -111,7 +111,7 @@ ListView { height: itemDelegate.height color: row.textColor text: type ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } @@ -121,7 +121,7 @@ ListView { height: itemDelegate.height color: row.textColor text: value ?? "" - anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } From d10b1fbee97713e5323567d73174ed71a819a409 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 16:33:12 +0200 Subject: [PATCH 155/266] QmlDesigner: Reparent Connection if retargeted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old implementation did the same. Change-Id: Iaf925bbbe7f47198aac319dbe930871854694316 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../components/connectioneditor/connectionmodel.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 5311046ddc1..bcfcb63cb7b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -936,8 +936,14 @@ void ConnectionModelBackendDelegate::handleTargetChanged() parentModelNode.signalHandlerProperty(handlerName).setSource(expression); } - if (oldTargetNodeName != newId) + 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); + } }); } From cdf0b83094277472999bd0085ddc418854eae63c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 16:32:13 +0200 Subject: [PATCH 156/266] QmlDesigner: Take property types into account MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A boolean property should only take a boolean, number only a number and so on... Change-Id: Ibc327390262c93bf262570a0c749cd826d06a678 Reviewed-by: Henning Gründl --- .../connectioneditor/connectionmodel.cpp | 23 +++++++++++++++++++ .../connectioneditor/connectionmodel.h | 1 + .../connectioneditor/propertytreemodel.cpp | 15 ++++++++++++ .../connectioneditor/propertytreemodel.h | 2 ++ 4 files changed, 41 insertions(+) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index bcfcb63cb7b..43ccfda84f9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -1168,6 +1168,8 @@ void ConnectionModelStatementDelegate::handleRhsAssignmentChanged() assignmentStatement.rhs.nodeId = m_rhsAssignmentDelegate.id(); assignmentStatement.rhs.propertyName = m_rhsAssignmentDelegate.name(); + setupPropertyType(); + emit statementChanged(); } @@ -1305,6 +1307,7 @@ void ConnectionModelStatementDelegate::setupAssignment() 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() @@ -1420,6 +1423,26 @@ void ConnectionModelStatementDelegate::setupPrintMessage() 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"); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 499e0abea8d..156227b4014 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -216,6 +216,7 @@ private: void setupChangeState(); void setupStates(); void setupPrintMessage(); + void setupPropertyType(); QString baseStateName() const; ActionType m_actionType; diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 14a4a9ca814..dcbbe5c1a60 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -833,6 +833,7 @@ PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m void PropertyTreeModelDelegate::setPropertyType(PropertyTreeModel::PropertyTypes type) { m_model.setPropertyType(type); + setupNameComboBox(m_idCombboBox.currentText(), m_nameCombboBox.currentText(), 0); } void PropertyTreeModelDelegate::setup(const QString &id, const QString &name, bool *nameExists) @@ -846,7 +847,13 @@ void PropertyTreeModelDelegate::setup(const QString &id, const QString &name, bo 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), @@ -889,6 +896,14 @@ void PropertyTreeModelDelegate::handleNameChanged() // 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(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index 48a1c768b2d..ec96328c02c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -164,8 +164,10 @@ 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(); From 44fa5d90d622f9eb598cc9aeaac0d3476f2fe0c5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Aug 2023 18:02:29 +0200 Subject: [PATCH 157/266] QmlDesigner: Refactor QML in Connection Editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving the StatementEditor into a separate component, so it can be used for else case. Change-Id: I2fc5a002333d63a484142a997882e502ffcc4016 Reviewed-by: Henning Gründl --- .../ConnectionsDialogForm.qml | 240 +--------------- .../connectionseditor/StatementEditor.qml | 258 ++++++++++++++++++ 2 files changed, 268 insertions(+), 230 deletions(-) create mode 100644 share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 7380d47aeb8..669bbc190c1 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -75,233 +75,13 @@ Column { } } - // Call Function - Row { - visible: action.currentValue === 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: action.currentValue === ConnectionModelStatementDelegate.CallFunction - spacing: root.horizontalSpacing - - StudioControls.TopLevelComboBox { - id: functionId - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - - model: backend.okStatement.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: backend.okStatement.function.name.model ?? 0 - - onActivated: backend.okStatement.function.name.activateIndex(functionName.currentIndex) - property int currentTypeIndex: backend.okStatement.function.name.currentIndex ?? 0 - onCurrentTypeIndexChanged: functionName.currentIndex = functionName.currentTypeIndex - } - } - - // Assign - Row { - visible: action.currentValue === 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: action.currentValue === ConnectionModelStatementDelegate.Assign - spacing: root.horizontalSpacing - - StudioControls.TopLevelComboBox { - id: rhsAssignmentId - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - //from - rhs - id - - model: backend.okStatement.rhsAssignment.id.model ?? 0 - - onActivated: backend.okStatement.rhsAssignment.id.activateIndex(rhsAssignmentId.currentIndex) - property int currentTypeIndex: backend.okStatement.rhsAssignment.id.currentIndex ?? 0 - onCurrentTypeIndexChanged: rhsAssignmentId.currentIndex = rhsAssignmentId.currentTypeIndex - } - - StudioControls.TopLevelComboBox { - id: lhsAssignmentId - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - //to lhs - id - model: backend.okStatement.lhs.id.model ?? 0 - - onActivated: backend.okStatement.lhs.id.activateIndex(lhsAssignmentId.currentIndex) - property int currentTypeIndex: backend.okStatement.lhs.id.currentIndex ?? 0 - onCurrentTypeIndexChanged: lhsAssignmentId.currentIndex = lhsAssignmentId.currentTypeIndex - } - } - - Row { - visible: action.currentValue === ConnectionModelStatementDelegate.Assign - spacing: root.horizontalSpacing - - StudioControls.TopLevelComboBox { - id: rhsAssignmentName - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - //from - rhs - name - - model: backend.okStatement.rhsAssignment.name.model ?? 0 - - onActivated: backend.okStatement.rhsAssignment.name.activateIndex(rhsAssignmentName.currentIndex) - property int currentTypeIndex: backend.okStatement.rhsAssignment.name.currentIndex ?? 0 - onCurrentTypeIndexChanged: rhsAssignmentName.currentIndex = rhsAssignmentName.currentTypeIndex - } - - StudioControls.TopLevelComboBox { - id: lhsAssignmentName - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - //to lhs - name - model: backend.okStatement.lhs.name.model ?? 0 - - onActivated: backend.okStatement.lhs.name.activateIndex(lhsAssignmentName.currentIndex) - property int currentTypeIndex: backend.okStatement.lhs.name.currentIndex ?? 0 - onCurrentTypeIndexChanged: lhsAssignmentName.currentIndex = lhsAssignmentName.currentTypeIndex - } - } - - // Change State - Row { - visible: action.currentValue === 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: action.currentValue === ConnectionModelStatementDelegate.ChangeState - spacing: root.horizontalSpacing - - StudioControls.TopLevelComboBox { - id: stateGroups - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - model: backend.okStatement.stateTargets.model ?? 0 - - onActivated: backend.okStatement.stateTargets.activateIndex(stateGroups.currentIndex) - property int currentTypeIndex: backend.okStatement.stateTargets.currentIndex ?? 0 - onCurrentTypeIndexChanged: stateGroups.currentIndex = stateGroups.currentTypeIndex - } - - StudioControls.TopLevelComboBox { - id: states - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - - model: backend.okStatement.states.model ?? 0 - - onActivated: backend.okStatement.states.activateIndex(states.currentIndex) - property int currentTypeIndex: backend.okStatement.states.currentIndex ?? 0 - onCurrentTypeIndexChanged: states.currentIndex = states.currentTypeIndex - } - } - - // Set Property - Row { - visible: action.currentValue === 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: action.currentValue === ConnectionModelStatementDelegate.SetProperty - spacing: root.horizontalSpacing - - StudioControls.TopLevelComboBox { - id: lhsPropertyId - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - - model: backend.okStatement.lhs.id.model ?? 0 - - onActivated: backend.okStatement.lhs.id.activateIndex(lhsPropertyId.currentIndex) - property int currentTypeIndex: backend.okStatement.lhs.id.currentIndex ?? 0 - onCurrentTypeIndexChanged: lhsPropertyId.currentIndex = lhsPropertyId.currentTypeIndex - - } - - StudioControls.TopLevelComboBox { - id: lhsPropertyName - style: StudioTheme.Values.connectionPopupControlStyle - width: root.columnWidth - model: backend.okStatement.lhs.name.model ?? 0 - - onActivated: backend.okStatement.lhs.name.activateIndex(lhsPropertyName.currentIndex) - property int currentTypeIndex: backend.okStatement.lhs.name.currentIndex ?? 0 - onCurrentTypeIndexChanged: lhsPropertyName.currentIndex = lhsPropertyName.currentTypeIndex - } - } - - PopupLabel { - visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty - text: qsTr("Value") - } - - StudioControls.TextField { - id: setPropertyArgument - visible: action.currentValue === ConnectionModelStatementDelegate.SetProperty - width: root.width - actionIndicatorVisible: false - translationIndicatorVisible: false - - text: backend.okStatement.stringArgument.text ?? "" - onEditingFinished: { - backend.okStatement.stringArgument.activateText(setPropertyArgument.text) - } - } - - // Print Message - PopupLabel { - visible: action.currentValue === ConnectionModelStatementDelegate.PrintMessage - text: qsTr("Message") - tooltip: qsTr("The message that is printed.") - } - - StudioControls.TextField { - id: messageString - visible: action.currentValue === ConnectionModelStatementDelegate.PrintMessage - width: root.width - actionIndicatorVisible: false - translationIndicatorVisible: false - text: backend.okStatement.stringArgument.text ?? "" - onEditingFinished: { - backend.okStatement.stringArgument.activateText(messageString.text) - } - } - - // Custom - PopupLabel { - visible: action.currentValue === 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 + StatementEditor { + actionType: action.currentValue + id: container + horizontalSpacing: root.horizontalSpacing + columnWidth: root.columnWidth + statement: backend.okStatement + spacing: root.verticalSpacing } HelperWidgets.AbstractButton { @@ -316,7 +96,7 @@ Column { onClicked: backend.addCondition() } - HelperWidgets.AbstractButton { + HelperWidgets.AbstractButton { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 buttonIcon: qsTr("Remove Condition") @@ -342,9 +122,9 @@ Column { opacity: 0.2 color: { if (type === ConditionListModel.Invalid) - return "red" + return "red" if (type === ConditionListModel.Operator) - return "blue" + return "blue" if (type === ConditionListModel.Literal) return "green" if (type === ConditionListModel.Variable) diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml new file mode 100644 index 00000000000..f33316dfcd7 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -0,0 +1,258 @@ +// 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) + + onActionTypeChanged: { + print("changed") + print(root.actionType) + print(ConnectionModelStatementDelegate.ChangeState) + } + + // 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 + } +} From 09236407d1b44ebd3816dc9b365dd95c99d6e1f1 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 31 Aug 2023 17:31:20 +0200 Subject: [PATCH 158/266] QmlPreview: add stopAllPreviews() Change-Id: I092361063d7f51602c2044e00feb8d8c5bccf4de Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmlpreview/qmlpreviewplugin.cpp | 6 ++++++ src/plugins/qmlpreview/qmlpreviewplugin.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index ed2d7fcb948..feec9317b74 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -237,6 +237,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; diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.h b/src/plugins/qmlpreview/qmlpreviewplugin.h index 15a5d3e1815..4dba793c4e5 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.h +++ b/src/plugins/qmlpreview/qmlpreviewplugin.h @@ -54,6 +54,7 @@ public: QString previewedFile() const; void setPreviewedFile(const QString &previewedFile); QmlPreviewRunControlList runningPreviews() const; + void stopAllPreviews(); void setFileLoader(QmlPreviewFileLoader fileLoader); QmlPreviewFileLoader fileLoader() const; From dc78c8faa2f4832444268d12a1c5fc8638964a94 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 25 Aug 2023 12:20:13 +0200 Subject: [PATCH 159/266] QmlPreview: introduce RefreshTranslationWorker Change-Id: I327cdd5d869f6cfdd47a826fd42306b8de69aa14 Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- .../qmlpreviewconnectionmanager.cpp | 4 +- .../qmlpreview/qmlpreviewconnectionmanager.h | 6 +-- .../qmlpreviewfileontargetfinder.cpp | 2 - .../qmlpreview/qmlpreviewfileontargetfinder.h | 2 - src/plugins/qmlpreview/qmlpreviewplugin.cpp | 11 ++++- src/plugins/qmlpreview/qmlpreviewplugin.h | 6 ++- .../qmlpreview/qmlpreviewruncontrol.cpp | 40 +++++++++++++++++-- src/plugins/qmlpreview/qmlpreviewruncontrol.h | 3 +- 8 files changed, 56 insertions(+), 18 deletions(-) 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 cf2ae2ef941..027541d77fb 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 feec9317b74..2c8f16f53e5 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -96,6 +96,9 @@ static std::unique_ptr defaultCreateDebugTranslationC return client; } +static void defaultRefreshTranslationFunction() +{} + class QmlPreviewPluginPrivate : public QObject { public: @@ -146,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); @@ -306,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 4dba793c4e5..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 { @@ -71,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 ca43b07a42f..2969e2aa3f6 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -16,19 +16,50 @@ #include #include +#include #include #include #include #include +#include + using namespace ProjectExplorer; using namespace Utils; -using namespace QmlPreview::Internal; namespace QmlPreview { static const QString 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 +82,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); @@ -99,6 +131,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..8512826280f 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.h +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.h @@ -16,7 +16,8 @@ struct QmlPreviewRunnerSetting QmlPreviewFpsHandler fpsHandler; float zoomFactor = -1.0; QString language; - QmlDebugTranslationClientCreator createDebugTranslationClientMethod; + QmlDebugTranslationClientFactoryFunction createDebugTranslationClientMethod; + QmlPreviewRefreshTranslationFunction refreshTranslationsFunction; }; class QmlPreviewRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory From 8ae76adbf568a3d84e6016c5fb91dcb357e7b28f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 31 Aug 2023 18:00:37 +0200 Subject: [PATCH 160/266] QmlPreview: fix use an init language Change-Id: I2d2774d88a237871eaddacd18c55490e9bd953ef Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- src/plugins/qmlpreview/qmlpreviewruncontrol.cpp | 8 +++++--- src/plugins/qmlpreview/qmlpreviewruncontrol.h | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index 2969e2aa3f6..3d776cb33ff 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -110,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(); }); diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.h b/src/plugins/qmlpreview/qmlpreviewruncontrol.h index 8512826280f..d1fbdb88d1c 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.h +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.h @@ -15,7 +15,6 @@ struct QmlPreviewRunnerSetting QmlPreviewFileClassifier fileClassifier; QmlPreviewFpsHandler fpsHandler; float zoomFactor = -1.0; - QString language; QmlDebugTranslationClientFactoryFunction createDebugTranslationClientMethod; QmlPreviewRefreshTranslationFunction refreshTranslationsFunction; }; From cec9e6493e6f7c82c7c5ba5b3c5e8c3f22173fba Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 1 Sep 2023 10:59:02 +0300 Subject: [PATCH 161/266] QmlDesigner: Add Effect Maker shader error propagation In case of manual edit of shaders, this simple system detects common errors that might be found in a shader. Task-number: QDS-10499 Change-Id: I0c70ac85ef519880dcd98642c5927f037f113f94 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../effectmaker/effectmakermodel.cpp | 75 +++++++++++++++++++ .../components/effectmaker/effectmakermodel.h | 19 +++++ 2 files changed, 94 insertions(+) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 228154fc6a4..b122a4e8530 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -6,6 +6,8 @@ #include "compositionnode.h" #include "uniform.h" +#include + #include namespace QmlDesigner { @@ -152,4 +154,77 @@ const QString EffectMakerModel::getFSUniforms() 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; + static QRegularExpression spaceReg("\\s+"); + QStringList errorStringList = errorMessage.split(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(); + } +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index de26b2f38f3..741647a1425 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -13,6 +13,17 @@ 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 @@ -36,6 +47,7 @@ public: signals: void isEmptyChanged(); void selectedIndexChanged(int idx); + void effectErrorChanged(); private: enum Roles { @@ -51,11 +63,18 @@ private: const QString getVSUniforms(); const QString getFSUniforms(); + QString detectErrorMessage(const QString &errorMessage); + EffectError effectError() const; + void setEffectError(const QString &errorMessage, int type, int lineNumber); + void resetEffectError(int type); + QList m_nodes; int m_selectedIndex = -1; bool m_isEmpty = true; + QMap m_effectErrors; + ShaderFeatures m_shaderFeatures; }; From 1710254b980c2d13cc4b791a4b53ed85410b5846 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 1 Sep 2023 10:08:03 +0200 Subject: [PATCH 162/266] QmlDesigner: Cleanup ConnectionsEditor list views Change-Id: Ib8f51d9e5640c768e6d6e815fd6c494e46743b65 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectionseditor/BindingsListView.qml | 97 ++++++++++--------- .../ConnectionsDialogForm.qml | 2 +- .../connectionseditor/ConnectionsListView.qml | 25 ++--- .../connectionseditor/PropertiesListView.qml | 90 +++++++++-------- 4 files changed, 117 insertions(+), 97 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index e260c7fe606..68ace4b0ecf 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -9,9 +9,10 @@ import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend ListView { - id: listView - width: 606 - height: 160 + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + clip: true interactive: true highlightMoveDuration: 0 @@ -21,83 +22,92 @@ ListView { dialog.hide() } - property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - - 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. 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 + dialog.backend.currentRow = root.currentIndex } + // Number of columns + readonly property int numColumns: 4 + // Proper row width calculation readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin - readonly property int rowSpace: listView.width - (listView.rowSpacing * 4) - - listView.style.squareControlSize.width - property int rowWidth: listView.rowSpace / 4 - property int rowRest: listView.rowSpace % 4 + 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: [ BindingsDialog { id: dialog visible: false - backend: listView.model.delegate + backend: root.model.delegate } ] - delegate: Rectangle { + delegate: Rectangle { id: itemDelegate + required property int index + + required property string target + required property string targetProperty + required property string source + required property string sourceProperty + width: ListView.view.width - height: listView.style.squareControlSize.height + height: root.style.squareControlSize.height color: mouseArea.containsMouse ? - itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover - : listView.style.background.hover + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover : "transparent" MouseArea { id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { - listView.model.currentIndex = index - listView.currentIndex = index - dialog.backend.currentRow = index + root.model.currentIndex = itemDelegate.index + root.currentIndex = itemDelegate.index + dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } - - property int currentIndex: listView.currentIndex } Row { id: row + property color textColor: itemDelegate.ListView.isCurrentItem ? root.style.text.selectedText + : root.style.icon.idle + height: itemDelegate.height - spacing: listView.rowSpacing - - property color textColor: itemDelegate.ListView.isCurrentItem ? listView.style.text.selectedText - : listView.style.icon.idle + spacing: root.rowSpacing Text { - width: listView.rowWidth + listView.rowRest + width: root.rowWidth + root.rowRest height: itemDelegate.height color: row.textColor - text: target ?? "" + text: itemDelegate.target ?? "" verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle - leftPadding: listView.rowSpacing + leftPadding: root.rowSpacing } Text { - width: listView.rowWidth + width: root.rowWidth height: itemDelegate.height - text: targetProperty ?? "" + text: itemDelegate.targetProperty ?? "" color: row.textColor verticalAlignment: Text.AlignVCenter font.bold: false @@ -105,9 +115,9 @@ ListView { } Text { - width: listView.rowWidth + width: root.rowWidth height: itemDelegate.height - text: source ?? "" + text: itemDelegate.source ?? "" verticalAlignment: Text.AlignVCenter color: row.textColor font.bold: false @@ -115,9 +125,9 @@ ListView { } Text { - width: listView.rowWidth + width: root.rowWidth height: itemDelegate.height - text: sourceProperty ?? "" + text: itemDelegate.sourceProperty ?? "" verticalAlignment: Text.AlignVCenter color: row.textColor font.bold: false @@ -125,12 +135,12 @@ ListView { } Rectangle { - width: listView.style.squareControlSize.width - height: listView.style.squareControlSize.height + width: root.style.squareControlSize.width + height: root.style.squareControlSize.height color: toolTipArea.containsMouse ? - itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover - : listView.style.background.hover + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover : "transparent" Text { @@ -138,7 +148,7 @@ ListView { text: StudioTheme.Constants.remove_medium font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: listView.style.baseIconFontSize + font.pixelSize: root.style.baseIconFontSize verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -151,15 +161,14 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: listView.model.remove(index) + onClicked: root.model.remove(itemDelegate.index) } } - } } highlight: Rectangle { - color: listView.style.interaction + color: root.style.interaction width: 600 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 669bbc190c1..ebef48e61b6 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -76,7 +76,7 @@ Column { } StatementEditor { - actionType: action.currentValue + actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom id: container horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 1a0f3905ee9..909ca187e6f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -41,6 +41,15 @@ ListView { dialog.backend.currentRow = root.currentIndex } + // Number of columns + readonly property int numColumns: 3 + // 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: [ ConnectionsDialog { id: dialog @@ -49,13 +58,6 @@ ListView { } ] - // Proper row width calculation - readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin - readonly property int rowSpace: root.width - (root.rowSpacing * 4) - - root.style.squareControlSize.width - property int rowWidth: root.rowSpace / 3 - property int rowRest: root.rowSpace % 3 - delegate: Rectangle { id: itemDelegate @@ -79,10 +81,9 @@ ListView { hoverEnabled: true onClicked: { - root.model.currentIndex = index - root.currentIndex = index - dialog.backend.currentRow = index - + root.model.currentIndex = itemDelegate.index + root.currentIndex = itemDelegate.index + dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } @@ -154,7 +155,7 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: root.model.remove(index) + onClicked: root.model.remove(itemDelegate.index) } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 78aebf85bdf..90b37aec7fa 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -9,9 +9,10 @@ import StudioTheme as StudioTheme import ConnectionsEditorEditorBackend ListView { - id: listView - width: 606 - height: 160 + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + clip: true interactive: true highlightMoveDuration: 0 @@ -21,57 +22,66 @@ ListView { dialog.hide() } - property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - - 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. 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 + dialog.backend.currentRow = root.currentIndex } + // Number of columns + readonly property int numColumns: 4 + // Proper row width calculation readonly property int rowSpacing: StudioTheme.Values.toolbarHorizontalMargin - readonly property int rowSpace: listView.width - (listView.rowSpacing * 4) - - listView.style.squareControlSize.width - property int rowWidth: listView.rowSpace / 4 - property int rowRest: listView.rowSpace % 4 + 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: Rectangle { id: itemDelegate + + 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: listView.style.squareControlSize.height + height: root.style.squareControlSize.height color: mouseArea.containsMouse ? - itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover - : listView.style.background.hover + 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.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } @@ -81,58 +91,58 @@ ListView { id: row height: itemDelegate.height - spacing: listView.rowSpacing + spacing: root.rowSpacing - property color textColor: itemDelegate.ListView.isCurrentItem ? listView.style.text.selectedText - : listView.style.icon.idle + property color textColor: itemDelegate.ListView.isCurrentItem ? root.style.text.selectedText + : root.style.icon.idle Text { - width: listView.rowWidth + listView.rowRest + width: root.rowWidth + root.rowRest height: itemDelegate.height color: row.textColor - text: target ?? "" + text: itemDelegate.target ?? "" verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle - leftPadding: listView.rowSpacing + leftPadding: root.rowSpacing } Text { - width: listView.rowWidth + width: root.rowWidth height: itemDelegate.height color: row.textColor - text: name ?? "" + text: itemDelegate.name ?? "" verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } Text { - width: listView.rowWidth + listView.rowRest + width: root.rowWidth + root.rowRest height: itemDelegate.height color: row.textColor - text: type ?? "" + text: itemDelegate.type ?? "" verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } Text { - width: listView.rowWidth + listView.rowRest + width: root.rowWidth + root.rowRest height: itemDelegate.height color: row.textColor - text: value ?? "" + text: itemDelegate.value ?? "" verticalAlignment: Text.AlignVCenter font.bold: false elide: Text.ElideMiddle } Rectangle { - width: listView.style.squareControlSize.width - height: listView.style.squareControlSize.height + width: root.style.squareControlSize.width + height: root.style.squareControlSize.height color: toolTipArea.containsMouse ? - itemDelegate.ListView.isCurrentItem ? listView.style.interactionHover - : listView.style.background.hover + itemDelegate.ListView.isCurrentItem ? root.style.interactionHover + : root.style.background.hover : "transparent" Text { @@ -140,7 +150,7 @@ ListView { text: StudioTheme.Constants.remove_medium font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: listView.style.baseIconFontSize + font.pixelSize: root.style.baseIconFontSize verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -153,7 +163,7 @@ ListView { id: toolTipArea tooltip: qsTr("This is a test.") anchors.fill: parent - onClicked: listView.model.remove(index) + onClicked: root.model.remove(itemDelegate.index) } } @@ -161,7 +171,7 @@ ListView { } highlight: Rectangle { - color: listView.style.interaction + color: root.style.interaction width: 600 } } From b30337e04c9f8851ad5b53558a32ba4994243609 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Wed, 30 Aug 2023 12:18:47 +0200 Subject: [PATCH 163/266] QmlDesigner: Process add/remove imports actions together Task-number: QDS-10529 Change-Id: Icd4efdb4c289f686b2e6eaed67432b662ba2ad29 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Marco Bubke Reviewed-by: Thomas Hartmann --- .../designercore/include/rewriterview.h | 4 ++-- .../designercore/model/modeltotextmerger.cpp | 16 ++++++++----- .../designercore/model/modeltotextmerger.h | 4 ++-- .../designercore/model/rewriterview.cpp | 23 +++++-------------- .../designercore/model/texttomodelmerger.cpp | 2 +- 5 files changed, 21 insertions(+), 28 deletions(-) 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/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index 1cbace80adf..bd7329f9923 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -112,16 +112,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) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h index efd199a2846..b9265639525 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(); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 1f80d6fe490..afd5a25321e 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -261,40 +261,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(); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 0d6c756ba43..ebd80f174d3 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1803,7 +1803,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, From b2ee8acafaec2827793256c59db9749117c2cefb Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 25 Aug 2023 16:53:39 +0200 Subject: [PATCH 164/266] QmlDesigner: Add QML views transient scrollbars * Add transient scrollbars to all QML based views * Remove version from QtQuick imports * Fix missing AssetsLibraryBackend reference in AssetsDelegate Task-number: QDS-9549 Change-Id: I7ab2e52106efae07caa37763b5b0592cfbf57c2e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../assetsLibraryQmlSources/AssetDelegate.qml | 2 +- .../assetsLibraryQmlSources/Assets.qml | 6 +- .../assetsLibraryQmlSources/AssetsView.qml | 18 +++- .../EmptyMaterialEditorPane.qml | 3 +- .../MaterialEditorPane.qml | 8 +- .../QtQuick/ItemPane.qml | 12 +-- .../QtQuick/QtObjectPane.qml | 4 +- .../QtQuick3D/Object3DPane.qml | 17 ++-- .../HelperWidgets/PropertyEditorPane.qml | 16 ++-- .../imports/HelperWidgets/ScrollBar.qml | 89 +++++++++++++++++++ .../imports/HelperWidgets/ScrollView.qml | 55 ++++++++---- .../imports/HelperWidgets/qmldir | 1 + .../qmldesigner/stateseditor/Main.qml | 22 ++++- .../EmptyTextureEditorPane.qml | 3 +- .../TextureEditorPane.qml | 8 +- src/libs/advanceddockingsystem/CMakeLists.txt | 2 +- src/libs/advanceddockingsystem/dockwidget.cpp | 20 ++++- src/libs/advanceddockingsystem/dockwidget.h | 4 +- .../connectioneditor/connectionview.cpp | 7 +- .../qmldesigner/qmldesignerconstants.h | 1 + .../studio/studioquickwidget.h | 4 + 21 files changed, 236 insertions(+), 66 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml 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 6520a5f4092..cfa40209950 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml @@ -260,10 +260,12 @@ 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 e4380a19b39..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 diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/EmptyMaterialEditorPane.qml index c8b2ab7ef6c..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 diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index 0816d01ec4c..0187cde6951 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -1,7 +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 import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 @@ -56,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/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/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..7a710e720d2 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml @@ -0,0 +1,89 @@ +// 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 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: "#D9D9D9" + } + + background: Rectangle { + id: controlTrack + color: "#D9D9D9" + 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/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/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/textureEditorQmlSource/EmptyTextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/EmptyTextureEditorPane.qml index 70fa841461a..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 diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml index 2098eda5165..ddf452d6f0f 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml @@ -1,7 +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 import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 @@ -44,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/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/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index 75d00438322..bb6dd00b322 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 { /** @@ -452,8 +453,25 @@ void DockWidget::setFocused(bool focused) return; d->m_focused = focused; + if (d->m_scrollArea) d->m_scrollArea->setProperty("focused", focused); + + QList quickWidgets = d->m_widget->findChildren(); + + for (const auto &quickWidget : std::as_const(quickWidgets)) { + QQuickItem *rootItem = quickWidget->rootObject(); + if (!rootItem) + continue; + + QQuickItem *scrollView = rootItem->findChild("__mainSrollView"); + if (!scrollView) + continue; + + scrollView->setProperty("adsFocus", focused); + } + + emit focusedChanged(); } bool DockWidget::isFocused() const diff --git a/src/libs/advanceddockingsystem/dockwidget.h b/src/libs/advanceddockingsystem/dockwidget.h index 72ac4789cb6..65495cc04d6 100644 --- a/src/libs/advanceddockingsystem/dockwidget.h +++ b/src/libs/advanceddockingsystem/dockwidget.h @@ -32,7 +32,7 @@ class AutoHideSideBar; class ADS_EXPORT DockWidget : public QFrame { Q_OBJECT - Q_PROPERTY(bool focused READ isFocused WRITE setFocused) + Q_PROPERTY(bool focused READ isFocused WRITE setFocused NOTIFY focusedChanged) private: DockWidgetPrivate *d; ///< private data (pimpl) @@ -639,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/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 5ee81b86a15..78625efd388 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -13,10 +13,11 @@ #include #include -#include #include -#include +#include #include +#include +#include #include @@ -59,7 +60,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); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 3a2d591a1bf..7e01b7de793 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -168,6 +168,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/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; }; From f4466ed7b6474a0b93c6fb6fb5c6105ffe97e731 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 1 Sep 2023 15:53:39 +0200 Subject: [PATCH 165/266] QmlDesigner: Apply theming to transient scrollbars Change-Id: Iea76d981447f2367615ce624da399ecb984cb662 Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/ScrollBar.qml | 6 ++++-- .../imports/StudioTheme/ControlStyle.qml | 3 ++- .../imports/StudioTheme/Values.qml | 1 + .../projects/shared-plugin/name/Constants.qml.tpl | 2 +- share/qtcreator/themes/design-light.creatortheme | 5 +++-- share/qtcreator/themes/design.creatortheme | 5 +++-- src/libs/utils/theme/theme.h | 5 +++-- src/plugins/qmldesignerbase/studio/studiostyle.cpp | 7 ++++--- 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml index 7a710e720d2..43456d46ddd 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollBar.qml @@ -8,6 +8,8 @@ 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 @@ -31,12 +33,12 @@ T.ScrollBar { implicitWidth: control.thickness implicitHeight: control.thickness radius: width / 2 - color: "#D9D9D9" + color: control.inUse ? control.style.scrollBar.handleHover : control.style.scrollBar.handle } background: Rectangle { id: controlTrack - color: "#D9D9D9" + color: control.style.scrollBar.track opacity: control.inUse || control.otherInUse ? 0.3 : 0.0 radius: width / 2 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml index a759560117d..460ff6db3f3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml @@ -150,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/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index a1089313673..388f82d0470 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -383,6 +383,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) 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/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index b0f34fcfbdd..46fc695570a 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -169,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 9a7065311ac..1d8295145e4 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -135,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/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 7a24c7fbd03..c3aa8842818 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -342,6 +342,9 @@ public: DSpopoutButtonBorder_hover, DSpopoutButtonBorder_interaction, DSpopoutButtonBorder_disabled, + DSscrollBarTrack, + DSscrollBarHandle, + DSscrollBarHandle_idle, /*Legacy QtDS*/ DSiconColor, @@ -365,8 +368,6 @@ public: DSsliderHandleHover, DSsliderHandleFocus, DSsliderHandleInteraction, - DSscrollBarTrack, - DSscrollBarHandle, DSsectionHeadBackground, DSstateDefaultHighlight, DSstateSeparatorColor, diff --git a/src/plugins/qmldesignerbase/studio/studiostyle.cpp b/src/plugins/qmldesignerbase/studio/studiostyle.cpp index cec8a323686..59f19e9f5c7 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle.cpp @@ -736,7 +736,8 @@ void StudioStyle::drawComplexControl( bool enabled = scrollBar->state & QStyle::State_Enabled; bool hovered = enabled && scrollBar->state & QStyle::State_MouseOver; - QColor buttonColor = hovered ? "#D9D9D9" : "#9B9B9B"; + QColor buttonColor = creatorTheme()->color(hovered ? Theme::DSscrollBarHandle + : Theme::DSscrollBarHandle_idle); QColor gradientStartColor = buttonColor.lighter(118); QColor gradientStopColor = buttonColor; if (hasTransientStyle) { @@ -755,12 +756,12 @@ void StudioStyle::drawComplexControl( painter->save(); painter->setPen(Qt::NoPen); if (hasTransientStyle) { - QColor brushColor("#D9D9D9"); + QColor brushColor(creatorTheme()->color(Theme::DSscrollBarTrack)); brushColor.setAlpha(0.3 * 255); painter->setBrush(QBrush(brushColor)); painter->drawRoundedRect(scrollBarGroove, 4, 4); } else { - painter->setBrush(QBrush("#773E3E")); + painter->setBrush(QBrush(creatorTheme()->color(Theme::DSscrollBarTrack))); painter->drawRect(rect); } painter->restore(); From a2d801ac0eddbbd219ff7f8aa1d58fd039a86d2b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 1 Sep 2023 15:48:35 +0200 Subject: [PATCH 166/266] QmlDesigner: Add support for else statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Idbb35555919cf90219c8da5ddf20a1799856f568 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../ConnectionsDialogForm.qml | 35 ++++- .../connectioneditorstatements.cpp | 11 ++ .../connectioneditorstatements.h | 2 + .../connectioneditor/connectionmodel.cpp | 126 +++++++++++------- .../connectioneditor/connectionmodel.h | 11 ++ 5 files changed, 136 insertions(+), 49 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index ebef48e61b6..e8909988de6 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -77,7 +77,6 @@ Column { StatementEditor { actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom - id: container horizontalSpacing: root.horizontalSpacing columnWidth: root.columnWidth statement: backend.okStatement @@ -147,6 +146,40 @@ Column { visible: !backend.conditionListModel.valid } + HelperWidgets.AbstractButton { + style: StudioTheme.Values.connectionPopupButtonStyle + width: 160 + buttonIcon: qsTr("Add Else Statement") + iconSize: StudioTheme.Values.baseFontSize + iconFont: StudioTheme.Constants.font + anchors.horizontalCenter: parent.horizontalCenter + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && !backend.hasElse + + onClicked: backend.addElse() + } + + HelperWidgets.AbstractButton { + style: StudioTheme.Values.connectionPopupButtonStyle + width: 160 + buttonIcon: qsTr("Remove Else Statement") + iconSize: StudioTheme.Values.baseFontSize + iconFont: StudioTheme.Constants.font + anchors.horizontalCenter: parent.horizontalCenter + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse + + onClicked: backend.removeElse() + } + + //Else Statement + StatementEditor { + actionType: action.currentValue ?? ConnectionModelStatementDelegate.Custom + horizontalSpacing: root.horizontalSpacing + columnWidth: root.columnWidth + statement: backend.koStatement + spacing: root.verticalSpacing + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse + } + // Editor Rectangle { id: editor diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp index 195f6f1ead6..571f2bf18be 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -345,6 +345,17 @@ MatchedCondition &ConnectionEditorStatements::matchedCondition(Handler &handler) 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) { diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h index 908b7305c69..7a4b079af9d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -121,6 +121,8 @@ QMLDESIGNER_EXPORT MatchedStatement &okStatement(ConnectionEditorStatements::Han QMLDESIGNER_EXPORT MatchedStatement &koStatement(ConnectionEditorStatements::Handler &handler); QMLDESIGNER_EXPORT MatchedCondition &matchedCondition(ConnectionEditorStatements::Handler &handler); +QMLDESIGNER_EXPORT ConditionalStatement &conditionalStatement( + ConnectionEditorStatements::Handler &handler); } // namespace ConnectionEditorStatements diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 43ccfda84f9..f5e42bbc99b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -575,6 +575,11 @@ ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel * this, [this]() { handleOkStatementChanged(); }); + connect(&m_koStatementDelegate, + &ConnectionModelStatementDelegate::statementChanged, + this, + [this]() { handleKOStatementChanged(); }); + connect(&m_conditionListModel, &ConditionListModel::conditionChanged, this, [this]() { handleConditionChanged(); }); @@ -673,19 +678,7 @@ void ConnectionModelBackendDelegate::addCondition() QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - ConnectionModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView()->isAttached(), return ); - - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", - [&]() { - signalHandlerProperty.setSource(newSource); - }); - - setSource(signalHandlerProperty.source()); + commitNewSource(newSource); setupHandlerAndStatements(); setupCondition(); @@ -693,8 +686,6 @@ void ConnectionModelBackendDelegate::addCondition() void ConnectionModelBackendDelegate::removeCondition() { - qDebug() << Q_FUNC_INFO; - ConnectionEditorStatements::MatchedStatement okStatement = ConnectionEditorStatements::okStatement(m_handler); @@ -702,24 +693,42 @@ void ConnectionModelBackendDelegate::removeCondition() QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - ConnectionModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView()->isAttached(), return ); - - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - - model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::removeCondition", - [&]() { - signalHandlerProperty.setSource(newSource); - }); - - setSource(signalHandlerProperty.source()); + 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; @@ -803,6 +812,11 @@ bool ConnectionModelBackendDelegate::hasCondition() const return m_hasCondition; } +bool ConnectionModelBackendDelegate::hasElse() const +{ + return m_hasElse; +} + void ConnectionModelBackendDelegate::setHasCondition(bool b) { if (b == m_hasCondition) @@ -812,6 +826,15 @@ void ConnectionModelBackendDelegate::setHasCondition(bool 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; @@ -902,6 +925,9 @@ void ConnectionModelBackendDelegate::setupHandlerAndStatements() m_koStatementDelegate.setActionType(m_actionType); } + ConnectionEditorStatements::isEmptyStatement(koStatement); + setHasElse(!ConnectionEditorStatements::isEmptyStatement(koStatement)); + emit actionTypeChanged(); } @@ -956,31 +982,28 @@ void ConnectionModelBackendDelegate::handleOkStatementChanged() QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - ConnectionModel *model = qobject_cast(parent()); + commitNewSource(newSource); +} - QTC_ASSERT(model, return ); +void ConnectionModelBackendDelegate::handleKOStatementChanged() +{ + ConnectionEditorStatements::MatchedStatement &koStatement + = ConnectionEditorStatements::koStatement(m_handler); - QTC_ASSERT(model->connectionView()->isAttached(), return ); + koStatement = m_koStatementDelegate.statement(); //TODO why? - SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); + QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - model->connectionView() - ->executeInTransaction("ConnectionModelBackendDelegate::handleOkStatementChanged", - [&]() { signalHandlerProperty.setSource(newSource); }); - - setSource(signalHandlerProperty.source()); + commitNewSource(newSource); } void ConnectionModelBackendDelegate::handleConditionChanged() { - qDebug() << Q_FUNC_INFO; ConnectionModel *model = qobject_cast(parent()); QTC_ASSERT(model, return ); QTC_ASSERT(model->connectionView()->isAttached(), return ); - auto view = model->connectionView(); - ConnectionEditorStatements::MatchedCondition &condition = ConnectionEditorStatements::matchedCondition(m_handler); condition = m_conditionListModel.condition(); //why? @@ -988,16 +1011,23 @@ void ConnectionModelBackendDelegate::handleConditionChanged() qDebug() << Q_FUNC_INFO << "new source" << newSource; + 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()); - try { - RewriterTransaction transaction = view->beginRewriterTransaction( - "ConnectionModelBackendDelegate::handleConditionChanged"); - signalHandlerProperty.setSource(newSource); - transaction.commit(); - } catch (const Exception &e) { - m_conditionListModel.setInvalid(e.description()); - } + model->connectionView()->executeInTransaction("ConnectionModelBackendDelegate::commitNewSource", + [&]() { + signalHandlerProperty.setSource(source); + }); setSource(signalHandlerProperty.source()); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 156227b4014..ecea5e7c058 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -242,6 +242,7 @@ class ConnectionModelBackendDelegate : public QObject 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) public: @@ -255,10 +256,14 @@ public: Q_INVOKABLE void addCondition(); Q_INVOKABLE void removeCondition(); + Q_INVOKABLE void addElse(); + Q_INVOKABLE void removeElse(); + signals: void currentRowChanged(); void actionTypeChanged(); void hasConditionChanged(); + void hasElseChanged(); void sourceChanged(); private: @@ -267,7 +272,9 @@ private: void handleException(); bool hasCondition() const; + bool hasElse() const; void setHasCondition(bool b); + void setHasElse(bool b); ActionType actionType() const; PropertyTreeModelDelegate *signal(); ConnectionModelStatementDelegate *okStatement(); @@ -280,8 +287,11 @@ private: void handleTargetChanged(); void handleOkStatementChanged(); + void handleKOStatementChanged(); void handleConditionChanged(); + void commitNewSource(const QString &source); + ActionType m_actionType; QString m_exceptionError; int m_currentRow = -1; @@ -291,6 +301,7 @@ private: ConnectionModelStatementDelegate m_koStatementDelegate; ConditionListModel m_conditionListModel; bool m_hasCondition = false; + bool m_hasElse = false; QString m_source; }; From 5907e480a0ad3e8df96b393c78c0ded97ec5accb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 1 Sep 2023 17:29:07 +0200 Subject: [PATCH 167/266] QmlDesigner: Fix QML for bindings/properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Ensure a "node" is added to the model if missing, for e.g. Singletons * Expose targetNode of property Change-Id: Ifbb131c1d6efaaceb15a6e630ce56023ace63311 Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/BindingsDialog.qml | 26 ++++ .../connectionseditor/BindingsDialogForm.qml | 123 ++++++++---------- .../connectionseditor/PropertiesDialog.qml | 26 ++++ .../PropertiesDialogForm.qml | 95 +++++++------- .../connectioneditor/bindingmodel.cpp | 6 +- .../dynamicpropertiesmodel.cpp | 15 ++- .../connectioneditor/dynamicpropertiesmodel.h | 4 + 7 files changed, 178 insertions(+), 117 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml index d544393a33b..65321b3b6e4 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml @@ -2,12 +2,38 @@ // 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 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 + 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 + + } + + } BindingsDialogForm { id: form y: 32 + height: 160 } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index 0d053771440..912bbad8287 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -3,86 +3,71 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtQuick.Controls -import StudioControls +import StudioControls as StudioControls +import StudioTheme as StudioTheme -Item { - width: 400 - height: 800 +Column { + id: root + + readonly property real horizontalSpacing: 10 + readonly property real verticalSpacing: 16 + readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 property var backend - PopupLabel { - x: 10 - y: 25 - text: qsTr("Target") + y: StudioTheme.Values.popupMargin + width: parent.width + spacing: root.verticalSpacing + + Row { + 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.") } } - PopupLabel { - id: text111 - x: 80 - y: 25 - text: backend.targetNode - font.pixelSize: 15 + Row { + spacing: root.horizontalSpacing + + StudioControls.TopLevelComboBox { + id: sourceNode + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth + + model: backend.sourceNode.model ?? [] + onModelChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex + onActivated: backend.sourceNode.activateIndex(sourceNode.currentIndex) + property int currentTypeIndex: backend.sourceNode.currentIndex ?? 0 + onCurrentTypeIndexChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex + } + + PopupLabel { + width: root.columnWidth + text: backend.targetNode + } + } - TopLevelComboBox { - id: target - x: 101 - width: 210 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -335 - model: backend.property.model ?? [] - enabled: false - //I see no use case to actually change the property name - //onActivated: backend.targetNode.activateIndex(target.currentIndex) - property int currentTypeIndex: backend.property.currentIndex ?? 0 - onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex - } + Row { + spacing: root.horizontalSpacing - PopupLabel { - x: 13 - y: 111 - text: qsTr("Source Propety") - } + StudioControls.TopLevelComboBox { + id: sourceProperty + style: StudioTheme.Values.connectionPopupControlStyle + width: root.columnWidth - TopLevelComboBox { - id: sourceNode - x: 135 - y: 98 - width: 156 + model: backend.sourceProperty.model ?? [] + onModelChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex + onActivated: backend.sourceProperty.activateIndex( + sourceProperty.currentIndex) + property int currentTypeIndex: backend.sourceProperty.currentIndex ?? 0 + onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex + } - model: backend.sourceNode.model ?? [] + PopupLabel { + width: root.columnWidth + text: backend.property.currentText + } - onModelChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex - - onActivated: backend.sourceNode.activateIndex(sourceNode.currentIndex) - property int currentTypeIndex: backend.sourceNode.currentIndex ?? 0 - onCurrentTypeIndexChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex - } - - PopupLabel { - x: 13 - y: 88 - text: qsTr("Source Node") - } - - TopLevelComboBox { - id: sourceProperty - x: 140 - y: 121 - width: 156 - - model: backend.sourceProperty.model ?? [] - onModelChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex - onActivated: backend.sourceProperty.activateIndex( - sourceProperty.currentIndex) - property int currentTypeIndex: backend.sourceProperty.currentIndex ?? 0 - onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex - } - - PopupLabel { - x: 10 - y: 55 - text: qsTr("Property") } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml index cd0ae16226a..d649d6380ef 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml @@ -2,12 +2,38 @@ // 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 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 + 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 0936581c86f..26398e7acbb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialogForm.qml @@ -1,68 +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 -Item { - width: 400 - height: 800 + readonly property real horizontalSpacing: 10 + readonly property real verticalSpacing: 16 + readonly property real columnWidth: (root.width - root.horizontalSpacing) / 2 property var backend + y: StudioTheme.Values.popupMargin + width: parent.width + spacing: root.verticalSpacing + PopupLabel { - x: 10 - y: 25 - - text: qsTr("Type:") - + 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 } - PopupLabel { - x: 10 - y: 131 - text: qsTr("Name") + 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 - 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 - PopupLabel { - x: 10 - y: 81 - text: qsTr("Value") - } + width: root.columnWidth + actionIndicatorVisible: false + translationIndicatorVisible: false - TextInput { - id: value - 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/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 2b2025ed83a..4fc3b81c310 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -553,12 +553,16 @@ void BindingModelBackendDelegate::setCurrentRow(int i) } std::sort(sourceNodes.begin(), sourceNodes.end()); - m_sourceNode.setModel(sourceNodes); QString sourceNodeName; QString sourcePropertyName; model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); + if (!sourceNodes.contains(sourceNodeName)) + sourceNodes.append(sourceNodeName); + + m_sourceNode.setModel(sourceNodes); + m_sourceNode.setCurrentText(sourceNodeName); m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty)); diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index eb0976eaed9..8557ebf5cae 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -1022,6 +1022,10 @@ 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() @@ -1077,9 +1081,10 @@ void DynamicPropertiesModelBackendDelegate::handleNameChanged() const QString expression = bindingProperty.expression(); const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName(); + targetNode.removeProperty(bindingProperty.name()); + targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression); - targetNode.removeProperty(bindingProperty.name()); }); return; @@ -1093,9 +1098,10 @@ void DynamicPropertiesModelBackendDelegate::handleNameChanged() ModelNode targetNode = variantProperty.parentModelNode(); model->view()->executeInTransaction(__FUNCTION__, [=]() { + targetNode.removeProperty(variantProperty.name()); + targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value); - targetNode.removeProperty(variantProperty.name()); }); } @@ -1167,6 +1173,11 @@ QVariant DynamicPropertiesModelBackendDelegate::variantValue() const return m_value.text(); } +QString DynamicPropertiesModelBackendDelegate::targetNode() const +{ + return m_targetNode; +} + StudioQmlComboBoxBackend *DynamicPropertiesModelBackendDelegate::type() { return &m_type; diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index c41875dfc09..766b4e7b87c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -135,6 +135,7 @@ 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) @@ -148,6 +149,7 @@ signals: void currentRowChanged(); void nameChanged(); void valueChanged(); + void targetNodeChanged(); private: int currentRow() const; @@ -157,6 +159,7 @@ private: void handleValueChanged(); void handleException(); QVariant variantValue() const; + QString targetNode() const; StudioQmlComboBoxBackend *type(); @@ -168,6 +171,7 @@ private: StudioQmlTextBackend m_value; int m_currentRow = -1; QString m_exceptionError; + QString m_targetNode; }; } // namespace QmlDesigner From 5302567fca6e24dbd803c7aac225853e411d843d Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 1 Sep 2023 16:43:56 +0300 Subject: [PATCH 168/266] QmlDesigner: Add shaders pre-compilation support functionality Task-number: QDS-10499 Change-Id: Idc2722b5b9fea7e2914bae5a67ce289a27c236ae Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectmaker/compositionnode.cpp | 15 ++ .../components/effectmaker/compositionnode.h | 13 + .../effectmaker/effectmakermodel.cpp | 222 +++++++++++++++++- .../components/effectmaker/effectmakermodel.h | 36 ++- 4 files changed, 283 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index eb2e7b3b322..d0e3c6c53cd 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -44,6 +44,21 @@ QStringList CompositionNode::requiredNodes() const return m_requiredNodes; } +bool CompositionNode::isEnabled() const +{ + return m_isEnabled; +} + +void CompositionNode::setIsEnabled(bool newIsEnabled) +{ + m_isEnabled = newIsEnabled; +} + +CompositionNode::NodeType CompositionNode::type() const +{ + return m_type; +} + void CompositionNode::parse(const QString &qenPath) { QFile qenFile(qenPath); diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index 2b2864efb97..dfca7fa0221 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -17,6 +17,12 @@ class CompositionNode : public QObject Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) public: + enum NodeType { + SourceNode = 0, + DestinationNode, + CustomNode + }; + CompositionNode(const QString &qenPath); QString fragmentCode() const; @@ -27,6 +33,11 @@ public: QStringList requiredNodes() const; + NodeType type() const; + + bool isEnabled() const; + void setIsEnabled(bool newIsEnabled); + signals: void uniformsModelChanged(); @@ -34,10 +45,12 @@ 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; EffectMakerUniformsModel m_unifomrsModel; }; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index b122a4e8530..76c9c572ff7 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -112,7 +112,7 @@ const QString EffectMakerModel::getVSUniforms() const QString EffectMakerModel::getFSUniforms() { - QList uniforms = allUniforms(); + const QList uniforms = allUniforms(); QString s; s += "#version 440\n"; s += '\n'; @@ -227,4 +227,224 @@ void EffectMakerModel::resetEffectError(int type) } } +const QString EffectMakerModel::getDefineProperties() +{ + // TODO + + return QString(); +} + +const QString EffectMakerModel::getConstVariables() +{ + // TODO + + return QString(); +} + +int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) +{ + Q_UNUSED(code) + Q_UNUSED(tag) + + // TODO + return 0; +} + +QString EffectMakerModel::processVertexRootLine(const QString &line) +{ + Q_UNUSED(line) + + // TODO + return QString(); +} + +QString EffectMakerModel:: processFragmentRootLine(const QString &line) +{ + Q_UNUSED(line) + + // TODO + return QString(); +} + +QStringList EffectMakerModel::getDefaultRootVertexShader() +{ + // TODO + return {}; +} + +QStringList EffectMakerModel::getDefaultRootFragmentShader() +{ + // TODO + return {}; +} + +QStringList EffectMakerModel::removeTagsFromCode(const QStringList &codeLines) +{ + Q_UNUSED(codeLines) + + // TODO + return {}; +} + +QString EffectMakerModel::removeTagsFromCode(const QString &code) +{ + Q_UNUSED(code) + + // TODO + return QString(); +} + +QString EffectMakerModel::getCustomShaderVaryings(bool outState) +{ + Q_UNUSED(outState) + + // TODO + return QString(); +} + +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; +} + +void EffectMakerModel::bakeShaders() +{ + resetEffectError(ErrorPreprocessor); + if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { + setShadersUpToDate(true); + return; + } + + setShadersUpToDate(false); + + // TODO: Compilation starts here +} + +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 index 741647a1425..71a58455499 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -18,6 +18,7 @@ struct EffectError { 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; @@ -30,6 +31,7 @@ class EffectMakerModel : public QAbstractListModel 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); @@ -44,10 +46,14 @@ public: Q_INVOKABLE void removeNode(int idx); + bool shadersUpToDate() const; + void setShadersUpToDate(bool newShadersUpToDate); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); void effectErrorChanged(); + void shadersUpToDateChanged(); private: enum Roles { @@ -55,6 +61,15 @@ private: UniformsRole }; + enum ErrorTypes { + ErrorCommon = -1, + ErrorQMLParsing, + ErrorVert, + ErrorFrag, + ErrorQMLRuntime, + ErrorPreprocessor + }; + bool isValidIndex(int idx) const; const QList allUniforms(); @@ -68,14 +83,31 @@ private: void setEffectError(const QString &errorMessage, int type, int lineNumber); void resetEffectError(int type); + const QString getDefineProperties(); + const QString getConstVariables(); + 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 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; }; } // namespace QmlDesigner From 4e0c1e0b8263bcd125f4a9c0a8a995ddc79d6aa2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 1 Sep 2023 15:59:14 +0300 Subject: [PATCH 169/266] QmlDesigner: Fix issues in 3D view when parent node has transform 1. Hiding the selection box resets the parent transform we calculate during selection box geometry generation. Worked around this issue by hiding the boxes by using null material instead of visible property. 2. Aligning cameras to views and vice versa didn't consider the possibility that the camera might be a child node with a parent node that has transform. Fixes: QDS-10502 Change-Id: Ibf4af71424b8b673a42042dbc1840754967799c5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 2 +- .../qml2puppet/mockfiles/qt6/SelectionBox.qml | 21 +++++++----- .../qml2puppet/editor3d/generalhelper.cpp | 32 ++++++++++++++++--- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 8a4db279628..14f1bb2037a 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -311,7 +311,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;}); } } } 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/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 57b02f4b168..7e53a766c3e 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -353,8 +353,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); } } @@ -376,8 +400,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); } From a8c5dbfe0d981f27d9bc47957692545e689c2842 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Tue, 29 Aug 2023 09:28:31 +0200 Subject: [PATCH 170/266] QmlDesigner: Add missing views to All Views Workspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I87dae8096e0a270e0ac16372998b2069d64593a9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tanja Remes Reviewed-by: Henning Gründl --- .../workspacePresets/Views-All.wrk | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) 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 From eb28d9e5dc94a42fc73c8597335df05698773a43 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 4 Sep 2023 13:09:34 +0300 Subject: [PATCH 171/266] QmlDesigner: Add shader variables and constants conversion Task-number: QDS-10499 Change-Id: I675f920a17415f618318337ea4ca3fa0c8c011f5 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../effectmaker/effectmakermodel.cpp | 41 ++++++++++++++++++- .../components/effectmaker/effectmakermodel.h | 1 + .../components/effectmaker/uniform.h | 1 - 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 76c9c572ff7..15ab784982f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -7,6 +7,7 @@ #include "uniform.h" #include +#include #include @@ -227,6 +228,33 @@ void EffectMakerModel::resetEffectError(int type) } } +// 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(); + } +} + const QString EffectMakerModel::getDefineProperties() { // TODO @@ -236,9 +264,18 @@ const QString EffectMakerModel::getDefineProperties() const QString EffectMakerModel::getConstVariables() { - // TODO + 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 QString(); + return s; } int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 71a58455499..824cfaa0470 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -83,6 +83,7 @@ private: void setEffectError(const QString &errorMessage, int type, int lineNumber); void resetEffectError(int type); + QString valueAsVariable(const Uniform &uniform); const QString getDefineProperties(); const QString getConstVariables(); int getTagIndex(const QStringList &code, const QString &tag); diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 7117a7591de..761a22199f5 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -68,7 +68,6 @@ public: static QString stringFromType(Uniform::Type type); static Uniform::Type typeFromString(const QString &typeString); - static QString typeToUniform(Uniform::Type type); signals: void uniformValueChanged(); From 23a61ca92630d226cb6740ec50eb50eba377a6e4 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 4 Sep 2023 14:56:30 +0300 Subject: [PATCH 172/266] QmlDesigner: Add syntax highlighter data for shader tags Task-number: QDS-10499 Change-Id: Idbf99f1a86055085c53adaaed15af6058e39b39c Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../effectmaker/effectmakermodel.cpp | 13 +- .../components/effectmaker/effectmakermodel.h | 4 +- .../effectmaker/syntaxhighlighterdata.cpp | 190 ++++++++++++++++++ .../effectmaker/syntaxhighlighterdata.h | 22 ++ 5 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp create mode 100644 src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index cd80c894ea6..d3f6cd599f3 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -724,6 +724,7 @@ extend_qtc_plugin(QmlDesigner effectutils.cpp effectutils.h effectmakercontextobject.cpp effectmakercontextobject.h shaderfeatures.cpp shaderfeatures.h + syntaxhighlighterdata.cpp syntaxhighlighterdata.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 15ab784982f..176678f328f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -255,13 +255,6 @@ QString EffectMakerModel::valueAsVariable(const Uniform &uniform) } } -const QString EffectMakerModel::getDefineProperties() -{ - // TODO - - return QString(); -} - const QString EffectMakerModel::getConstVariables() { const QList uniforms = allUniforms(); @@ -278,6 +271,12 @@ const QString EffectMakerModel::getConstVariables() return s; } +const QString EffectMakerModel::getDefineProperties() +{ + // TODO + return QString(); +} + int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) { Q_UNUSED(code) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 824cfaa0470..1d00c6dda71 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -80,12 +80,12 @@ private: QString detectErrorMessage(const QString &errorMessage); EffectError effectError() const; - void setEffectError(const QString &errorMessage, int type, int lineNumber); + void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); void resetEffectError(int type); QString valueAsVariable(const Uniform &uniform); - const QString getDefineProperties(); const QString getConstVariables(); + const QString getDefineProperties(); int getTagIndex(const QStringList &code, const QString &tag); QString processVertexRootLine(const QString &line); QString processFragmentRootLine(const QString &line); 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 + From 0d04bf95601a869bbb99ab2397454de22b548bb3 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 4 Sep 2023 16:39:24 +0300 Subject: [PATCH 173/266] QmlDesigner: Prevent the dynamic property from being overwritten Change-Id: Ia0bd78e0b1293b6d61251a68d7b4cf4ee234603e Reviewed-by: Thomas Hartmann Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/model/qmltextgenerator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp index e5721d21c32..1f01f63930f 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltextgenerator.cpp @@ -265,8 +265,7 @@ QString QmlTextGenerator::propertyToQml(const AbstractProperty &property, int in + QString::fromUtf8(property.name()) + QStringLiteral(": ") + toQml(property, indentDepth); - } - if (property.isSignalDeclarationProperty()) { + } else if (property.isSignalDeclarationProperty()) { result = m_tabSettings.indentationString(0, indentDepth, 0) + "signal" + " " + QString::fromUtf8(property.name()) + " " + toQml(property, indentDepth); } else { From 6251730f8f6def8afa019bc4beb01586d13fe74b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 4 Sep 2023 17:08:37 +0300 Subject: [PATCH 174/266] QmlDesigner: Refactor 3D view background and grid color implementation The new implementation doesn't require these colors to be stored in external dependencies anymore, as auxiliary properties are used for them instead. Fixes: QDS-10496 Change-Id: Ie71408617259d1af73a45f327d4bdfa4f2fa3a2b Reviewed-by: Mahmoud Badri --- .../commands/createscenecommand.h | 12 +- .../interfaces/nodeinstanceglobal.h | 3 - .../edit3d/backgroundcolorselection.cpp | 14 +-- .../edit3d/backgroundcolorselection.h | 11 +- .../components/edit3d/edit3dactions.cpp | 4 +- .../components/edit3d/edit3dview.cpp | 31 +++-- .../components/edit3d/edit3dviewconfig.h | 17 ++- .../include/externaldependenciesinterface.h | 2 - .../instances/nodeinstanceview.cpp | 7 +- .../qmldesignerexternaldependencies.cpp | 14 --- .../qmldesignerexternaldependencies.h | 2 - .../qml2puppet/mockfiles/qt6/EditView3D.qml | 36 +++--- .../qml2puppet/editor3d/generalhelper.cpp | 13 ++ .../qml2puppet/editor3d/generalhelper.h | 9 +- .../qt5informationnodeinstanceserver.cpp | 114 ++++++++++-------- .../qt5informationnodeinstanceserver.h | 2 + .../qmldesigner/coretests/tst_testcore.cpp | 2 - .../tests/mocks/externaldependenciesmock.h | 2 - 18 files changed, 153 insertions(+), 142 deletions(-) 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/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp index a50966292fa..74f2417bd16 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); @@ -43,8 +43,8 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, 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/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 588aad00c1d..3b1c282148b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -119,6 +119,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(); @@ -189,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_particlesPlayAction->action()->setChecked(true); + if (sceneState.contains(syncBgColorKey)) + m_syncBackgroundColorAction->action()->setChecked(sceneState[syncBgColorKey].toBool()); + else + m_syncBackgroundColorAction->action()->setChecked(true); + // Selection context change updates visible and enabled states SelectionContext selectionContext(this); selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast); @@ -202,6 +208,13 @@ void Edit3DView::modelAttached(Model *model) syncSnapAuxPropsToSettings(); + rootModelNode().setAuxiliaryData(edit3dGridColorProperty, + QVariant::fromValue(Edit3DViewConfig::loadColor( + DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR))); + rootModelNode().setAuxiliaryData(edit3dBgColorProperty, + QVariant::fromValue(Edit3DViewConfig::loadColor( + DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR))); + checkImports(); auto cachedImage = m_canvasCache.take(model); if (cachedImage) { @@ -423,10 +436,10 @@ void Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorA 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); } }); @@ -434,7 +447,7 @@ void Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorA m_selectBackgroundColorAction = std::make_unique( Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, - View3DActionType::SelectBackgroundColor, + View3DActionType::Empty, description, QKeySequence(), false, @@ -456,12 +469,12 @@ void Edit3DView::createGridColorSelectionAction() edit3DWidget(), DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, this, - View3DActionType::SelectGridColor); + edit3dGridColorProperty); }; m_selectGridColorAction = std::make_unique( Constants::EDIT3D_EDIT_SELECT_GRID_COLOR, - View3DActionType::SelectGridColor, + View3DActionType::Empty, description, QKeySequence(), false, @@ -481,22 +494,22 @@ void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) 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}); + 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); } }; m_resetColorAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, - View3DActionType::ResetBackgroundColor, + View3DActionType::Empty, description, QKeySequence(), false, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h index 5c461cc7327..2783cba08bc 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 @@ -38,20 +38,20 @@ public: QmlDesignerPlugin::settings().insert(key, value); } - static void setColors(AbstractView *view, View3DActionType type, const QList &colorConfig) + static void setColors(AbstractView *view, const AuxiliaryDataKeyView &auxProp, const QList &colorConfig) { QVariant param; - if (type == View3DActionType::SelectGridColor) + if (auxProp.name == "edit3dGridColor") param = colorConfig.isEmpty() ? QColor() : colorConfig[0]; else param = QVariant::fromValue(colorConfig); - setVariant(view, type, param); + 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) @@ -66,11 +66,10 @@ public: static bool colorsValid(const QList &colorConfig) { return !colorConfig.isEmpty(); } private: - static void setVariant(AbstractView *view, View3DActionType type, const QVariant &value) + static void setVariant(AbstractView *view, const AuxiliaryDataKeyView &auxProp, const QVariant &value) { - view->emitView3DAction(type, value); + view->rootModelNode().setAuxiliaryData(auxProp, value); } - }; } // namespace QmlDesigner 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/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index d49ed5b7d46..42c0a4263a2 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 diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index a703f838663..4b2cd9212b3 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -62,20 +62,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( 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/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 14f1bb2037a..9de033e5349 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -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) - viewRoot.gridColor = viewStates.selectGridColor } // If resetToDefault is true, tool states not specifically set to anything will be reset to @@ -224,13 +222,13 @@ Item { if ("syncBackgroundColor" in toolStates) { syncBackgroundColor = toolStates.syncBackgroundColor; - if (syncBackgroundColor) { - var color = []; - color[0] = _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) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 7e53a766c3e..099855384d5 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -568,6 +568,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; @@ -977,6 +982,14 @@ QVector3D GeneralHelper::adjustScaleForSnap(const QVector3D &newScale) return adjScale; } +void GeneralHelper::setBgColor(const QVariant &colors) +{ + if (m_bgColor != colors) { + m_bgColor = colors; + emit bgColorChanged(); + } +} + void GeneralHelper::handlePendingToolStateUpdate() { m_toolStateUpdateTimer.stop(); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 70605345c0c..a2416c82799 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(); @@ -94,6 +95,7 @@ public: void setSceneEnvironmentColor(const QString &sceneId, const QColor &color); Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; + void clearSceneEnvironmentColors(); bool isMacOS() const; @@ -118,13 +120,16 @@ public: 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; @@ -159,6 +164,8 @@ private: double m_snapPositionInterval = 50.; double m_snapRotationInterval = 5.; double m_snapScaleInterval = .1; + + QVariant m_bgColor; }; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 203a2971ea5..5d198a2ca70 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -393,6 +393,25 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings(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) { @@ -943,20 +962,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 } @@ -1036,6 +1043,9 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots() } ++it; } + + updateSceneEnvColorsToHelper(); + if (updateActiveScene) { m_active3DView = findView3DForSceneRoot(m_active3DScene); updateActiveSceneToEditView3D(); @@ -1975,18 +1985,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 @@ -2000,19 +1999,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 @@ -2140,6 +2126,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); updateSnapSettings(command.auxiliaryChanges); + updateColorSettings(command.auxiliaryChanges); } QObject::connect(&m_renderModelNodeImageViewTimer, &QTimer::timeout, @@ -2346,11 +2333,10 @@ 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 @@ -2396,6 +2382,42 @@ QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const return cameras; } +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 +} + void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command) { bool hasDynamicProperties = false; @@ -2510,13 +2532,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()); @@ -2581,6 +2596,7 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); updateSnapSettings(command.auxiliaryChanges); + updateColorSettings(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 3f0e68f854d..c774bc8f33f 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -126,6 +126,7 @@ private: 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); @@ -137,6 +138,7 @@ private: #endif void setSceneEnvironmentColor(const PropertyValueContainer &container); QVariantList alignCameraList() const; + void updateSceneEnvColorsToHelper(); RenderViewData m_editView3DData; RenderViewData m_modelNode3DImageViewData; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index f8fb4860243..ed2b110e967 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/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)); From 9bbe78df8b9905c3540a2bf10043d14fb0ba68ee Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 4 Sep 2023 14:17:01 +0300 Subject: [PATCH 175/266] QmlDesigner: Implement effect maker node drag to reorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also small relevant tweaks Fixes: QDS-10411 Change-Id: I332482d4726c79786edbc0a5fa1e8f6489d77f11 Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- .../EffectCompositionNode.qml | 3 + .../effectMakerQmlSources/EffectMaker.qml | 78 +++++++++++++++---- .../effectMakerQmlSources/EffectNode.qml | 6 +- .../imports/HelperWidgets/IconButton.qml | 15 +++- .../imports/HelperWidgets/Section.qml | 52 ++++++++++++- .../effectmaker/effectmakermodel.cpp | 11 +++ .../components/effectmaker/effectmakermodel.h | 1 + 7 files changed, 147 insertions(+), 19 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml index 6efa67919d0..ed89ea149bd 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -15,8 +15,11 @@ HelperWidgets.Section { caption: nodeName category: "EffectMaker" + draggable: true + fillBackground: true showCloseButton: true closeButtonToolTip: qsTr("Remove") + onCloseButtonClicked: { EffectMakerBackend.effectMakerModel.removeNode(index) } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 3e41d32c633..5c32df314a0 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -10,6 +10,11 @@ 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 @@ -47,7 +52,6 @@ Item { } } - HelperWidgets.ScrollView { id: scrollView @@ -55,29 +59,77 @@ Item { height: parent.height - y clip: true - Behavior on contentY { - id: contentYBehavior - PropertyAnimation { - id: scrollViewAnim - easing.type: Easing.InOutQuad - } - } - Column { width: scrollView.width spacing: 1 Repeater { - id: compositionRepeater + id: repeater width: root.width model: EffectMakerBackend.effectMakerModel 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 } } diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml index 324304e4e45..8ae703adb7e 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectNode.qml @@ -13,7 +13,7 @@ Rectangle { id: root width: 140 - height: 22 + height: 32 color: mouseArea.containsMouse ? StudioTheme.Values.themeControlBackgroundInteraction : "transparent" @@ -38,8 +38,8 @@ Rectangle { IconImage { id: nodeIcon - width: 22 - height: 22 + width: 32 + height: 32 color: StudioTheme.Values.themeTextColor source: modelData.nodeIcon 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/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index dd7b750ffaa..b1161147f9a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -21,6 +21,9 @@ Item { property alias showCloseButton: closeButton.visible property alias closeButtonToolTip: closeButton.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 @@ -82,6 +85,8 @@ Item { signal expand() signal collapse() signal closeButtonClicked() + signal startDrag(var section) + signal stopDrag() DropArea { id: dropArea @@ -128,7 +133,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) anchors.verticalCenter: parent.verticalCenter } @@ -136,7 +141,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 } @@ -174,8 +179,34 @@ Item { onClicked: root.closeButtonClicked() } + + IconButton { + id: dragButton + + icon: StudioTheme.Constants.dragmarks + buttonSize: 22 + iconScale: containsMouse ? 1.2 : 1 + transparentBg: true + + visible: false + drag.target: section + drag.axis: Drag.YAxis + + onPressed: { + section.startDrag(section) + + section.z = ++section.parent.z // put the dragged section on top + } + + onReleased: { + section.stopDrag() + } + } } + Drag.active: dragButton.drag.active + Drag.source: dragButton + Rectangle { id: topSeparator height: 1 @@ -193,6 +224,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/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 176678f328f..47e77305e60 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -49,6 +49,17 @@ void EffectMakerModel::addNode(const QString &nodeQenPath) endInsertRows(); } +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); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 1d00c6dda71..02fb5cec2c9 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -44,6 +44,7 @@ public: void addNode(const QString &nodeQenPath); + Q_INVOKABLE void moveNode(int fromIdx, int toIdx); Q_INVOKABLE void removeNode(int idx); bool shadersUpToDate() const; From 14b96533fcb88258aa467b3e64e453dfa47de0e6 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 1 Sep 2023 17:06:03 +0200 Subject: [PATCH 176/266] QmlDesigner: Fix Binding Editor for When Condition Task-number: QDS-10537 Change-Id: Icc996fe3a52f66d5e16dd2aa840032b9687a7f1d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Aleksei German --- .../qmldesigner/components/bindingeditor/bindingeditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"); From b8f97713c5c1d00f3de3ad63d23d3af90efa717a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 5 Sep 2023 16:32:58 +0200 Subject: [PATCH 177/266] Utils: Exchange std::span implementation The old implementation was not supporting iterators. Which made it a little bit complicated to use it together with algorithms. Change-Id: I99cf43dffb4bcb83a953ca1c68ebc65722142ad9 Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot --- README.md | 4 +- .../overview/creator-acknowledgements.qdoc | 4 +- src/libs/3rdparty/span/README.md | 643 ++++- src/libs/3rdparty/span/span.hpp | 2144 +++++++++++++---- src/libs/utils/span.h | 17 +- 5 files changed, 2289 insertions(+), 523 deletions(-) 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/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index 7f607374759..d39b36e9df8 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -368,7 +368,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}. @@ -376,7 +376,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 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/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 From d04f08e3203a793697819a340919cf544c819604 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 4 Sep 2023 18:02:08 +0200 Subject: [PATCH 178/266] QmlDesigner: Hopefully fix strange MSVC fail Change-Id: I9677a85269a7496d557e7e3e9127604e48f668a4 Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/libs/sqlite/sqlitedatabasebackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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()) From 250b2cd7bd2481def4bbc83af7ecfc4f1f6a430c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 29 Aug 2023 15:37:23 +0200 Subject: [PATCH 179/266] Sqlite: Introduce implicit transaction Sqlite adds an implicit transaction if no transaction is used. That can be problematic if you use multiple statements but it is okay for one read statement. Write and read write statements should use immediate transactions because the acquire the write lock. Otherwise the write lock is only acquired as you try to write. From there it is much harder to recover. Change-Id: I04b0be7447f2b82b6921738d789c33cbbaa8de6e Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/libs/sqlite/sqlitereadstatement.h | 10 ++-- src/libs/sqlite/sqlitetransaction.h | 30 +++++++++++ .../sqlite/sqlitetransaction-test.cpp | 53 +++++++++++++++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 4f8091598b0..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,7 +42,7 @@ public: template auto optionalValueWithTransaction(const QueryTypes &...queryValues) { - return withDeferredTransaction(Base::database(), [&] { + return withImplicitTransaction(Base::database(), [&] { return Base::template optionalValue(queryValues...); }); } @@ -50,7 +50,7 @@ public: template auto valuesWithTransaction(const QueryTypes &...queryValues) { - return withDeferredTransaction(Base::database(), [&] { + return withImplicitTransaction(Base::database(), [&] { return Base::template values(queryValues...); }); } @@ -58,7 +58,7 @@ public: 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/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/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; From aaf7fba0c3bdfafff659cd8ac9adbdf17586b420 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 4 Sep 2023 16:44:47 +0200 Subject: [PATCH 180/266] QmlDesigner: Fix flyout workspace selection Task-number: QDS-10494 Change-Id: I2ebd65c744c085c55325b5f73f4e0da45ef5d04c Reviewed-by: Brook Cronin Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- share/qtcreator/qmldesigner/toolbar/Main.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 816e849143d..1ddedd08b48 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -400,6 +400,7 @@ Rectangle { currentIndex: workspacesFlyout.indexOfValue(backend.currentWorkspace) onCompressedActivated: backend.setCurrentWorkspace(workspacesFlyout.currentValue) + onCountChanged: workspacesFlyout.currentIndex = workspacesFlyout.indexOfValue(backend.currentWorkspace) } } } From 99cabee246338da7b2d6fc959cce16943d4149e9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 6 Sep 2023 12:24:20 +0200 Subject: [PATCH 181/266] Sqlite: Add hash operator to BasicId Because QHash is supporting std::hash we don't need to add a qHash function. That is saving us an include to qhashfunction.h too. Change-Id: I457051c828d25771028f3f28bf19ecb65c51edd1 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/libs/sqlite/sqliteids.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 6296f84718a..2309bd58e5a 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 From 1474afe8db116de301fa5b0e3e7366bbbdb21a83 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 5 Sep 2023 16:07:38 +0300 Subject: [PATCH 182/266] QmlDesigner: Add more shader compiling support functionality Functionality for adding default shaders, tags, and varying variables Task-number: QDS-10499 Change-Id: Ib32e558510fb37c29765d2467cfc4c047a446a87 Reviewed-by: Mahmoud Badri --- .../effectmaker/effectmakermodel.cpp | 138 +++++++++++++----- .../components/effectmaker/effectmakermodel.h | 4 + 2 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 47e77305e60..59f4b149408 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -4,8 +4,10 @@ #include "effectmakermodel.h" #include "compositionnode.h" +#include "syntaxhighlighterdata.h" #include "uniform.h" +#include #include #include @@ -166,7 +168,6 @@ const QString EffectMakerModel::getFSUniforms() return s; } - // Detects common GLSL error messages and returns potential // additional error information related to them. QString EffectMakerModel::detectErrorMessage(const QString &errorMessage) @@ -210,8 +211,7 @@ void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int // For shaders, get the line number from baker output. // Which is something like "ERROR: :15: message" int glslErrorLineNumber = -1; - static QRegularExpression spaceReg("\\s+"); - QStringList errorStringList = errorMessage.split(spaceReg, Qt::SkipEmptyParts); + QStringList errorStringList = errorMessage.split(m_spaceReg, Qt::SkipEmptyParts); if (errorStringList.size() >= 2) { QString lineString = errorStringList.at(1).trimmed(); if (lineString.size() >= 3) { @@ -284,69 +284,137 @@ const QString EffectMakerModel::getConstVariables() const QString EffectMakerModel::getDefineProperties() { - // TODO - return QString(); + 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) { - Q_UNUSED(code) - Q_UNUSED(tag) - - // TODO - return 0; + 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) { - Q_UNUSED(line) - - // TODO - return QString(); + 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 EffectMakerModel::processFragmentRootLine(const QString &line) { - Q_UNUSED(line) - - // TODO - return QString(); + 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() { - // TODO - return {}; + 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() { - // TODO - return {}; + 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) { - Q_UNUSED(codeLines) - - // TODO - return {}; + 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) { - Q_UNUSED(code) - - // TODO - return QString(); + QStringList codeLines = removeTagsFromCode(code.split('\n')); + return codeLines.join('\n'); } QString EffectMakerModel::getCustomShaderVaryings(bool outState) { - Q_UNUSED(outState) - - // TODO - return QString(); + 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) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 02fb5cec2c9..70a7f7b50c7 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -110,6 +110,10 @@ private: QStringList m_shaderVaryingVariables; QString m_fragmentShader; QString m_vertexShader; + QStringList m_defaultRootVertexShader; + QStringList m_defaultRootFragmentShader; + + const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; } // namespace QmlDesigner From 94003d76af990d9d5d596b95666ad86f1c3d7329 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 6 Sep 2023 15:55:24 +0200 Subject: [PATCH 183/266] Utils: Disable warning about unsafe buffer access There is a compile time check for the size but it seem that clang is missing that information. Change-Id: I53ea97cd2a56f11e7ff4428a13b39d19a7d73cf1 Reviewed-by: Marco Bubke --- src/libs/utils/smallstringlayout.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 6efcad449bb..a7f4fd3a188 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -86,6 +86,8 @@ struct alignas(16) StringDataLayout , reference{{string}, size, 0} {} + QT_WARNING_PUSH + QT_WARNING_DISABLE_CLANG("-Wunsafe-buffer-usage") template constexpr StringDataLayout(const char (&string)[Size]) noexcept { @@ -110,6 +112,7 @@ struct alignas(16) StringDataLayout reference = {{string}, Size - 1, 0}; } } + QT_WARNING_POP constexpr static size_type shortStringCapacity() noexcept { @@ -159,6 +162,8 @@ struct alignas(16) StringDataLayout constexpr StringDataLayout(const char (&string)[Size]) noexcept { @@ -183,6 +188,7 @@ struct alignas(16) StringDataLayout Date: Wed, 6 Sep 2023 15:26:59 +0200 Subject: [PATCH 184/266] StudioWelcoome: Fix help url Change-Id: I3566d09e884b661d58795178fd97e4c9ae345a9d Reviewed-by: Thomas Hartmann --- src/plugins/studiowelcome/studiowelcomeplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index f1e9ac284d2..6b4168f9b64 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -265,7 +265,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, From 4978cbc5f6323613a5196898fb65d35da0e37d9c Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Wed, 6 Sep 2023 16:32:10 +0300 Subject: [PATCH 185/266] QmlDesigner: Fix missing forward declration Change-Id: I8969153ec6bb531c2c5bcfa47bf99b15aefbd552 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/effectmaker/effectmakermodel.cpp | 1 - .../qmldesigner/components/effectmaker/effectmakermodel.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 59f4b149408..72ae2574fb3 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -8,7 +8,6 @@ #include "uniform.h" #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 70a7f7b50c7..b2814705cfd 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -6,6 +6,7 @@ #include "shaderfeatures.h" #include +#include #include namespace QmlDesigner { From 37c0737844d5de82a5c65acd6eb52883077c14db Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 4 Sep 2023 16:30:31 +0200 Subject: [PATCH 186/266] UnitTest: Fix or disable some flacky tests If we get better tools we can come back and look again into it. Change-Id: I8ded8f9831b0c46c09e15d283a6298ad09aaaeb6 Reviewed-by: Thomas Hartmann Reviewed-by: --- .../asynchronousexplicitimagecache-test.cpp | 28 +++++++++++++------ .../asynchronousimagefactory-test.cpp | 6 +++- 2 files changed, 25 insertions(+), 9 deletions(-) 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); From e0c129d4b1e0e0545f70155c82f6f50310de0e64 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 1 Sep 2023 16:03:21 +0200 Subject: [PATCH 187/266] QmlDesigner: Improve task queue The abort callbacks does not need any lock. It even can lead to a deadlock if they call back. And a thread was always newly started. Change-Id: I0e25b66bb3647aae16113628487acaa4c7377819 Reviewed-by: Reviewed-by: Thomas Hartmann --- .../designercore/imagecache/taskqueue.h | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index e331b2f5716..67b0904eed0 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -13,18 +13,15 @@ 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() - { - auto lock = clearTasks(); - stopThread(std::move(lock)); - joinThread(); - } + ~TaskQueue() { destroy(); } template void addTask(Arguments &&...arguments) @@ -39,9 +36,24 @@ public: m_condition.notify_all(); } - void clean() { clearTasks(); } + 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; @@ -80,7 +92,7 @@ private: return; if (m_backgroundThread.joinable()) - m_backgroundThread.join(); + return; m_sleeping = false; @@ -95,20 +107,16 @@ private: }}; } - std::unique_lock clearTasks() + void clearTasks(Tasks &tasks) { - std::unique_lock lock{m_mutex}; - for (Task &task : m_tasks) + for (Task &task : tasks) m_clearCallback(task); - m_tasks.clear(); - - return lock; } - void stopThread(std::unique_lock lock) + void stopThread() { { - auto l = std::move(lock); + std::unique_lock lock{m_mutex}; m_finishing = true; } m_condition.notify_all(); @@ -121,7 +129,7 @@ private: } private: - std::deque m_tasks; + Tasks m_tasks; std::mutex m_mutex; std::condition_variable m_condition; std::thread m_backgroundThread; From 759b560baba2d8fdc2e3f598475ff1c675c7791d Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 23 Aug 2023 12:12:07 +0200 Subject: [PATCH 188/266] Cleanup ConnectionEditor models Simplified interface for the BindingModel and DynamicPropertiesModel. Change-Id: I772f31be704afe2a43c6368aefab1b026b85ec8b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectionseditor/BindingsListView.qml | 2 - .../connectionseditor/PropertiesListView.qml | 2 - src/plugins/qmldesigner/CMakeLists.txt | 6 +- .../connectioneditor/bindingmodel.cpp | 788 +++------- .../connectioneditor/bindingmodel.h | 100 +- .../connectioneditor/bindingmodelitem.cpp | 55 + .../connectioneditor/bindingmodelitem.h | 35 + .../connectioneditorutils.cpp | 392 +++++ .../connectioneditor/connectioneditorutils.h | 44 + .../connectioneditor/connectionmodel.cpp | 4 - .../connectioneditor/connectionview.cpp | 120 +- .../connectioneditor/connectionview.h | 13 +- .../connectioneditor/connectionviewwidget.cpp | 614 -------- .../connectioneditor/connectionviewwidget.h | 94 -- .../connectioneditor/connectionviewwidget.ui | 265 ---- .../components/connectioneditor/delegates.cpp | 409 ----- .../components/connectioneditor/delegates.h | 78 - .../dynamicpropertiesitem.cpp | 74 + .../connectioneditor/dynamicpropertiesitem.h | 35 + .../dynamicpropertiesmodel.cpp | 1363 ++++------------- .../connectioneditor/dynamicpropertiesmodel.h | 148 +- .../materialeditor/materialeditorview.cpp | 12 +- .../dynamicpropertiesproxymodel.cpp | 54 +- .../textureeditor/textureeditorview.cpp | 12 +- 24 files changed, 1330 insertions(+), 3389 deletions(-) create mode 100644 src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/bindingmodelitem.h create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.ui delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/delegates.cpp delete mode 100644 src/plugins/qmldesigner/components/connectioneditor/delegates.h create mode 100644 src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.cpp create mode 100644 src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesitem.h diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index 68ace4b0ecf..d2252bba95b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -33,7 +33,6 @@ ListView { onCurrentIndexChanged: { root.currentIndex = root.model.currentIndex - dialog.backend.currentRow = root.currentIndex } // Number of columns @@ -79,7 +78,6 @@ ListView { onClicked: { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index 90b37aec7fa..bf1ed237cb5 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -33,7 +33,6 @@ ListView { onCurrentIndexChanged: { root.currentIndex = root.model.currentIndex - dialog.backend.currentRow = root.currentIndex } // Number of columns @@ -81,7 +80,6 @@ ListView { function onClicked() { root.model.currentIndex = itemDelegate.index root.currentIndex = itemDelegate.index - dialog.backend.currentRow = itemDelegate.index dialog.popup(mouseArea) } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index d3f6cd599f3..09d630ade83 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -485,7 +485,6 @@ 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" @@ -940,14 +939,15 @@ 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 ) diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 4fc3b81c310..69f6b5cc127 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -2,111 +2,27 @@ // 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 #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,465 +30,235 @@ 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 QStringList(); - } - - 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(); -} - -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 QStringList(); - } - - 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)); +} + +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]() { + bindingProperty.setExpression(expression.trimmed()); + }); +} + +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(); }); } -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()); - - 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()); - - QString sourceNodeName; - QString sourcePropertyName; - model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName); - - if (!sourceNodes.contains(sourceNodeName)) - sourceNodes.append(sourceNodeName); - - m_sourceNode.setModel(sourceNodes); + QString targetName = QString::fromUtf8(property.name()); + m_targetNode = idOrTypeName(property.parentModelNode()); + auto modelNodes = addName(availableModelNodes(view), sourceNodeName); + m_sourceNode.setModel(modelNodes); 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 + auto targetProperties = addName(availableTargetProperties(property), targetName); + m_property.setModel(targetProperties); + m_property.setCurrentText(targetName); + + emit targetNodeChanged(); } QString BindingModelBackendDelegate::targetNode() const @@ -595,54 +281,22 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() return &m_sourceNodeProperty; } -void BindingModelBackendDelegate::handleSourceNodeChanged() +void BindingModelBackendDelegate::expressionChanged() const { BindingModel *model = qobject_cast(parent()); - - QTC_ASSERT(model, return ); - QTC_ASSERT(model->connectionView(), return ); + QTC_ASSERT(model, return); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); QString expression; - if (sourceProperty.isEmpty()) { + if (sourceProperty.isEmpty()) expression = sourceNode; - } else { + else expression = sourceNode + QLatin1String(".") + sourceProperty; - } - BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow()); - model->connectionView()->executeInTransaction("BindingModel::updateExpression", - [&bindingProperty, expression]() { - bindingProperty.setExpression( - expression.trimmed()); - }); -} - -void BindingModelBackendDelegate::handleSourcePropertyChanged() -{ - BindingModel *model = qobject_cast(parent()); - - 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()); - }); + int row = model->currentIndex(); + model->commitExpression(row, expression); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 1f469876852..e167f2b5afb 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -3,132 +3,98 @@ #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); 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; 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/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp new file mode 100644 index 00000000000..c4ce5c23fe4 --- /dev/null +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -0,0 +1,392 @@ +// 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 + +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 { }; +} + +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 availableModelNodes(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 sourceNodes; +} + +QStringList dynamicPropertyNamesFromNode(const ModelNode& node) +{ + QStringList dynamicProperties; + for (const VariantProperty &variantProperty : node.variantProperties()) { + if (variantProperty.isDynamic()) + dynamicProperties << QString::fromUtf8(variantProperty.name()); + } + + for (const BindingProperty &bindingProperty : node.bindingProperties()) { + if (bindingProperty.isDynamic()) + dynamicProperties << QString::fromUtf8((bindingProperty.name())); + } + return dynamicProperties; +} + +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 dynamicPropertyNamesFromNode(modelNode) + writableProperties; + } + + return dynamicPropertyNamesFromNode(modelNode); +} + +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 {}; +} + +bool metaInfoIsCompatible(const NodeMetaInfo& sourceType, const PropertyMetaInfo& metaInfo) +{ + if (sourceType.isVariant()) + return true; + + NodeMetaInfo targetType = metaInfo.propertyType(); + 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; +} + +QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view) +{ + const QString expression = bindingProperty.expression(); + const QStringList stringlist = expression.split(QLatin1String(".")); + QStringList possibleProperties; + + const QString &id = stringlist.constFirst(); + ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode()); + if (!modelNode.isValid()) { + //if it's not a valid model node, maybe it's a singleton + if (RewriterView *rv = view->rewriterView()) { + for (const QmlTypeData &data : rv->getQMLTypes()) { + if (!data.typeName.isEmpty() && data.typeName == id) { + NodeMetaInfo metaInfo = view->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() << __FUNCTION__ << " invalid model node"; + return QStringList(); + } + + possibleProperties = possibleProperties + dynamicPropertyNamesFromNode(modelNode); + + 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"; + + 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..8a1cafa3e54 --- /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 availableModelNodes(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 f5e42bbc99b..08e662953e9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -80,10 +80,6 @@ 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(); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 78625efd388..d33e67d1164 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -2,7 +2,6 @@ // 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" @@ -70,9 +69,13 @@ public: "ConnectionsEditorEditorBackend", 1); map->setProperties( - {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}, - {"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}, - {"dynamicPropertiesModel", + {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}}); + + map->setProperties( + {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}}); + + map->setProperties( + {{"dynamicPropertiesModel", QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); qmlRegisterType("ConnectionsEditorEditorBackend", @@ -98,7 +101,6 @@ public: // init the first load of the QML UI elements reloadQmlSource(); } - ~ConnectionViewQuickWidget() = default; static QString qmlSourcesPath() @@ -136,17 +138,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() { @@ -156,25 +154,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(); } @@ -194,8 +189,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) @@ -212,10 +207,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()); } @@ -227,7 +222,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(); @@ -241,9 +236,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(); @@ -263,39 +258,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*/) @@ -310,14 +274,7 @@ void ConnectionView::currentStateChanged(const ModelNode &) WidgetInfo ConnectionView::widgetInfo() { - /* Enable new connection editor here */ - const bool newEditor = true; - - QWidget *widget = m_connectionViewWidget.data(); - if (newEditor) - widget = m_connectionViewQuickWidget.data(); - - return createWidgetInfo(widget, + return createWidgetInfo(m_connectionViewQuickWidget.data(), QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, @@ -334,31 +291,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; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 507637b4b91..b865bf51cc4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -51,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; @@ -63,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; @@ -83,9 +74,7 @@ public: signals: void currentIndexChanged(); -private: //variables - QPointer m_connectionViewWidget; - +private: ConnectionModel *m_connectionModel; BindingModel *m_bindingModel; DynamicPropertiesModel *m_dynamicPropertiesModel; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp deleted file mode 100644 index 655cc1a3914..00000000000 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ /dev/null @@ -1,614 +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"); - 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 8557ebf5cae..084afb8e100 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,293 @@ 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); + emit currentIndexChanged(); +} + +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 +346,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 QStringList(); - } - - 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 QStringList(); - } - - 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 +367,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())); @@ -1024,153 +404,62 @@ void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i) 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.removeProperty(bindingProperty.name()); + if (type == "real" || type == "int") + return value.toFloat(); - targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, - expression); - }); + if (type == "bool") + return value == "true"; - return; - } - - 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.removeProperty(variantProperty.name()); - - targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, - value); - }); - } - - 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); - } - } -} - -void DynamicPropertiesModelBackendDelegate::handleException() -{ - 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(); + int current = model->currentIndex(); + QVariant value = valueFromText(m_value.text(), m_type.currentText()); + model->commitPropertyValue(current, value); } QString DynamicPropertiesModelBackendDelegate::targetNode() const diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index 766b4e7b87c..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,112 +21,67 @@ 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 @@ -137,40 +90,33 @@ class DynamicPropertiesModelBackendDelegate : public QObject 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; - QString targetNode() 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; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 3192269b3ca..ac3a92c5d44 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -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/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/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 7c6ba5fc497..42d909400c3 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -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, From 9e60df9f7fab059d63f573f01db06f6c441fa980 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Sep 2023 10:11:23 +0200 Subject: [PATCH 189/266] CMake: Silence warning in third party code Adding SYSTEM_INCLUDE for which you get no warnings. Fix PUBLIC_SYSTEM_INCLUDE to work for all cases where PUBLIC_INLCUDES works. Change-Id: I7059c2879004743c13c368220596081dd054407a Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot --- cmake/QtCreatorAPI.cmake | 8 ++++++-- cmake/QtCreatorAPIInternal.cmake | 3 ++- src/plugins/cmakeprojectmanager/CMakeLists.txt | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cmake/QtCreatorAPI.cmake b/cmake/QtCreatorAPI.cmake index effbb454047..60a47b4fc93 100644 --- a/cmake/QtCreatorAPI.cmake +++ b/cmake/QtCreatorAPI.cmake @@ -124,7 +124,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}) @@ -197,7 +197,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} @@ -321,7 +323,7 @@ function(add_qtc_plugin target_name) cmake_parse_arguments(_arg "SKIP_INSTALL;INTERNAL_ONLY;SKIP_TRANSLATION;EXPORT;SKIP_PCH" "VERSION;COMPAT_VERSION;PLUGIN_JSON_IN;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} ) @@ -473,7 +475,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 39735a80c7a..27e64a5a954 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -465,7 +465,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;PUBLIC_SYSTEM_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} ) @@ -506,6 +506,7 @@ 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_SYSTEM_INCLUDES}" "SYSTEM") diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt index 057b4139459..3c8b75081f7 100644 --- a/src/plugins/cmakeprojectmanager/CMakeLists.txt +++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt @@ -2,7 +2,7 @@ add_qtc_plugin(CMakeProjectManager PLUGIN_CLASS CMakeProjectPlugin DEPENDS QmlJS PLUGIN_DEPENDS Core CppEditor ProjectExplorer TextEditor QtSupport - INCLUDES 3dparty/cmake + SYSTEM_INCLUDES 3dparty/cmake SOURCES builddirparameters.cpp builddirparameters.h cmake_global.h From 6f21cb0b230679821b651068518001c64c2cff57 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 5 Sep 2023 15:52:28 +0300 Subject: [PATCH 190/266] QmlDesigner: Cleanup puppet and related versioned code QDS requires minimum Qt version 6.4.3 to build at all, so removed all code and files from puppet that were relevant only for prior versions. Fixes: QDS-10556 Change-Id: I58a151fc25f733d4815869e749f77fe30072c19b Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../itemlibrary/itemlibraryassetimporter.cpp | 93 +- .../itemlibrary/itemlibraryassetimporter.h | 4 - .../metainfo/subcomponentmanager.cpp | 5 - .../qmldesigner/qmldesignerconstants.h | 1 - src/tools/qml2puppet/CMakeLists.txt | 22 - src/tools/qml2puppet/editor3d_qt5.qrc | 58 -- src/tools/qml2puppet/editor3d_qt6.qrc | 3 - .../instances/nodeinstanceclientproxy.cpp | 4 - .../qml2puppet/mockfiles/GenericBackend.qml | 8 - .../qml2puppet/mockfiles/images/area.png | Bin 1250 -> 0 bytes .../qml2puppet/mockfiles/images/area@2x.png | Bin 2326 -> 0 bytes .../mockfiles/images/static_floor.png | Bin 2685 -> 0 bytes .../mockfiles/qt5/AdjustableArrow.qml | 29 - .../mockfiles/qt5/AreaLightHandle.qml | 63 -- src/tools/qml2puppet/mockfiles/qt5/Arrow.qml | 41 - .../mockfiles/qt5/AutoScaleHelper.qml | 52 - .../qml2puppet/mockfiles/qt5/AxisHelper.qml | 111 -- .../mockfiles/qt5/AxisHelperArm.qml | 48 - .../mockfiles/qt5/CameraFrustum.qml | 39 - .../qml2puppet/mockfiles/qt5/CameraGizmo.qml | 40 - .../mockfiles/qt5/DirectionalDraggable.qml | 122 --- .../mockfiles/qt5/EditCameraController.qml | 231 ----- .../qml2puppet/mockfiles/qt5/EditView3D.qml | 981 ------------------ .../qml2puppet/mockfiles/qt5/FadeHandle.qml | 106 -- .../qml2puppet/mockfiles/qt5/HelperGrid.qml | 95 -- .../qml2puppet/mockfiles/qt5/IconGizmo.qml | 112 -- .../mockfiles/qt5/IconRenderer3D.qml | 66 -- .../qml2puppet/mockfiles/qt5/LightGizmo.qml | 334 ------ .../mockfiles/qt5/LightIconGizmo.qml | 22 - .../qml2puppet/mockfiles/qt5/LightModel.qml | 28 - src/tools/qml2puppet/mockfiles/qt5/Line3D.qml | 28 - .../mockfiles/qt5/MaterialNodeView.qml | 81 -- .../mockfiles/qt5/ModelNode2DImageView.qml | 23 - .../mockfiles/qt5/ModelNode3DImageView.qml | 116 --- .../mockfiles/qt5/ModelNodeView.qml | 55 - .../qml2puppet/mockfiles/qt5/MoveGizmo.qml | 153 --- .../qml2puppet/mockfiles/qt5/NodeNodeView.qml | 41 - .../qml2puppet/mockfiles/qt5/Overlay2D.qml | 47 - .../mockfiles/qt5/PlanarDraggable.qml | 97 -- .../mockfiles/qt5/PlanarMoveHandle.qml | 42 - .../mockfiles/qt5/PlanarScaleHandle.qml | 42 - .../qml2puppet/mockfiles/qt5/RotateGizmo.qml | 271 ----- .../qml2puppet/mockfiles/qt5/RotateRing.qml | 142 --- .../qml2puppet/mockfiles/qt5/ScaleGizmo.qml | 219 ---- .../qml2puppet/mockfiles/qt5/ScaleRod.qml | 51 - .../qml2puppet/mockfiles/qt5/SceneView3D.qml | 71 -- .../qml2puppet/mockfiles/qt5/SelectionBox.qml | 46 - .../mockfiles/qt5/SpotLightHandle.qml | 60 -- .../mockfiles/qt6/AreaLightHandle.qml | 64 -- .../qml2puppet/mockfiles/qt6/LightGizmo.qml | 74 +- .../mockfiles/qt6/LightIconGizmo.qml | 8 +- .../qml2puppet/editor3d/camerageometry.cpp | 10 - .../qml2puppet/editor3d/camerageometry.h | 3 - .../qml2puppet/editor3d/editor3d.pri | 5 - .../qml2puppet/editor3d/generalhelper.cpp | 40 - .../qml2puppet/editor3d/generalhelper.h | 2 - .../qml2puppet/editor3d/geometrybase.cpp | 2 - .../qml2puppet/editor3d/geometrybase.h | 2 - .../qml2puppet/editor3d/mousearea3d.cpp | 5 - .../editor3d/qt5compat/qquick3darealight.cpp | 37 - .../editor3d/qt5compat/qquick3darealight_p.h | 43 - .../editor3d/selectionboxgeometry.cpp | 17 - .../qml2puppet/iconrenderer/iconrenderer.cpp | 297 ------ .../qml2puppet/iconrenderer/iconrenderer.h | 60 -- .../qml2puppet/iconrenderer/iconrenderer.pri | 3 - .../qml2puppet/import3d/import3d.cpp | 4 - .../instances/nodeinstanceserver.cpp | 12 - .../qml2puppet/instances/nodeinstanceserver.h | 4 - .../instances/objectnodeinstance.cpp | 4 - .../instances/qmlstatenodeinstance.cpp | 2 - .../qt5informationnodeinstanceserver.cpp | 246 +---- .../instances/qt5nodeinstanceclientproxy.cpp | 8 - .../instances/qt5nodeinstanceserver.cpp | 42 +- .../instances/qt5nodeinstanceserver.h | 4 - .../instances/quick3dnodeinstance.cpp | 6 +- .../quick3drenderablenodeinstance.cpp | 7 - .../instances/quickitemnodeinstance.cpp | 88 -- .../instances/quickitemnodeinstance.h | 3 - .../instances/servernodeinstance.cpp | 5 - .../qml2puppet/instances/servernodeinstance.h | 8 +- src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp | 12 +- src/tools/qml2puppet/qml2puppet/qmlpuppet.h | 4 - .../qmlprivategate/qmlprivategate.cpp | 109 +- .../qmlprivategate/qmlprivategate.h | 7 - src/tools/qml2puppet/qmlpuppet.qrc | 1 - 85 files changed, 44 insertions(+), 5507 deletions(-) delete mode 100644 src/tools/qml2puppet/editor3d_qt5.qrc delete mode 100644 src/tools/qml2puppet/mockfiles/GenericBackend.qml delete mode 100644 src/tools/qml2puppet/mockfiles/images/area.png delete mode 100644 src/tools/qml2puppet/mockfiles/images/area@2x.png delete mode 100644 src/tools/qml2puppet/mockfiles/images/static_floor.png delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/AdjustableArrow.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/AreaLightHandle.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/Arrow.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/AutoScaleHelper.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/AxisHelper.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/AxisHelperArm.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/CameraFrustum.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/CameraGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/DirectionalDraggable.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/EditCameraController.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/EditView3D.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/FadeHandle.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/HelperGrid.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/IconGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/IconRenderer3D.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/LightGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/LightIconGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/LightModel.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/Line3D.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/MaterialNodeView.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/ModelNode2DImageView.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/ModelNode3DImageView.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/ModelNodeView.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/MoveGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/NodeNodeView.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/Overlay2D.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/PlanarDraggable.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/PlanarMoveHandle.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/PlanarScaleHandle.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/RotateGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/RotateRing.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/ScaleGizmo.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/ScaleRod.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/SceneView3D.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/SelectionBox.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt5/SpotLightHandle.qml delete mode 100644 src/tools/qml2puppet/mockfiles/qt6/AreaLightHandle.qml delete mode 100644 src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight.cpp delete mode 100644 src/tools/qml2puppet/qml2puppet/editor3d/qt5compat/qquick3darealight_p.h delete mode 100644 src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.cpp delete mode 100644 src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.h delete mode 100644 src/tools/qml2puppet/qml2puppet/iconrenderer/iconrenderer.pri 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/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 0afd0d735c7..9a3fca77abb 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/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 7e01b7de793..c2003441e0a 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -72,7 +72,6 @@ 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"; diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 77bde355e9f..271d16023ed 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -17,9 +17,6 @@ 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} COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml REQUIRED @@ -78,12 +75,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 @@ -140,19 +131,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..750c4054aab 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 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 ed261ff2d50255480dd02c5f995473424a8b4254..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1250 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?Foon^Al51-)_oyK zBS|}jn~_^7tVYW&gsW+zg2?uF%R)8Tq+g$rC_a5s?QdY&WY4@u+rC!IJe%`*Zt=UF z#@}}rbb5bi^}hZ!I&QtW*6B~P`#o~+=57ByyQ`~f$?doQl&+ulU%4vl{MoY`XU&od z4!z2M>hQlq46^|M_A?bfVayS1#W?8d8CT9-}E3b}rCtK8AN;C0oKojY DkgXl++X& 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 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 93073719f55f658c6dfcfb2a2f45e4839567a12b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2685 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 Iol`;+0Psp60ssI2 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/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/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 099855384d5..056e9ca6a02 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -218,11 +218,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; @@ -235,11 +231,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()); @@ -430,7 +422,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); @@ -440,12 +431,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(); } @@ -486,13 +471,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) { @@ -607,16 +590,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) { @@ -1042,15 +1015,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; } @@ -1117,11 +1085,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; @@ -1129,11 +1093,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 a2416c82799..a80ab516ae2 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -82,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(); 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 f4f22996e1f..a271c98a5fe 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -892,10 +892,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 @@ -907,7 +903,6 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) break; } } -#endif } } } 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 373bd6dbefb..77de3a5661b 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 5d198a2ca70..c407645e77a 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,7 +338,8 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks( #endif } -void Qt5InformationNodeInstanceServer::updateSnapSettings(const QVector &valueChanges) +void Qt5InformationNodeInstanceServer::updateSnapSettings( + [[maybe_unused]] const QVector &valueChanges) { #ifdef QUICK3D_MODULE auto helper = qobject_cast(m_3dHelper); @@ -393,7 +364,8 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings(const QVector &valueChanges) +void Qt5InformationNodeInstanceServer::updateColorSettings( + [[maybe_unused]] const QVector &valueChanges) { #ifdef QUICK3D_MODULE if (m_editView3DData.rootItem) { @@ -432,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 @@ -481,8 +453,6 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos(const QPointF &pos) data.append(pos3d); nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, QVariant::fromValue(data)}); -#else - Q_UNUSED(pos) #endif } @@ -498,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, @@ -511,11 +478,7 @@ void Qt5InformationNodeInstanceServer::createEditView3D() 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 @@ -527,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(); } @@ -555,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] () { @@ -923,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(); @@ -1069,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; } @@ -1106,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) { @@ -1130,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. @@ -1213,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 @@ -1237,7 +1165,6 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView() m_modelNodePreviewImageCommands.remove(cmd); if (!m_modelNodePreviewImageCommands.isEmpty()) m_renderModelNodeImageViewTimer.start(17); -#endif } void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView( @@ -1273,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); @@ -1309,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"); @@ -1409,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); @@ -1421,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(); @@ -1503,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() @@ -1605,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); } @@ -1639,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); @@ -1669,7 +1529,6 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() if (auto pickTarget = obj->property("_pickTarget").value()) handlePicking(pickTarget); } -#endif } m_dynamicObjectConstructors.clear(); } @@ -2305,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); @@ -2339,8 +2199,6 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa Q_ARG(QVariant, QVariant::fromValue(colors))); } } -#else - Q_UNUSED(container) #endif } @@ -2729,65 +2587,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 } } } @@ -2797,10 +2602,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; @@ -2902,12 +2704,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/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 6dab9421243..785357fe624 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 @@ -74,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 @@ -90,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()); @@ -132,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. @@ -142,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 @@ -152,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); } @@ -309,7 +281,6 @@ void Qt5NodeInstanceServer::handleRciSet() 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; @@ -389,7 +360,7 @@ bool Qt5NodeInstanceServer::initRhi([[maybe_unused]] RenderViewData &viewData) viewData.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(viewData.texTarget)); viewData.bufferDirty = false; -#endif + return true; } @@ -398,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; @@ -426,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; @@ -443,8 +412,6 @@ bool Qt5NodeInstanceServer::renderWindow() m_viewData.renderControl->render(); m_viewData.renderControl->endFrame(); return true; -#endif - return false; } QImage Qt5NodeInstanceServer::grabWindow() @@ -471,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); @@ -492,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 {}; @@ -623,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 29f8e253efc..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,7 +71,6 @@ protected: QRhiRenderBuffer *buffer = nullptr; QRhiTextureRenderTarget *texTarget = nullptr; QRhiRenderPassDescriptor *rpDesc = nullptr; -#endif }; virtual bool initRhi(RenderViewData &viewData); 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 6d3fa2ce36a..40aa4585971 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 From 00aad5e89bf6cfee95fcd9ac27f321c79729c50d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 5 Sep 2023 18:03:05 +0300 Subject: [PATCH 191/266] QmlDesigner: Effect maker UI tweaks - Corrected property name font size - Disabled zoom controls when not needed - Updated Zoom Fit icon - Show vec2, 3, and 4 properties in 1 line Change-Id: I6d5474163b708790b61b6d3462068b138431bd49 Reviewed-by: Amr Elsayed Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNodeUniform.qml | 2 +- .../EffectMakerPreview.qml | 11 +- .../effectMakerQmlSources/ValueFloat.qml | 5 +- .../effectMakerQmlSources/ValueInt.qml | 5 +- .../effectMakerQmlSources/ValueVec2.qml | 139 ++++----- .../effectMakerQmlSources/ValueVec3.qml | 209 ++++++------- .../effectMakerQmlSources/ValueVec4.qml | 278 +++++++++--------- .../components/effectmaker/uniform.cpp | 4 + .../components/effectmaker/uniform.h | 3 +- 9 files changed, 337 insertions(+), 319 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index fbd36ef6cf7..550f03083e4 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -45,7 +45,7 @@ Item { Text { text: uniformName color: StudioTheme.Values.themeTextColor - font.pointSize: StudioTheme.Values.smallFontSize + font.pixelSize: StudioTheme.Values.baseFontSize horizontalAlignment: Text.AlignRight Layout.maximumWidth: 140 Layout.minimumWidth: 140 diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index a73e1503b40..10f6b516024 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -30,30 +30,31 @@ Column { } HelperWidgets.AbstractButton { + enabled: previewImage.scale > .4 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomOut_medium tooltip: qsTr("Zoom out") onClicked: { - if (previewImage.scale > .4) - previewImage.scale -= .2 + previewImage.scale -= .2 } } HelperWidgets.AbstractButton { + enabled: previewImage.scale < 2 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomIn_medium tooltip: qsTr("Zoom In") onClicked: { - if (previewImage.scale < 2) - previewImage.scale += .2 + previewImage.scale += .2 } } HelperWidgets.AbstractButton { + enabled: previewImage.scale !== 1 style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.cornersAll + buttonIcon: StudioTheme.Constants.fitAll_medium tooltip: qsTr("Zoom Fit") onClicked: { diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml index 78e13f23a6b..3f95bef38f9 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml @@ -14,7 +14,7 @@ Row { StudioControls.RealSpinBox { id: spinBox - width: 40 + width: 60 actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter @@ -29,7 +29,8 @@ Row { StudioControls.Slider { id: slider - width: parent.width - 80 + width: parent.width - 100 + visible: width > 20 labels: false decimals: 2 actionIndicatorVisible: false diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml index dfcb1ebf234..1b7e020b33f 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml @@ -14,7 +14,7 @@ Row { StudioControls.SpinBox { id: spinBox - width: 40 + width: 60 actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter @@ -27,7 +27,8 @@ Row { StudioControls.Slider { id: slider - width: parent.width - 80 + width: parent.width - 100 + visible: width > 20 labels: false actionIndicatorVisible: false from: uniformMinValue diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml index 4d27d1f8a4c..9207e98a621 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec2.qml @@ -2,84 +2,87 @@ // 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 -Column { +RowLayout { width: parent.width - spacing: 1 + spacing: 0 - Row { - width: parent.width - spacing: 5 + StudioControls.RealSpinBox { + id: vX - StudioControls.RealSpinBox { - id: vX + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.x = realValue - } - - StudioControls.Slider { - id: sliderX - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.x - to: uniformMaxValue.x - value: uniformValue.x - onMoved: { - uniformValue.x = value - vX.realValue = value // binding isn't working for this property so update it - } - } + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vY - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.y = realValue - } - - StudioControls.Slider { - id: sliderY - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.y - to: uniformMaxValue.y - value: uniformValue.y - onMoved: { - uniformValue.y = value - vY.realValue = value // binding isn't working for this property so update it - } - } + 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 index 6902907e767..52df4c999a6 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec3.qml @@ -2,121 +2,124 @@ // 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 -Column { +RowLayout { width: parent.width + spacing: 0 - spacing: 1 + StudioControls.RealSpinBox { + id: vX - Row { - width: parent.width - spacing: 5 + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 - StudioControls.RealSpinBox { - id: vX - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.x = realValue - } - - StudioControls.Slider { - id: sliderX - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.x - to: uniformMaxValue.x - value: uniformValue.x - onMoved: { - uniformValue.x = value - vX.realValue = value // binding isn't working for this property so update it - } - } + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vY - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.y = realValue - } - - StudioControls.Slider { - id: sliderY - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.y - to: uniformMaxValue.y - value: uniformValue.y - onMoved: { - uniformValue.y = value - vY.realValue = value // binding isn't working for this property so update it - } - } + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vZ - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.z - realTo: uniformMaxValue.z - realValue: uniformValue.z - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.z = realValue - } - - StudioControls.Slider { - id: sliderZ - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.z - to: uniformMaxValue.z - value: uniformValue.z - onMoved: { - uniformValue.z = value - vZ.realValue = value // binding isn't working for this property so update it - } - } + 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 index ae7d13c89f5..05954e5b65c 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueVec4.qml @@ -2,157 +2,161 @@ // 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 -Column { +RowLayout { width: parent.width + spacing: 0 - spacing: 1 + StudioControls.RealSpinBox { + id: vX - Row { - width: parent.width - spacing: 5 + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 60 - StudioControls.RealSpinBox { - id: vX - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.x = realValue - } - - StudioControls.Slider { - id: sliderX - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.x - to: uniformMaxValue.x - value: uniformValue.x - onMoved: { - uniformValue.x = value - vX.realValue = value // binding isn't working for this property so update it - } - } + actionIndicatorVisible: false + spinBoxIndicatorVisible: false + inputHAlignment: Qt.AlignHCenter + realFrom: uniformMinValue.x + realTo: uniformMaxValue.x + realValue: uniformValue.x + realStepSize: .01 + decimals: 2 + onRealValueModified: uniformValue.x = realValue } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vY - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.y = realValue - } - - StudioControls.Slider { - id: sliderY - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.y - to: uniformMaxValue.y - value: uniformValue.y - onMoved: { - uniformValue.y = value - vY.realValue = value // binding isn't working for this property so update it - } - } + Item { // spacer + Layout.fillWidth: true + Layout.minimumWidth: 2 + Layout.maximumWidth: 10 } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vZ - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.z - realTo: uniformMaxValue.z - realValue: uniformValue.z - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.z = realValue - } - - StudioControls.Slider { - id: sliderZ - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.z - to: uniformMaxValue.z - value: uniformValue.z - onMoved: { - uniformValue.z = value - vZ.realValue = value // binding isn't working for this property so update it - } - } + Text { + text: qsTr("X") + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + Layout.alignment: Qt.AlignVCenter } - Row { - width: parent.width - spacing: 5 - - StudioControls.RealSpinBox { - id: vW - - width: 40 - actionIndicatorVisible: false - spinBoxIndicatorVisible: false - inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.w - realTo: uniformMaxValue.w - realValue: uniformValue.w - realStepSize: .01 - decimals: 2 - onRealValueModified: uniformValue.w = realValue - } - - StudioControls.Slider { - id: sliderW - - width: parent.width - 80 - labels: false - decimals: 2 - actionIndicatorVisible: false - from: uniformMinValue.w - to: uniformMaxValue.w - value: uniformValue.w - onMoved: { - uniformValue.w = value - vW.realValue = value // binding isn't working for this property so update it - } - } + 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/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index e15cbb885ca..422fa372aef 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -20,6 +20,10 @@ Uniform::Uniform(const QJsonObject &propObj) 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); diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index 761a22199f5..bfe35308c05 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -18,7 +18,7 @@ class Uniform : public QObject { Q_OBJECT - Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) + Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT) Q_PROPERTY(QString uniformType READ typeName CONSTANT) Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged) @@ -89,6 +89,7 @@ private: QVariant m_minValue; QVariant m_maxValue; QString m_name; + QString m_displayName; QString m_description; QString m_customValue; bool m_useCustomValue = false; From 6ad56316380cdb1d56fdf85c5d37122ba2bf0c59 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 6 Sep 2023 15:52:10 +0300 Subject: [PATCH 192/266] QmlDesigner: Update the icon font Updated icons: snapping_conf_medium and visible_small Added icon: invisible_small Change-Id: I13771ede164454b89ce6357598db05e451f36afb Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 363 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 59092 -> 59344 bytes .../components/componentcore/theme.h | 1 + 3 files changed, 183 insertions(+), 181 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index f8f213faebf..0feb5063441 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -165,187 +165,188 @@ QtObject { readonly property string importedModels_small: "\u00B8" readonly property string infinity: "\u00B9" readonly property string invisible_medium: "\u00BA" - readonly property string keyframe: "\u00BB" - readonly property string languageList_medium: "\u00BC" - readonly property string layouts_small: "\u00BD" - readonly property string lights_small: "\u00BE" - readonly property string linear_medium: "\u00BF" - readonly property string linkTriangle: "\u00C0" - readonly property string linked: "\u00C1" - readonly property string listView: "\u00C2" - readonly property string list_medium: "\u00C3" - readonly property string localOrient_medium: "\u00C4" - readonly property string lockOff: "\u00C5" - readonly property string lockOn: "\u00C6" - readonly property string loopPlayback_medium: "\u00C7" - readonly property string materialBrowser_medium: "\u00C8" - readonly property string materialPreviewEnvironment: "\u00C9" - readonly property string materialPreviewModel: "\u00CA" - readonly property string material_medium: "\u00CB" - readonly property string maxBar_small: "\u00CC" - readonly property string mergeCells: "\u00CD" - readonly property string merge_small: "\u00CE" - readonly property string minus: "\u00CF" - readonly property string mirror: "\u00D0" - readonly property string more_medium: "\u00D1" - readonly property string mouseArea_small: "\u00D2" - readonly property string moveDown_medium: "\u00D3" - readonly property string moveInwards_medium: "\u00D4" - readonly property string moveUp_medium: "\u00D5" - readonly property string moveUpwards_medium: "\u00D6" - readonly property string move_medium: "\u00D7" - readonly property string newMaterial: "\u00D8" - readonly property string nextFile_large: "\u00D9" - readonly property string normalBar_small: "\u00DA" - readonly property string openLink: "\u00DB" - readonly property string openMaterialBrowser: "\u00DC" - readonly property string orientation: "\u00DD" - readonly property string orthCam_medium: "\u00DE" - readonly property string orthCam_small: "\u00DF" - readonly property string paddingEdge: "\u00E0" - readonly property string paddingFrame: "\u00E1" - readonly property string particleAnimation_medium: "\u00E2" - readonly property string pasteStyle: "\u00E3" - readonly property string paste_small: "\u00E4" - readonly property string pause: "\u00E5" - readonly property string perspectiveCam_medium: "\u00E6" - readonly property string perspectiveCam_small: "\u00E7" - readonly property string pin: "\u00E8" - readonly property string plane_medium: "\u00E9" - readonly property string plane_small: "\u00EA" - readonly property string play: "\u00EB" - readonly property string playFill_medium: "\u00EC" - readonly property string playOutline_medium: "\u00ED" - readonly property string plus: "\u00EE" - readonly property string pointLight_small: "\u00EF" - readonly property string positioners_small: "\u00F0" - readonly property string previewEnv_medium: "\u00F1" - readonly property string previousFile_large: "\u00F2" - readonly property string promote: "\u00F3" - readonly property string properties_medium: "\u00F4" - readonly property string readOnly: "\u00F5" - readonly property string recordFill_medium: "\u00F6" - readonly property string recordOutline_medium: "\u00F7" - readonly property string redo: "\u00F8" - readonly property string reload_medium: "\u00F9" - readonly property string remove_medium: "\u00FA" - readonly property string remove_small: "\u00FB" - readonly property string rename_small: "\u00FC" - readonly property string replace_small: "\u00FD" - readonly property string resetView_small: "\u00FE" - readonly property string restartParticles_medium: "\u00FF" - readonly property string reverseOrder_medium: "\u0100" - readonly property string roatate_medium: "\u0101" - readonly property string rotationFill: "\u0102" - readonly property string rotationOutline: "\u0103" - readonly property string runProjFill_large: "\u0104" - readonly property string runProjOutline_large: "\u0105" - readonly property string s_anchors: "\u0106" - readonly property string s_annotations: "\u0107" - readonly property string s_arrange: "\u0108" - readonly property string s_boundingBox: "\u0109" - readonly property string s_component: "\u010A" - readonly property string s_connections: "\u010B" - readonly property string s_edit: "\u010C" - readonly property string s_enterComponent: "\u010D" - readonly property string s_eventList: "\u010E" - readonly property string s_group: "\u010F" - readonly property string s_layouts: "\u0110" - readonly property string s_merging: "\u0111" - readonly property string s_mouseArea: "\u0112" - readonly property string s_positioners: "\u0113" - readonly property string s_selection: "\u0114" - readonly property string s_snapping: "\u0115" - readonly property string s_timeline: "\u0116" - readonly property string s_visibility: "\u0117" - readonly property string saveLogs_medium: "\u0118" - readonly property string scale_medium: "\u0119" - readonly property string search: "\u011A" - readonly property string search_small: "\u011B" - readonly property string sectionToggle: "\u011C" - readonly property string selectFill_medium: "\u011D" - readonly property string selectOutline_medium: "\u011E" - readonly property string selectParent_small: "\u011F" - readonly property string selection_small: "\u0120" - readonly property string settings_medium: "\u0121" - readonly property string signal_small: "\u0122" - readonly property string snapping_conf_medium: "\u0123" - readonly property string snapping_medium: "\u0124" - readonly property string snapping_small: "\u0125" - readonly property string sphere_medium: "\u0126" - readonly property string sphere_small: "\u0127" - readonly property string splitColumns: "\u0128" - readonly property string splitRows: "\u0129" - readonly property string spotLight_small: "\u012A" - readonly property string stackedContainer_small: "\u012B" - readonly property string startNode: "\u012C" - readonly property string step_medium: "\u012D" - readonly property string stop_medium: "\u012E" - readonly property string testIcon: "\u012F" - readonly property string textAlignBottom: "\u0130" - readonly property string textAlignCenter: "\u0131" - readonly property string textAlignJustified: "\u0132" - readonly property string textAlignLeft: "\u0133" - readonly property string textAlignMiddle: "\u0134" - readonly property string textAlignRight: "\u0135" - readonly property string textAlignTop: "\u0136" - readonly property string textBulletList: "\u0137" - readonly property string textFullJustification: "\u0138" - readonly property string textNumberedList: "\u0139" - readonly property string textures_medium: "\u013A" - readonly property string tickIcon: "\u013B" - readonly property string tickMark_small: "\u013C" - readonly property string timeline_small: "\u013D" - readonly property string toEndFrame_medium: "\u013E" - readonly property string toNextFrame_medium: "\u013F" - readonly property string toPrevFrame_medium: "\u0140" - readonly property string toStartFrame_medium: "\u0141" - readonly property string topToolbar_annotations: "\u0142" - readonly property string topToolbar_closeFile: "\u0143" - readonly property string topToolbar_designMode: "\u0144" - readonly property string topToolbar_enterComponent: "\u0145" - readonly property string topToolbar_home: "\u0146" - readonly property string topToolbar_makeComponent: "\u0147" - readonly property string topToolbar_navFile: "\u0148" - readonly property string topToolbar_runProject: "\u0149" - readonly property string translationCreateFiles: "\u014A" - readonly property string translationCreateReport: "\u014B" - readonly property string translationExport: "\u014C" - readonly property string translationImport: "\u014D" - readonly property string translationSelectLanguages: "\u014E" - readonly property string translationTest: "\u014F" - readonly property string transparent: "\u0150" - readonly property string triState: "\u0151" - readonly property string triangleArcA: "\u0152" - readonly property string triangleArcB: "\u0153" - readonly property string triangleCornerA: "\u0154" - readonly property string triangleCornerB: "\u0155" - readonly property string unLinked: "\u0156" - readonly property string undo: "\u0157" - readonly property string unify_medium: "\u0158" - readonly property string unpin: "\u0159" - readonly property string upDownIcon: "\u015A" - readonly property string upDownSquare2: "\u015B" - readonly property string updateAvailable_medium: "\u015C" - readonly property string updateContent_medium: "\u015D" - readonly property string visibilityOff: "\u015E" - readonly property string visibilityOn: "\u015F" - readonly property string visible_medium: "\u0160" - readonly property string visible_small: "\u0161" - readonly property string wildcard: "\u0162" - readonly property string wizardsAutomotive: "\u0163" - readonly property string wizardsDesktop: "\u0164" - readonly property string wizardsGeneric: "\u0165" - readonly property string wizardsMcuEmpty: "\u0166" - readonly property string wizardsMcuGraph: "\u0167" - readonly property string wizardsMobile: "\u0168" - readonly property string wizardsUnknown: "\u0169" - readonly property string zoomAll: "\u016A" - readonly property string zoomIn: "\u016B" - readonly property string zoomIn_medium: "\u016C" - readonly property string zoomOut: "\u016D" - readonly property string zoomOut_medium: "\u016E" - readonly property string zoomSelection: "\u016F" + 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/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index fe3e70b2edac9ebf6f9acd43d8cb2714d878f719..75f1fb94fad07b083e695b22a6660a9d14d0512a 100644 GIT binary patch delta 1330 zcmca|mifYY=6VK31_lORh6V;^h5$FW5Z?t(C4LMH&$lozFv$1^>l;PRNRwe;V2og3 zU`R;LO)Sv(ulpMhgZ828F!D+|)j? zfaeShCm%2{Fr6*PFD_v{>U4>L;migG2C1K5&oMGEtZg`^6VGq+m4Tc21p@;E!$Uch z_Yj&%<-;UKTgJf2c8sx%1)JwECNggR&6L47Igv$=v0(EAmQ?2YSz4R4i?ny>@aRP8 z6zFWz71K@A-KWQ-=b^Vo-%tO5fr!B_gC~YMhJA+13=bH-G7>U!Fv>ETVRX#c%(%#S zmhlY}J(F1`w@mF!7n!azJ!BSU)@Js??2mb!1%ri$MV-YCi(?iKESW3~EH_zxvkJ1R zv07*K#=6A1&w8KrHyeX`8#kLcn;o_iwr;jt>{#ra?27E>*zL3XVxML|%R#_lmBSxL z8^;#MS5AIT4NfPVRh)gCCpf=wv2v+%+2bnU+Tyyy^@Hm_Hzl_Ow-xRz?hicLJk312 zJRf*jdDVEW^19(2<9){amXDTCoKKCUa(wnPw<-%yO4~Kc_B|iJwn?;kAyjdWrf`c`w-3*t`hDLUKYM3 z{7pns#J5PHNVUj_$XQV`QC(3dqP|6Y#HhtAh`AK&6}ux&Caxv!Uc5qlQT&?tcL{cz z6F6%_>X{iB{+lzuU{+v|Wzc4@WMEKbS5jwWQ&v(lF=tdWH8C?b5@$3s5))x(6cZI; zV^uRXQ86|WXXj%S6%hvsu_!C4o7*v(ny52=VPp~cSH&wPBFE3z#%is}BEs0luPMj> zuZq?BpKlhU>UlNYgoTfMna*bYyA5HR`JP~8{a3{=r^(OQF2bT|&C1vY;{2-uae@NU zYBsPYFjsq8m@}#(aW>zIy=^VX!ocv~0_^r^hGK?#hVIEnW?D{Woh3b4U>28=x{;ZQ znl_`7nu(b;qmh^h8-#7Fju261M`APYne0DHY_i`halJTob941Qyh1{}dmLh79KfvI zAg&!M!MIY0_wU)s6|+=qn2bSEx8p!$942|2x8K~)Tu4eNrZHMbN@$Oel#q~=&@TwJ z`RpuhcLrHo2U#;kZ1}NZ!;gP^>Za7yO_8z&2{LTX-gksc-GosZoJAQJ zSQsQ3TcB)K1_8zcP&ONbBopi8u#>v#84Md4m>C#ZI2l+Nj2Hu;Y*q#Z#(7XS8-o$! z#m%cuaxt=+G3YW_PTqaWg2PPLQrF19$ar$?>43>TryCg=CZ9g7#2C2w-RT8POiccp p`_8@P5)jVJE6Xg-Ov*`(FV0QO$=O_f(}s8Q#%B`T@RCC10|1_PhTs4I delta 1113 zcmca`p83jI=6VK31_lORh6V;^h5$FW5Z?v5l1~{Jp6y^@V36?-);EfrktWT+z!<^6 zz>tufn^>UnU;8Hm14{w}15;RXS&0IJB2y&;0~<&^OL}5)!T+m$StXm zXA+voz`(eJfq`j7PJVJ?*gUm&3=C|07#Nrq7BeuMKEuEu^%(3qMh1olF~6nb`E9;3a5KMPU|?W)C|tf9 zLNk@`p2TR&7%7?1b6DW;z){1o z&+(I!ic^l$38xRvCeA(1_gs`*5?r>pYPiO@PH|o2dd!W*&Btwpdy9vb#|}>(&m_-1 zp8vdbyrR7BdHZ?Cc(3t(;Qh}h#AkueD_=9;9^V^&dVXDg*Zhn8>+b|G1ZV~11uO`- z6v!565m*(tA!tD`UvOUVf#6pm3L$ABr$RYG-9itAz6py8D+=ogTNZXG>_@m+cwYFE zh>D0Q5i25YMao3Zi{gp$ifW6x5xp(OASNm1R;)+tnmCcTytobVOz}4Hb@AsI7#Nrs z6d4#8wU}lxFf*_-=xt=&T*jFlvUz6Q4Qqa828RC@%rBS~8KN1o8S*DL&9Y12V~`ojNC$*8GIS4gX=Nmti0Ef(Vab9r*^ zY!yRhU0xv}-hbM+LDX$blJP2UwwiRMw3@n9rL@|fy1LD0XKO3h|3A+Bf~kdppFx^I znSnvo%tT#}QHhP6O+;Ld(a4NVL66bIj!{95QG|^_giT4!#LS3UCL_aQ=DL{{5Gv!} zhi7eV&lm-swYB|wW$a~a?A748%R^mHPu*jer@F50#Mnjq7RJRb+_#8P!{5Tf--3aG zkwN5t1oK{IMuyGN`*(1un=;CR6E_0`3xgzMJCx1JAi#JS%4TDbWa5}?bxKz~i(w-J zGXo^zr!6>*EOjk(jSP&8 tC&!%$n5=uIaq`A93XB1pFP&Myw7KN`TdvKew=8%kuYWGa4bM>Jy8&S3R=fZJ diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index f936a98028f..1124e9b4e84 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -173,6 +173,7 @@ public: importedModels_small, infinity, invisible_medium, + invisible_small, keyframe, languageList_medium, layouts_small, From 291e3096cd5d0763370238252143c3bb4fe93839 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 7 Sep 2023 12:04:59 +0300 Subject: [PATCH 193/266] QmlDesigner: Adapt to quick3d private API change Some quick3d utility functions were moved under QSSGUtils namespace. We only used one one-liner function from there, so just copied the implementation into puppet to avoid this dependency. Change-Id: I0b08157732161c750b3e53873fd10e1b20137b04 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 6 ++---- src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 056e9ca6a02..47f176b4219 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -993,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. diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp index a271c98a5fe..8c6f6c23d8a 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include namespace QmlDesigner { @@ -745,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 From e4a470ed0b5933708ca43aba516e88bfa14fd36d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Sep 2023 10:50:58 +0200 Subject: [PATCH 194/266] CMake: Fix warning glibc 2.12 is now over 10 years old. I think we do not support it anymore. Change-Id: I3df4ebaa73de07c22ff71e50ddbee5938bd0ce55 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Cristian Adam Reviewed-by: --- .../cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h | 5 ----- 1 file changed, 5 deletions(-) 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 */ From d97bf7cc3bb91069528b7f74e198ecd39a5a02d3 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 2 Jun 2023 18:18:00 +0200 Subject: [PATCH 195/266] QmlDesigner: Add percentages to GradientModel Task-number: QDS-9904 Change-Id: I98c9e67e46f282053755d143af19eb83f62a5c72 Reviewed-by: Reviewed-by: Marco Bubke --- .../HelperWidgets/ColorEditorPopup.qml | 41 +- .../HelperWidgets/GradientPropertySpinBox.qml | 57 ++- .../propertyeditor/gradientmodel.cpp | 467 +++++++++++++++--- .../components/propertyeditor/gradientmodel.h | 23 +- 4 files changed, 508 insertions(+), 80 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index aa902e7bd1b..2a4b036effb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -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/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/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 570927a8638..490af54e49c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -3,24 +3,182 @@ #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; +} GradientModel::GradientModel(QObject *parent) : QAbstractListModel(parent) @@ -268,7 +426,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 +434,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 +444,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 +464,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 +580,29 @@ 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") { + std::for_each(std::begin(defaultLinearShapeGradients), + std::end(defaultLinearShapeGradients), + [&](auto &a) { + gradient.variantProperty(a.name.toByteArray()) + .setValue(a.getDefaultValue(m_itemNode)); + }); + } else if (m_gradientTypeName == u"RadialGradient") { + std::for_each(std::begin(defaultRadialShapeGradients), + std::end(defaultRadialShapeGradients), + [&](auto &a) { + gradient.variantProperty(a.name.toByteArray()) + .setValue(a.getDefaultValue(m_itemNode)); + }); + } else if (m_gradientTypeName == u"ConicalGradient") { + std::for_each(std::begin(defaultConicalShapeGradients), + std::end(defaultConicalShapeGradients), + [&](auto &a) { + gradient.variantProperty(a.name.toByteArray()) + .setValue(a.getDefaultValue(m_itemNode)); + }); } } @@ -450,7 +625,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 +664,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 +783,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 +836,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; From 1b0f25eee89b66845da3ebba7004eceaa85cba64 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 7 Sep 2023 14:53:32 +0300 Subject: [PATCH 196/266] QmlDesigner: React to clearColor reset when syncing background color 3D view now changes the background color to black as expected if clearColor of the associated SceneEnvironment is reset and color syncing is enabled. Fixes: QDS-10580 Change-Id: Ie3bfd96012fc76dc28b28d2ac9be65e6e9784ea1 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../instances/qt5informationnodeinstanceserver.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c407645e77a..663adcf3e17 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -2501,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(); From c5c1612c6b286bedcdf2b1f5a905a74946c77f58 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 6 Sep 2023 15:18:02 +0300 Subject: [PATCH 197/266] QmlDesigner: Implement disabling effect maker composition nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disabling happens using a new button added to the section. Also a small tweak to disable drag button when there is only one section in a category. Fixes: QDS-10575 Change-Id: I33884a5b333c54b2bae650943940d4858f489f7a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Amr Elsayed Reviewed-by: Henning Gründl --- .../EffectCompositionNode.qml | 21 ++++++++++-- .../effectMakerQmlSources/EffectMaker.qml | 4 +++ .../imports/HelperWidgets/Controller.qml | 18 ++++++++++- .../imports/HelperWidgets/Section.qml | 32 +++++++++++++++++-- .../effectmaker/compositionnode.cpp | 5 ++- .../components/effectmaker/compositionnode.h | 4 ++- .../effectmaker/effectmakermodel.cpp | 14 ++++++++ .../components/effectmaker/effectmakermodel.h | 2 ++ 8 files changed, 91 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml index ed89ea149bd..9b0046f67c3 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNode.qml @@ -12,7 +12,14 @@ import EffectMakerBackend HelperWidgets.Section { id: root - caption: nodeName + // 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 @@ -21,14 +28,22 @@ HelperWidgets.Section { closeButtonToolTip: qsTr("Remove") onCloseButtonClicked: { - EffectMakerBackend.effectMakerModel.removeNode(index) + 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: nodeUniformsModel + model: root.nodeUniformsModel EffectCompositionNodeUniform { width: root.width diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 5c32df314a0..7453ba9349c 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -69,6 +69,10 @@ Item { width: root.width model: EffectMakerBackend.effectMakerModel + onCountChanged: { + HelperWidgets.Controller.setCount("EffectMaker", repeater.count) + } + delegate: EffectCompositionNode { width: root.width 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/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index b1161147f9a..bdc05a0e103 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -20,6 +20,8 @@ Item { 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 @@ -40,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 @@ -75,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) @@ -85,6 +92,7 @@ Item { signal expand() signal collapse() signal closeButtonClicked() + signal eyeButtonClicked() signal startDrag(var section) signal stopDrag() @@ -133,7 +141,7 @@ Item { height: 4 source: "image://icons/down-arrow" anchors.left: parent.left - anchors.leftMargin: 4 + (section.level * section.levelShift) + (section.draggable ? 20 : 0) + anchors.leftMargin: 4 + (section.level * section.levelShift) + (section.draggable ? 20 : 0) + (section.showEyeButton ? 25 : 0) anchors.verticalCenter: parent.verticalCenter } @@ -185,11 +193,11 @@ Item { icon: StudioTheme.Constants.dragmarks buttonSize: 22 - iconScale: containsMouse ? 1.2 : 1 + iconScale: dragButton.enabled && dragButton.containsMouse ? 1.2 : 1 transparentBg: true visible: false - drag.target: section + drag.target: dragButton.enabled ? section : null drag.axis: Drag.YAxis onPressed: { @@ -202,6 +210,24 @@ Item { 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 diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp index d0e3c6c53cd..2e707636098 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp @@ -51,7 +51,10 @@ bool CompositionNode::isEnabled() const void CompositionNode::setIsEnabled(bool newIsEnabled) { - m_isEnabled = newIsEnabled; + if (newIsEnabled != m_isEnabled) { + m_isEnabled = newIsEnabled; + emit isEnabledChanged(); + } } CompositionNode::NodeType CompositionNode::type() const diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h index dfca7fa0221..840abde96ea 100644 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h @@ -14,6 +14,7 @@ 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: @@ -40,6 +41,7 @@ public: signals: void uniformsModelChanged(); + void isEnabledChanged(); private: void parse(const QString &qenPath); @@ -50,7 +52,7 @@ private: QString m_vertexCode; QString m_description; QStringList m_requiredNodes; - bool m_isEnabled; + bool m_isEnabled = true; EffectMakerUniformsModel m_unifomrsModel; }; diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 72ae2574fb3..2b8435030fd 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -23,6 +23,7 @@ QHash EffectMakerModel::roleNames() const { QHash roles; roles[NameRole] = "nodeName"; + roles[EnabledRole] = "nodeEnabled"; roles[UniformsRole] = "nodeUniformsModel"; return roles; } @@ -42,6 +43,19 @@ QVariant EffectMakerModel::data(const QModelIndex &index, int role) const 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::addNode(const QString &nodeQenPath) { beginInsertRows({}, m_nodes.size(), m_nodes.size()); diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index b2814705cfd..182de9b97df 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -40,6 +40,7 @@ public: 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; } @@ -60,6 +61,7 @@ signals: private: enum Roles { NameRole = Qt::UserRole + 1, + EnabledRole, UniformsRole }; From 1e35f3cf19088efd19ee582288efc6ad614fcec1 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 30 Aug 2023 09:17:13 +0200 Subject: [PATCH 198/266] QmlDesigner: Modernize string usage In many cases it is nicer to use a QStringView or QLatin1StringView. ""_L1 is a nice short cut for writing QLatin1StringView("") too. Actual QStringLiteral is not really a literal but uses a char16_t[] literal instead of a char[] to create a QString. Change-Id: Ib86ab93771a950c655a06bdd6f858afd38cfcfae Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../designercore/include/stringutils.h | 22 ++-- .../designercore/model/annotation.cpp | 18 +-- .../designercore/model/bindingproperty.cpp | 4 +- .../qmldesigner/designercore/model/model.cpp | 4 +- .../designercore/model/modeltotextmerger.cpp | 13 +- .../designercore/model/modeltotextmerger.h | 2 +- .../designercore/model/propertyparser.cpp | 4 +- .../designercore/model/qmlobjectnode.cpp | 11 +- .../designercore/model/qmltextgenerator.cpp | 73 ++++++----- .../designercore/model/qmlvisualnode.cpp | 2 +- .../designercore/model/rewriteaction.cpp | 100 ++++++++------- .../designercore/model/rewriterview.cpp | 14 +-- .../designercore/model/texttomodelmerger.cpp | 119 +++++++++--------- .../qmlpreviewplugin/qmlpreviewplugin.cpp | 4 +- 14 files changed, 206 insertions(+), 184 deletions(-) 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/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/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 5369884a841..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) @@ -120,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()); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 79556bc1715..38c20a65952 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1779,7 +1779,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()) { @@ -1798,7 +1798,7 @@ QString Model::generateNewId(const QString &prefixName, while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId) || 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/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index bd7329f9923..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) @@ -203,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; @@ -220,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; @@ -395,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 b9265639525..75d7178039e 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.h @@ -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/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 1f01f63930f..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,25 +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); + 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 afd5a25321e..ea344d3e62b 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -39,6 +39,7 @@ #include using namespace QmlDesigner::Internal; +using namespace Qt::StringLiterals; namespace QmlDesigner { @@ -596,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 + "\""; } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index ebd80f174d3..eb5529519c0 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,8 +608,8 @@ 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) @@ -649,7 +654,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 +712,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)); } @@ -1478,7 +1483,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(); } @@ -2225,7 +2230,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/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); From a4f8cf709e1619daab4f7f91f79465dcbe384523 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 6 Sep 2023 16:50:45 +0200 Subject: [PATCH 199/266] Utils: Add more conversion operators to SmallString An explicit operator for QLatin1StringView. That is dangerous if the string is not ASCII and one for QUtf8StringView. Change-Id: I2d0a0ca3854b47595563a19263aacd7f8825d026 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 9 +++++++++ src/libs/utils/smallstringview.h | 11 +++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index c64e46f6f87..2ef565bde2e 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -203,6 +203,15 @@ public: SmallStringView toStringView() const noexcept { return SmallStringView(data(), size()); } operator SmallStringView() const noexcept { return SmallStringView(data(), size()); } + explicit operator QLatin1StringView() const noexcept + { + return QLatin1StringView(data(), Utils::ssize(*this)); + } + + operator QUtf8StringView() const noexcept + { + return QUtf8StringView(data(), Utils::ssize(*this)); + } explicit operator QString() const noexcept { return toQString(); } 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()) From 6d79f308c19333820130000130f52a315f8db5bd Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 6 Sep 2023 13:42:14 +0200 Subject: [PATCH 200/266] QmlDesigner: Add std::hash to NodeMetaInfo Change-Id: Ib4c9bb3e275167e846629845d16773c03386b39b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqliteids.h | 2 +- .../designercore/include/nodemetainfo.h | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 2309bd58e5a..d64e4d9645a 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -85,7 +85,7 @@ struct hash> { auto operator()(const Sqlite::BasicId &id) const { - return std::hash(id.internalId()); + return std::hash{}(id.internalId()); } }; } // namespace std diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index f8a864ab815..0003831280d 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -217,6 +217,14 @@ public: 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; @@ -231,3 +239,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 From 4bafb087ab3742726d0a5c03c43cbed2e093ae68 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Sep 2023 16:37:55 +0200 Subject: [PATCH 201/266] QmlDesigner: Optimized isBasedOn a little bit The very common case that the typeId is equal to the base type id is now handled outside of a database query. Change-Id: I0b81bc66ee473302646af79efc11eab607dc30c0 Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/projectstorage.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index b1190874f75..1158572e5e0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -295,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) || ...)) @@ -404,7 +407,7 @@ public: auto fetchPrototypes(TypeId type) { - return selectPrototypeIdsStatement.template rangeWithTransaction(type); + return selectPrototypeIdsInOrderStatement.template rangeWithTransaction(type); } SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) @@ -2836,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" @@ -3428,14 +3431,14 @@ 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))" From 1e00fdf9f96070b46b05473280775eec50f43533 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 7 Sep 2023 15:14:17 +0300 Subject: [PATCH 202/266] QmlDesigner: Hide effect maker prop. slider's handle label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ica266d0a9009ee4b5ba3e1cb1e9292f3b1091df7 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Amr Elsayed Reviewed-by: Miikka Heikkinen Reviewed-by: Henning Gründl --- .../qmldesigner/effectMakerQmlSources/ValueFloat.qml | 1 + .../qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml | 1 + .../imports/StudioControls/Slider.qml | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml index 3f95bef38f9..9c2d2c80a23 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueFloat.qml @@ -34,6 +34,7 @@ Row { labels: false decimals: 2 actionIndicatorVisible: false + handleLabelVisible: false from: uniformMinValue to: uniformMaxValue value: uniformValue diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml index 1b7e020b33f..b5db8db05e8 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/ValueInt.qml @@ -31,6 +31,7 @@ Row { visible: width > 20 labels: false actionIndicatorVisible: false + handleLabelVisible: false from: uniformMinValue to: uniformMaxValue value: uniformValue 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 From 5b264232576266ab08bc997726e41b2c60dd0b4e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 7 Sep 2023 14:42:42 +0300 Subject: [PATCH 203/266] QmlDesigner: Add ListModel and ListElement node testers - ListModel and ListElement testers are added to the NodeMetaInfo - Adding unknown properties to the ListElement node will not create a warning message Change-Id: I208ab4f8030ec60a4ef275680adefa9902647ff0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Marco Bubke --- .../designercore/include/nodemetainfo.h | 2 ++ .../designercore/metainfo/nodemetainfo.cpp | 20 +++++++++++++++++++ .../designercore/model/texttomodelmerger.cpp | 13 +++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 0003831280d..b91b650e50c 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -143,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; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index d2bf00c9b67..7a825ccc8ba 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2516,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()) { diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index eb5529519c0..dd4fe3b2555 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -612,11 +612,14 @@ public: && 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); } From 8ce8bd4300318de30ae72a016c1cd1b0980667c1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 7 Sep 2023 16:44:43 +0300 Subject: [PATCH 204/266] QmlDesigner: Disable "Use scene environment color" when not under View3D For standalone 3D scenes that are not part of View3D, syncing to scene environment doesn't make sense, so disable that option and force it unchecked when activating a standalone 3D scene. Fixes: QDS-10583 Change-Id: I8e5b205c35ec12bbf4340fda943345f5bd5396a6 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/edit3dview.cpp | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 3b1c282148b..59ec7be87e6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -14,6 +14,7 @@ #include "edit3dwidget.h" #include "materialutils.h" #include "metainfo.h" +#include "nodeabstractproperty.h" #include "nodehints.h" #include "nodeinstanceview.h" #include "qmldesignerconstants.h" @@ -190,10 +191,34 @@ 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)) - m_syncBackgroundColorAction->action()->setChecked(sceneState[syncBgColorKey].toBool()); - else - m_syncBackgroundColorAction->action()->setChecked(true); + 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); From 7fbcf4a38e05f66c9db73d2ad59e8231b645963d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 8 Sep 2023 11:07:10 +0300 Subject: [PATCH 205/266] QmlDesigner: Add empty placeholder to effect maker's view Fixes: QDS-10592 Change-Id: Icc6193b358b83fce2a229321e30880e35e074055 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../effectMakerQmlSources/EffectMaker.qml | 13 +++++++++++++ .../imports/HelperWidgets/Section.qml | 2 +- .../components/effectmaker/effectmakermodel.cpp | 13 +++++++++++++ .../components/effectmaker/effectmakermodel.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 7453ba9349c..456539d13cf 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -136,4 +136,17 @@ Item { } // 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/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index bdc05a0e103..5eb40e42cc5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -177,7 +177,7 @@ Item { IconButton { id: closeButton - icon: StudioTheme.Constants.close_small + icon: StudioTheme.Constants.closeCross buttonSize: 22 iconScale: containsMouse ? 1.2 : 1 transparentBg: true diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 2b8435030fd..6d03a56663f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -56,12 +56,22 @@ bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, 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) @@ -82,6 +92,9 @@ void EffectMakerModel::removeNode(int idx) m_nodes.removeAt(idx); delete node; endRemoveRows(); + + if (m_nodes.isEmpty()) + setIsEmpty(true); } const QList EffectMakerModel::allUniforms() diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 182de9b97df..9d4db5adb8d 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -43,6 +43,7 @@ public: 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); From af2738ec3389a13433fd1e5dc4cbca03d341f211 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 8 Sep 2023 13:21:27 +0300 Subject: [PATCH 206/266] QmlDesigner: Add effect maker uniform's tooltip Change-Id: I4787e1bcadb24d8ff34d26964bdc4f2e97704124 Reviewed-by: Miikka Heikkinen --- .../effectMakerQmlSources/EffectCompositionNodeUniform.qml | 5 +++++ src/plugins/qmldesigner/components/effectmaker/uniform.h | 1 + 2 files changed, 6 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml index 550f03083e4..7c632730ccb 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectCompositionNodeUniform.qml @@ -50,6 +50,11 @@ Item { Layout.maximumWidth: 140 Layout.minimumWidth: 140 Layout.preferredWidth: 140 + + HelperWidgets.ToolTipArea { + anchors.fill: parent + tooltip: uniformDescription + } } Loader { diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h index bfe35308c05..5c1efc115a4 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -20,6 +20,7 @@ class Uniform : public QObject 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) From d6179c7cec67003c0e656017fd1e5e1786ac2b31 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 8 Sep 2023 12:52:39 +0300 Subject: [PATCH 207/266] QmlDesigner: Refactor 3D view toolbar popup positioning The same positioning code had several copies. Consolidated those into single function and adjusted the Y-coordinate a bit to make it visually more pleasing (i.e. no more highlighted buttons peeking from under the popup). Change-Id: I7fb251f46ee3395b18577d782693e3873d07af6a Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/edit3dview.cpp | 57 ++++++++----------- .../components/edit3d/edit3dview.h | 4 +- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 59ec7be87e6..1783c171b14 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -583,6 +583,21 @@ void Edit3DView::createSeekerSliderAction() }); } +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()) @@ -880,17 +895,9 @@ void Edit3DView::createEdit3DActions() 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 = std::make_unique( @@ -909,20 +916,12 @@ 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 = std::make_unique( + m_backgroundColorMenuAction = std::make_unique( QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, View3DActionType::Empty, QCoreApplication::translate("BackgroundColorMenuActions", @@ -959,17 +958,9 @@ void Edit3DView::createEdit3DActions() snapToggleTrigger); SelectionContextOperation snapConfigTrigger = [this](const SelectionContext &) { - QPoint pos; - const auto &actionWidgets = m_snapConfigAction->action()->associatedWidgets(); - for (auto actionWidget : actionWidgets) { - if (auto button = qobject_cast(actionWidget)) { - pos = button->mapToGlobal(QPoint(0, 0)); - break; - } - } if (!m_snapConfiguration) m_snapConfiguration = new SnapConfiguration(this); - m_snapConfiguration->showConfigDialog(pos); + m_snapConfiguration->showConfigDialog(resolveToolbarPopupPos(m_snapConfigAction.get())); }; m_snapConfigAction = std::make_unique( @@ -1003,7 +994,7 @@ void Edit3DView::createEdit3DActions() m_leftActions << m_alignViewAction.get(); m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction.get(); - m_leftActions << m_backgrondColorMenuAction.get(); + m_leftActions << m_backgroundColorMenuAction.get(); m_rightActions << m_particleViewModeAction.get(); m_rightActions << m_particlesPlayAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index aa3f482dac5..2b2bd57d93f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -107,6 +107,8 @@ private: void createSyncBackgroundColorAction(); void createSeekerSliderAction(); + QPoint resolveToolbarPopupPos(Edit3DAction *action) const; + QPointer m_edit3DWidget; QVector m_leftActions; QVector m_rightActions; @@ -141,7 +143,7 @@ private: // View3DActionType::Empty actions std::unique_ptr m_resetAction; std::unique_ptr m_visibilityTogglesAction; - std::unique_ptr m_backgrondColorMenuAction; + std::unique_ptr m_backgroundColorMenuAction; std::unique_ptr m_snapToggleAction; std::unique_ptr m_snapConfigAction; std::unique_ptr m_bakeLightsAction; From cbf4273baba618e9b53288e63a880098312be707 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 8 Sep 2023 11:27:00 +0300 Subject: [PATCH 208/266] QmlDesigner: Add custom uniforms handlers Also minor updates and additions. Task-number: QDS-10499 Change-Id: I0a1009e943cc0908f19872eb79ce5da80e736249 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectmaker/effectmakermodel.cpp | 174 +++++++++++++++++- .../components/effectmaker/effectmakermodel.h | 15 ++ .../components/effectmaker/uniform.cpp | 23 +++ .../components/effectmaker/uniform.h | 1 + 4 files changed, 212 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp index 6d03a56663f..42f0977f7dc 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp @@ -97,6 +97,32 @@ void EffectMakerModel::removeNode(int idx) 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 = {}; @@ -265,6 +291,72 @@ void EffectMakerModel::resetEffectError(int type) } } +// 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) { @@ -292,6 +384,14 @@ QString EffectMakerModel::valueAsVariable(const Uniform &uniform) } } +// 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(); @@ -562,6 +662,72 @@ QString EffectMakerModel::generateFragmentShader(bool includeUniforms) 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); @@ -572,7 +738,13 @@ void EffectMakerModel::bakeShaders() setShadersUpToDate(false); - // TODO: Compilation starts here + // 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 diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h index 9d4db5adb8d..58c6a93fd8f 100644 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h @@ -53,6 +53,11 @@ public: 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); @@ -88,7 +93,10 @@ private: 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); @@ -101,6 +109,7 @@ private: QString getCustomShaderVaryings(bool outState); QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); + void updateCustomUniforms(); void bakeShaders(); QList m_nodes; @@ -116,6 +125,12 @@ private: 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+"); }; diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp index 422fa372aef..8074c3cc95a 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp @@ -298,4 +298,27 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) 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 index 5c1efc115a4..67699c5e53a 100644 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ b/src/plugins/qmldesigner/components/effectmaker/uniform.h @@ -69,6 +69,7 @@ public: static QString stringFromType(Uniform::Type type); static Uniform::Type typeFromString(const QString &typeString); + static QString typeToProperty(Uniform::Type type); signals: void uniformValueChanged(); From 5e8b5ec1f01420d520c0ff7650b6ea431967272c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 4 Sep 2023 16:45:54 +0200 Subject: [PATCH 209/266] QmlDesigner: Integrate Expression Builder Task-number: QDS-10587 Change-Id: Ifc13a8364fccb74cb60d683f0e6c322d80baab50 Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 61 ++- .../connectionseditor/ExpressionBuilder.qml | 387 ++++++++++++++++++ .../connectionseditor/MyListViewDelegate.qml | 29 ++ .../connectionseditor/MyTreeViewDelegate.qml | 66 +++ .../qmldesigner/connectionseditor/Pill.qml | 167 ++++++++ .../connectionseditor/SuggestionPopup.qml | 257 ++++++++++++ .../connectionseditor/TabCheckButton.qml | 1 + .../imports/HelperWidgets/ToolTipArea.qml | 6 +- .../imports/StudioControls/SearchBox.qml | 4 +- .../imports/StudioTheme/Values.qml | 10 + .../connectioneditor/connectionmodel.cpp | 121 +++++- .../connectioneditor/connectionmodel.h | 21 +- .../connectioneditor/connectionview.cpp | 11 +- .../connectioneditor/propertytreemodel.cpp | 92 ++++- .../connectioneditor/propertytreemodel.h | 25 +- 15 files changed, 1205 insertions(+), 53 deletions(-) create mode 100644 share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml create mode 100644 share/qtcreator/qmldesigner/connectionseditor/MyListViewDelegate.qml create mode 100644 share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml create mode 100644 share/qtcreator/qmldesigner/connectionseditor/Pill.qml create mode 100644 share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index e8909988de6..af3a44ffae1 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -107,19 +107,64 @@ Column { onClicked: backend.removeCondition() } + ExpressionBuilder { + style: StudioTheme.Values.connectionPopupControlStyle + width: root.width + + visible: backend.hasCondition + model: backend.conditionListModel + + onRemove: function(index) { + console.log("remove", index) + backend.conditionListModel.removeToken(index) + } + + onUpdate: function(index, value) { + console.log("update", index, value) + backend.conditionListModel.updateToken(index, value) + } + + onAdd: function(value) { + console.log("add", value) + backend.conditionListModel.appendToken(value) + } + + onInsert: function(index, value, type) { + console.log("insert", index, value, type) + + if (type === ConditionListModel.Intermediate) + backend.conditionListModel.insertIntermediateToken(index, value) + else if (type === ConditionListModel.Shadow) + backend.conditionListModel.insertShadowToken(index, value) + else + backend.conditionListModel.insertToken(index, value) + } + + onSetValue: function(index, value) { + console.log("setValue", index, value) + + backend.conditionListModel.setShadowToken(index, value) + } + } + Flow { spacing: root.horizontalSpacing width: root.width - Repeater { + Repeater { model: backend.conditionListModel + Text { text: value color: "white" + Rectangle { z: -1 opacity: 0.2 + anchors.fill: parent color: { + if (type === ConditionListModel.Intermediate) + return "darkorange" if (type === ConditionListModel.Invalid) return "red" if (type === ConditionListModel.Operator) @@ -128,8 +173,9 @@ Column { return "green" if (type === ConditionListModel.Variable) return "yellow" + if (type === ConditionListModel.Shadow) + return "hotpink" } - anchors.fill: parent } } } @@ -138,7 +184,7 @@ Column { TextInput { id: commandInput width: root.width - onAccepted: backend.conditionListModel.command(commandInput.text) + onAccepted: backend.conditionListModel.command(commandInput.text) } Text { @@ -153,7 +199,8 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && !backend.hasElse + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + && backend.hasCondition && !backend.hasElse onClicked: backend.addElse() } @@ -165,7 +212,8 @@ Column { iconSize: StudioTheme.Values.baseFontSize iconFont: StudioTheme.Constants.font anchors.horizontalCenter: parent.horizontalCenter - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + && backend.hasCondition && backend.hasElse onClicked: backend.removeElse() } @@ -177,7 +225,8 @@ Column { columnWidth: root.columnWidth statement: backend.koStatement spacing: root.verticalSpacing - visible: action.currentValue !== ConnectionModelStatementDelegate.Custom && backend.hasCondition && backend.hasElse + visible: action.currentValue !== ConnectionModelStatementDelegate.Custom + && backend.hasCondition && backend.hasElse } // Editor diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml new file mode 100644 index 00000000000..70d3f096173 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -0,0 +1,387 @@ +// 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 + +Rectangle { + id: root + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property var conditionListModel: ConnectionsEditorEditorBackend.connectionModel.delegate.conditionListModel + + property alias model: repeater.model + property int shadowPillIndex: -1 + property bool shadowPillVisible: root.shadowPillIndex !== -1 + + property int heightBeforeShadowPill: Math.min(20, flow.childrenRect.height) // TODO Proper size value + property int expressionHeight: { + if (popup.visible) + return root.heightBeforeShadowPill + flow.spacing + 20 + + return root.heightBeforeShadowPill + } + + signal remove(int index) + signal update(int index, var value) + signal add(var value) + signal insert(int index, var value, int type) + + signal setValue(int index, var value) + signal setValueType(int index, var value, int type) + + width: 400 + height: root.expressionHeight + 2 * StudioTheme.Values.flowMargin + color: root.style.background.idle + border { + color: root.conditionListModel.valid ? root.style.border.idle + : StudioTheme.Values.themeError + width: root.style.borderWidth + } + + onVisibleChanged: { + if (!root.visible) + popup.close() + } + + // Is text input for creating new items currently used. + function textInputActive() { // TODO Make property + return newTextInput.activeFocus && newTextInput.visible + } + + function getMappedItemRect(index: int) : rect { + let item = repeater.itemAt(index) + let itemRect = Qt.rect(item.x, item.y, item.width, item.height) + return flow.mapToItem(root, itemRect) + } + + function placeCursor(index: int) : void { + var textInputPosition = Qt.point(0, 0) + + if (!repeater.count) { // Empty repeater + let mappedItemRect = flow.mapToItem(root, 0, 0, 0, 0) + + textInputPosition = Qt.point(mappedItemRect.x, mappedItemRect.y) + index = 0 + } else { // Repeater is not empty + // Clamp index to 0 and num items in repeater + index = Math.min(Math.max(index, 0), repeater.count) + + if (index === 0) { + // Needs to be placed in front of first repeater item + let mappedItemRect = root.getMappedItemRect(index) + textInputPosition = Qt.point(mappedItemRect.x - 4, // - 4 due to spacing of flow + mappedItemRect.y) + } else { + let mappedItemRect = root.getMappedItemRect(index - 1) + textInputPosition = Qt.point(mappedItemRect.x + mappedItemRect.width + 3, + mappedItemRect.y) + } + } + + // Position text input, make it visible and set focus + newTextInput.x = textInputPosition.x + newTextInput.y = textInputPosition.y + newTextInput.index = index + newTextInput.visible = true + newTextInput.forceActiveFocus() + // Open suggestion popup + popup.open() + } + + 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) + } + } + + 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 = flow.childrenRect.height + } + + 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 + + if (!root.conditionListModel.valid && index === root.conditionListModel.errorIndex) + item.invalid = true + } + + Pill { + id: pill + + 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: { + console.log("SUBMIT") + + //newTextInput.index = pill.index + 1 + newTextInput.visible = true + newTextInput.forceActiveFocus() + } + } + } + } + + 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.+/ } + + //onActiveFocusChanged: { + // if (!newTextInput.activeFocus && !root.shadowPillVisible) { + // console.log("CLOSE POPUP") + // popup.close() + // } + //} + + 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 + + //onOpened: console.log("POPUP opened") + //onClosed: console.log("POPUP closed") + + 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 = flow.childrenRect.height + 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 = flow.childrenRect.height + 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/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..bccb6c645d6 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml @@ -0,0 +1,66 @@ +// 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 + + //implicitWidth: leftMargin + __contentIndent + implicitContentWidth + rightPadding + rightMargin + //implicitHeight: Math.max(indicator ? indicator.height : 0, implicitContentHeight) * 1.25 + + indentation: 12 + //leftMargin: 4 + //rightMargin: 4 + //spacing: 4 + + //topPadding: contentItem ? (height - contentItem.implicitHeight) / 2 : 0 + leftPadding: control.leftMargin + control.__contentIndent + + //required property int row + //required property var model + readonly property real __contentIndent: !control.isTreeNode ? 0 + : (control.depth * control.indentation) + + (control.indicator ? control.indicator.width + control.spacing : 0) + + indicator: Item { + readonly property real __indicatorIndent: control.leftMargin + (control.depth * control.indentation) + + x: __indicatorIndent + width: 30 + height: 30 + + Text { + id: caret + 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..942a61b6db1 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -0,0 +1,167 @@ +// 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 || root.invalid } + + signal remove() + signal update(var value) + signal submit() + + readonly property int margin: StudioTheme.Values.flowPillMargin + + property bool invalid: false + + 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: 4 + + 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.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 + + property bool dirty: false + + 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() // emit + } + + onTextEdited: textInput.dirty = true + + Keys.onPressed: function (event) { + if (event.key === Qt.Key_Backspace) { + if (textInput.text !== "") + return + + root.remove() // emit + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml new file mode 100644 index 00000000000..a4a1789eb55 --- /dev/null +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -0,0 +1,257 @@ +// 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 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 + + function reset() { + search.clear() + stack.pop(null, Controls.StackView.Immediate) + root.listModel.reset() + } + + closePolicy: Controls.Popup.NoAutoClose + padding: 0 + + 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 + + onSearchChanged: function(value) { + root.treeModel.setFilter(value) + } + } + + Controls.StackView { + id: stack + + 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(380, childrenRect.height) + clip: true + model: root.listModel + + 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(380, childrenRect.height) + clip: true + model: root.treeModel + + // This is currently a workaround and should be cleaned up. Calling + // expandRecursively every time the filter changes is performance wise not good. + //Connections { + // target: proxyModel + // function onFilterChanged() { treeView.expandRecursively() } + //} + + 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: false + 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 + + // TODO actual value + tooltip + model: ["AND", "OR", "equal", "not equal", "greater", "less", "greater then", "less then"] + + Rectangle { + width: textItem.contentWidth + 14 + height: 26 + color: "#161616" + radius: 4 + border { + color: "white" + width: mouseArea.containsMouse ? 1 : 0 + } + + MouseArea { + id: mouseArea + hoverEnabled: true + anchors.fill: parent + } + + Text { + id: textItem + font.pixelSize: 12 + color: "white" + text: modelData + anchors.centerIn: parent + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml index 8165a69a1b4..a16e18962cb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/TabCheckButton.qml @@ -131,6 +131,7 @@ T.TabButton { State { name: "check" when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" PropertyChanges { target: controlBackground color: control.style.interaction 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/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/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 388f82d0470..0e5b4e2c7b8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -238,6 +238,12 @@ QtObject { 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 + // Theme Colors property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue @@ -434,6 +440,10 @@ 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 {} diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 08e662953e9..f132d76ffc9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -559,8 +559,13 @@ QHash ConnectionModel::roleNames() const } ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent) - : QObject(parent), m_signalDelegate(parent->connectionView()), m_okStatementDelegate(parent), - m_koStatementDelegate(parent), m_conditionListModel(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(); @@ -753,6 +758,9 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) m_currentRow = i; + m_propertyTreeModel.resetModel(); + m_propertyListProxyModel.setRowAndInternalId(0, -1); + //setup ConnectionModel *model = qobject_cast(parent()); @@ -870,6 +878,16 @@ void ConnectionModelBackendDelegate::setSource(const QString &source) emit sourceChanged(); } +PropertyTreeModel *ConnectionModelBackendDelegate::propertyTreeModel() +{ + return &m_propertyTreeModel; +} + +PropertyListProxyModel *ConnectionModelBackendDelegate::propertyListProxyModel() +{ + return &m_propertyListProxyModel; +} + void ConnectionModelBackendDelegate::setupCondition() { auto &condition = ConnectionEditorStatements::matchedCondition(m_handler); @@ -1575,30 +1593,83 @@ ConditionListModel::ConditionToken ConditionListModel::tokenFromComparativeState void ConditionListModel::insertToken(int index, const QString &value) { + beginInsertRows({}, index, index); + m_tokens.insert(index, valueToToken(value)); validateAndRebuildTokens(); - resetModel(); + + endInsertRows(); + //resetModel(); } void ConditionListModel::updateToken(int index, const QString &value) { m_tokens[index] = valueToToken(value); validateAndRebuildTokens(); - resetModel(); + + dataChanged(createIndex(index, 0), createIndex(index, 0)); + //resetModel(); } void ConditionListModel::appendToken(const QString &value) { + beginInsertRows({}, rowCount() - 1, rowCount() - 1); + insertToken(rowCount(), value); validateAndRebuildTokens(); - resetModel(); + + endInsertRows(); + //resetModel(); } void ConditionListModel::removeToken(int index) { + beginRemoveRows({}, index, index); + m_tokens.remove(index, 1); validateAndRebuildTokens(); - resetModel(); + + 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 @@ -1655,20 +1726,29 @@ void ConditionListModel::command(const QString &string) } } -void ConditionListModel::setInvalid(const QString &errorMessage) +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 @@ -1676,6 +1756,11 @@ QString ConditionListModel::error() const return m_errorMessage; } +int ConditionListModel::errorIndex() const +{ + return m_errorIndex; +} + void ConditionListModel::internalSetup() { setInvalid(tr("No Valid Condition")); @@ -1766,11 +1851,26 @@ int ConditionListModel::checkOrder() const 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) { @@ -1780,12 +1880,12 @@ void ConditionListModel::validateAndRebuildTokens() }); if (invalidToken) { - setInvalid(tr("Invalid token %").arg(invalidToken)); + setInvalid(tr("Invalid token %1").arg(invalidValue)); return; } if (int firstError = checkOrder() != -1) { - setInvalid(tr("Invalid order at %1").arg(firstError)); + setInvalid(tr("Invalid order at %1").arg(firstError), firstError); return; } @@ -1831,9 +1931,6 @@ ConnectionEditorStatements::ConditionToken ConditionListModel::toOperatorStateme if (token.value == "!==") return ConnectionEditorStatements::ConditionToken::Not; - if (token.value == "!==") - return ConnectionEditorStatements::ConditionToken::Not; - if (token.value == ">") return ConnectionEditorStatements::ConditionToken::LargerThan; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index ecea5e7c058..59b92a405e9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -96,9 +96,10 @@ class ConditionListModel : public QAbstractListModel 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 { Invalid, Operator, Literal, Variable }; + enum ConditionType { Intermediate, Invalid, Operator, Literal, Variable, Shadow }; Q_ENUM(ConditionType) struct ConditionToken @@ -129,22 +130,28 @@ public: 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); + void setInvalid(const QString &errorMessage, int index = -1); void setValid(); QString error() const; + int errorIndex() const; signals: void validChanged(); void emptyChanged(); void conditionChanged(); void errorChanged(); + void errorIndexChanged(); private: void internalSetup(); @@ -162,6 +169,7 @@ private: QList m_tokens; bool m_valid = false; QString m_errorMessage; + int m_errorIndex = -1; }; class ConnectionModelStatementDelegate : public QObject @@ -245,6 +253,9 @@ class ConnectionModelBackendDelegate : public QObject 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); @@ -282,6 +293,10 @@ private: ConditionListModel *conditionListModel(); QString source() const; void setSource(const QString &source); + + PropertyTreeModel *propertyTreeModel(); + PropertyListProxyModel *propertyListProxyModel(); + void setupCondition(); void setupHandlerAndStatements(); @@ -303,6 +318,8 @@ private: bool m_hasCondition = false; bool m_hasElse = false; QString m_source; + PropertyTreeModel m_propertyTreeModel; + PropertyListProxyModel m_propertyListProxyModel; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index d33e67d1164..41eee97be48 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -88,10 +88,13 @@ public: 0, "ConnectionModelStatementDelegate"); - qmlRegisterType("ConnectionsEditorEditorBackend", - 1, - 0, - "ConditionListModel"); + qmlRegisterType("ConnectionsEditorEditorBackend", 1, 0, "ConditionListModel"); + + qmlRegisterType("ConnectionsEditorEditorBackend", 1, 0, "PropertyTreeModel"); + qmlRegisterType("ConnectionsEditorEditorBackend", + 1, + 0, + "PropertyListProxyModel"); Theme::setupTheme(engine()); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index dcbbe5c1a60..759d47037ae 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -155,7 +155,8 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const if (role == RowRole) return index.row(); - if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole) { + if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole + || role == ChildCountRole) { if (!index.isValid()) return {}; @@ -166,26 +167,33 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const DataCacheItem item = m_indexHash[index.internalId()]; - if (item.propertyName.isEmpty()) { //node - if (role == PropertyNameRole) - return item.modelNode.displayName(); - - return true; //nodes are always shown - } + if (role == ChildCountRole) + return rowCount(index); if (role == ExpressionRole) - return QString(item.modelNode.id() + item.propertyName); + return QString(item.modelNode.id() + "." + item.propertyName); - if (role == PropertyNameRole) - return item.propertyName; + if (role == PropertyNameRole) { + if (!item.propertyName.isEmpty()) + return 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 + 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 + return true; // dynamic properties have priority + + if (item.propertyName.isEmpty()) { //node + //if (role == PropertyNameRole) + // return item.modelNode.displayName(); + + return true; // nodes are always shown + } return false; } @@ -216,7 +224,7 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const } if (role == Qt::DisplayRole) - return item.propertyName; + return QString::fromUtf8(item.propertyName); QFont f; auto priority = properityLists(); @@ -241,7 +249,7 @@ QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &par return {}; if (!hasIndex(row, column, parent)) - return QModelIndex(); + return {}; const int rootId = -1; @@ -312,7 +320,7 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const return ensureModelIndex(item.modelNode, row); } -QPersistentModelIndex PropertyTreeModel::indexForInernalIdAndRow(int internalId, int row) +QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(int internalId, int row) { return createIndex(row, 0, internalId); } @@ -323,7 +331,7 @@ int PropertyTreeModel::rowCount(const QModelIndex &parent) const return 0; if (!parent.isValid()) - return 1; + return 1; //m_nodeList.size(); int internalId = parent.internalId(); @@ -783,7 +791,8 @@ QHash PropertyTreeModel::roleNames() const { static QHash roleNames{{PropertyNameRole, "propertyName"}, {PropertyPriorityRole, "hasPriority"}, - {ExpressionRole, "expression"}}; + {ExpressionRole, "expression"}, + {ChildCountRole, "childCount"}}; return roleNames; } @@ -792,17 +801,24 @@ PropertyListProxyModel::PropertyListProxyModel(PropertyTreeModel *parent) : QAbstractListModel(), m_treeModel(parent) {} -void PropertyListProxyModel::setRowandInternalId(int row, int internalId) +void PropertyListProxyModel::resetModel() { + beginResetModel(); + endResetModel(); +} + +void PropertyListProxyModel::setRowAndInternalId(int row, int internalId) +{ + qDebug() << Q_FUNC_INFO << row << internalId; QTC_ASSERT(m_treeModel, return ); if (internalId == -1) m_parentIndex = m_treeModel->index(0, 0); else - m_parentIndex = m_treeModel->indexForInernalIdAndRow(internalId, row); + m_parentIndex = m_treeModel->index(row, 0, m_parentIndex); + //m_parentIndex = m_treeModel->indexForInternalIdAndRow(internalId, row); - beginResetModel(); - endResetModel(); + resetModel(); } int PropertyListProxyModel::rowCount(const QModelIndex &) const @@ -820,6 +836,40 @@ QVariant PropertyListProxyModel::data(const QModelIndex &index, int role) const return m_treeModel->data(treeIndex, role); } +QHash PropertyListProxyModel::roleNames() const +{ + return m_treeModel->roleNames(); +} + +void PropertyListProxyModel::goInto(int row) +{ + qDebug() << Q_FUNC_INFO << row << m_parentIndex.internalId(); + setRowAndInternalId(row, 0); //m_parentIndex.internalId()); + + emit parentNameChanged(); +} + +void PropertyListProxyModel::goUp() +{ + qDebug() << Q_FUNC_INFO; + m_parentIndex = m_treeModel->parent(m_parentIndex); + resetModel(); + + emit parentNameChanged(); +} + +void PropertyListProxyModel::reset() +{ + setRowAndInternalId(0, -1); // TODO ??? + + emit parentNameChanged(); +} + +QString PropertyListProxyModel::parentName() const +{ + return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); +} + PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent) { connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this]() { diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index ec96328c02c..9c1c876b466 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -31,6 +31,7 @@ public: PropertyNameRole = Qt::UserRole + 1, PropertyPriorityRole, ExpressionRole, + ChildCountRole, RowRole, InternalIdRole }; @@ -57,7 +58,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; - QPersistentModelIndex indexForInernalIdAndRow(int internalId, int row); + QPersistentModelIndex indexForInternalIdAndRow(int internalId, int row); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -70,14 +71,13 @@ public: }; void setPropertyType(PropertyTypes type); - void setFilter(const QString &filter); + Q_INVOKABLE void setFilter(const QString &filter); QList nodeList() const; const std::vector getProperties(const ModelNode &modelNode) const; ModelNode getModelNodeForId(const QString &id) const; -protected: QHash roleNames() const override; private: @@ -127,12 +127,29 @@ private: class PropertyListProxyModel : public QAbstractListModel { Q_OBJECT + + Q_PROPERTY(QString parentName READ parentName NOTIFY parentNameChanged) + public: PropertyListProxyModel(PropertyTreeModel *parent); - void setRowandInternalId(int row, int internalId); + + void resetModel(); + + void setRowAndInternalId(int row, int 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; From 618797a80a6a3e6a94bd8f70b04508b230fcf37d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 16:14:24 +0200 Subject: [PATCH 210/266] QmlDesigner: Fix expression in empty property case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib0aa08891862218b6ec9d84ca6170d2bc644aded Reviewed-by: Henning Gründl --- .../connectioneditor/connectioneditorstatements.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h index 7a4b079af9d..63d457a84a4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.h @@ -45,7 +45,12 @@ struct Variable QString nodeId; QString propertyName; - QString expression() const { return nodeId + "." + propertyName; } + QString expression() const + { + if (propertyName.isEmpty()) + return nodeId; + return nodeId + "." + propertyName; + } }; struct MatchedFunction From 5f0556536ece408972f6aa6b18aad9e80c378d86 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 16:14:06 +0200 Subject: [PATCH 211/266] QmlDesigner: Cleanup PropertyTreeModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Some cleanup * Add cache * Strip full qualified name from PropertyNameRole * Hide nodes without children if filtering Change-Id: Ibd368174f5a56d6be33b6e55dc7a8099bbe2d119 Reviewed-by: Henning Gründl --- .../connectioneditor/propertytreemodel.cpp | 84 ++++++++----------- .../connectioneditor/propertytreemodel.h | 7 +- 2 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 759d47037ae..ece4d1d927a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -141,10 +141,23 @@ void PropertyTreeModel::resetModel() 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(); + }); + } + + m_sortedAndFilteredPropertyNamesSignalsSlots.clear(); + endResetModel(); testModel(); } +QString stripQualification(const QString &string) +{ + return string.split(".").last(); +} QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const { int internalId = index.internalId(); @@ -161,7 +174,7 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const return {}; if (internalId < 0) - return {}; + return "--root item--"; QTC_ASSERT(internalId < m_indexCount, return {"assert"}); @@ -175,7 +188,7 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const if (role == PropertyNameRole) { if (!item.propertyName.isEmpty()) - return QString::fromUtf8(item.propertyName); + return stripQualification(QString::fromUtf8(item.propertyName)); else return item.modelNode.displayName(); } @@ -188,53 +201,14 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end()) return true; // dynamic properties have priority - if (item.propertyName.isEmpty()) { //node - //if (role == PropertyNameRole) - // return item.modelNode.displayName(); - + if (item.propertyName.isEmpty()) { return true; // nodes are always shown } return false; } - // can be removed later since we only use the two roles above in QML - // just for testing - - if (!(role == Qt::DisplayRole || role == Qt::FontRole)) - return {}; - - if (!index.isValid()) - return {}; - - if (internalId < 0) - return {}; - - QTC_ASSERT(internalId < m_indexCount, return {"assert"}); - - DataCacheItem item = m_indexHash[index.internalId()]; - - if (item.propertyName.isEmpty()) { - const QString name = item.modelNode.displayName(); - if (role == Qt::DisplayRole) - return name; - QFont f; - f.setBold(true); - return f; - } - - if (role == Qt::DisplayRole) - return QString::fromUtf8(item.propertyName); - - QFont f; - auto priority = properityLists(); - if (std::find(priority.begin(), priority.end(), item.propertyName) != priority.end()) - f.setBold(true); - auto dynamic = getDynamicProperties(item.modelNode); - if (std::find(dynamic.begin(), dynamic.end(), item.propertyName) != dynamic.end()) - f.setBold(true); - - return f; + return {}; } Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const @@ -248,14 +222,14 @@ QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &par if (!m_connectionView->isAttached()) return {}; - if (!hasIndex(row, column, parent)) - return {}; - const int rootId = -1; if (!parent.isValid()) return createIndex(0, 0, rootId); + if (!hasIndex(row, column, parent)) + return {}; + if (internalId == rootId) { //root level model node const ModelNode modelNode = m_nodeList[row]; return ensureModelIndex(modelNode, row); @@ -290,7 +264,7 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const int internalId = index.internalId(); - if (internalId == -1) + if (internalId == m_internalRootIndex) return {}; QTC_ASSERT(internalId < m_indexCount, return {}); @@ -471,6 +445,12 @@ const std::vector PropertyTreeModel::sortedAndFilteredPropertyName 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) { @@ -484,9 +464,13 @@ const std::vector PropertyTreeModel::sortedAndFilteredPropertyName if (m_filter.isEmpty() || modelNode.displayName().contains(m_filter)) return returnValue; - return Utils::filtered(returnValue, [this](const PropertyName &name) { + 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( @@ -852,6 +836,10 @@ void PropertyListProxyModel::goInto(int row) void PropertyListProxyModel::goUp() { qDebug() << Q_FUNC_INFO; + + if (m_parentIndex.internalId() == -1) + return; + m_parentIndex = m_treeModel->parent(m_parentIndex); resetModel(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index 9c1c876b466..241cd53e528 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -28,8 +28,8 @@ class PropertyTreeModel : public QAbstractItemModel Q_OBJECT public: enum UserRoles { - PropertyNameRole = Qt::UserRole + 1, - PropertyPriorityRole, + PropertyNameRole = Qt::DisplayRole, + PropertyPriorityRole = Qt::UserRole + 1, ExpressionRole, ChildCountRole, RowRole, @@ -122,6 +122,8 @@ private: QList m_nodeList; PropertyTypes m_type = AllTypes; QString m_filter; + mutable QHash> m_sortedAndFilteredPropertyNamesSignalsSlots; + int m_internalRootIndex = -1; }; class PropertyListProxyModel : public QAbstractListModel @@ -158,6 +160,7 @@ private: PropertyTreeModel *m_treeModel = nullptr; }; + inline bool operator==(const PropertyTreeModel::DataCacheItem &lhs, const PropertyTreeModel::DataCacheItem &rhs) { From 1d884898be5acdf62c06a64a2fdc90b99d5a8511 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 8 Sep 2023 18:40:33 +0200 Subject: [PATCH 212/266] QmlDesigner: Fix build Yes, it is possible to set a unsigend integer to -1. It is defined that a unsigned integer is underflowing and then you get the largest integer. With https://en.wikipedia.org/wiki/Two%27s_complement it exactly the same bit pattern as a -1 signed integer too. But many compiler like the mix of signed and unsigned because it can lead to bugs. So just use the unsigned interId() data type and set the special value to the maximal integer. Change-Id: Ie2ee1b5e2b476ec12abb5a48eee3eae2ae8cd14b Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectioneditor/connectionmodel.cpp | 2 +- .../connectioneditor/propertytreemodel.cpp | 38 +++++++++---------- .../connectioneditor/propertytreemodel.h | 11 +++--- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f132d76ffc9..28be4827325 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -759,7 +759,7 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) m_currentRow = i; m_propertyTreeModel.resetModel(); - m_propertyListProxyModel.setRowAndInternalId(0, -1); + m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); //setup diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index ece4d1d927a..b46c0b9bf64 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -160,7 +160,7 @@ QString stripQualification(const QString &string) } QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const { - int internalId = index.internalId(); + auto internalId = index.internalId(); if (role == InternalIdRole) return internalId; @@ -173,7 +173,7 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return {}; - if (internalId < 0) + if (internalId == internalRootIndex) return "--root item--"; QTC_ASSERT(internalId < m_indexCount, return {"assert"}); @@ -218,26 +218,24 @@ Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex &) const QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex &parent) const { - int internalId = parent.internalId(); + auto internalId = parent.internalId(); if (!m_connectionView->isAttached()) return {}; - const int rootId = -1; - if (!parent.isValid()) - return createIndex(0, 0, rootId); + return createIndex(0, 0, internalRootIndex); if (!hasIndex(row, column, parent)) return {}; - if (internalId == rootId) { //root level model node + if (internalId == internalRootIndex) { //root level model node const ModelNode modelNode = m_nodeList[row]; return ensureModelIndex(modelNode, row); } //property - QTC_ASSERT(internalId >= 0, return {}); + QTC_ASSERT(internalId != internalRootIndex, return {}); DataCacheItem item = m_indexHash[internalId]; QTC_ASSERT(item.modelNode.isValid(), return {}); @@ -262,9 +260,9 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const if (!index.isValid()) return {}; - int internalId = index.internalId(); + auto internalId = index.internalId(); - if (internalId == m_internalRootIndex) + if (internalId == internalRootIndex) return {}; QTC_ASSERT(internalId < m_indexCount, return {}); @@ -273,7 +271,7 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const // no property means the parent is the root item if (item.propertyName.isEmpty()) - return createIndex(0, 0, -1); + return createIndex(0, 0, internalRootIndex); if (item.propertyName.contains(".")) { auto list = item.propertyName.split('.'); @@ -294,7 +292,7 @@ QModelIndex PropertyTreeModel::parent(const QModelIndex &index) const return ensureModelIndex(item.modelNode, row); } -QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(int internalId, int row) +QPersistentModelIndex PropertyTreeModel::indexForInternalIdAndRow(quintptr internalId, int row) { return createIndex(row, 0, internalId); } @@ -307,9 +305,9 @@ int PropertyTreeModel::rowCount(const QModelIndex &parent) const if (!parent.isValid()) return 1; //m_nodeList.size(); - int internalId = parent.internalId(); + auto internalId = parent.internalId(); - if (internalId == -1) + if (internalId == internalRootIndex) return m_nodeList.size(); QTC_ASSERT(internalId < m_indexCount, return 0); @@ -791,12 +789,12 @@ void PropertyListProxyModel::resetModel() endResetModel(); } -void PropertyListProxyModel::setRowAndInternalId(int row, int internalId) +void PropertyListProxyModel::setRowAndInternalId(int row, quintptr internalId) { qDebug() << Q_FUNC_INFO << row << internalId; QTC_ASSERT(m_treeModel, return ); - if (internalId == -1) + if (internalId == internalRootIndex) m_parentIndex = m_treeModel->index(0, 0); else m_parentIndex = m_treeModel->index(row, 0, m_parentIndex); @@ -837,7 +835,7 @@ void PropertyListProxyModel::goUp() { qDebug() << Q_FUNC_INFO; - if (m_parentIndex.internalId() == -1) + if (m_parentIndex.internalId() == internalRootIndex) return; m_parentIndex = m_treeModel->parent(m_parentIndex); @@ -848,7 +846,7 @@ void PropertyListProxyModel::goUp() void PropertyListProxyModel::reset() { - setRowAndInternalId(0, -1); // TODO ??? + setRowAndInternalId(0, internalRootIndex); // TODO ??? emit parentNameChanged(); } @@ -871,7 +869,7 @@ PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m void PropertyTreeModelDelegate::setPropertyType(PropertyTreeModel::PropertyTypes type) { m_model.setPropertyType(type); - setupNameComboBox(m_idCombboBox.currentText(), m_nameCombboBox.currentText(), 0); + setupNameComboBox(m_idCombboBox.currentText(), m_nameCombboBox.currentText(), nullptr); } void PropertyTreeModelDelegate::setup(const QString &id, const QString &name, bool *nameExists) @@ -899,7 +897,7 @@ void PropertyTreeModelDelegate::setupNameComboBox(const QString &id, return QString::fromUtf8(name); }); QStringList nameList; - nameList.reserve(nameVector.size()); + nameList.reserve(Utils::ssize(nameVector)); std::copy(nameVector.begin(), nameVector.end(), std::back_inserter(nameList)); if (!nameList.contains(name)) { diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index 241cd53e528..67836cb2698 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -15,6 +15,8 @@ namespace QmlDesigner { +inline constexpr quintptr internalRootIndex = std::numeric_limits::max(); + class AbstractProperty; class ModelNode; class BindingProperty; @@ -58,7 +60,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; - QPersistentModelIndex indexForInternalIdAndRow(int internalId, int row); + QPersistentModelIndex indexForInternalIdAndRow(quintptr internalId, int row); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -67,7 +69,7 @@ public: { ModelNode modelNode; PropertyName propertyName; - int internalIndex = -1; + std::size_t internalIndex = internalRootIndex; }; void setPropertyType(PropertyTypes type); @@ -118,12 +120,11 @@ private: mutable std::set m_indexCache; mutable std::vector m_indexHash; - mutable int m_indexCount = 0; + mutable std::size_t m_indexCount = 0; QList m_nodeList; PropertyTypes m_type = AllTypes; QString m_filter; mutable QHash> m_sortedAndFilteredPropertyNamesSignalsSlots; - int m_internalRootIndex = -1; }; class PropertyListProxyModel : public QAbstractListModel @@ -137,7 +138,7 @@ public: void resetModel(); - void setRowAndInternalId(int row, int internalId); + void setRowAndInternalId(int row, quintptr internalId); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; From 2dda34f781048f67637df68d574573018bf94224 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 8 Sep 2023 15:49:58 +0200 Subject: [PATCH 213/266] Extend bindingmodel and minor fixes - Include singletons in the list of sources (former sourceModelNodes) - Do not suggest sources on bindings that are initialized without a source node. Prevent constructs like "property string p: button.qsTr("STREXPR3")". - Allow changing the name of the target property and update the delegate properly when doing so. - Remove superfluous emitCurrentIndex emission from the dynamicpropertiesmodel. - Add missing qml imports. Note that it is still possible to create illegal constructs if the user wants to. Change-Id: I084978f2f8d451df076d1b556db15764e17e31fb Reviewed-by: Thomas Hartmann --- .../connectionseditor/BindingsDialog.qml | 3 +- .../connectionseditor/BindingsDialogForm.qml | 14 +- .../connectionseditor/ConnectionsDialog.qml | 3 +- .../connectionseditor/PopupLabel.qml | 2 +- .../connectionseditor/PropertiesDialog.qml | 3 +- .../connectioneditor/bindingmodel.cpp | 85 ++++++++-- .../connectioneditor/bindingmodel.h | 2 + .../connectioneditorutils.cpp | 155 ++++++++++-------- .../connectioneditor/connectioneditorutils.h | 2 +- .../dynamicpropertiesmodel.cpp | 1 - 10 files changed, 172 insertions(+), 98 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml index 65321b3b6e4..8830e298df3 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialog.qml @@ -3,6 +3,7 @@ import QtQuick import StudioTheme 1.0 as StudioTheme +import HelperWidgets as HelperWidgets PopupDialog { property alias backend: form.backend @@ -15,7 +16,7 @@ PopupDialog { text: qsTr("Owner") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter - ToolTipArea { + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("The owner of the property") } diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml index 912bbad8287..157d0d35177 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsDialogForm.qml @@ -64,10 +64,16 @@ Column { onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex } - PopupLabel { - width: root.columnWidth - text: backend.property.currentText - } + StudioControls.TopLevelComboBox { + id: name + width: root.columnWidth + style: StudioTheme.Values.connectionPopupControlStyle + model: backend.property.model ?? [] + + onActivated: backend.property.activateIndex(name.currentIndex) + property int currentTypeIndex: backend.property.currentIndex ?? 0 + onCurrentTypeIndexChanged: name.currentIndex = name.currentTypeIndex + } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 210fcc81d3a..b880916e4d8 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -4,6 +4,7 @@ import QtQuick import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme +import HelperWidgets 2.0 as HelperWidgets PopupDialog { @@ -18,7 +19,7 @@ PopupDialog { text: qsTr("Target") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter - ToolTipArea { + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("Choose the target for the signal.") } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml index 1af0950be4f..6e5a117d8ce 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupLabel.qml @@ -12,7 +12,7 @@ Text { color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize property alias tooltip: area.tooltip - ToolTipArea { + 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 d649d6380ef..20e9568c14b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesDialog.qml @@ -3,6 +3,7 @@ import QtQuick import StudioTheme 1.0 as StudioTheme +import HelperWidgets as HelperWidgets PopupDialog { property alias backend: form.backend @@ -16,7 +17,7 @@ PopupDialog { text: qsTr("Owner") font.pixelSize: StudioTheme.Values.myFontSize anchors.verticalCenter: parent.verticalCenter - ToolTipArea { + HelperWidgets.ToolTipArea { anchors.fill: parent tooltip: qsTr("The owner of the property") } diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index 69f6b5cc127..0b38b9d35d9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -5,6 +5,7 @@ #include "bindingmodelitem.h" #include "connectionview.h" #include "connectioneditorutils.h" +#include "modelfwd.h" #include #include @@ -145,11 +146,12 @@ void BindingModel::updateItem(const BindingProperty &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)); @@ -167,7 +169,33 @@ void BindingModel::commitExpression(int row, const QString &expression) return; connectionView()->executeInTransaction(__FUNCTION__, [&bindingProperty, expression]() { - bindingProperty.setExpression(expression.trimmed()); + 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); }); } @@ -228,6 +256,10 @@ BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() { expressionChanged(); }); + + connect(&m_property, &StudioQmlComboBoxBackend::activated, this, [this]() { + propertyNameChanged(); + }); } void BindingModelBackendDelegate::update(const BindingProperty &property, AbstractView *view) @@ -243,17 +275,20 @@ void BindingModelBackendDelegate::update(const BindingProperty &property, Abstra auto [sourceNodeName, sourcePropertyName] = splitExpression(property.expression()); - QString targetName = QString::fromUtf8(property.name()); - m_targetNode = idOrTypeName(property.parentModelNode()); + QStringList sourceNodes = {}; + if (!sourceNodeName.isEmpty()) + sourceNodes = addName(availableSources(view), sourceNodeName); - auto modelNodes = addName(availableModelNodes(view), sourceNodeName); - m_sourceNode.setModel(modelNodes); + m_sourceNode.setModel(sourceNodes); m_sourceNode.setCurrentText(sourceNodeName); auto sourceproperties = addName(availableSourceProperties(property, view), sourcePropertyName); m_sourceNodeProperty.setModel(sourceproperties); m_sourceNodeProperty.setCurrentText(sourcePropertyName); + 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); @@ -283,20 +318,36 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() void BindingModelBackendDelegate::expressionChanged() const { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); + auto commit = [this]() { + BindingModel *model = qobject_cast(parent()); + QTC_ASSERT(model, return); - const QString sourceNode = m_sourceNode.currentText(); - const QString sourceProperty = m_sourceNodeProperty.currentText(); + const QString sourceNode = m_sourceNode.currentText(); + const QString sourceProperty = m_sourceNodeProperty.currentText(); + QString expression; + if (sourceProperty.isEmpty()) + expression = sourceNode; + else + expression = sourceNode + QLatin1String(".") + sourceProperty; - QString expression; - if (sourceProperty.isEmpty()) - expression = sourceNode; - else - expression = sourceNode + QLatin1String(".") + sourceProperty; + int row = model->currentIndex(); + model->commitExpression(row, expression); + }; - int row = model->currentIndex(); - model->commitExpression(row, expression); + callLater(commit); +} + +void BindingModelBackendDelegate::propertyNameChanged() const +{ + 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); + }; + + callLater(commit); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index e167f2b5afb..4c3dd1210e5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -49,6 +49,7 @@ public: void removeItem(const AbstractProperty &property); void commitExpression(int row, const QString &expression); + void commitPropertyName(int row, const PropertyName &name); protected: QHash roleNames() const override; @@ -86,6 +87,7 @@ public: private: QString targetNode() const; void expressionChanged() const; + void propertyNameChanged() const; StudioQmlComboBoxBackend *property(); StudioQmlComboBoxBackend *sourceNode(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp index c4ce5c23fe4..d43ac648a6f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -82,6 +83,38 @@ NodeMetaInfo dynamicTypeMetaInfo(const AbstractProperty& property) 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; @@ -217,7 +250,21 @@ QString defaultExpressionForType(const TypeName &type) return expression; } -QStringList availableModelNodes(AbstractView *view) +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()) { @@ -225,22 +272,7 @@ QStringList availableModelNodes(AbstractView *view) sourceNodes.append(modelNode.id()); } std::sort(sourceNodes.begin(), sourceNodes.end()); - return sourceNodes; -} - -QStringList dynamicPropertyNamesFromNode(const ModelNode& node) -{ - QStringList dynamicProperties; - for (const VariantProperty &variantProperty : node.variantProperties()) { - if (variantProperty.isDynamic()) - dynamicProperties << QString::fromUtf8(variantProperty.name()); - } - - for (const BindingProperty &bindingProperty : node.bindingProperties()) { - if (bindingProperty.isDynamic()) - dynamicProperties << QString::fromUtf8((bindingProperty.name())); - } - return dynamicProperties; + return singletonsFromView(view) + sourceNodes; } QStringList availableTargetProperties(const BindingProperty &bindingProperty) @@ -261,10 +293,9 @@ QStringList availableTargetProperties(const BindingProperty &bindingProperty) writableProperties.push_back(QString::fromUtf8(property.name())); } - return dynamicPropertyNamesFromNode(modelNode) + writableProperties; + return writableProperties; } - - return dynamicPropertyNamesFromNode(modelNode); + return { }; } ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const ModelNode &targetNode) @@ -278,64 +309,13 @@ ModelNode getNodeByIdOrParent(AbstractView *view, const QString &id, const Model return {}; } -bool metaInfoIsCompatible(const NodeMetaInfo& sourceType, const PropertyMetaInfo& metaInfo) -{ - if (sourceType.isVariant()) - return true; - - NodeMetaInfo targetType = metaInfo.propertyType(); - 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; -} - QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view) { const QString expression = bindingProperty.expression(); const QStringList stringlist = expression.split(QLatin1String(".")); - QStringList possibleProperties; const QString &id = stringlist.constFirst(); ModelNode modelNode = getNodeByIdOrParent(view, id, bindingProperty.parentModelNode()); - if (!modelNode.isValid()) { - //if it's not a valid model node, maybe it's a singleton - if (RewriterView *rv = view->rewriterView()) { - for (const QmlTypeData &data : rv->getQMLTypes()) { - if (!data.typeName.isEmpty() && data.typeName == id) { - NodeMetaInfo metaInfo = view->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() << __FUNCTION__ << " invalid model node"; - return QStringList(); - } - - possibleProperties = possibleProperties + dynamicPropertyNamesFromNode(modelNode); NodeMetaInfo type; if (bindingProperty.isDynamic()) { @@ -345,6 +325,39 @@ QStringList availableSourceProperties(const BindingProperty &bindingProperty, Ab } 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()) { diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h index 8a1cafa3e54..1f05d654596 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.h @@ -34,7 +34,7 @@ bool isDynamicVariantPropertyType(const TypeName &type); QVariant defaultValueForType(const TypeName &type); QString defaultExpressionForType(const TypeName &type); -QStringList availableModelNodes(AbstractView *view); +QStringList availableSources(AbstractView *view); QStringList availableTargetProperties(const BindingProperty &bindingProperty); QStringList availableSourceProperties(const BindingProperty &bindingProperty, AbstractView *view); QList dynamicPropertiesFromNode(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 084afb8e100..69f51a277d2 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -168,7 +168,6 @@ void DynamicPropertiesModel::removeItem(const AbstractProperty &property) static_cast(removeRow(*index)); setCurrentProperty(current); - emit currentIndexChanged(); } QHash DynamicPropertiesModel::roleNames() const From 522a622e9ad4b625b392a3ab3c2ab5062f7896ef Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 8 Sep 2023 18:09:20 +0200 Subject: [PATCH 214/266] QmlDesigner: Fix QML window hide issue on macOS Task-number: QDS-10585 Change-Id: Iabf834627701d84bd693db6d22c3699e1209fccf Reviewed-by: Reviewed-by: Thomas Hartmann --- .../imports/StudioControls/TopLevelComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index eb79352ad9c..7c475175571 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 From 06878ee9cb25220712575fe0d289719d44f8db0e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 8 Sep 2023 15:49:39 +0300 Subject: [PATCH 215/266] QmlDesigner: Fix grid color not loading properly on model attach Fixes: QDS-10594 Change-Id: I61fbce411776b14078078d6137c8963e22c0fc11 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: --- .../components/edit3d/backgroundcolorselection.cpp | 2 +- .../qmldesigner/components/edit3d/edit3dview.cpp | 2 +- .../qmldesigner/components/edit3d/edit3dviewconfig.h | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp index 74f2417bd16..d0b23f1b128 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -38,7 +38,7 @@ 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(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 1783c171b14..002606d7f0d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -237,7 +237,7 @@ void Edit3DView::modelAttached(Model *model) QVariant::fromValue(Edit3DViewConfig::loadColor( DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR))); rootModelNode().setAuxiliaryData(edit3dBgColorProperty, - QVariant::fromValue(Edit3DViewConfig::loadColor( + QVariant::fromValue(Edit3DViewConfig::loadColors( DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR))); checkImports(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h index 2783cba08bc..33d40b332e7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h @@ -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); From 171e11b4c7123296d7e4b835449ecb565683d527 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 11 Sep 2023 11:47:34 +0200 Subject: [PATCH 216/266] QmlDesigner: Fix Expression Editor * Fix cursor placement after submit * Remove pill indicator for wrong ordering * Fix PropertyTreeModel root child count and parent name Change-Id: I169396cf3cc62890fafe0bfe75d736be3ca8d47f Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/ExpressionBuilder.qml | 15 +++++++-------- .../qmldesigner/connectionseditor/Pill.qml | 14 ++++---------- .../connectioneditor/propertytreemodel.cpp | 19 +++++++++++++------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index 70d3f096173..266ae8d48ce 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -192,9 +192,6 @@ Rectangle { if (index >= newTextInput.index) newTextInput.index = newTextInput.index + 1 - - if (!root.conditionListModel.valid && index === root.conditionListModel.errorIndex) - item.invalid = true } Pill { @@ -220,12 +217,14 @@ Rectangle { flow.focusIndex = pill.index } - onSubmit: { - console.log("SUBMIT") + 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++ - //newTextInput.index = pill.index + 1 - newTextInput.visible = true - newTextInput.forceActiveFocus() + root.placeCursor(index) } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index 942a61b6db1..aa46b71cc6b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -24,16 +24,14 @@ FocusScope { 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 || root.invalid } + function isInvalid() { return root.type === ConditionListModel.Invalid } signal remove() signal update(var value) - signal submit() + signal submit(int cursorPosition) readonly property int margin: StudioTheme.Values.flowPillMargin - property bool invalid: false - width: { if (root.isEditable()) { if (root.isInvalid()) @@ -133,8 +131,6 @@ FocusScope { TextInput { id: textInput - property bool dirty: false - x: root.isInvalid() ? root.margin : 0 height: StudioTheme.Values.flowPillHeight topPadding: 1 @@ -145,15 +141,13 @@ FocusScope { visible: root.isEditable() enabled: root.isEditable() - validator: RegularExpressionValidator { regularExpression: /^\S+/ } + //validator: RegularExpressionValidator { regularExpression: /^\S+/ } onEditingFinished: { root.update(textInput.text) // emit - root.submit() // emit + root.submit(textInput.cursorPosition) // emit } - onTextEdited: textInput.dirty = true - Keys.onPressed: function (event) { if (event.key === Qt.Key_Backspace) { if (textInput.text !== "") diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index b46c0b9bf64..476bdad1c1f 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -160,6 +160,9 @@ QString stripQualification(const QString &string) } QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return {}; + auto internalId = index.internalId(); if (role == InternalIdRole) @@ -170,13 +173,14 @@ QVariant PropertyTreeModel::data(const QModelIndex &index, int role) const if (role == PropertyNameRole || role == PropertyPriorityRole || role == ExpressionRole || role == ChildCountRole) { - if (!index.isValid()) + if (internalId == internalRootIndex) { + if (role == PropertyNameRole) + return "--root item--"; + if (role == ChildCountRole) + return rowCount(index); + return {}; - - if (internalId == internalRootIndex) - return "--root item--"; - - QTC_ASSERT(internalId < m_indexCount, return {"assert"}); + } DataCacheItem item = m_indexHash[index.internalId()]; @@ -853,6 +857,9 @@ void PropertyListProxyModel::reset() QString PropertyListProxyModel::parentName() const { + if (!m_treeModel->parent(m_parentIndex).isValid()) + return {}; + return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); } From 41ee64b8a4a8aa8134e8eb6260cda3780bec7362 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 11 Sep 2023 11:50:36 +0200 Subject: [PATCH 217/266] QmlDesigner: Remove debug UI in connections editor Change-Id: Ifc3c0b3c1fa09eff0b0ea650540ceeb00091789d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index af3a44ffae1..8b0404353b2 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -147,51 +147,6 @@ Column { } } - Flow { - spacing: root.horizontalSpacing - width: root.width - - Repeater { - model: backend.conditionListModel - - Text { - text: value - color: "white" - - Rectangle { - z: -1 - opacity: 0.2 - anchors.fill: parent - color: { - if (type === ConditionListModel.Intermediate) - return "darkorange" - if (type === ConditionListModel.Invalid) - return "red" - if (type === ConditionListModel.Operator) - return "blue" - if (type === ConditionListModel.Literal) - return "green" - if (type === ConditionListModel.Variable) - return "yellow" - if (type === ConditionListModel.Shadow) - return "hotpink" - } - } - } - } - } - - TextInput { - id: commandInput - width: root.width - onAccepted: backend.conditionListModel.command(commandInput.text) - } - - Text { - text: "invalid " + backend.conditionListModel.error - visible: !backend.conditionListModel.valid - } - HelperWidgets.AbstractButton { style: StudioTheme.Values.connectionPopupButtonStyle width: 160 From 28933f2d4d83acf545efc654de08d497a3cda5c7 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 11 Sep 2023 12:45:25 +0200 Subject: [PATCH 218/266] QmlDesigner: Expand and hide root item Hide and expand root item in the tree model of the connections editor search result. Change-Id: If6591077fee59542153f2167dfd43c8132b66c95 Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 11 ---------- .../connectionseditor/MyTreeViewDelegate.qml | 22 +++++-------------- .../connectionseditor/SuggestionPopup.qml | 13 ++++++----- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 8b0404353b2..3b0f0fdaadd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -16,17 +16,6 @@ Column { property var backend - - /* replaced by ConnectionModelStatementDelegate defined in C++ - enum ActionType { - CallFunction, - Assign, - ChangeState, - SetProperty, - PrintMessage, - Custom - } */ - y: StudioTheme.Values.popupMargin width: parent.width spacing: root.verticalSpacing diff --git a/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml index bccb6c645d6..366860b6757 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/MyTreeViewDelegate.qml @@ -9,36 +9,24 @@ import StudioTheme as StudioTheme T.TreeViewDelegate { id: control hoverEnabled: true - implicitWidth: 200 implicitHeight: 30 - - //implicitWidth: leftMargin + __contentIndent + implicitContentWidth + rightPadding + rightMargin - //implicitHeight: Math.max(indicator ? indicator.height : 0, implicitContentHeight) * 1.25 - indentation: 12 - //leftMargin: 4 - //rightMargin: 4 - //spacing: 4 - - //topPadding: contentItem ? (height - contentItem.implicitHeight) / 2 : 0 leftPadding: control.leftMargin + control.__contentIndent - //required property int row - //required property var model + readonly property int customDepth: control.depth - 1 + readonly property real __contentIndent: !control.isTreeNode ? 0 - : (control.depth * control.indentation) + : (control.customDepth * control.indentation) + (control.indicator ? control.indicator.width + control.spacing : 0) indicator: Item { - readonly property real __indicatorIndent: control.leftMargin + (control.depth * control.indentation) - - x: __indicatorIndent + x: control.leftMargin + (control.customDepth * control.indentation) width: 30 height: 30 Text { - id: caret + id: icon font.family: StudioTheme.Constants.iconFont.family font.pixelSize: StudioTheme.Values.smallIconFontSize color: control.hovered ? "#111111" : "white" // TODO colors diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index a4a1789eb55..eae421e927a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -168,12 +168,13 @@ Controls.Popup { clip: true model: root.treeModel - // This is currently a workaround and should be cleaned up. Calling - // expandRecursively every time the filter changes is performance wise not good. - //Connections { - // target: proxyModel - // function onFilterChanged() { treeView.expandRecursively() } - //} + onLayoutChanged: function() { + treeView.expand(0) + } + + rowHeightProvider: function(row) { + return (row <= 0) ? 0 : -1 + } delegate: MyTreeViewDelegate { id: treeViewDelegate From 54b0c2d4359f99b6348a4a1b1809b91938b702a2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 16:25:59 +0200 Subject: [PATCH 219/266] QmlDesigner: Add another test to ConnectionEditor This covers field expressions with more then two members. Change-Id: Idccfac607f72ff9aa78ed3b8da560f7d7a8e694d Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Ali Kianian --- .../connectioneditor/tst_connectioneditor.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp index 7df48b92f37..61e19d1253d 100644 --- a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -35,6 +35,7 @@ private slots: void parseConsoleLog(); void parseCallFunction(); void parseEmpty(); + void parseComplexCondition(); QByteArray countOne(); private: @@ -337,6 +338,42 @@ void tst_ConnectionEditor::parseEmpty() 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); From a1f773bb6675c43519251d34f64ea116a4177aae Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 10:24:11 +0200 Subject: [PATCH 220/266] QmlDesigner: Only show valid kits with valid Qt version Task-number: QDS-10137 Change-Id: I582d714b2f4b670ca42b2d6e734489efe0efbf75 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../qmldesigner/components/toolbar/toolbarbackend.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 99c46b43b33..f350bd276d5 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 @@ -617,8 +618,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 From 17da102695d707f0ad8881ac542c6c0fdc72b536 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 09:31:49 +0200 Subject: [PATCH 221/266] QmlDesigner: Fix crash on exit Change-Id: If02837c0af08b037a10e0f91172eafb757e0c81f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../qmldesigner/components/connectioneditor/connectionmodel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 28be4827325..873750b2af6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -768,6 +768,8 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) qDebug() << Q_FUNC_INFO << i << model; QTC_ASSERT(model, return ); + if (!model->connectionView()->isAttached()) + return; SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); From 902a1206dda91d9d9db445b172e00d86fe903e7a Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 4 Sep 2023 12:44:32 +0300 Subject: [PATCH 222/266] Doc: Add note about Qt Quick Studio Modules Task-number: QDS-9586 Change-Id: I0550a3ebcfc7bab6c00bc8ada3497c4d6f880030 Reviewed-by: Thomas Hartmann Reviewed-by: Leena Miettinen --- doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc | 3 +++ 1 file changed, 3 insertions(+) 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 From dc05298696d4f9ce3263ff9c2d1a88f560f44c03 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Sep 2023 12:54:33 +0200 Subject: [PATCH 223/266] QmlDesigner: Fix superfluous '.' in condition editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iaad4ead4bd170adc9b00c215ba93e01cfb7bf03b Reviewed-by: Henning Gründl --- .../components/connectioneditor/connectionmodel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 873750b2af6..693829973aa 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -1956,7 +1956,8 @@ ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement ConnectionEditorStatements::Variable variable; variable.nodeId = list.first(); - variable.propertyName = list.last(); + if (list.count() > 1) + variable.propertyName = list.last(); return variable; } else if (token.type == Literal) { return parseTextArgumentComparativeStatement(token.value); From 803bb23bdb269b1d6eb4d8a98e0570ab92600d94 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Sep 2023 12:56:16 +0200 Subject: [PATCH 224/266] QmlDesigner: Add missing emit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I81bb36f586dd596c03bfe0fe8fefa543d6333397 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index f350bd276d5..0c027a99d5b 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -549,6 +549,7 @@ void ToolBarBackend::updateDocumentModel() m_openDocuments.append(entry->displayName()); emit openDocumentsChanged(); + emit documentIndexChanged(); } int ToolBarBackend::documentIndex() const From 6ec68ca98a0b935b2f87add132f6552e0dd5fb5e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Sep 2023 12:58:03 +0200 Subject: [PATCH 225/266] QmlDesigner: Properly handle empty source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I46550e4210b966db26e719f6eec10d0eb3243d91 Reviewed-by: Henning Gründl --- .../connectioneditor/connectionmodel.cpp | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 693829973aa..09ea8f70d88 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -737,6 +737,8 @@ int ConnectionModelBackendDelegate::currentRow() const QString removeOnFromSignalName(const QString &signal) { + if (signal.isEmpty()) + return {}; QString ret = signal; ret.remove(0, 2); ret[0] = ret.at(0).toLower(); @@ -903,29 +905,35 @@ void ConnectionModelBackendDelegate::setupHandlerAndStatements() QTC_ASSERT(model, return ); SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); - m_handler = ConnectionEditorEvaluator::parseStatement(signalHandlerProperty.source()); - - const QString statementType = QmlDesigner::ConnectionEditorStatements::toDisplayName(m_handler); - - if (statementType == ConnectionEditorStatements::EMPTY_DISPLAY_NAME) { + if (signalHandlerProperty.source().isEmpty()) { 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(); + m_handler = ConnectionEditorStatements::EmptyBlock(); } else { - m_actionType = ConnectionModelStatementDelegate::Custom; + 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 From 6744c8e081cf0168fa734864ee248c7dd2630374 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Sep 2023 12:55:41 +0200 Subject: [PATCH 226/266] QmlDesigner: Move update to extra function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I66cfef4ecbc1276b816fcd8397a67e8a99039813 Reviewed-by: Henning Gründl --- .../components/connectioneditor/connectionmodel.cpp | 7 +++++-- .../components/connectioneditor/connectionmodel.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 09ea8f70d88..242347be194 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -765,9 +765,12 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) //setup - ConnectionModel *model = qobject_cast(parent()); + update(); +} - qDebug() << Q_FUNC_INFO << i << model; +void ConnectionModelBackendDelegate::update() +{ + ConnectionModel *model = qobject_cast(parent()); QTC_ASSERT(model, return ); if (!model->connectionView()->isAttached()) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 59b92a405e9..850447de654 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -270,6 +270,8 @@ public: Q_INVOKABLE void addElse(); Q_INVOKABLE void removeElse(); + void update(); + signals: void currentRowChanged(); void actionTypeChanged(); From 6c17805ddc0f7c94298c9e253d298f87bad6490a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 9 Sep 2023 11:53:32 +0200 Subject: [PATCH 227/266] Utils: Improve union layout It is undefined behavior to access a uninitialized part of a union but in this case it is not because both member have the same start member. ControlBlock is in both at the address with the same layout and so it is allowed by the C++ standard. Change-Id: I3a165f67030d6726d2cb0cfbcbfb2622bcf7dcc4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 3 +++ src/libs/utils/smallstringlayout.h | 34 ++++++++++++++++++------------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 2ef565bde2e..16e872d5afa 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -425,7 +425,10 @@ public: 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); } diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index a7f4fd3a188..03e764dfee3 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 { @@ -82,7 +86,7 @@ struct alignas(16) StringDataLayout 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} {} @@ -130,18 +134,18 @@ struct alignas(16) StringDataLayout #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 @@ -158,7 +162,7 @@ struct alignas(16) StringDataLayout 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 From 74222a76f47ba0bc463c765af237cd5ccb28f722 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Sep 2023 10:29:15 +0200 Subject: [PATCH 228/266] QmlDesigner: Add QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT in qml2puppet Task-number: QDS-10255 Change-Id: I6d9698a77c1dfae9345d70a1edb40210d17c4ba7 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/puppetenvironmentbuilder.cpp | 6 ++++++ src/plugins/qmldesigner/puppetenvironmentbuilder.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index fbf76b96630..f0545adadba 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -52,6 +52,7 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const addImportPaths(); addCustomFileSelectors(); addDisableDeferredProperties(); + addResolveUrlsOnAssignment(); qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); @@ -250,6 +251,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; From a94ab799f976fc1931ea5477fd3d7d560d3cb9b7 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Sep 2023 14:32:47 +0200 Subject: [PATCH 229/266] QmlDesigner: Add line endings to source The line-endings have to be correct, because the rewriter only fixes the indentation. Change-Id: If08f3b1bff8f35d63ee51521e6b07c7017fa3cf0 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectioneditor/connectioneditorstatements.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp index 571f2bf18be..5fbad9acadb 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorstatements.cpp @@ -229,10 +229,10 @@ struct JSOverload value += JSOverload()(conditional.ok); if (!isEmptyStatement(conditional.ko)) { - value += "} else {\n"; + value += "\n} else {\n"; value += JSOverload()(conditional.ko); } - value += "}"; + value += "\n}"; return value; } From 97a312aad375d54fa5c5df4362e28c4576bf9db3 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 11 Sep 2023 17:27:29 +0200 Subject: [PATCH 230/266] QmlDesigner: Fix crash on NodeProperty Reparenting Task-number: QDS-10645 Change-Id: I67a0fd5618ad020bd83d2e62e9e1f278bd1696f1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke Reviewed-by: --- .../qmldesigner/designercore/model/model.cpp | 41 ++++++++++--------- .../qmldesigner/designercore/model/model_p.h | 3 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 38c20a65952..d511ae5d64d 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -989,7 +989,8 @@ void ModelPrivate::notifyVariantPropertiesChanged(const InternalNodePointer &nod } void ModelPrivate::notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractProperty *newPropertyParent, + const InternalNodePointer &newParent, + const PropertyName &newPropertyName, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange) @@ -998,12 +999,12 @@ 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->name(), - newPropertyParent->propertyOwner(), + if (!newPropertyName.isEmpty() && newParent && newParent->isValid) { + newProperty = NodeAbstractProperty(newPropertyName, + newParent, m_model, view); } @@ -1394,6 +1395,22 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, const TypeName &dynamicTypeName) { AbstractView::PropertyChangeFlags propertyChange = AbstractView::NoAdditionalChanges; + + InternalNodeAbstractPropertyPointer oldParentProperty(childNode->parentProperty()); + InternalNodePointer oldParentNode; + PropertyName oldParentPropertyName; + if (oldParentProperty && oldParentProperty->isValid()) { + oldParentNode = childNode->parentProperty()->propertyOwner(); + oldParentPropertyName = childNode->parentProperty()->name(); + } + + notifyNodeAboutToBeReparent(childNode, + parentNode, + name, + oldParentNode, + oldParentPropertyName, + propertyChange); + InternalNodeAbstractProperty *newParentProperty = nullptr; if (auto property = parentNode->property(name)) { newParentProperty = property->to(); @@ -1406,22 +1423,8 @@ void ModelPrivate::reparentNode(const InternalNodePointer &parentNode, propertyChange |= AbstractView::PropertiesAdded; } - InternalNodeAbstractPropertyPointer oldParentProperty(childNode->parentProperty()); - InternalNodePointer oldParentNode; - PropertyName oldParentPropertyName; - if (oldParentProperty && oldParentProperty->isValid()) { - oldParentNode = childNode->parentProperty()->propertyOwner(); - oldParentPropertyName = childNode->parentProperty()->name(); - } - Q_ASSERT(newParentProperty); - notifyNodeAboutToBeReparent(childNode, - newParentProperty, - oldParentNode, - oldParentPropertyName, - propertyChange); - if (newParentProperty) { childNode->setParentProperty( newParentProperty->toShared()); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 20b83a564b6..8ba7c69b595 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -149,7 +149,8 @@ public: void notifyNodeCreated(const InternalNodePointer &newNode); void notifyNodeAboutToBeReparent(const InternalNodePointer &node, - const InternalNodeAbstractProperty *newPropertyParent, + const InternalNodePointer &newParent, + const PropertyName &newPropertyName, const InternalNodePointer &oldParent, const PropertyName &oldPropertyName, AbstractView::PropertyChangeFlags propertyChange); From 59ecf10f06366939826b41170f236d1adffb126b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Tue, 12 Sep 2023 09:12:00 +0200 Subject: [PATCH 231/266] qml2puppet: fix QT_VERSION_MAJOR is empty in single builds Task-number: QDS-10640 Change-Id: I02d384337218070b2f934de0bd85bc93e7cb85ca Reviewed-by: Thomas Hartmann --- src/tools/qml2puppet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 271d16023ed..0aca34c1e8f 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -17,7 +17,7 @@ if (NOT QT_CREATOR_API_DEFINED) include(QtCreatorIDEBranding) include(QtCreatorAPI) - find_package(Qt${QT_VERSION_MAJOR} + find_package(Qt6 COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml REQUIRED ) From c5d5a564c85c13ab700c04e82067194f336c206c Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 11 Sep 2023 10:05:08 +0300 Subject: [PATCH 232/266] Doc: Document snapping Document the new snapping function. Also small typo fix. Task-number: QDS-10526 Change-Id: I1f11e6485c6445e1357e9cd042205428c7bd1270 Reviewed-by: Inkamari Harjula Reviewed-by: Miikka Heikkinen --- .../images/icons/snapping-3d-conf.png | Bin 0 -> 1912 bytes .../images/icons/snapping-3d.png | Bin 0 -> 1854 bytes .../qtdesignstudio-3d-editor.qdoc | 41 +++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 doc/qtdesignstudio/images/icons/snapping-3d-conf.png create mode 100644 doc/qtdesignstudio/images/icons/snapping-3d.png 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 0000000000000000000000000000000000000000..bf57f857be199e9b5787d9f09fa354f71a7b6902 GIT binary patch literal 1912 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4kiW$h6xih%orFLI14-?iy0XBj({-ZRBb+K z1_n06%#etZ2wxwokg&dz0$52&wylyQ$U=n(-v9;Y{GwC^OFcu~WCH_31#?TiM2i#?b5mm-1tS9^ zV|@c-eIsLC0}CrtGb>|r1t?ImQ?MyYNwW%aaf4b`l#*tvlu=SrV5P5LUS6(OZmgGI zl&)`RX=$l%V5Dzkq+67drdwQ@SCUwvn^&w1Gr=XbIJqdZpd>RtPXT0NVp4u-iLH_n z)YyvL0=Thx#n5m{&d=4aNG#Ad)H4A23GCUFWVpJ5(xM!&kGF7t6Oq&;Z_uvxR#aRS6v)ZS&*t9lvXKNJYO4f_ zHC+QsT?2~{14}DILn}iAZ3AN~0|SUs-~5!!v`TDBjSZ{}O;D7kmL#SmmLw8XoRVyn zmS2>cSYoS`nVXoNs$YpKqU6%`B5sj13c0jFJ$h`4?rT zXXYj5Ae#zuRZ3=xRf<_!lDWBgs%~1Mk%4ZCnTe@xQeskyu34gqxmj9TQlepsF(@^I z6Nr^ZW^qY=Q6)5g1g924Svi>s;51>Cm<&qmwo1vMNKQ>rf(mD12@j%dhUzcRNX^3= z`4HQS^V3So6N^$E(^K9Z=B!*o<;oY3s~{(vKmdKI;Vst0FxShhX4Qo literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/snapping-3d.png b/doc/qtdesignstudio/images/icons/snapping-3d.png new file mode 100644 index 0000000000000000000000000000000000000000..a434305e257346110d09e2164ec55eced830993b GIT binary patch literal 1854 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4kiW$h6xih%orFLI14-?iy0XBj({-ZRBb+K z1_svMnIRD+5xzcF$@#f@i7EL>sd^Q;1q>iyV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTP3i$ zR(Zu%AYpwa1+bEmY+EHqkcA2nz5xo(`9-M;mU@P|$p!|73g(u2i54j)=BCCv3PuJ- z#`*@v`bNgO1{PMPW>&`L3Q(YAr(jc*l4cd;;s&*>C?(BSDWjyMz)D}gyu4hm+*mKa zC|%#s($Z4jz)0W7NVg~@O}Dr*uOzWTH?LS3W`avc7m0xRdD)WnkfqLBRj99T>Rz?`gLWTI!Tpb?&#my%yztcj!{)g`ec)m8}< zYq|!Ox&{^@29{QahE|3K+6Kl}1_ltNzWFJcX_eTN8W~!dnxiO9ElEsCEJ-A&I3?LC zEx#x?vBXv>GdD3kRlguFT}c5Rj8@M1Ir&At`6;QkO2%MU`1)FT<`tJD<|U_ky4WgJ zRKk7Sm;_N8l>nNr=+BsB`2CDnL~Zy>ucqbpIi!x zS(yHm%oHn2V@pFrb7M2zL=y`m-83T;LtP8wL{nWe^F*^0Ln9+Y12apQ{r*Ln>6v+n zIb@py%5b10VdaroT#{c@3C$6~sfAEhPNo7l6<8%EgHpGxQZgu7Q&W_n!kJh?fH)sO zj4jVd&BGhr5M{;rX(i=}MX8SIsd*&?laoF!#}_A-;deT5kq2=THm9Q`KZr8YoDL1v zUta`}*4CijR+u zo}8@Sx$()}+uPse<@r2%^e8PYZN6QtRlx^^I`11dZ`S|+%iY_)zy9Bs?_bwOY-H+h z`z~+)si4@|l!J@&>>R`7pPN!EzU18BC;LKdN*VkKKUcSCIT3tco q#KCiXCRa~gGSb)gkBpSm;AXh+bj})%J;GL?8qU+z&t;ucLK6UQ3xZ7m literal 0 HcmV?d00001 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 From ed6f870a814a33168501c6f55f21c926f70049db Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 12 Sep 2023 11:14:58 +0200 Subject: [PATCH 233/266] QmlDesigner: Add operators to expression builder * Show operator fills when cursor is placed at position where operator is required * Convert operators from real syntax to text version Task-number: QDS-10630 Task-number: QDS-10638 Change-Id: Iffceb24024b7cde9a93c2acd0033fedd54e2ee32 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 2 + .../connectionseditor/ExpressionBuilder.qml | 62 +++++++++++++++++++ .../qmldesigner/connectionseditor/Pill.qml | 6 +- .../connectionseditor/SuggestionPopup.qml | 38 +++++++----- .../imports/StudioTheme/Values.qml | 1 + .../connectioneditor/connectionmodel.cpp | 13 ++++ .../connectioneditor/connectionmodel.h | 2 + 7 files changed, 108 insertions(+), 16 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 3b0f0fdaadd..1ce21ad0a68 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -181,10 +181,12 @@ Column { color: StudioTheme.Values.themeToolbarBackground Text { + width: parent.width - 8 // twice the editor button margins anchors.centerIn: parent text: backend.source color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.myFontSize + wrapMode: Text.WordWrap } HelperWidgets.AbstractButton { diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index 266ae8d48ce..889d62debc0 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -87,10 +87,68 @@ Rectangle { newTextInput.index = index newTextInput.visible = true newTextInput.forceActiveFocus() + + if (!root.shadowPillVisible) + popup.showOperators = root.conditionListModel.operatorAllowed(index) + // Open suggestion popup popup.open() } + ListModel { + id: __operatorModel + function convertValueToName(value) { + for (var i = 0; i < __operatorModel.count; ++i) { + let element = __operatorModel.get(i) + if (element.value === value ) + return element.name + } + + return value + } + + ListElement { + name: "AND" + value: "&&" + tooltip: QT_TR_NOOP("This is AND (&&)") + } + ListElement { + name: "OR" + value: "||" + tooltip: QT_TR_NOOP("This is OR (||)") + } + ListElement { + name: "EQUAL" + value: "===" + tooltip: QT_TR_NOOP("This is EQUAL (===)") + } + ListElement { + name: "NOT EQUAL" + value: "!==" + tooltip: QT_TR_NOOP("This is NOT EQUAL (!==)") + } + ListElement { + name: "GREATER" + value: ">" + 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 !== "" @@ -197,6 +255,8 @@ Rectangle { Pill { id: pill + operatorModel: __operatorModel + onRemove: function() { // If pill has focus due to selection or keyboard navigation if (pill.focus) @@ -289,6 +349,8 @@ Rectangle { y: root.height width: root.width + operatorModel: __operatorModel + //onOpened: console.log("POPUP opened") //onClosed: console.log("POPUP closed") diff --git a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml index aa46b71cc6b..bbaefb16e4c 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Pill.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Pill.qml @@ -31,6 +31,7 @@ FocusScope { signal submit(int cursorPosition) readonly property int margin: StudioTheme.Values.flowPillMargin + property var operatorModel width: { if (root.isEditable()) { @@ -88,7 +89,7 @@ FocusScope { return 0 } - radius: 4 + radius: StudioTheme.Values.flowPillRadius Row { id: row @@ -102,7 +103,8 @@ FocusScope { font.pixelSize: StudioTheme.Values.baseFontSize color: root.isShadow() ? StudioTheme.Values.themeTextSelectedTextColor : StudioTheme.Values.themeTextColor - text: root.value + text: root.isOperator() ? root.operatorModel.convertValueToName(root.value) + : root.value anchors.verticalCenter: parent.verticalCenter } diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index eae421e927a..e12342e8340 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls as Controls +import HelperWidgets as HelperWidgets import StudioTheme as StudioTheme import StudioControls as StudioControls import ConnectionsEditorEditorBackend @@ -20,6 +21,8 @@ Controls.Popup { signal exited(var value) property alias searchActive: search.activeFocus + property bool showOperators: false + property alias operatorModel: repeater.model function reset() { search.clear() @@ -43,6 +46,7 @@ Controls.Popup { StudioControls.SearchBox { id: search width: parent.width + visible: !root.showOperators onSearchChanged: function(value) { root.treeModel.setFilter(value) @@ -51,12 +55,10 @@ Controls.Popup { Controls.StackView { id: stack - + visible: !root.showOperators width: parent.width height: currentItem?.implicitHeight - clip: true - initialItem: mainView } @@ -210,7 +212,7 @@ Controls.Popup { } Item { - visible: false + visible: root.showOperators width: stack.width height: flow.childrenRect.height + 2 * StudioTheme.Values.flowMargin @@ -224,30 +226,38 @@ Controls.Popup { Repeater { id: repeater - // TODO actual value + tooltip - model: ["AND", "OR", "equal", "not equal", "greater", "less", "greater then", "less then"] - Rectangle { - width: textItem.contentWidth + 14 - height: 26 + 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: 4 + radius: StudioTheme.Values.flowPillRadius border { color: "white" width: mouseArea.containsMouse ? 1 : 0 } - MouseArea { + HelperWidgets.ToolTipArea { id: mouseArea hoverEnabled: true anchors.fill: parent + tooltip: delegate.tooltip + + onClicked: root.select(delegate.value) } Text { id: textItem - font.pixelSize: 12 - color: "white" - text: modelData + font.pixelSize: StudioTheme.Values.baseFontSize + color: StudioTheme.Values.themeTextColor + text: delegate.name anchors.centerIn: parent } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 0e5b4e2c7b8..43badf91300 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -243,6 +243,7 @@ QtObject { 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 diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 242347be194..cf166dfc3ae 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -1774,6 +1774,19 @@ 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 && m_tokens[tokenIdx].type != Operator) + return true; + + return false; +} + void ConditionListModel::internalSetup() { setInvalid(tr("No Valid Condition")); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 850447de654..706fceed2ff 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -146,6 +146,8 @@ public: QString error() const; int errorIndex() const; + Q_INVOKABLE bool operatorAllowed(int cursorPosition); + signals: void validChanged(); void emptyChanged(); From 74640c6d5b42c2d431f1064fee7057139896b9c8 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Tue, 12 Sep 2023 12:44:58 +0200 Subject: [PATCH 234/266] CMakePM: Fix crash Amends 6528bd3eeda563d7d692c54b2b7035488f5f8974 Fixes: QTCREATORBUG-29587 Change-Id: I62e469a461dd730858e05d0309151dba4e9fe93c Reviewed-by: Eike Ziller --- src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 25120aea9e9..733bcb48dfa 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -661,7 +661,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; From f979ca36ddc81c2d6efb13c669abfb35dcde2548 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 11 Sep 2023 08:36:08 +0300 Subject: [PATCH 235/266] Doc: Add page for Qt Insight view Task-number: QDS-10551 Change-Id: Iaf918b71082a62dc9a30564df2ac38e1db82f433 Reviewed-by: Leena Miettinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tanja Remes --- doc/qtdesignstudio/images/qt-insight-view.png | Bin 0 -> 42685 bytes .../src/qtdesignstudio-toc.qdoc | 1 + .../src/views/studio-qtinsight.qdoc | 28 ++++++++++++++++++ .../src/views/studio-texture-editor.qdoc | 2 +- .../src/views/studio-workspaces.qdoc | 2 +- 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 doc/qtdesignstudio/images/qt-insight-view.png create mode 100644 doc/qtdesignstudio/src/views/studio-qtinsight.qdoc diff --git a/doc/qtdesignstudio/images/qt-insight-view.png b/doc/qtdesignstudio/images/qt-insight-view.png new file mode 100644 index 0000000000000000000000000000000000000000..7e814f12342dd1e136143d6af653918953be8934 GIT binary patch literal 42685 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 IbxsLQ0B)HP(f|Me literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index e6fc6e4a602..48512d5eade 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} 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 From 336912cf2ff30ecf69939d48efe0da2d8c571ede Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 12 Sep 2023 16:00:07 +0300 Subject: [PATCH 236/266] QmlDesigner: Fix crash when taking 2D view screenshot If 2D view was hidden, taking screenshot crashed. Fixes: QDS-10657 Change-Id: I96fe7a37793a902d517ad891fd7fe1914e681c30 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/formeditor/formeditorwidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index ceb0356c1d2..2e7a5b2c887 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -548,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); From f33f55659805e138b1d8db8c2cd5e7b001582d79 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 12 Sep 2023 15:50:18 +0200 Subject: [PATCH 237/266] Doc: Remove acknowledgment of three.js, no longer shipped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I857840f4bfe4ccac6d18f03efbb70e0ea9aafdc8 Reviewed-by: Kai Köhne --- doc/qtcreator/src/overview/creator-acknowledgements.qdoc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index 7f607374759..e9deb37f3d9 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -578,14 +578,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 From 92363e1a49bd0ba8396b8c12229257fea543405e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 12 Sep 2023 14:06:45 +0300 Subject: [PATCH 238/266] QmlDesigner: Set the correct color for tooltips A static palette is set for the QToolTip to apply the theme color. Change-Id: I9d7dd2290c1ac4feb5cdebc8388a611bc2b38add Reviewed-by: Thomas Hartmann --- src/plugins/qmldesignerbase/studio/studiostyle_p.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp index b13310f27ad..c1e1dece05f 100644 --- a/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp +++ b/src/plugins/qmldesignerbase/studio/studiostyle_p.cpp @@ -10,6 +10,7 @@ #include #include +#include using namespace Utils; using namespace QmlDesigner; @@ -76,7 +77,12 @@ StudioStylePrivate::StudioStylePrivate(StudioStyle *q) 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 From 251e1cca4c9dbb69e708aaf31397a6dc74556db0 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 12 Sep 2023 09:40:16 +0300 Subject: [PATCH 239/266] QmlDesigner: Make shortcut accessible from the MenuItem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shortcut property is accessible directly for the MenuItem. Change-Id: I6d956859411a02dcd9c9610cfa4ccffb2e7867e7 Reviewed-by: Henning Gründl Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../imports/StudioControls/MenuItem.qml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 } } } From 781e9dd5e3c67088d974744f5b1f47ac01a5693e Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Tue, 12 Sep 2023 16:02:31 +0300 Subject: [PATCH 240/266] Doc: Unify product naming Unify naming for Qt Online Installer and Qt Maintenance Tool. Ass macros for theses as well. Task-number: QTBUG-116573 Change-Id: I90247956300607b852a17df96aa667bf55805e36 Reviewed-by: Leena Miettinen Reviewed-by: Qt CI Patch Build Bot --- doc/config/macros.qdocconf | 2 ++ .../src/conan/creator-projects-conan.qdoc | 2 +- .../creator-only/creator-debugger-setup.qdoc | 2 +- doc/qtcreator/src/howto/creator-telemetry.qdoc | 2 +- .../linux-mobile/creator-embedded-platforms.qdoc | 4 ++-- doc/qtcreator/src/mcu/creator-mcu-dev.qdoc | 2 +- .../overview/creator-only/creator-configuring.qdoc | 4 ++-- .../creator-only/creator-getting-started.qdoc | 4 ++-- .../creator-only/creator-mobile-platforms.qdoc | 4 ++-- .../creator-only/creator-projects-compilers.qdoc | 2 +- .../creator-only/creator-projects-qt-versions.qdoc | 4 ++-- .../creator-projects-settings-overview.qdoc | 4 ++-- .../src/qtquick/creator-only/qtquick-creating.qdoc | 4 ++-- .../src/webassembly/creator-webassembly.qdoc | 4 ++-- doc/qtcreatordev/src/getting-and-building.qdoc | 4 ++-- .../src/qtbridge/qtbridge-overview.qdoc | 2 +- .../src/qtbridge/qtbridge-ps-setup.qdoc | 2 +- .../src/qtbridge/qtbridge-sketch-setup.qdoc | 2 +- .../src/qtbridge/qtbridge-xd-setup.qdoc | 2 +- .../src/qtdesignstudio-getting-started.qdoc | 2 +- .../src/qtdesignstudio-installation.qdoc | 14 +++++++------- 21 files changed, 37 insertions(+), 35 deletions(-) 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 905e486c9ea..d91ce1cc575 100644 --- a/doc/qtcreator/src/conan/creator-projects-conan.qdoc +++ b/doc/qtcreator/src/conan/creator-projects-conan.qdoc @@ -28,7 +28,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 d457abc4f97..8c2c61a413a 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-telemetry.qdoc b/doc/qtcreator/src/howto/creator-telemetry.qdoc index 959887080c4..b3cad6bf004 100644 --- a/doc/qtcreator/src/howto/creator-telemetry.qdoc +++ b/doc/qtcreator/src/howto/creator-telemetry.qdoc @@ -77,7 +77,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 Specifying 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 8f7a7378392..51746e8f901 100644 --- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc @@ -19,10 +19,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-only/creator-configuring.qdoc b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc index 95906339121..456777f1542 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 c6d689547b6..58ad65844ac 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc @@ -16,10 +16,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 9d3af4d82c7..8cddb9375eb 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 fff52cf5707..0b27f1e9316 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. Select the kits to use in the \uicontrol {Configure Projects} view when you open a project for the first time. At least one kit must be diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc index 55458349642..9bc2c37c1ce 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/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/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-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-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/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-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 From 034f107851274a37eeaee7a6206b66a3b7f9d10b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Sep 2023 20:11:47 +0200 Subject: [PATCH 241/266] QmlDesigner: Fix update issue The cache has to be cleared fist Change-Id: Iba2a6035becf0dc60f2bc95b94dd1b6ef59ad4d3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../components/connectioneditor/propertytreemodel.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 476bdad1c1f..862390986f0 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -91,10 +91,11 @@ const std::vector priorityListSignals = {"clicked", "exited", "canceled", "triggered", - "stateChanged", + "pressAndHold", "started", "stopped", "finished" + "stateChanged", "enabledChanged", "visibleChanged", "opacityChanged", @@ -136,6 +137,8 @@ void PropertyTreeModel::resetModel() { beginResetModel(); + m_sortedAndFilteredPropertyNamesSignalsSlots.clear(); + m_indexCache.clear(); m_indexHash.clear(); m_indexCount = 0; @@ -148,8 +151,6 @@ void PropertyTreeModel::resetModel() }); } - m_sortedAndFilteredPropertyNamesSignalsSlots.clear(); - endResetModel(); testModel(); } From 72a45dc2fb241316feb9302f843165da97a8dfc5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Sep 2023 20:09:49 +0200 Subject: [PATCH 242/266] QmlDesigner: Fix update issues and reflection * Whenever there are changes on the model we have to update the delegate. * Avoid reflection on source changes * If the target changes the order of the model changes and we have to sync selection Change-Id: I34a87cd5ac329745bf48bb739fcf7555ee46b1c4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectioneditor/connectionmodel.cpp | 68 ++++++++++++++----- .../connectioneditor/connectionmodel.h | 16 ++++- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index cf166dfc3ae..7f0db656005 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -81,6 +81,7 @@ void ConnectionModel::resetModel() addModelNode(modelNode); } endResetModel(); + m_delegate->update(); } SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const @@ -332,6 +333,8 @@ void ConnectionModel::addConnection() } newNode.signalHandlerProperty("onClicked").setSource(source); + + selectProperty(newNode.signalHandlerProperty("onClicked")); }); } } @@ -393,6 +396,32 @@ 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); @@ -559,13 +588,10 @@ QHash ConnectionModel::roleNames() const } 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) + : 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(); @@ -760,16 +786,17 @@ void ConnectionModelBackendDelegate::setCurrentRow(int i) m_currentRow = i; - m_propertyTreeModel.resetModel(); - m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); - - //setup - 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 ); @@ -797,9 +824,6 @@ void ConnectionModelBackendDelegate::update() setSource(signalHandlerProperty.source()); - qDebug() << Q_FUNC_INFO - << removeOnFromSignalName(QString::fromUtf8(signalHandlerProperty.name())); - m_signalDelegate.setup(targetNodeName, removeOnFromSignalName(QString::fromUtf8(signalHandlerProperty.name()))); @@ -970,14 +994,14 @@ void ConnectionModelBackendDelegate::handleTargetChanged() const PropertyName handlerName = addOnToSignalName(m_signalDelegate.name()).toUtf8(); - qDebug() << Q_FUNC_INFO << m_signalDelegate.id() << handlerName; - 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 @@ -996,8 +1020,15 @@ void ConnectionModelBackendDelegate::handleTargetChanged() 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() @@ -1051,12 +1082,14 @@ void ConnectionModelBackendDelegate::commitNewSource(const QString &source) 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; @@ -1637,6 +1670,7 @@ void ConditionListModel::appendToken(const QString &value) void ConditionListModel::removeToken(int index) { + QTC_ASSERT(index < m_tokens.count(), return ); beginRemoveRows({}, index, index); m_tokens.remove(index, 1); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 706fceed2ff..bef5b79637d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -27,6 +27,9 @@ class ConnectionModel : public QStandardItemModel Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT) +public: + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + public: enum ColumnRoles { TargetModelNodeRow = 0, @@ -63,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); @@ -87,6 +98,7 @@ private: bool m_lock = false; QString m_exceptionError; ConnectionModelBackendDelegate *m_delegate = nullptr; + int m_currentIndex = -1; }; class ConditionListModel : public QAbstractListModel @@ -272,6 +284,7 @@ public: Q_INVOKABLE void addElse(); Q_INVOKABLE void removeElse(); + void setCurrentRow(int i); void update(); signals: @@ -283,8 +296,6 @@ signals: private: int currentRow() const; - void setCurrentRow(int i); - void handleException(); bool hasCondition() const; bool hasElse() const; @@ -324,6 +335,7 @@ private: QString m_source; PropertyTreeModel m_propertyTreeModel; PropertyListProxyModel m_propertyListProxyModel; + bool m_blockReflection = false; }; } // namespace QmlDesigner From 22009b69674a8679a1a90bcea4b9914538287f8b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Sep 2023 11:39:41 +0200 Subject: [PATCH 243/266] QmlDesigner: Add PropertyNameLiteral That hopefully makes it really hard to create a literal from dynamic or automatic memory. Only global string literals should be used. In header you should use: MakeHeaderPropertyViewLiteral(foo, "Foo"); Makes foo constexpr and provides internal linkage. In cpp files you should use: MakeHeaderPropertyViewLiteral(foo, "Foo"); Makes foo constexpr and provides external linkage. In a function scope you should use: MakeCppPropertyViewLiteral(foo, "Foo"); Makes foo constexpr but does not add it to the stack. All three provide a const QString& on demand. So no code is executed as the program is started. Change-Id: I924efbb1f5c2b2c811f3eeae042ff2eec8de3faf Reviewed-by: Aleksei German Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/modelfwd.h | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 59c5c467691..eedc46e3b56 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -7,11 +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; From 155036a784f262b8c00d45c9a33ba3e051760b1a Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 12 Sep 2023 15:42:14 +0200 Subject: [PATCH 244/266] Doc: Remove acknowledgment of ANGLE, no longer used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Qt 6 no longer uses ANGLE. Change-Id: I873adc0773a11b1b8d707ecd098aee549d4e6769 Reviewed-by: Kai Köhne Reviewed-by: Leena Miettinen --- .../overview/creator-acknowledgements.qdoc | 174 ------------------ 1 file changed, 174 deletions(-) diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index e9deb37f3d9..ac1c1c975be 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -386,180 +386,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, From 40273101c68f0346553c08ffe5b95ecf38f43cf5 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 13 Sep 2023 08:56:47 +0200 Subject: [PATCH 245/266] Doc: Update information about Qt Quick render backend Qt 6 works different than Qt 5. Remove references to ANGLE. Change-Id: Ie27922d7c9a26d29a1ad4a1f52daa3a1aff7b19a Reviewed-by: Leena Miettinen --- .../src/howto/creator-only/qtcreator-faq.qdoc | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index 28c431d397a..7cf86218e50 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -100,21 +100,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} From 83bf9df04309405aa75283285bae58e91b225d26 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 11 Sep 2023 17:07:22 +0200 Subject: [PATCH 246/266] Utils: Fix fix for string size It was not using the whole short string size. Change-Id: Id6f6d4d0241ef74dbc0fe766bc1e19e8671e0b94 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstringlayout.h | 40 +++++++++--------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 03e764dfee3..0d87bd101ba 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -90,33 +90,25 @@ struct alignas(16) StringDataLayout , reference{{string}, size, 0} {} - QT_WARNING_PUSH - QT_WARNING_DISABLE_CLANG("-Wunsafe-buffer-usage") 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) + for (size_type i = size; i < MaximumShortStringDataAreaSize; ++i) shortString[i] = 0; } #endif } else { control = {0, true, true}; -#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L - if (std::is_constant_evaluated()) { - for (size_type i = 0; i < sizeof(dummy); ++i) - dummy[i] = 0; - } -#endif - reference = {{string}, Size - 1, 0}; + reference = {{string}, size, 0}; } } - QT_WARNING_POP constexpr static size_type shortStringCapacity() noexcept { @@ -166,33 +158,25 @@ 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) + for (size_type i = size; i < MaximumShortStringDataAreaSize; ++i) shortString[i] = 0; } #endif } else { control = {0, true, true}; -#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L - if (std::is_constant_evaluated()) { - for (size_type i = 0; i < sizeof(dummy); ++i) - dummy[i] = 0; - } -#endif - reference = {{string}, Size - 1, 0}; + reference = {{string}, size, 0}; } } - QT_WARNING_POP void copyHere(const StringDataLayout &other) noexcept { From fc79d938efd83268970339635cfaa00ce2540bc5 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 11 Sep 2023 16:40:43 +0200 Subject: [PATCH 247/266] RemoteLinux: Fix deployment to root directory Skip mkdir in this case. Fixes: QTCREATORBUG-29597 Change-Id: I716e2703e3599a71306a9126e0a627a519398937 Reviewed-by: Reviewed-by: Jarek Kobus --- src/plugins/remotelinux/rsyncdeploystep.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 317b08bb77c..e32129fd299 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -89,8 +89,13 @@ GroupItem RsyncDeployStep::mkdirTask() { const auto setupHandler = [this](Process &process) { QStringList remoteDirs; - for (const FileToTransfer &file : std::as_const(m_files)) - remoteDirs << file.m_target.parentDir().path(); + for (const FileToTransfer &file : std::as_const(m_files)) { + const QString parentDir = file.m_target.parentDir().path(); + if (!parentDir.isEmpty()) + remoteDirs << parentDir; + } + if (remoteDirs.isEmpty()) + return SetupResult::StopWithDone; remoteDirs.sort(); remoteDirs.removeDuplicates(); process.setCommand({deviceConfiguration()->filePath("mkdir"), @@ -98,6 +103,7 @@ GroupItem RsyncDeployStep::mkdirTask() connect(&process, &Process::readyReadStandardError, this, [this, proc = &process] { handleStdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError())); }); + return SetupResult::Continue; }; const auto errorHandler = [this](const Process &process) { QString finalMessage = process.errorString(); From 1ae4b0559921e44079cd5d780b2f882d4945e424 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 13 Sep 2023 16:34:30 +0200 Subject: [PATCH 248/266] QmlDesigner: Fix crash in Expression Builder Change-Id: I8ef783c1407f06aea630ce111b3d4f02e195eff5 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/connectioneditor/connectionmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 7f0db656005..838e1d587dd 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -1815,7 +1815,7 @@ bool ConditionListModel::operatorAllowed(int cursorPosition) int tokenIdx = cursorPosition - 1; - if (tokenIdx >= 0 && m_tokens[tokenIdx].type != Operator) + if (tokenIdx >= 0 && tokenIdx < m_tokens.length() && m_tokens[tokenIdx].type != Operator) return true; return false; From 9f391acdd95c609457ffcddff5007fe5a1fea4c2 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 13 Sep 2023 16:40:11 +0200 Subject: [PATCH 249/266] QmlDesigner: Add focus handling to close popup Add proper focus handling to the expression builder in order to close the popup when e.g. clicking outside. Change-Id: I5ea8422c727b5fc6e47dc77866b709e4ec04e6fd Reviewed-by: Thomas Hartmann --- .../ConnectionsDialogForm.qml | 4 + .../connectionseditor/ExpressionBuilder.qml | 230 +++++++++--------- .../connectionseditor/SuggestionPopup.qml | 3 +- .../ConnectionPopupControlStyle.qml | 2 +- 4 files changed, 126 insertions(+), 113 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 1ce21ad0a68..7b43abc12be 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -20,6 +20,10 @@ Column { width: parent.width spacing: root.verticalSpacing + TapHandler { + onTapped: root.forceActiveFocus() + } + Row { spacing: root.horizontalSpacing diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index 889d62debc0..8964d53867d 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -34,7 +34,8 @@ Rectangle { width: 400 height: root.expressionHeight + 2 * StudioTheme.Values.flowMargin - color: root.style.background.idle + color: (focusScope.activeFocus || popup.searchActive) ? root.style.background.interaction + : root.style.background.idle border { color: root.conditionListModel.valid ? root.style.border.idle : StudioTheme.Values.themeError @@ -215,129 +216,133 @@ Rectangle { } } - Flow { - id: flow - - property int focusIndex: -1 - + FocusScope { + id: focusScope 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 = flow.childrenRect.height + onActiveFocusChanged: { + if (!focusScope.activeFocus && !popup.searchActive) + popup.close() } - Repeater { - id: repeater + Flow { + id: flow - onItemRemoved: function(index, item) { - if (!root.textInputActive()) - return + property int focusIndex: -1 - // Udpate the cursor position - if (index < newTextInput.index) - newTextInput.index = newTextInput.index - 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 = flow.childrenRect.height } - onItemAdded: function(index, item) { - if (!root.textInputActive()) - return + Repeater { + id: repeater - 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.+/ } - - //onActiveFocusChanged: { - // if (!newTextInput.activeFocus && !root.shadowPillVisible) { - // console.log("CLOSE POPUP") - // popup.close() - // } - //} - - 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) + onItemRemoved: function(index, item) { + if (!root.textInputActive()) return - let item = repeater.itemAt(previousIndex) - item.setCursorEnd() - item.forceActiveFocus() - popup.close() + // 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 { @@ -348,12 +353,15 @@ Rectangle { 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() diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index e12342e8340..511e92ced98 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -30,8 +30,9 @@ Controls.Popup { root.listModel.reset() } - closePolicy: Controls.Popup.NoAutoClose + closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent padding: 0 + focus: search.activeFocus background: Rectangle { implicitWidth: root.width diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml index 9aaea8dae2c..9a4d1194c67 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ConnectionPopupControlStyle.qml @@ -15,7 +15,7 @@ ControlStyle { idle: Values.themePopoutControlBackground_idle hover: Values.themePopoutControlBackground_hover globalHover: Values.themePopoutControlBackground_globalHover - interaction: Values.themeInteraction + interaction: Values.themeControlBackgroundInteraction disabled: Values.themePopoutControlBackground_disabled } From ffd23a68a29f9cd01bdfc8a5460791956307b4ec Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 13 Sep 2023 16:34:54 +0200 Subject: [PATCH 250/266] QmlDesigner: Remove QML output Change-Id: I40263a26304904a827a90f86272eacb2d1684a69 Reviewed-by: Thomas Hartmann --- .../qmldesigner/connectionseditor/StatementEditor.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml index f33316dfcd7..3e7e552a28f 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/StatementEditor.qml @@ -21,12 +21,6 @@ Column { //implicitWidth: Math.max(16, container.childrenRect.width + container.childrenRect.x) //implicitHeight: Math.max(16, container.childrenRect.height + container.childrenRect.y) - onActionTypeChanged: { - print("changed") - print(root.actionType) - print(ConnectionModelStatementDelegate.ChangeState) - } - // Call Function Row { visible: root.actionType === ConnectionModelStatementDelegate.CallFunction From 91029f975237de83c02b49272d989780c95d7d62 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 13 Sep 2023 16:47:48 +0200 Subject: [PATCH 251/266] QmlDesigner: Fix ComboBox not closing Change-Id: I562005d3102efafc52cc01b161aeaeba56581095 Reviewed-by: Thomas Hartmann --- .../imports/StudioControls/TopLevelComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 7c475175571..61483feadfb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -147,9 +147,9 @@ T.ComboBox { id: itemDelegate onClicked: { + comboBoxPopup.close() control.currentIndex = index control.activated(index) - comboBoxPopup.close() } width: control.width From 9bf4a084b8bd96d4e7bf0c47960d2cfd7401e211 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 13 Sep 2023 17:05:23 +0200 Subject: [PATCH 252/266] QmlDesigner: Remove repeating gradient code Change-Id: I46ab46fb5a96c9c62933c845936d3ddba3d4bdc8 Reviewed-by: Aleksei German --- .../propertyeditor/gradientmodel.cpp | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 490af54e49c..eaf907e8f82 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -180,6 +180,16 @@ const ShapeGradientPropertyData *getDefaultGradientData(const QmlDesigner::Prope 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) { @@ -583,26 +593,11 @@ void GradientModel::setupGradientProperties(const QmlDesigner::ModelNode &gradie if (m_gradientTypeName == u"Gradient") { gradient.variantProperty("orientation").setEnumeration("Gradient.Vertical"); } else if (m_gradientTypeName == u"LinearGradient") { - std::for_each(std::begin(defaultLinearShapeGradients), - std::end(defaultLinearShapeGradients), - [&](auto &a) { - gradient.variantProperty(a.name.toByteArray()) - .setValue(a.getDefaultValue(m_itemNode)); - }); + prepareGradient(defaultLinearShapeGradients, gradient, m_itemNode); } else if (m_gradientTypeName == u"RadialGradient") { - std::for_each(std::begin(defaultRadialShapeGradients), - std::end(defaultRadialShapeGradients), - [&](auto &a) { - gradient.variantProperty(a.name.toByteArray()) - .setValue(a.getDefaultValue(m_itemNode)); - }); + prepareGradient(defaultRadialShapeGradients, gradient, m_itemNode); } else if (m_gradientTypeName == u"ConicalGradient") { - std::for_each(std::begin(defaultConicalShapeGradients), - std::end(defaultConicalShapeGradients), - [&](auto &a) { - gradient.variantProperty(a.name.toByteArray()) - .setValue(a.getDefaultValue(m_itemNode)); - }); + prepareGradient(defaultConicalShapeGradients, gradient, m_itemNode); } } From c216dff156e3715e1ab1262229df20ef7598f654 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 13 Sep 2023 17:30:26 +0200 Subject: [PATCH 253/266] QmlDesigner: Show default condition as empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I33f217bf02035414b2d69c863c7039c485161579 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../connectioneditor/connectionmodel.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 838e1d587dd..15ab1341a38 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -27,6 +27,8 @@ namespace { +const char defaultCondition[] = "condition"; + QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList) { QStringList stringList; @@ -693,7 +695,7 @@ void ConnectionModelBackendDelegate::addCondition() ConnectionEditorStatements::MatchedCondition newCondition; ConnectionEditorStatements::Variable variable; - variable.nodeId = "condition"; + variable.nodeId = defaultCondition; newCondition.statements.append(variable); ConnectionEditorStatements::ConditionalStatement conditionalStatement; @@ -1067,8 +1069,6 @@ void ConnectionModelBackendDelegate::handleConditionChanged() condition = m_conditionListModel.condition(); //why? QString newSource = ConnectionEditorStatements::toJavascript(m_handler); - qDebug() << Q_FUNC_INFO << "new source" << newSource; - commitNewSource(newSource); } @@ -1830,6 +1830,12 @@ void ConditionListModel::internalSetup() 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(); @@ -1842,9 +1848,6 @@ void ConditionListModel::internalSetup() } m_tokens.append(tokenFromComparativeStatement(*s_it)); - for (const auto &token : m_tokens) - qDebug() << token.value; - setValid(); } From 7e09f7d4ff58fad24b4cecc2584d3e6c94ab8f6a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 13 Sep 2023 17:29:18 +0200 Subject: [PATCH 254/266] QmlDesigner: Clean up PropertyTree Model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove call to test * Remve qdebugs * Fix priorityListSignals Change-Id: I0828a9eb7b90c6d7a4903f2cbaf4382e1f3a413e Reviewed-by: Henning Gründl --- .../connectioneditor/propertytreemodel.cpp | 46 ++++--------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 862390986f0..ae0f8972c5d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -152,7 +152,6 @@ void PropertyTreeModel::resetModel() } endResetModel(); - testModel(); } QString stripQualification(const QString &string) @@ -563,41 +562,16 @@ const std::vector PropertyTreeModel::sortedAndFilteredSignalNames( { Q_UNUSED(recursive); - const std::vector priorityListSignals = {"clicked", - "doubleClicked", - "pressed", - "released", - "toggled", - "valueModified", - "valueChanged", - "checkedChanged", - "moved", - "accepted", - "editingFinished", - "entered", - "exited", - "canceled", - "triggered", - "stateChanged", - "started", - "stopped", - "finished" - "enabledChanged", - "visibleChanged", - "opacityChanged", - "rotationChanged"}; + auto filtered = Utils::filtered(metaInfo.signalNames(), [](const PropertyName &name) { + if (std::find(priorityListSignals.cbegin(), priorityListSignals.cend(), name) + != priorityListSignals.cend()) + return true; - auto filtered - = Utils::filtered(metaInfo.signalNames(), [&priorityListSignals](const PropertyName &name) { - if (std::find(priorityListSignals.cbegin(), priorityListSignals.cend(), name) - != priorityListSignals.cend()) - return true; + if (name.endsWith("Changed")) //option? + return false; - if (name.endsWith("Changed")) //option? - return false; - - return true; - }); + return true; + }); auto sorted = Utils::sorted(filtered); @@ -796,7 +770,6 @@ void PropertyListProxyModel::resetModel() void PropertyListProxyModel::setRowAndInternalId(int row, quintptr internalId) { - qDebug() << Q_FUNC_INFO << row << internalId; QTC_ASSERT(m_treeModel, return ); if (internalId == internalRootIndex) @@ -830,7 +803,6 @@ QHash PropertyListProxyModel::roleNames() const void PropertyListProxyModel::goInto(int row) { - qDebug() << Q_FUNC_INFO << row << m_parentIndex.internalId(); setRowAndInternalId(row, 0); //m_parentIndex.internalId()); emit parentNameChanged(); @@ -838,8 +810,6 @@ void PropertyListProxyModel::goInto(int row) void PropertyListProxyModel::goUp() { - qDebug() << Q_FUNC_INFO; - if (m_parentIndex.internalId() == internalRootIndex) return; From 1327a2c7cee174dc982a5fbfbf6102c93e18eaeb Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 13 Sep 2023 17:56:31 +0200 Subject: [PATCH 255/266] QmlDesigner: Add transient scroll bar Add transient scroll bar to connections editor Change-Id: Id4afd661da2fcdeacd2a4a0de0e0b9999c14dec2 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/BindingsListView.qml | 18 ++++++++++++++++++ .../connectionseditor/ConnectionsListView.qml | 19 ++++++++++++++++--- .../qmldesigner/connectionseditor/Main.qml | 8 ++++++++ .../connectionseditor/PropertiesListView.qml | 18 ++++++++++++++++++ src/libs/advanceddockingsystem/dockwidget.cpp | 9 ++++++++- 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml index d2252bba95b..07a6fa7c8db 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/BindingsListView.qml @@ -13,10 +13,28 @@ ListView { 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() diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml index 909ca187e6f..e48c0b48603 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsListView.qml @@ -13,14 +13,27 @@ ListView { 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 - ScrollBar.vertical: ScrollBar { - id: comboBoxPopupScrollBar - visible: root.height < root.contentHeight + 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: { diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 6e47848b12e..4c6dd50c625 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -14,6 +14,11 @@ Rectangle { 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 @@ -109,6 +114,7 @@ Rectangle { width: parent.width height: parent.height - toolbar.height - column.spacing model: ConnectionsEditorEditorBackend.connectionModel + adsFocus: root.adsFocus } BindingsListView { @@ -116,6 +122,7 @@ Rectangle { width: parent.width height: parent.height - toolbar.height - column.spacing model: ConnectionsEditorEditorBackend.bindingModel + adsFocus: root.adsFocus } PropertiesListView { @@ -123,6 +130,7 @@ Rectangle { width: parent.width height: parent.height - toolbar.height - column.spacing model: ConnectionsEditorEditorBackend.dynamicPropertiesModel + adsFocus: root.adsFocus } } } diff --git a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml index bf1ed237cb5..2f4382020b3 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PropertiesListView.qml @@ -13,10 +13,28 @@ ListView { 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() diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp index bb6dd00b322..f2e0e72249f 100644 --- a/src/libs/advanceddockingsystem/dockwidget.cpp +++ b/src/libs/advanceddockingsystem/dockwidget.cpp @@ -457,6 +457,8 @@ void DockWidget::setFocused(bool 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)) { @@ -464,7 +466,12 @@ void DockWidget::setFocused(bool focused) if (!rootItem) continue; - QQuickItem *scrollView = rootItem->findChild("__mainSrollView"); + if (rootItem->objectName() == customObjectName) { + rootItem->setProperty("adsFocus", focused); + continue; + } + + QQuickItem *scrollView = rootItem->findChild(customObjectName); if (!scrollView) continue; From fb20ecff577c4d7f6de2c63d50a82a32fe92f713 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 12 Sep 2023 15:32:34 +0300 Subject: [PATCH 256/266] QmlDesigner: Make 3D view grid fade away at distance Fixes: QDS-10622 Change-Id: I39179e3927bfef5b0e9d99cbbd2ff16886b6dc38 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../components/edit3d/edit3dview.cpp | 2 +- .../utils/designersettings.cpp | 2 +- src/tools/qml2puppet/editor3d_qt6.qrc | 3 + .../qml2puppet/mockfiles/qt6/EditView3D.qml | 2 +- .../qml2puppet/mockfiles/qt6/GridMaterial.qml | 22 +++++ .../qml2puppet/mockfiles/qt6/HelperGrid.qml | 90 +++++++++++++------ .../qml2puppet/mockfiles/qt6/SceneView3D.qml | 51 ++--------- .../mockfiles/shaders/gridmaterial.frag | 22 +++++ .../mockfiles/shaders/gridmaterial.vert | 10 +++ 9 files changed, 127 insertions(+), 77 deletions(-) create mode 100644 src/tools/qml2puppet/mockfiles/qt6/GridMaterial.qml create mode 100644 src/tools/qml2puppet/mockfiles/shaders/gridmaterial.frag create mode 100644 src/tools/qml2puppet/mockfiles/shaders/gridmaterial.vert diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 002606d7f0d..f05f813929a 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -522,7 +522,7 @@ void Edit3DView::createResetColorAction(QAction *syncBackgroundColorAction) Edit3DViewConfig::setColors(this, edit3dBgColorProperty, bgColors); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors); - QColor gridColor{0xaaaaaa}; + QColor gridColor{0xcccccc}; Edit3DViewConfig::setColors(this, edit3dGridColorProperty, {gridColor}); Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, {gridColor}); diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index b75575c6236..8157712437b 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -82,7 +82,7 @@ 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); diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index 750c4054aab..df498e643cf 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -54,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/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 9de033e5349..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 } 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/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index e06140199eb..e3f585de545 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -17,19 +17,6 @@ View3D { property alias orthoCamera: sceneOrthoCamera property vector3d cameraLookAt - // This is step of the main line of the grid, between those is always one subdiv line - property int gridStep: 100 - - property int minGridStep: 50 - readonly property int maxGridStep: 32 * minGridStep - - readonly property int gridArea: minGridStep * 128 - - // 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 - // 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. @@ -45,34 +32,6 @@ View3D { camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera - function calcRad() - { - return Math.atan(gridStep / cameraDistance) - } - - onCameraDistanceChanged: { - if (cameraDistance === 0) - return - - // Calculate new grid step - let gridRad = calcRad() - while (gridRad < minGridRad && gridStep < maxGridStep) { - gridStep *= 2 - if (gridStep > maxGridStep) - gridStep = maxGridStep - gridRad = calcRad() - } - while (gridRad > minGridRad * 2 && gridStep > minGridStep) { - gridStep /= 2 - if (gridStep < minGridStep) - gridStep = minGridStep - gridRad = calcRad() - } - - // Calculate alpha for subgrid. Smaller the perceived spacing, more transparent subgrid is. - helperGrid.subdivAlpha = 2 * (1 - (minGridRad / gridRad)) - } - environment: sceneEnv SceneEnvironment { id: sceneEnv @@ -85,14 +44,14 @@ View3D { HelperGrid { id: helperGrid - lines: gridArea / gridStep - step: gridStep + 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 } @@ -115,7 +74,7 @@ View3D { y: 600 eulerRotation.x: -45 clipFar: 100000 - clipNear: -10000 + clipNear: -100000 } } } 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; +} From b55d44080a3d7b4c0e3b38eff5be0fe3441b6e75 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 10:05:23 +0200 Subject: [PATCH 257/266] QmlDesigner: Remove QML debug output Change-Id: Icdd705a43ddcca10ca1ec5e6dc1ec72f8f208a94 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/connectionseditor/Main.qml | 4 ---- .../qtcreator/qmldesigner/connectionseditor/PopupDialog.qml | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/Main.qml b/share/qtcreator/qmldesigner/connectionseditor/Main.qml index 4c6dd50c625..74efe6dddfd 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/Main.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/Main.qml @@ -93,10 +93,6 @@ Rectangle { buttonIcon: StudioTheme.Constants.add_medium tooltip: qsTr("Add something.") onClicked: { - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate) - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type) - print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type.model) - if (connections.checked) ConnectionsEditorEditorBackend.connectionModel.add() else if (bindings.checked) diff --git a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml index 8032ae84ec7..9d17218b00a 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/PopupDialog.qml @@ -20,8 +20,7 @@ Window { flags: Qt.FramelessWindowHint | Qt.Dialog color: StudioTheme.Values.themePopoutBackground - function ensureVerticalPosition() - { + function ensureVerticalPosition() { if ((window.y + window.height) > (Screen.height - window.style.dialogScreenMargin)) { window.y = (Screen.height - window.height - window.style.dialogScreenMargin) } @@ -29,9 +28,7 @@ Window { onHeightChanged: window.ensureVerticalPosition() - function popup(item) { - print("popup " + item) var padding = 12 var p = item.mapToGlobal(0, 0) window.x = p.x - window.width - padding From 1824ccd3807cff8c687864c18df5195a2e5b8413 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 10:40:03 +0200 Subject: [PATCH 258/266] QmlDesigner: Add placeholder in expression builder Task-number: QDS-10667 Change-Id: Iad83ba5e398b06ebafb64ab1d5d9a2a5543a7204 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/ExpressionBuilder.qml | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index 8964d53867d..dac40865daa 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -16,8 +16,14 @@ Rectangle { property int shadowPillIndex: -1 property bool shadowPillVisible: root.shadowPillIndex !== -1 - property int heightBeforeShadowPill: Math.min(20, flow.childrenRect.height) // TODO Proper size value + // Make expression editor at least 20 px high, especially when empty + property int baseHeight: Math.max(20, flow.childrenRect.height) + property int heightBeforeShadowPill: root.baseHeight property int expressionHeight: { + // If expression is empty or the only item is a shadow pill + if (repeater.count === 0 || (repeater.count === 1 && root.shadowPillVisible)) + return root.heightBeforeShadowPill + if (popup.visible) return root.heightBeforeShadowPill + flow.spacing + 20 @@ -34,11 +40,28 @@ Rectangle { width: 400 height: root.expressionHeight + 2 * StudioTheme.Values.flowMargin - color: (focusScope.activeFocus || popup.searchActive) ? root.style.background.interaction - : root.style.background.idle + color: { + if (focusScope.activeFocus || popup.searchActive) + return root.style.background.interaction + + if (mouseArea.containsMouse) + return root.style.background.hover + + return root.style.background.idle + } border { - color: root.conditionListModel.valid ? root.style.border.idle - : StudioTheme.Values.themeError + color: { + if (!root.conditionListModel.valid) + return StudioTheme.Values.themeError + + if (focusScope.activeFocus || popup.searchActive) + return root.style.border.interaction + + if (mouseArea.containsMouse) + return root.style.border.hover + + return root.style.border.idle + } width: root.style.borderWidth } @@ -216,6 +239,23 @@ Rectangle { } } + 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 @@ -239,7 +279,7 @@ Rectangle { root.placeCursor(newTextInput.index) if (!root.shadowPillVisible) - root.heightBeforeShadowPill = flow.childrenRect.height + root.heightBeforeShadowPill = root.baseHeight } Repeater { @@ -342,7 +382,6 @@ Rectangle { } } } - } SuggestionPopup { @@ -379,7 +418,7 @@ Rectangle { onSearchActiveChanged: { if (popup.searchActive) { - root.heightBeforeShadowPill = flow.childrenRect.height + root.heightBeforeShadowPill = root.baseHeight root.insert(newTextInput.index, "...", ConditionListModel.Shadow) root.shadowPillIndex = newTextInput.index } else { @@ -394,7 +433,7 @@ Rectangle { onEntered: function(value) { if (!popup.searchActive) { if (!root.shadowPillVisible) { - root.heightBeforeShadowPill = flow.childrenRect.height + root.heightBeforeShadowPill = root.baseHeight root.shadowPillIndex = newTextInput.index root.insert(newTextInput.index, value, ConditionListModel.Shadow) } else { From 89d5795f7a968c506415ef5b31a1fadb36f5183e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 11:36:13 +0200 Subject: [PATCH 259/266] QmlDesigner: Hide QML debug output Change-Id: Ice49d7ab5a5d90feccf52f98b1049576f96ae275 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../connectionseditor/ConnectionsDialogForm.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml index 7b43abc12be..f3c6fc00bcb 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialogForm.qml @@ -108,23 +108,22 @@ Column { model: backend.conditionListModel onRemove: function(index) { - console.log("remove", index) + //console.log("remove", index) backend.conditionListModel.removeToken(index) } onUpdate: function(index, value) { - console.log("update", index, value) + //console.log("update", index, value) backend.conditionListModel.updateToken(index, value) } onAdd: function(value) { - console.log("add", value) + //console.log("add", value) backend.conditionListModel.appendToken(value) } onInsert: function(index, value, type) { - console.log("insert", index, value, type) - + //console.log("insert", index, value, type) if (type === ConditionListModel.Intermediate) backend.conditionListModel.insertIntermediateToken(index, value) else if (type === ConditionListModel.Shadow) @@ -134,8 +133,7 @@ Column { } onSetValue: function(index, value) { - console.log("setValue", index, value) - + //console.log("setValue", index, value) backend.conditionListModel.setShadowToken(index, value) } } From 650dabdc254174bc1304f61a7a3702ef72d51c5e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 14 Sep 2023 11:35:01 +0200 Subject: [PATCH 260/266] QmlDesigner: Add scroll bars to SuggestionPopup * Add scroll bars * Fix focus style for expression builder Change-Id: I08f4334b3d480e4395a0c017634dd42f8eff74bb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../connectionseditor/ExpressionBuilder.qml | 2 +- .../connectionseditor/SuggestionPopup.qml | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml index dac40865daa..bd367debb75 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ExpressionBuilder.qml @@ -54,7 +54,7 @@ Rectangle { if (!root.conditionListModel.valid) return StudioTheme.Values.themeError - if (focusScope.activeFocus || popup.searchActive) + if (focusScope.activeFocus) return root.style.border.interaction if (mouseArea.containsMouse) diff --git a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml index 511e92ced98..fb9643ca14b 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/SuggestionPopup.qml @@ -124,10 +124,27 @@ Controls.Popup { id: listView visible: search.empty width: stack.width - implicitHeight: Math.min(380, childrenRect.height) + 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 @@ -167,10 +184,27 @@ Controls.Popup { id: treeView visible: !search.empty width: stack.width - implicitHeight: Math.min(380, childrenRect.height) + 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) } From 955534ce45f576bd3dec531afc5e28fa0b6a0bee Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 14 Sep 2023 12:16:12 +0200 Subject: [PATCH 261/266] qml2puppet: we only support Qt6 these days This amends 59ecf10f06366939826b41170f236d1adffb126b "fix QT_VERSION_MAJOR is empty in single builds" Task-number: QDS-10669 Change-Id: I730fe8d36b22fce82ff71b0f9aee5657d6380f1f Reviewed-by: Miikka Heikkinen --- src/tools/qml2puppet/CMakeLists.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 0aca34c1e8f..2ffebbc59db 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -21,9 +21,6 @@ if (NOT QT_CREATOR_API_DEFINED) 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) @@ -86,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" @@ -106,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" @@ -114,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" @@ -123,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" From f12f3790dacb5086d832c1cee1a8f73bcabdb4d2 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 25 Aug 2023 11:28:29 +0300 Subject: [PATCH 262/266] QmlDesigner: Implement Collection Editor Data could be loaded from a csv or json file, and would be appended to the collection node. Task-number: QDS-10462 Change-Id: I60294582331ba20eb5ecb5d8fd591055c0eb6d1e Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../CollectionItem.qml | 288 +++++++++ .../CollectionView.qml | 156 +++++ .../collectionEditorQmlSource/CsvImport.qml | 188 ++++++ .../IconTextButton.qml | 85 +++ .../collectionEditorQmlSource/JsonImport.qml | 127 ++++ .../collectionEditorQmlSource/Message.qml | 41 ++ .../NewCollectionDialog.qml | 93 +++ src/plugins/qmldesigner/CMakeLists.txt | 9 + .../collectioneditor/collectionmodel.cpp | 253 ++++++++ .../collectioneditor/collectionmodel.h | 70 ++ .../collectioneditor/collectionview.cpp | 598 ++++++++++++++++++ .../collectioneditor/collectionview.h | 59 ++ .../collectioneditor/collectionwidget.cpp | 162 +++++ .../collectioneditor/collectionwidget.h | 42 ++ .../components/componentcore/viewmanager.cpp | 11 +- src/plugins/qmldesigner/designmodecontext.cpp | 17 +- src/plugins/qmldesigner/designmodecontext.h | 12 +- .../qmldesigner/qmldesignerconstants.h | 19 +- 18 files changed, 2215 insertions(+), 15 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CsvImport.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/JsonImport.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/Message.qml create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/NewCollectionDialog.qml create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionview.h create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h 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..17ac70167d2 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -0,0 +1,156 @@ +// 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 + + 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 { + height: StudioTheme.Values.height + 5 + color: StudioTheme.Values.themeToolbarBackground + width: parent.width + + 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() + } + } + } + } + + Rectangle { + id: collectionRect + + color: StudioTheme.Values.themeBackgroundColorAlternate + + 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[^*> + +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); +} + +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..f3a1e7de786 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h @@ -0,0 +1,70 @@ +// 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); + + 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..e967e850d19 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -0,0 +1,598 @@ +// 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 "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); + } + + 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..df119654d08 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -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 + +#include "collectionwidget.h" +#include "collectionmodel.h" +#include "collectionview.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.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_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())}}); + + 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; +} + +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..1f6f57470c2 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -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 +#pragma once + +#include + +#include + +class StudioQuickWidget; + +namespace QmlDesigner { + +class CollectionModel; +class CollectionView; + +class CollectionWidget : public QFrame +{ + Q_OBJECT + +public: + CollectionWidget(CollectionView *view); + void contextHelp(const Core::IContext::HelpCallback &callback) const; + + QPointer collectionModel() 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; + QScopedPointer m_quickWidget; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index b61f09885d7..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 @@ -23,11 +24,11 @@ #include #include #include -#include #include #include #include #include +#include #include @@ -51,6 +52,7 @@ public: : connectionManager, externalDependencies, true) + , collectionView{externalDependencies} , contentLibraryView{externalDependencies} , componentView{externalDependencies} , edit3DView{externalDependencies} @@ -73,6 +75,7 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; + CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; Edit3DView edit3DView; @@ -212,6 +215,9 @@ QList ViewManager::standardViews() const 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); @@ -390,6 +396,9 @@ QList ViewManager::widgetInfos() const 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/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index fade9e9a953..13a03bc10b1 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,6 +3,7 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" +#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" #include "effectmakerwidget.h" @@ -12,11 +13,10 @@ #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)); @@ -111,6 +111,15 @@ 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 0829b2a8222..72c0a195239 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -83,5 +83,13 @@ public: 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/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index c2003441e0a..60e1ded8474 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -11,14 +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_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_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"; @@ -77,6 +78,7 @@ 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"; @@ -159,6 +161,7 @@ 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"; From 252a1f3c74d5ded4d76e9bdc96d254a6e2f8bf48 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 8 Sep 2023 15:14:42 +0300 Subject: [PATCH 263/266] QmlDesigner: Add a view for single collection Change-Id: Iee103cf9344872e0f2eaa564fa1feeaea4d26d6a Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionView.qml | 10 +- .../SingleCollectionView.qml | 134 ++++++++++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../collectioneditor/collectionmodel.cpp | 9 ++ .../collectioneditor/collectionmodel.h | 2 + .../collectioneditor/collectionview.cpp | 7 + .../collectioneditor/collectionwidget.cpp | 11 +- .../collectioneditor/collectionwidget.h | 3 + .../singlecollectionmodel.cpp | 110 ++++++++++++++ .../collectioneditor/singlecollectionmodel.h | 46 ++++++ 10 files changed, 326 insertions(+), 7 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml create mode 100644 src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/singlecollectionmodel.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 17ac70167d2..30128ecadf4 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -14,6 +14,7 @@ Item { property var rootView: CollectionEditorBackend.rootView property var model: CollectionEditorBackend.model + property var singleCollectionModel: CollectionEditorBackend.singleCollectionModel function showWarning(title, message) { warningDialog.title = title @@ -60,9 +61,9 @@ Item { width: parent.width Rectangle { + width: parent.width height: StudioTheme.Values.height + 5 color: StudioTheme.Values.themeToolbarBackground - width: parent.width Text { id: collectionText @@ -141,11 +142,8 @@ Item { } } - Rectangle { - id: collectionRect - - color: StudioTheme.Values.themeBackgroundColorAlternate - + SingleCollectionView { + model: root.singleCollectionModel anchors { left: collectionsRect.right right: parent.right diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml new file mode 100644 index 00000000000..8ad8a63e916 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/SingleCollectionView.qml @@ -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 + +import QtQuick +import QtQuick.Controls +import StudioTheme 1.0 as StudioTheme + +Rectangle { + id: root + + required property var model + + property alias leftPadding: topRow.leftPadding + property real rightPadding: topRow.rightPadding + + color: StudioTheme.Values.themeBackgroundColorAlternate + + Column { + id: topRow + + spacing: 0 + width: parent.width + leftPadding: 20 + rightPadding: 0 + topPadding: 5 + + Text { + id: collectionNameText + + leftPadding: 8 + rightPadding: 8 + topPadding: 3 + bottomPadding: 3 + + color: StudioTheme.Values.themeTextColor + text: root.model.collectionName + font.pixelSize: StudioTheme.Values.mediumIconFont + elide: Text.ElideRight + + Rectangle { + anchors.fill: parent + z: parent.z - 1 + color: StudioTheme.Values.themeBackgroundColorNormal + } + } + + Item { // spacer + width: 1 + height: 10 + } + + HorizontalHeaderView { + id: headerView + + property real topPadding: 5 + property real bottomPadding: 5 + + height: headerMetrics.height + topPadding + bottomPadding + + syncView: tableView + clip: true + + delegate: Rectangle { + implicitWidth: 100 + implicitHeight: headerText.height + color: StudioTheme.Values.themeControlBackground + border.width: 2 + border.color: StudioTheme.Values.themeControlOutline + clip: true + + Text { + id: headerText + + topPadding: headerView.topPadding + bottomPadding: headerView.bottomPadding + leftPadding: 5 + rightPadding: 5 + text: display + font.pixelSize: headerMetrics.font + color: StudioTheme.Values.themeIdleGreen + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.centerIn: parent + elide: Text.ElideRight + } + } + + TextMetrics { + id: headerMetrics + + font.pixelSize: StudioTheme.Values.baseFontSize + text: "Xq" + } + } + } + + TableView { + id: tableView + + anchors { + top: topRow.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + leftMargin: root.leftPadding + rightMargin: root.rightPadding + } + + model: root.model + + delegate: Rectangle { + implicitWidth: 100 + implicitHeight: itemText.height + color: StudioTheme.Values.themeControlBackground + border.width: 1 + border.color: StudioTheme.Values.themeControlOutline + + Text { + id: itemText + + text: display + width: parent.width + leftPadding: 5 + topPadding: 3 + bottomPadding: 3 + font.pixelSize: StudioTheme.Values.baseFontSize + color: StudioTheme.Values.themeTextColor + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 93a384217a5..5d9263771c2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -808,6 +808,7 @@ extend_qtc_plugin(QmlDesigner collectionmodel.cpp collectionmodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h + singlecollectionmodel.cpp singlecollectionmodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp index a0cb6bc6bf2..18e651fc2e2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.cpp @@ -171,6 +171,15 @@ void CollectionModel::selectCollection(const ModelNode &node) 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(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h index f3a1e7de786..17831732543 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionmodel.h @@ -41,6 +41,8 @@ public: 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(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index e967e850d19..aaf2605b64f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -9,6 +9,7 @@ #include "nodemetainfo.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "singlecollectionmodel.h" #include "variantproperty.h" #include @@ -352,6 +353,12 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() 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(), diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index df119654d08..7e65516143c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -6,6 +6,7 @@ #include "collectionview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "singlecollectionmodel.h" #include "theme.h" #include @@ -36,6 +37,7 @@ 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")); @@ -62,7 +64,9 @@ CollectionWidget::CollectionWidget(CollectionView *view) qmlRegisterAnonymousType("CollectionEditorBackend", 1); auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); map->setProperties( - {{"rootView", QVariant::fromValue(this)}, {"model", QVariant::fromValue(m_model.data())}}); + {{"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); @@ -83,6 +87,11 @@ QPointer CollectionWidget::collectionModel() const return m_model; } +QPointer CollectionWidget::singleCollectionModel() const +{ + return m_singleCollectionModel; +} + void CollectionWidget::reloadQmlSource() { const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 1f6f57470c2..e76237d0c14 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -12,6 +12,7 @@ namespace QmlDesigner { class CollectionModel; class CollectionView; +class SingleCollectionModel; class CollectionWidget : public QFrame { @@ -22,6 +23,7 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const; QPointer collectionModel() const; + QPointer singleCollectionModel() const; void reloadQmlSource(); @@ -36,6 +38,7 @@ public: private: QPointer m_view; QPointer m_model; + QPointer m_singleCollectionModel; QScopedPointer m_quickWidget; }; 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 From 4b1862fdb166354f80d48b734d8a7d93781b1f1f Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 14 Sep 2023 09:51:41 +0300 Subject: [PATCH 264/266] QmlDesigner: Flash transient scrollbars only when they are enabled Visibility has a more complex definition in transient scrollbars since they are transient. Also, it's meaningful when it's disabled, it shouldn't be flashed. So here we use this ability to prevent them from being flashed when they are disabled. Also, the enabled property would be changed instead of the visibility for the cases that transient scrollbars are out of the scroll area. (TimelineWidget and TransitionEditorWidget) Task-number: QDS-10644 Change-Id: If74d71571a216153a2d3aa09694760d7a02026ca Reviewed-by: Thomas Hartmann --- src/libs/utils/transientscroll.cpp | 12 ++++++++++- .../timelineeditor/timelinewidget.cpp | 20 +++++++++++++------ .../transitioneditorwidget.cpp | 20 +++++++++++++------ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/libs/utils/transientscroll.cpp b/src/libs/utils/transientscroll.cpp index 1d3df1259d8..3a0a9153769 100644 --- a/src/libs/utils/transientscroll.cpp +++ b/src/libs/utils/transientscroll.cpp @@ -365,13 +365,23 @@ ScrollBar::~ScrollBar() 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); } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index f6e9a3ad27c..b05083ff054 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -538,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->setRange(min, max); - m_scrollbar->setValue(current); - m_scrollbar->setSingleStep((max - min) / 10); - m_scrollbar->blockSignals(b); - m_scrollbar->flash(); + 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) @@ -575,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); @@ -584,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); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp index 6cb9c285be7..235722e4c8f 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp @@ -231,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); @@ -240,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); @@ -378,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) From 77a1c0fb15a104a2a8f0e2a503e721f2ea09152c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 14 Sep 2023 14:48:47 +0300 Subject: [PATCH 265/266] QmlDesigner: Allow snap back to 0 rotation during drag Start rotation should be restored when desired angle for rotation is 0. Fixes: QDS-10652 Change-Id: I8e733b714f6a0f14561c270aefc5049bdfaff39e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp index 8c6f6c23d8a..118227360f7 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -699,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); } From 2598e788ccb05f98aea77b301de91804ce9fd06c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 14 Sep 2023 15:16:03 +0300 Subject: [PATCH 266/266] QmlDesigner: Block import of 3D types for Qt5 projects Quick3D is not supported anymore for Qt5 projects, so we shouldn't allow 3D imports, either. Fixes: QDS-10662 Change-Id: I40da07a67e57527c7ad85443f2463e3f7783693f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qmldesigner/designercore/instances/nodeinstanceview.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 42c0a4263a2..23146996afc 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1735,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());