From 82150e2cee8726ca8fb19c360c16328e34506065 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 17 Oct 2024 15:09:48 +0200 Subject: [PATCH 001/322] QmlDesigner: Enable links in descriptions Task-number: QDS-13773 Change-Id: Ie36dee2ab6c962487489d544cb596a389554641d Reviewed-by: Knud Dollereder --- .../qmldesigner/welcomepage/ThumbnailDelegate.qml | 12 ++++++++++++ src/plugins/studiowelcome/studiowelcomeplugin.cpp | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/share/qtcreator/qmldesigner/welcomepage/ThumbnailDelegate.qml b/share/qtcreator/qmldesigner/welcomepage/ThumbnailDelegate.qml index d50627d05e9..3d918328bf0 100644 --- a/share/qtcreator/qmldesigner/welcomepage/ThumbnailDelegate.qml +++ b/share/qtcreator/qmldesigner/welcomepage/ThumbnailDelegate.qml @@ -382,6 +382,7 @@ Item { Text { id: recentProjectInfo color: Constants.currentGlobalText + linkColor: Constants.currentBrand text: typeof(description) === "undefined" ? "" : description anchors.fill: parent font.pixelSize: 12 @@ -390,6 +391,17 @@ Item { wrapMode: Text.WordWrap anchors.margins: Constants.thumbnailMargin anchors.topMargin: 25 + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: recentProjectInfo.hoveredLink === "" ? Qt.ArrowCursor : Qt.PointingHandCursor + } + + Connections { + target: recentProjectInfo + onLinkActivated: (link)=> Constants.projectModel.showLink(link) + } } TagArea { diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 68e8deb8277..7878ba1c963 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -273,6 +273,11 @@ public: QUrl("qthelp://org.qt-project.qtdesignstudio/doc/studio-getting-started.html")); } + Q_INVOKABLE void showLink(const QString &link) + { + QDesktopServices::openUrl(QUrl::fromUserInput(link)); + } + Q_INVOKABLE void openExample(const QString &examplePath, const QString &exampleName, const QString &formFile, From 82f990558bec15e6292f8f1e7220b97dbf2a324d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 21 Oct 2024 16:34:51 +0300 Subject: [PATCH 002/322] QmlDesigner: Add generated components when exporting a bundle When exporting a bundle of a component that has a dependency on another component loaded via a lib import from the Generated folder, those dependency components get properly added to the exported bundle. Also the generated imports are removed from the main component as they are not valid anymore. Task-number: QDS-13746 Change-Id: Ic3370fc62baa6b2a7cbbef432b1c2a3853170250 Reviewed-by: Miikka Heikkinen --- .../components/componentcore/bundlehelper.cpp | 71 ++++++++++++++++--- .../components/componentcore/bundlehelper.h | 11 ++- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp index 347c08330f2..1db2622e345 100644 --- a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp @@ -231,7 +231,16 @@ void BundleHelper::exportComponent(const ModelNode &node) QStringList filesList; for (const AssetPath &asset : compDependencies) { Utils::FilePath assetAbsPath = asset.absFilPath(); - m_zipWriter->addFile(asset.relativePath, assetAbsPath.fileContents().value_or("")); + QByteArray assetContent = assetAbsPath.fileContents().value_or(""); + + // remove imports of sub components + for (const QString &import : std::as_const(asset.importsToRemove)) { + int removeIdx = assetContent.indexOf(QByteArray("import " + import.toLatin1())); + int removeLen = assetContent.indexOf('\n', removeIdx) - removeIdx; + assetContent.remove(removeIdx, removeLen); + } + + m_zipWriter->addFile(asset.relativePath, assetContent); if (assetAbsPath.fileName() != compFileName) // skip component file (only collect dependencies) filesList.append(asset.relativePath); @@ -599,15 +608,45 @@ bool BundleHelper::isItemBundle(const QString &bundleId) const namespace { -// library imported Components won't be detected. TODO: find a feasible solution for detecting them -// and either add them as dependencies or warn the user Utils::FilePath getComponentFilePath(const QString &nodeType, const Utils::FilePath &compDir) { - Utils::FilePath compFilePath = compDir.pathAppended(QLatin1String("%1.qml").arg(nodeType)); - if (compFilePath.exists()) - return compFilePath; + QString compName = nodeType.split('.').last(); - compFilePath = compDir.pathAppended(QLatin1String("%1.ui.qml").arg(nodeType)); + auto findCompFilePath = [&](const Utils::FilePath &dir) -> Utils::FilePath { + Utils::FilePath compFP = dir.pathAppended(QLatin1String("%1.qml").arg(compName)); + if (compFP.exists()) + return compFP; + + compFP = dir.pathAppended(QLatin1String("%1.ui.qml").arg(compName)); + if (compFP.exists()) + return compFP; + + return {}; + }; + + Utils::FilePath compFilePath; + + // a component in "Generated" folder + if (nodeType.startsWith("Generated.")) { + Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); + QString nodeTypeSlashSep = nodeType; + nodeTypeSlashSep.replace('.', '/'); + Utils::FilePath genCompDir = projectPath.pathAppended(nodeTypeSlashSep); + + if (!genCompDir.exists()) + genCompDir = genCompDir.parentDir(); + + compFilePath = findCompFilePath(genCompDir); + if (compFilePath.exists()) + return compFilePath; + + + qWarning() << __FUNCTION__ << "Couldn't find Generated component path"; + return {}; + } + + // for components in the same dir as the main comp., search recursively for the comp. file + compFilePath = findCompFilePath(compDir); if (compFilePath.exists()) return compFilePath; @@ -618,6 +657,7 @@ Utils::FilePath getComponentFilePath(const QString &nodeType, const Utils::FileP return compFilePath; } + qWarning() << __FUNCTION__ << "Couldn't find component path"; return {}; } @@ -627,8 +667,7 @@ QSet BundleHelper::getComponentDependencies(const Utils::FilePath &fi const Utils::FilePath &mainCompDir) { QSet depList; - - depList.insert({mainCompDir, filePath.relativePathFrom(mainCompDir).toFSPathString()}); + AssetPath compAssetPath = {mainCompDir, filePath.relativePathFrom(mainCompDir).toFSPathString()}; #ifdef QDS_USE_PROJECTSTORAGE // TODO add model with ProjectStorageDependencies @@ -658,7 +697,16 @@ QSet BundleHelper::getComponentDependencies(const Utils::FilePath &fi if (!nodeType.startsWith("QtQuick")) { Utils::FilePath compFilPath = getComponentFilePath(nodeType, mainCompDir); if (!compFilPath.isEmpty()) { - depList.unite(getComponentDependencies(compFilPath, mainCompDir)); + Utils::FilePath compDir = compFilPath.isChildOf(mainCompDir) ? mainCompDir + : compFilPath.parentDir(); + depList.unite(getComponentDependencies(compFilPath, compDir)); + + // for sub components, mark their imports to be removed from their parent component + // as they will be moved to the same folder as the parent + QString import = nodeType.left(nodeType.lastIndexOf('.')); + if (model->hasImport(import)) + compAssetPath.importsToRemove.append(import); + return; } } @@ -687,6 +735,7 @@ QSet BundleHelper::getComponentDependencies(const Utils::FilePath &fi assetPathBase = mainCompDir; } + QTC_ASSERT(!assetPathRelative.isEmpty(), continue); depList.insert({assetPathBase, assetPathRelative}); } } @@ -701,6 +750,8 @@ QSet BundleHelper::getComponentDependencies(const Utils::FilePath &fi parseNode(rootNode); + depList.insert(compAssetPath); + return depList; } diff --git a/src/plugins/qmldesigner/components/componentcore/bundlehelper.h b/src/plugins/qmldesigner/components/componentcore/bundlehelper.h index 270109cd0b4..f853afea579 100644 --- a/src/plugins/qmldesigner/components/componentcore/bundlehelper.h +++ b/src/plugins/qmldesigner/components/componentcore/bundlehelper.h @@ -25,8 +25,11 @@ class NodeMetaInfo; class AssetPath { public: - Utils::FilePath basePath; - QString relativePath; + AssetPath(const Utils::FilePath &bsPath, const QString &relPath, const QStringList &imports = {}) + : basePath(bsPath) + , relativePath(relPath) + , importsToRemove(imports) + {} Utils::FilePath absFilPath() const; @@ -35,6 +38,10 @@ public: return basePath == other.basePath && relativePath == other.relativePath; } + Utils::FilePath basePath; + QString relativePath; + QStringList importsToRemove; + private: friend size_t qHash(const AssetPath &asset) { From b32bd7f236a75ce060dfbb7a5ee99ebb5510585f Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Fri, 18 Oct 2024 12:04:45 +0200 Subject: [PATCH 003/322] QmlProjectManager: Generate .qmlproject file based on qmldir (McuModuleProjectItem) Task-number: QDS-13811 Change-Id: I187f04fbc4ba7752e9e1d7eb634c0904abae3a2f Reviewed-by: Marco Bubke Reviewed-by: Thomas Hartmann --- src/plugins/qmlprojectmanager/CMakeLists.txt | 8 + .../mcumoduleprojectitem.cpp | 261 +++++++++++++ .../qmldirtoqmlproject/mcumoduleprojectitem.h | 50 +++ .../qmlprojectmanager/CMakeLists.txt | 1 + .../qmlprojectmanager/data/README.md | 10 + .../existing_qmlproject/File1.qml | 5 + .../existing_qmlproject/File2.qml | 5 + .../existing_qmlproject/Internal/File3.qml | 5 + .../existing_qmlproject/TestSingleton.qml | 6 + .../existing_qmlproject/qmldir | 3 + .../test_module.qmlproject | 16 + .../TestSingleton.qml | 6 + .../incorrect_module_name_qmldir/qmldir | 3 + .../invalid_qmlproject/test_module.qmlproject | 5 + .../TestSingleton.qml | 6 + .../missing_module_name_qmldir/qmldir | 2 + .../missing_qml_files_qmldir/qmldir | 2 + .../missing_qmlproject/File1.qml | 5 + .../missing_qmlproject/File2.qml | 5 + .../missing_qmlproject/Internal/File3.qml | 5 + .../missing_qmlproject/TestSingleton.qml | 6 + .../missing_qmlproject/qmldir | 3 + .../mcumoduleprojectitem-test.cpp | 368 ++++++++++++++++++ 23 files changed, 786 insertions(+) create mode 100644 src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.cpp create mode 100644 src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.h create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File1.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File2.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/Internal/File3.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/TestSingleton.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/qmldir create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/TestSingleton.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/qmldir create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/TestSingleton.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/qmldir create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qml_files_qmldir/qmldir create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File1.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File2.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/Internal/File3.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/TestSingleton.qml create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/qmldir create mode 100644 tests/unit/tests/unittests/qmlprojectmanager/mcumoduleprojectitem-test.cpp diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 5e237cccc89..08d46d35a50 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -53,6 +53,13 @@ extend_qtc_plugin(QmlProjectManager boilerplate.qrc ) +extend_qtc_plugin(QmlProjectManager + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qmldirtoqmlproject + SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/qmldirtoqmlproject + SOURCES + mcumoduleprojectitem.cpp mcumoduleprojectitem.h +) + add_qtc_library(QmlProjectManagerLib OBJECT CONDITION WITH_TESTS AND Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 EXCLUDE_FROM_INSTALL @@ -66,4 +73,5 @@ add_qtc_library(QmlProjectManagerLib OBJECT buildsystem/projectitem/qmlprojectitem.cpp buildsystem/projectitem/qmlprojectitem.h buildsystem/projectitem/converters.cpp buildsystem/projectitem/converters.h qmlprojectexporter/filetypes.cpp qmlprojectexporter/filetypes.h + qmldirtoqmlproject/mcumoduleprojectitem.cpp qmldirtoqmlproject/mcumoduleprojectitem.h ) diff --git a/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.cpp b/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.cpp new file mode 100644 index 00000000000..867f12ee180 --- /dev/null +++ b/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.cpp @@ -0,0 +1,261 @@ +// Copyright (C) 2024 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 "mcumoduleprojectitem.h" + +#include +#include + +#include +#include + +using namespace Qt::Literals::StringLiterals; + +namespace Constants { +namespace QmlDir { +constexpr auto QMLDIR = "qmldir"_L1; +constexpr auto MODULE = "module"_L1; +constexpr auto QML_FILE_FILTER = "*.qml"_L1; +} // namespace QmlDir + +namespace Json { +constexpr auto MODULE_URI = "moduleUri"_L1; +constexpr auto QML_FILES = "qmlFiles"_L1; +constexpr auto QMLPROJECT_PATH = "qmlProjectPath"_L1; +} // namespace Json + +namespace QmlProject { +constexpr auto QMLPROJECT_EXTENSION = ".qmlproject"_L1; +constexpr auto MCU_MODULE = "MCU.Module"_L1; +constexpr auto URI = "uri"_L1; +constexpr auto QML_FILES = "QmlFiles"_L1; +constexpr auto FILES = "files"_L1; + +const auto QMLPROJECT_TEMPLATE = QString(R"(/* File generated by Qt Design Studio */ + +import QmlProject 1.3 +Project { + MCU.Module { + uri: %1 + } + QmlFiles { + files: [ + %2 + ] + } +} +)"); +} // namespace QmlProject +} // namespace Constants + +namespace { +Q_LOGGING_CATEGORY(log, "QmlProjectManager.McuModuleProjectItem") + +bool isValidQmlProjectPath(const Utils::FilePath &path) +{ + return path.endsWith(Constants::QmlProject::QMLPROJECT_EXTENSION) + && (path.ensureExistingFile() || path.parentDir().isWritableDir()); +} + +QJsonObject parseQmlProjectFile(const Utils::FilePath &qmlproject) +{ + auto qmlprojectPathStr = qmlproject.toFSPathString(); + + if (!qmlproject.exists()) { + qCCritical(log) << "qmlproject file not found:" << qmlprojectPathStr; + return {}; + } + + QmlJS::SimpleReader reader; + QmlJS::SimpleReaderNode::Ptr rootNode = reader.readFile(qmlprojectPathStr); + if (!reader.errors().isEmpty() || !rootNode->isValid()) { + qCCritical(log) << "Unable to parse:" << qmlprojectPathStr; + qCCritical(log) << reader.errors(); + return {}; + } + + QJsonObject result; + result.insert(Constants::Json::QMLPROJECT_PATH, qmlprojectPathStr); + + auto checkNodeName = [](const QString &node, const QString &expecedName) { + return node.compare(expecedName, Qt::CaseInsensitive) == 0; + }; + + //expected just two nodes: MCU.Module and QmlFiles + for (const auto &childNode : rootNode->children()) { + auto nodeName = childNode->name(); + if (checkNodeName(nodeName, Constants::QmlProject::MCU_MODULE)) { + result.insert(Constants::Json::MODULE_URI, + childNode->property(Constants::QmlProject::URI).value.toString()); + } else if (checkNodeName(nodeName, Constants::QmlProject::QML_FILES)) { + result.insert(Constants::Json::QML_FILES, + childNode->property(Constants::QmlProject::FILES).value.toJsonArray()); + } else { + qCWarning(log) << "Unsupported node:" << nodeName; + } + } + + return result; +} +} // namespace + +namespace QmlProjectManager { +McuModuleProjectItem::McuModuleProjectItem(const QJsonObject &project) + : m_project(project) +{} + +McuModuleProjectItem::McuModuleProjectItem(const Utils::FilePath &qmlprojectFile) + : m_qmlProjectFile(qmlprojectFile) + , m_project(parseQmlProjectFile(m_qmlProjectFile)) +{ +} + +std::optional McuModuleProjectItem::fromQmldirModule(const Utils::FilePath &qmldirFile) +{ + auto qmldirFileStr = qmldirFile.toFSPathString(); + + // check qmldirFile + if (!qmldirFile.exists()) { + qCWarning(log) << "File not found:" << qmldirFileStr; + return {}; + } + if (qmldirFile.fileName() != Constants::QmlDir::QMLDIR) { + qCWarning(log) << "It's not qmldir file:" << qmldirFileStr; + return {}; + } + + auto qmldirContents = qmldirFile.fileContents(); + if (!qmldirContents) { + qCWarning(log) << "Unable to read the file:" << qmldirFileStr + << ", error:" << qmldirContents.error(); + return {}; + } + + // find module name + QByteArray fileContents = qmldirContents.value(); + QTextStream ts(fileContents); + QString moduleName; + + while (!ts.atEnd()) { + QString line = ts.readLine().trimmed(); + if (line.startsWith(Constants::QmlDir::MODULE, Qt::CaseInsensitive)) { + auto list = line.split(' '); + if (list.size() != 2) { + qCWarning(log) << "Invalid module identifier:" << line; + return {}; + } + moduleName = list.last(); + break; + } + } + + if (moduleName.isEmpty()) { + qCWarning(log) << "Module name not found in the qmldir"; + return {}; + } + + // list qml files + const auto qmldirParent = qmldirFile.parentDir(); + auto qmlDirEntries = qmldirParent.dirEntries(Utils::FileFilter{{Constants::QmlDir::QML_FILE_FILTER}, + QDir::NoFilter, + QDirIterator::Subdirectories}); + if (qmlDirEntries.empty()) { + qCWarning(log) << "No qml files found in:" << qmldirParent; + return {}; + } + auto qmlFiles = Utils::transform(qmlDirEntries, [qmldirParent](const Utils::FilePath &path) { + return path.relativePathFrom(qmldirParent).toFSPathString(); + }); + + // build mcu module project + QJsonObject result; + result.insert(Constants::Json::MODULE_URI, moduleName); + result.insert(Constants::Json::QML_FILES, QJsonArray::fromStringList(qmlFiles)); + + auto filename = moduleName.replace('.', '_'); + auto qmlprojectPath = qmldirParent.resolvePath( + Utils::FilePath::fromString(filename + Constants::QmlProject::QMLPROJECT_EXTENSION)); + result.insert(Constants::Json::QMLPROJECT_PATH, qmlprojectPath.toFSPathString()); + + return McuModuleProjectItem(result); +} + +bool McuModuleProjectItem::isValid() const noexcept +{ + return !uri().isEmpty() && !qmlFiles().isEmpty() && isValidQmlProjectPath(qmlProjectPath()); +} + +QString McuModuleProjectItem::uri() const noexcept +{ + return m_project[Constants::Json::MODULE_URI].toString(); +} + +void McuModuleProjectItem::setUri(const QString &moduleUri) +{ + m_project[Constants::Json::MODULE_URI] = moduleUri; +} + +QStringList McuModuleProjectItem::qmlFiles() const noexcept +{ + return m_project[Constants::Json::QML_FILES].toVariant().toStringList(); +} + +void McuModuleProjectItem::setQmlFiles(const QStringList &files) +{ + m_project[Constants::Json::QML_FILES] = QJsonArray::fromStringList(files); +} + +Utils::FilePath McuModuleProjectItem::qmlProjectPath() const noexcept +{ + return Utils::FilePath::fromString(m_project[Constants::Json::QMLPROJECT_PATH].toString()); +} + +void McuModuleProjectItem::setQmlProjectPath(const Utils::FilePath &path) +{ + m_project[Constants::Json::QMLPROJECT_PATH] = path.toFSPathString(); +} + +QJsonObject McuModuleProjectItem::project() const noexcept +{ + return m_project; +} + +bool McuModuleProjectItem::saveQmlProjectFile() const +{ + if (!isValid()) { + return false; + } + + auto path = qmlProjectPath(); + if (path.exists()) { + if (McuModuleProjectItem old(path); old == *this) { + return false; + } + } + + QTC_ASSERT_EXPECTED(path.writeFileContents(jsonToQmlproject()), return false); + return true; +} + +bool McuModuleProjectItem::operator==(const McuModuleProjectItem &other) const noexcept +{ + return this->project() == other.project(); +} + +QByteArray McuModuleProjectItem::jsonToQmlproject() const +{ + auto quoted = [](const QString &s) { return QString("\"%1\"").arg(s); }; + auto indent = [](int tabs = 1) { return QString(" ").repeated(tabs * 4); }; + auto quotedQmlFiles = Utils::transform(qmlFiles(), [quoted](const QString &file) { + return quoted(file); + }); + + QString qmlFilesSeparator; + QTextStream ts(&qmlFilesSeparator); + ts << "," << Qt::endl << indent(3); + + return Constants::QmlProject::QMLPROJECT_TEMPLATE + .arg(quoted(uri()), quotedQmlFiles.join(qmlFilesSeparator)) + .toUtf8(); +} +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.h b/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.h new file mode 100644 index 00000000000..fea924e29b7 --- /dev/null +++ b/src/plugins/qmlprojectmanager/qmldirtoqmlproject/mcumoduleprojectitem.h @@ -0,0 +1,50 @@ +// Copyright (C) 2024 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 "../qmlprojectmanager_global.h" + +#include + +#include + +#ifndef UNIT_TESTS +# define MCUMODULEPROJEC_EXPORT QMLPROJECTMANAGER_EXPORT +#else +# define MCUMODULEPROJEC_EXPORT +#endif + +namespace QmlProjectManager { +class MCUMODULEPROJEC_EXPORT McuModuleProjectItem +{ +public: + explicit McuModuleProjectItem(const QJsonObject &project); + explicit McuModuleProjectItem(const Utils::FilePath &qmlprojectFile); + + static std::optional fromQmldirModule(const Utils::FilePath &qmldirFile); + + bool isValid() const noexcept; + + QString uri() const noexcept; + void setUri(const QString &moduleUri); + + QStringList qmlFiles() const noexcept; + void setQmlFiles(const QStringList &files); + + Utils::FilePath qmlProjectPath() const noexcept; + void setQmlProjectPath(const Utils::FilePath &path); + + QJsonObject project() const noexcept; + + bool saveQmlProjectFile() const; + + bool operator==(const McuModuleProjectItem &other) const noexcept; + +private: + QByteArray jsonToQmlproject() const; + + Utils::FilePath m_qmlProjectFile; + QJsonObject m_project; +}; +} // namespace QmlProjectManager diff --git a/tests/unit/tests/unittests/qmlprojectmanager/CMakeLists.txt b/tests/unit/tests/unittests/qmlprojectmanager/CMakeLists.txt index a3859a9ce7b..52c5dcac459 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/CMakeLists.txt +++ b/tests/unit/tests/unittests/qmlprojectmanager/CMakeLists.txt @@ -6,6 +6,7 @@ extend_qtc_test(unittest SOURCES converters-test.cpp projectitem-test.cpp + mcumoduleprojectitem-test.cpp ) unittest_copy_data_folder() diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md index afb4db417f5..c5067a10695 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md @@ -70,3 +70,13 @@ Some main points for qmlproject files for MCU projects: Test data contains an example project folders that file filters will be initialized and tested. * **filelist.txt**: List of the files need to be found by the file filters. + +## Qml to qmlproject test data +Input data for the McuModuleProjectItem tests. McuModuleProjectItem represents the MCU module and can be generated from a regular QML module (based on qmldir). + +* **existing_qmlproject**: read and process valid .qmlproject module +* **incorrect_module_name_qmldir**: generate .qmlproject based on qmldir (failure - module name is wrong) +* **invalid_qmlproject**: read and process invalid .qmlproject module +* **missing_module_name_qmldir**: generate .qmlproject based on qmldir (failure - missing module name) +* **missing_qml_files_qmldir**: generate .qmlproject based on qmldir (failure - missing qml files) +* **missing_qmlproject**: generate .qmlproject based on qmldir (success) diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File1.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File1.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File1.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File2.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File2.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/File2.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/Internal/File3.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/Internal/File3.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/Internal/File3.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/TestSingleton.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/TestSingleton.qml new file mode 100644 index 00000000000..d27a193422d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/TestSingleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/qmldir b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/qmldir new file mode 100644 index 00000000000..bc85f5eb760 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/qmldir @@ -0,0 +1,3 @@ +# test comment +module test.module +singleton TestSingleton 1.0 TestSingleton.qml diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject new file mode 100644 index 00000000000..8dea55b7dc2 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.3 + +Project { + MCU.Module { + uri: "test.module" + } + + QmlFiles { + files: [ + "TestSingleton.qml", + "File1.qml", + "File2.qml", + "Internal/File3.qml" + ] + } +} \ No newline at end of file diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/TestSingleton.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/TestSingleton.qml new file mode 100644 index 00000000000..d27a193422d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/TestSingleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/qmldir b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/qmldir new file mode 100644 index 00000000000..870207b6326 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/incorrect_module_name_qmldir/qmldir @@ -0,0 +1,3 @@ +# test comment +module test module +singleton TestSingleton 1.0 TestSingleton.qml diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject new file mode 100644 index 00000000000..05ee3902ec0 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject @@ -0,0 +1,5 @@ +import QmlProject 1.3 + +Project { + +} \ No newline at end of file diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/TestSingleton.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/TestSingleton.qml new file mode 100644 index 00000000000..d27a193422d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/TestSingleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/qmldir b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/qmldir new file mode 100644 index 00000000000..89a03cef545 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_module_name_qmldir/qmldir @@ -0,0 +1,2 @@ +# test comment +singleton TestSingleton 1.0 TestSingleton.qml diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qml_files_qmldir/qmldir b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qml_files_qmldir/qmldir new file mode 100644 index 00000000000..0c44ccb6166 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qml_files_qmldir/qmldir @@ -0,0 +1,2 @@ +# test comment +module test.module \ No newline at end of file diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File1.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File1.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File1.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File2.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File2.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/File2.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/Internal/File3.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/Internal/File3.qml new file mode 100644 index 00000000000..e136cb34d19 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/Internal/File3.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/TestSingleton.qml b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/TestSingleton.qml new file mode 100644 index 00000000000..d27a193422d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/TestSingleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick + +QtObject { + +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/qmldir b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/qmldir new file mode 100644 index 00000000000..bc85f5eb760 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/qmldirtoqmlproject/missing_qmlproject/qmldir @@ -0,0 +1,3 @@ +# test comment +module test.module +singleton TestSingleton 1.0 TestSingleton.qml diff --git a/tests/unit/tests/unittests/qmlprojectmanager/mcumoduleprojectitem-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/mcumoduleprojectitem-test.cpp new file mode 100644 index 00000000000..885e6ca238c --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/mcumoduleprojectitem-test.cpp @@ -0,0 +1,368 @@ +// Copyright (C) 2024 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" // IWYU pragma: keep + +#include + +#include + +namespace { + +constexpr QLatin1String localTestDataDir{UNITTEST_DIR "/qmlprojectmanager/data"}; + +constexpr QLatin1String jsonProject{R"( +{ + "moduleUri": "test.module", + "qmlFiles": [ + "TestSingleton.qml", + "File1.qml", + "File2.qml", + "Internal/File3.qml" + ], + "qmlProjectPath": "%1" +} +)"}; + +class McuModuleProjectItem : public testing::Test +{ +protected: + static void SetUpTestSuite() + { + existingQmlProject = std::make_unique( + Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject")); + + auto fromQmldir = QmlProjectManager::McuModuleProjectItem::fromQmldirModule( + Utils::FilePath::fromString(localTestDataDir + + "/qmldirtoqmlproject/missing_qmlproject/qmldir")); + missingQmlProject = std::make_unique( + fromQmldir ? *fromQmldir : QmlProjectManager::McuModuleProjectItem{QJsonObject{}}); + + invalidQmlProject = std::make_unique( + Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject")); + + fromJsonObject = std::make_unique( + QJsonDocument::fromJson( + jsonProject.arg(localTestDataDir + "/qmldirtoqmlproject/test_module.qmlproject").toUtf8()) + .object()); + + fromIncompleteJsonObject = std::make_unique( + QJsonDocument::fromJson(jsonProject.toString().toUtf8()).object()); + + createFromEmpty = std::make_unique(QJsonObject{}); + } + + static void TearDownTestSuite() + { + existingQmlProject.reset(); + missingQmlProject->qmlProjectPath().removeFile(); + missingQmlProject.reset(); + invalidQmlProject.reset(); + fromJsonObject.reset(); + fromIncompleteJsonObject.reset(); + createFromEmpty->qmlProjectPath().removeFile(); + createFromEmpty.reset(); + } + +protected: + inline static std::unique_ptr existingQmlProject; + inline static std::unique_ptr missingQmlProject; + inline static std::unique_ptr invalidQmlProject; + inline static std::unique_ptr fromJsonObject; + inline static std::unique_ptr fromIncompleteJsonObject; + inline static std::unique_ptr createFromEmpty; +}; +} // namespace + +TEST_F(McuModuleProjectItem, is_valid_existing_qmlproject) +{ + auto isValid = existingQmlProject->isValid(); + + ASSERT_TRUE(isValid); +} + +TEST_F(McuModuleProjectItem, get_uri_existing_qmlproject) +{ + auto uri = existingQmlProject->uri(); + + ASSERT_THAT(uri, Eq("test.module")); +} + +TEST_F(McuModuleProjectItem, get_qml_files_existing_qmlproject) +{ + auto files = existingQmlProject->qmlFiles(); + + ASSERT_THAT(files, + UnorderedElementsAre("Internal/File3.qml", "File2.qml", "File1.qml", "TestSingleton.qml")); +} + +TEST_F(McuModuleProjectItem, get_qmlproject_path_existing_qmlproject) +{ + auto path = existingQmlProject->qmlProjectPath(); + auto expectedPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject"); + + ASSERT_THAT(path, Eq(expectedPath)); +} + +TEST_F(McuModuleProjectItem, get_project_existing_qmlproject) +{ + auto project = existingQmlProject->project(); + auto expectedJsonProject = QJsonDocument::fromJson( + jsonProject + .arg(localTestDataDir + "/qmldirtoqmlproject/existing_qmlproject/test_module.qmlproject") + .toUtf8()) + .object(); + + ASSERT_THAT(project, Eq(expectedJsonProject)); +} + +TEST_F(McuModuleProjectItem, save_qmlproject_file_existing_qmlproject) +{ + bool saved = existingQmlProject->saveQmlProjectFile(); + + ASSERT_FALSE(saved); +} + +TEST_F(McuModuleProjectItem, is_valid_missing_qmlproject) +{ + auto isValid = missingQmlProject->isValid(); + + ASSERT_TRUE(isValid); +} + +TEST_F(McuModuleProjectItem, get_uri_missing_qmlproject) +{ + auto uri = missingQmlProject->uri(); + + ASSERT_THAT(uri, Eq("test.module")); +} + +TEST_F(McuModuleProjectItem, get_qml_files_missing_qmlproject) +{ + auto files = missingQmlProject->qmlFiles(); + + ASSERT_THAT(files, + UnorderedElementsAre("Internal/File3.qml", "File2.qml", "File1.qml", "TestSingleton.qml")); +} + +TEST_F(McuModuleProjectItem, get_qmlproject_path_missing_qmlproject) +{ + auto path = missingQmlProject->qmlProjectPath(); + auto expectedPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/missing_qmlproject/test_module.qmlproject"); + + ASSERT_THAT(path, Eq(expectedPath)); +} + +TEST_F(McuModuleProjectItem, check_saved_qmlproject_file_missing_qmlproject) +{ + auto projectPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/missing_qmlproject/test_module.qmlproject"); + missingQmlProject->saveQmlProjectFile(); + + QmlProjectManager::McuModuleProjectItem savedQmlProject(projectPath); + + ASSERT_THAT(*missingQmlProject, Eq(savedQmlProject)); +} + +TEST_F(McuModuleProjectItem, is_valid_invalid_qmlproject) +{ + auto isValid = invalidQmlProject->isValid(); + + ASSERT_FALSE(isValid); +} + +TEST_F(McuModuleProjectItem, get_uri_invalid_qmlproject) +{ + auto uri = invalidQmlProject->uri(); + + ASSERT_THAT(uri, IsEmpty()); +} + +TEST_F(McuModuleProjectItem, get_qml_files_invalid_qmlproject) +{ + auto files = invalidQmlProject->qmlFiles(); + + ASSERT_THAT(files, IsEmpty()); +} + +TEST_F(McuModuleProjectItem, get_qmlproject_path_invalid_qmlproject) +{ + auto path = invalidQmlProject->qmlProjectPath(); + auto expectedPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/invalid_qmlproject/test_module.qmlproject"); + + ASSERT_THAT(path, Eq(expectedPath)); +} + +TEST_F(McuModuleProjectItem, save_qmlproject_file_invalid_qmlproject) +{ + bool saved = invalidQmlProject->saveQmlProjectFile(); + + ASSERT_FALSE(saved); +} + +TEST_F(McuModuleProjectItem, is_valid_from_json_object) +{ + auto isValid = fromJsonObject->isValid(); + + ASSERT_TRUE(isValid); +} + +TEST_F(McuModuleProjectItem, get_uri_from_json_object) +{ + auto uri = fromJsonObject->uri(); + + ASSERT_THAT(uri, Eq("test.module")); +} + +TEST_F(McuModuleProjectItem, get_qml_files_from_json_object) +{ + auto files = fromJsonObject->qmlFiles(); + + ASSERT_THAT(files, + UnorderedElementsAre("Internal/File3.qml", "File2.qml", "File1.qml", "TestSingleton.qml")); +} + +TEST_F(McuModuleProjectItem, get_qmlproject_path_from_json_object) +{ + auto path = fromJsonObject->qmlProjectPath(); + auto expectedPath = Utils::FilePath::fromString(localTestDataDir + + "/qmldirtoqmlproject/test_module.qmlproject"); + + ASSERT_THAT(path, Eq(expectedPath)); +} + +TEST_F(McuModuleProjectItem, get_project_from_json_object) +{ + auto project = fromJsonObject->project(); + auto expectedJsonProject = QJsonDocument::fromJson( + jsonProject + .arg(localTestDataDir + + "/qmldirtoqmlproject/test_module.qmlproject") + .toUtf8()) + .object(); + + ASSERT_THAT(project, Eq(expectedJsonProject)); +} + +TEST_F(McuModuleProjectItem, is_valid_from_incomplete_json_object) +{ + auto isValid = fromIncompleteJsonObject->isValid(); + + ASSERT_FALSE(isValid); +} + +TEST_F(McuModuleProjectItem, get_uri_from_incomplete_json_object) +{ + auto uri = fromIncompleteJsonObject->uri(); + + ASSERT_THAT(uri, Eq("test.module")); +} + +TEST_F(McuModuleProjectItem, get_qml_files_from_incomplete_json_object) +{ + auto files = fromIncompleteJsonObject->qmlFiles(); + + ASSERT_THAT(files, + UnorderedElementsAre("Internal/File3.qml", "File2.qml", "File1.qml", "TestSingleton.qml")); +} + +TEST_F(McuModuleProjectItem, get_qmlproject_path_from_incomplete_json_object) +{ + auto path = fromIncompleteJsonObject->qmlProjectPath(); + + ASSERT_FALSE(path.endsWith(".qmlproject")); +} + +TEST_F(McuModuleProjectItem, save_qmlproject_from_incomplete_json_object) +{ + bool saved = fromIncompleteJsonObject->saveQmlProjectFile(); + + ASSERT_FALSE(saved); +} + +TEST_F(McuModuleProjectItem, set_uri_create_from_empty) +{ + createFromEmpty->setUri("test.module"); + + ASSERT_THAT(createFromEmpty->uri(), Eq("test.module")); +} + +TEST_F(McuModuleProjectItem, set_qml_files_create_from_empty) +{ + createFromEmpty->setQmlFiles({"File1.qml", "File2.qml"}); + + ASSERT_THAT(createFromEmpty->qmlFiles(), UnorderedElementsAre("File1.qml", "File2.qml")); +} + +TEST_F(McuModuleProjectItem, set_qmlproject_path_create_from_empty) +{ + auto projectPath = Utils::FilePath::fromString(localTestDataDir + + "/qmldirtoqmlproject/test_module.qmlproject"); + + createFromEmpty->setQmlProjectPath(projectPath); + + ASSERT_THAT(createFromEmpty->qmlProjectPath(), Eq(projectPath)); +} + +TEST_F(McuModuleProjectItem, is_valid_create_from_empty) +{ + bool isValid = createFromEmpty->isValid(); + + ASSERT_TRUE(isValid); +} + +TEST_F(McuModuleProjectItem, check_saved_qmlproject_create_from_empty) +{ + auto projectPath = Utils::FilePath::fromString(localTestDataDir + + "/qmldirtoqmlproject/test_module.qmlproject"); + createFromEmpty->saveQmlProjectFile(); + + QmlProjectManager::McuModuleProjectItem savedQmlProject(projectPath); + + ASSERT_THAT(*createFromEmpty, Eq(savedQmlProject)); +} + +TEST_F(McuModuleProjectItem, create_from_nonexisting_file) +{ + auto projectPath = Utils::FilePath::fromString(localTestDataDir + + "/qmldirtoqmlproject/nonexisting"); + + auto projectItem = QmlProjectManager::McuModuleProjectItem::fromQmldirModule(projectPath); + + ASSERT_FALSE(projectItem); +} + +TEST_F(McuModuleProjectItem, create_from_missing_module_name_qmldir) +{ + auto projectPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/missing_module_name_qmldir/qmldir"); + + auto projectItem = QmlProjectManager::McuModuleProjectItem::fromQmldirModule(projectPath); + + ASSERT_FALSE(projectItem); +} + +TEST_F(McuModuleProjectItem, create_from_incorrect_module_name_qmldir) +{ + auto projectPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/incorrect_module_name_qmldir/qmldir"); + + auto projectItem = QmlProjectManager::McuModuleProjectItem::fromQmldirModule(projectPath); + + ASSERT_FALSE(projectItem); +} + +TEST_F(McuModuleProjectItem, create_from_missing_qml_files_qmldir) +{ + auto projectPath = Utils::FilePath::fromString( + localTestDataDir + "/qmldirtoqmlproject/missing_qml_files_qmldir/qmldir"); + + auto projectItem = QmlProjectManager::McuModuleProjectItem::fromQmldirModule(projectPath); + + ASSERT_FALSE(projectItem); +} From 32cad175c20b720d45751564c05cca82110d0be6 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 22 Oct 2024 16:23:48 +0300 Subject: [PATCH 004/322] QmlDesigner: Set selected node to dynamic properties model at qml setup Fixes: QDS-13789 Change-Id: I147bca2b7f9bcb10e248d5275db9255dfb12b26c Reviewed-by: Mahmoud Badri --- .../components/materialeditor/materialeditorview.cpp | 5 +---- .../components/textureeditor/textureeditorview.cpp | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 59101f56f35..12cf8ed128b 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -602,10 +602,7 @@ void MaterialEditorView::setupQmlBackend() m_qmlBackEnd = currentQmlBackend; - if (m_hasMaterialRoot) - m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial); - else - m_dynamicPropertiesModel->reset(); + m_dynamicPropertiesModel->setSelectedNode(m_selectedMaterial); initPreviewData(); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 64b1523303e..f084735c016 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -485,10 +485,7 @@ void TextureEditorView::setupQmlBackend() m_qmlBackEnd = currentQmlBackend; - if (m_hasTextureRoot) - m_dynamicPropertiesModel->setSelectedNode(m_selectedTexture); - else - m_dynamicPropertiesModel->reset(); + m_dynamicPropertiesModel->setSelectedNode(m_selectedTexture); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); #endif From f3046da7d4abbdc3317030dcacab53a533998ef0 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 22 Oct 2024 15:44:01 +0200 Subject: [PATCH 005/322] DeviceShare: Add Android app connector and device manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I30cb3dc8b71e87fc27482aa503cb53ce98c6bde0 Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/CMakeLists.txt | 13 + .../components/devicesharing/device.cpp | 198 +++++++++++++ .../components/devicesharing/device.h | 64 +++++ .../components/devicesharing/deviceinfo.cpp | 130 +++++++++ .../components/devicesharing/deviceinfo.h | 81 ++++++ .../devicesharing/devicemanager.cpp | 260 ++++++++++++++++++ .../components/devicesharing/devicemanager.h | 69 +++++ .../devicesharing/devicemanagermodel.cpp | 162 +++++++++++ .../devicesharing/devicemanagermodel.h | 47 ++++ 9 files changed, 1024 insertions(+) create mode 100644 src/plugins/qmldesigner/components/devicesharing/device.cpp create mode 100644 src/plugins/qmldesigner/components/devicesharing/device.h create mode 100644 src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp create mode 100644 src/plugins/qmldesigner/components/devicesharing/deviceinfo.h create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanager.h create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 4ee17a05dff..e19d2e3c226 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -34,6 +34,8 @@ add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") add_subdirectory(libs) +find_package(Qt6 REQUIRED COMPONENTS WebSockets) + add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview CONDITION TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg @@ -728,6 +730,17 @@ extend_qtc_plugin(QmlDesigner messagemodel.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/devicesharing + DEPENDS + Qt::WebSockets + SOURCES + device.cpp device.h + deviceinfo.cpp deviceinfo.h + devicemanager.cpp devicemanager.h + devicemanagermodel.cpp devicemanagermodel.h +) + add_qtc_plugin(assetexporterplugin PLUGIN_CLASS AssetExporterPlugin CONDITION TARGET QmlDesigner diff --git a/src/plugins/qmldesigner/components/devicesharing/device.cpp b/src/plugins/qmldesigner/components/devicesharing/device.cpp new file mode 100644 index 00000000000..7c3ebe37ddc --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/device.cpp @@ -0,0 +1,198 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "device.h" + +#include +#include + +namespace QmlDesigner::DeviceShare { + +// Below are the constants that are used in the communication between the Design Studio and the device. +namespace PackageToDevice { +using namespace Qt::Literals; +constexpr auto designStudioReady = "designStudioReady"_L1; +constexpr auto projectData = "projectData"_L1; +constexpr auto stopRunningProject = "stopRunningProject"_L1; +}; // namespace PackageToDevice + +namespace PackageFromDevice { +using namespace Qt::Literals; +constexpr auto deviceInfo = "deviceInfo"_L1; +constexpr auto projectRunning = "projectRunning"_L1; +constexpr auto projectStopped = "projectStopped"_L1; +constexpr auto projectLogs = "projectLogs"_L1; +}; // namespace PackageFromDevice + +Device::Device(const DeviceInfo &deviceInfo, const DeviceSettings &deviceSettings, QObject *parent) + : QObject(parent) + , m_deviceInfo(deviceInfo) + , m_deviceSettings(deviceSettings) + , m_socket(nullptr) + , m_socketWasConnected(false) +{ + qCDebug(deviceSharePluginLog) << "initial device info:" << m_deviceInfo; + + m_socket.reset(new QWebSocket()); + connect(m_socket.data(), &QWebSocket::textMessageReceived, this, &Device::processTextMessage); + connect(m_socket.data(), &QWebSocket::disconnected, this, [this]() { + m_reconnectTimer.start(); + if (!m_socketWasConnected) + return; + + m_socketWasConnected = false; + m_pingTimer.stop(); + m_pongTimer.stop(); + emit disconnected(m_deviceInfo.deviceId()); + }); + connect(m_socket.data(), &QWebSocket::connected, this, [this]() { + m_socketWasConnected = true; + m_reconnectTimer.stop(); + m_pingTimer.start(15000); + sendDesignStudioReady(m_deviceInfo.deviceId()); + emit connected(m_deviceInfo.deviceId()); + }); + + m_reconnectTimer.setSingleShot(true); + m_reconnectTimer.setInterval(5000); + connect(&m_reconnectTimer, &QTimer::timeout, this, &Device::reconnect); + + initPingPong(); + reconnect(); +} + +Device::~Device() +{ + m_socket->close(); + m_socket.reset(); +} + +void Device::initPingPong() +{ + connect(&m_pingTimer, &QTimer::timeout, this, [this]() { + m_socket->ping(); + m_pongTimer.start(15000); + }); + + connect(m_socket.data(), + &QWebSocket::pong, + this, + [this](quint64 elapsedTime, [[maybe_unused]] const QByteArray &payload) { + qCDebug(deviceSharePluginLog) + << "Pong received from Device" << m_deviceInfo.deviceId() << "in" << elapsedTime + << "ms"; + m_pongTimer.stop(); + }); + + connect(&m_pongTimer, &QTimer::timeout, this, [this]() { + qCDebug(deviceSharePluginLog) + << "Device" << m_deviceInfo.deviceId() << "is not responding. Closing connection."; + m_socket->close(); + }); + + m_pongTimer.setSingleShot(true); +} + +void Device::reconnect() +{ + if (m_socket->state() == QAbstractSocket::ConnectedState) + m_socket->close(); + + QUrl url(QStringLiteral("ws://%1:%2").arg(m_deviceSettings.ipAddress()).arg(40000)); + m_socket->open(url); +} + +DeviceInfo Device::deviceInfo() const +{ + return m_deviceInfo; +} + +void Device::setDeviceInfo(const DeviceInfo &deviceInfo) +{ + m_deviceInfo = deviceInfo; +} + +DeviceSettings Device::deviceSettings() const +{ + return m_deviceSettings; +} + +void Device::setDeviceSettings(const DeviceSettings &deviceSettings) +{ + m_deviceSettings = deviceSettings; + reconnect(); +} + +void Device::sendDesignStudioReady(const QString &uuid) { + sendTextMessage(PackageToDevice::designStudioReady, uuid); +} + +void Device::sendProjectNotification() +{ + sendTextMessage(PackageToDevice::projectData); +} + +void Device::sendProjectData(const QByteArray &data) +{ + sendBinaryMessage(data); +} + +void Device::sendProjectStopped() +{ + sendTextMessage(PackageToDevice::stopRunningProject); +} + +bool Device::isConnected() const +{ + return m_socket ? m_socket->state() == QAbstractSocket::ConnectedState : false; +} + +void Device::sendTextMessage(const QLatin1String &dataType, const QJsonValue &data) +{ + if (!isConnected()) + return; + + QJsonObject message; + message["dataType"] = dataType; + message["data"] = data; + const QString jsonMessage = QString::fromLatin1( + QJsonDocument(message).toJson(QJsonDocument::Compact)); + m_socket->sendTextMessage(jsonMessage); +} + +void Device::sendBinaryMessage(const QByteArray &data) +{ + if (!isConnected()) + return; + + m_socket->sendBinaryMessage(data); +} + +void Device::processTextMessage(const QString &data) +{ + QJsonParseError jsonError; + const QJsonDocument jsonDoc = QJsonDocument::fromJson(data.toLatin1(), &jsonError); + if (jsonError.error != QJsonParseError::NoError) { + qCDebug(deviceSharePluginLog) + << "Failed to parse JSON message:" << jsonError.errorString() << data; + return; + } + + const QJsonObject jsonObj = jsonDoc.object(); + const QString dataType = jsonObj.value("dataType").toString(); + if (dataType == PackageFromDevice::deviceInfo) { + QJsonObject deviceInfo = jsonObj.value("data").toObject(); + m_deviceInfo.setJsonObject(deviceInfo); + emit deviceInfoReady(m_deviceInfo.deviceId(), m_deviceInfo); + } else if (dataType == PackageFromDevice::projectRunning) { + emit projectStarted(m_deviceInfo.deviceId()); + } else if (dataType == PackageFromDevice::projectStopped) { + emit projectStopped(m_deviceInfo.deviceId()); + } else if (dataType == PackageFromDevice::projectLogs) { + emit projectLogsReceived(m_deviceInfo.deviceId(), jsonObj.value("data").toString()); + } else { + qCDebug(deviceSharePluginLog) << "Invalid JSON message:" << jsonObj; + } +} + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/device.h b/src/plugins/qmldesigner/components/devicesharing/device.h new file mode 100644 index 00000000000..1d9042893de --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/device.h @@ -0,0 +1,64 @@ +// Copyright (C) 2024 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 "deviceinfo.h" + +namespace QmlDesigner::DeviceShare { + +class Device : public QObject +{ + Q_OBJECT +public: + Device(const DeviceInfo &deviceInfo = {}, + const DeviceSettings &deviceSettings = {}, + QObject *parent = nullptr); + ~Device(); + + // device management + DeviceInfo deviceInfo() const; + DeviceSettings deviceSettings() const; + void setDeviceInfo(const DeviceInfo &deviceInfo); + void setDeviceSettings(const DeviceSettings &deviceSettings); + + // device communication + void sendDesignStudioReady(const QString &uuid); + void sendProjectNotification(); + void sendProjectData(const QByteArray &data); + void sendProjectStopped(); + + // socket + bool isConnected() const; + void reconnect(); + +private slots: + void processTextMessage(const QString &data); + +private: + DeviceInfo m_deviceInfo; + DeviceSettings m_deviceSettings; + + QScopedPointer m_socket; + bool m_socketWasConnected; + + QTimer m_reconnectTimer; + QTimer m_pingTimer; + QTimer m_pongTimer; + + void initPingPong(); + void sendTextMessage(const QLatin1String &dataType, const QJsonValue &data = QJsonValue()); + void sendBinaryMessage(const QByteArray &data); + +signals: + void connected(const QString &deviceId); + void disconnected(const QString &deviceId); + void deviceInfoReady(const QString &deviceId, const DeviceInfo &deviceInfo); + void projectStarted(const QString &deviceId); + void projectStopped(const QString &deviceId); + void projectLogsReceived(const QString &deviceId, const QString &logs); +}; +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp new file mode 100644 index 00000000000..f2557100f09 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "deviceinfo.h" + +#include + +namespace QmlDesigner::DeviceShare { +Q_LOGGING_CATEGORY(deviceSharePluginLog, "qtc.designer.deviceSharePluginLog") + +IDeviceData::IDeviceData(const QJsonObject &data) + : m_data(data) +{} + +QJsonObject IDeviceData::jsonObject() const +{ + return m_data; +} + +void IDeviceData::setJsonObject(const QJsonObject &data) +{ + m_data = data; +} + +IDeviceData::operator QString() const +{ + return QString::fromLatin1(QJsonDocument(m_data).toJson(QJsonDocument::Compact)); +} + +bool DeviceSettings::active() const +{ + return m_data.value(keyActive).toBool(); +} + +QString DeviceSettings::alias() const +{ + return m_data.value(keyAlias).toString(); +} + +QString DeviceSettings::ipAddress() const +{ + return m_data.value(keyIpAddress).toString(); +} + +void DeviceSettings::setActive(const bool &active) +{ + m_data[keyActive] = active; +} + +void DeviceSettings::setAlias(const QString &alias) +{ + m_data[keyAlias] = alias; +} + +void DeviceSettings::setIpAddress(const QString &ipAddress) +{ + m_data[keyIpAddress] = ipAddress; +} + +QString DeviceInfo::os() const +{ + return m_data.value(keyOs).toString(); +} + +QString DeviceInfo::osVersion() const +{ + return m_data.value(keyOsVersion).toString(); +} + +QString DeviceInfo::architecture() const +{ + return m_data.value(keyArchitecture).toString(); +} + +int DeviceInfo::screenWidth() const +{ + return m_data.value(keyScreenWidth).toInt(); +} + +int DeviceInfo::screenHeight() const +{ + return m_data.value(keyScreenHeight).toInt(); +} + +QString DeviceInfo::deviceId() const +{ + return m_data.value(keyDeviceId).toString(); +} + +QString DeviceInfo::appVersion() const +{ + return m_data.value(keyAppVersion).toString(); +} + +void DeviceInfo::setOs(const QString &os) +{ + m_data[keyOs] = os; +} + +void DeviceInfo::setOsVersion(const QString &osVersion) +{ + m_data[keyOsVersion] = osVersion; +} + +void DeviceInfo::setArchitecture(const QString &architecture) +{ + m_data[keyArchitecture] = architecture; +} + +void DeviceInfo::setScreenWidth(const int &screenWidth) +{ + m_data[keyScreenWidth] = screenWidth; +} + +void DeviceInfo::setScreenHeight(const int &screenHeight) +{ + m_data[keyScreenHeight] = screenHeight; +} + +void DeviceInfo::setDeviceId(const QString &deviceId) +{ + m_data[keyDeviceId] = deviceId; +} + +void DeviceInfo::setAppVersion(const QString &appVersion) +{ + m_data[keyAppVersion] = appVersion; +} + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/deviceinfo.h b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.h new file mode 100644 index 00000000000..cc79e546795 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.h @@ -0,0 +1,81 @@ +// Copyright (C) 2024 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::DeviceShare { +Q_DECLARE_LOGGING_CATEGORY(deviceSharePluginLog); + +class IDeviceData +{ +public: + IDeviceData(const QJsonObject &data = QJsonObject()); + virtual ~IDeviceData() = default; + + // converters + QJsonObject jsonObject() const; + void setJsonObject(const QJsonObject &data); + operator QString() const; + +protected: + QJsonObject m_data; +}; + +class DeviceSettings : public IDeviceData +{ +public: + DeviceSettings() = default; + + // Getters + bool active() const; + QString alias() const; + QString ipAddress() const; + + // Setters + void setActive(const bool &active); + void setAlias(const QString &alias); + void setIpAddress(const QString &ipAddress); + +private: + static constexpr char keyActive[] = "deviceActive"; + static constexpr char keyAlias[] = "deviceAlias"; + static constexpr char keyIpAddress[] = "ipAddress"; +}; + +class DeviceInfo : public IDeviceData +{ +public: + DeviceInfo() = default; + + // Getters + QString os() const; + QString osVersion() const; + QString architecture() const; + int screenWidth() const; + int screenHeight() const; + QString deviceId() const; + QString appVersion() const; + + // Setters + void setOs(const QString &os); + void setOsVersion(const QString &osVersion); + void setArchitecture(const QString &architecture); + void setScreenWidth(const int &screenWidth); + void setScreenHeight(const int &screenHeight); + void setDeviceId(const QString &deviceId); + void setAppVersion(const QString &appVersion); + +private: + static constexpr char keyOs[] = "os"; + static constexpr char keyOsVersion[] = "osVersion"; + static constexpr char keyScreenWidth[] = "screenWidth"; + static constexpr char keyScreenHeight[] = "screenHeight"; + static constexpr char keyDeviceId[] = "deviceId"; + static constexpr char keyArchitecture[] = "architecture"; + static constexpr char keyAppVersion[] = "appVersion"; +}; + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp new file mode 100644 index 00000000000..79fefa2e010 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -0,0 +1,260 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "devicemanager.h" + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner::DeviceShare { + +DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath) + : QObject(parent) + , m_settingsPath(settingsPath) +{ + readSettings(); + if (m_uuid.isEmpty()) { + m_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); + writeSettings(); + } +} + +void DeviceManager::writeSettings() +{ + QJsonObject root; + QJsonArray devices; + for (const auto &device : m_devices) { + QJsonObject deviceInfo; + deviceInfo.insert("deviceInfo", device->deviceInfo().jsonObject()); + deviceInfo.insert("deviceSettings", device->deviceSettings().jsonObject()); + devices.append(deviceInfo); + } + + root.insert("devices", devices); + root.insert("uuid", m_uuid); + + QJsonDocument doc(root); + QFile file(m_settingsPath); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(deviceSharePluginLog) << "Failed to open settings file" << file.fileName(); + return; + } + + file.write(doc.toJson()); +} + +void DeviceManager::readSettings() +{ + QFile file(m_settingsPath); + qCDebug(deviceSharePluginLog) << "Reading settings from" << file.fileName(); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(deviceSharePluginLog) << "Failed to open settings file" << file.fileName(); + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + m_uuid = doc.object()["uuid"].toString(); + QJsonArray devices = doc.object()["devices"].toArray(); + for (const QJsonValue &deviceInfoJson : devices) { + DeviceInfo deviceInfo; + DeviceSettings deviceSettings; + deviceInfo.setJsonObject(deviceInfoJson.toObject()["deviceInfo"].toObject()); + deviceSettings.setJsonObject(deviceInfoJson.toObject()["deviceSettings"].toObject()); + auto device = initDevice(deviceInfo, deviceSettings); + m_devices.append(device); + } +} + +QList> DeviceManager::devices() const +{ + return m_devices; +} + +QSharedPointer DeviceManager::findDevice(const QString &deviceId) const +{ + auto it = std::find_if(m_devices.begin(), m_devices.end(), [deviceId](const auto &device) { + return device->deviceInfo().deviceId() == deviceId; + }); + + return it != m_devices.end() ? *it : nullptr; +} + +std::optional DeviceManager::deviceInfo(const QString &deviceId) const +{ + auto device = findDevice(deviceId); + if (!device) + return {}; + + return device->deviceInfo(); +} + +void DeviceManager::setDeviceAlias(const QString &deviceId, const QString &alias) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + auto deviceSettings = device->deviceSettings(); + deviceSettings.setAlias(alias); + device->setDeviceSettings(deviceSettings); + writeSettings(); +} + +void DeviceManager::setDeviceActive(const QString &deviceId, const bool active) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + auto deviceSettings = device->deviceSettings(); + deviceSettings.setActive(active); + device->setDeviceSettings(deviceSettings); + writeSettings(); +} + +void DeviceManager::setDeviceIP(const QString &deviceId, const QString &ip) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + auto deviceSettings = device->deviceSettings(); + deviceSettings.setIpAddress(ip); + device->setDeviceSettings(deviceSettings); + writeSettings(); +} + +void DeviceManager::addDevice(const QString &ip) +{ + if (ip.isEmpty()) + return; + + const auto trimmedIp = ip.trimmed(); + + // check regex for xxx.xxx.xxx.xxx + QRegularExpression ipRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)"); + if (!ipRegex.match(trimmedIp).hasMatch()) { + qCWarning(deviceSharePluginLog) << "Invalid IP address" << ip; + return; + } + + for (const auto &device : m_devices) { + if (device->deviceSettings().ipAddress() == trimmedIp) { + qCWarning(deviceSharePluginLog) << "Device" << trimmedIp << "already exists"; + return; + } + } + + DeviceSettings deviceSettings; + deviceSettings.setIpAddress(trimmedIp); + auto device = initDevice({}, deviceSettings); + m_devices.append(device); + writeSettings(); + emit deviceAdded(device->deviceInfo()); +} + +QSharedPointer DeviceManager::initDevice(const DeviceInfo &deviceInfo, + const DeviceSettings &deviceSettings) +{ + QSharedPointer device = QSharedPointer(new Device{deviceInfo, deviceSettings}, + &QObject::deleteLater); + connect(device.data(), &Device::deviceInfoReady, this, &DeviceManager::deviceInfoReceived); + connect(device.data(), &Device::disconnected, this, &DeviceManager::deviceDisconnected); + connect(device.data(), &Device::projectStarted, this, [this](const QString deviceId) { + auto device = findDevice(deviceId); + qCDebug(deviceSharePluginLog) << "Project started on device" << deviceId; + emit projectStarted(device->deviceInfo()); + }); + connect(device.data(), &Device::projectStopped, this, [this](const QString deviceId) { + auto device = findDevice(deviceId); + qCDebug(deviceSharePluginLog) << "Project stopped on device" << deviceId; + emit projectStopped(device->deviceInfo()); + }); + connect(device.data(), + &Device::projectLogsReceived, + this, + [this](const QString deviceId, const QString &logs) { + auto device = findDevice(deviceId); + qCDebug(deviceSharePluginLog) << "Log:" << deviceId << logs; + emit projectLogsReceived(device->deviceInfo(), logs); + }); + + return device; +} + +void DeviceManager::deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo) +{ + writeSettings(); + qCDebug(deviceSharePluginLog) << "Device" << deviceId << "is online"; + emit deviceOnline(deviceInfo); +} + +void DeviceManager::deviceDisconnected(const QString &deviceId) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + qCDebug(deviceSharePluginLog) << "Device" << deviceId << "disconnected"; + emit deviceOffline(device->deviceInfo()); +} + +void DeviceManager::removeDevice(const QString &deviceId) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + const auto deviceInfo = device->deviceInfo(); + m_devices.removeOne(device); + writeSettings(); + emit deviceRemoved(deviceInfo); +} + +void DeviceManager::removeDeviceAt(int index) +{ + if (index < 0 || index >= m_devices.size()) + return; + + auto deviceInfo = m_devices[index]->deviceInfo(); + m_devices.removeAt(index); + writeSettings(); + emit deviceRemoved(deviceInfo); +} + +void DeviceManager::sendProjectFile(const QString &deviceId, const QString &projectFile) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + device->sendProjectNotification(); + + QFile file(projectFile); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(deviceSharePluginLog) << "Failed to open project file" << projectFile; + return; + } + + qCDebug(deviceSharePluginLog) << "Sending project file to device" << deviceId; + + QByteArray projectData = file.readAll(); + device->sendProjectData(projectData); + + qCDebug(deviceSharePluginLog) << "Project file sent to device" << deviceId; +} + +void DeviceManager::stopRunningProject(const QString &deviceId) +{ + auto device = findDevice(deviceId); + if (!device) + return; + + device->sendProjectStopped(); +} + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h new file mode 100644 index 00000000000..bd3484b6d18 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -0,0 +1,69 @@ +// Copyright (C) 2024 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 "device.h" + +namespace QmlDesigner::DeviceShare { + +class DeviceManager : public QObject +{ + Q_OBJECT +public: + explicit DeviceManager(QObject *parent = nullptr, const QString &settingsPath = "settings.json"); + + // internal init functions + void addDevice(const QString &ip); + + // Getters + QList> devices() const; + std::optional deviceInfo(const QString &deviceId) const; + + // Device management functions + void setDeviceAlias(const QString &deviceId, const QString &alias); + void setDeviceActive(const QString &deviceId, const bool active); + void setDeviceIP(const QString &deviceId, const QString &ip); + + void removeDevice(const QString &deviceId); + void removeDeviceAt(int index); + void sendProjectFile(const QString &deviceId, const QString &projectFile); + void stopRunningProject(const QString &deviceId); + +private: + // Devices management + QList> m_devices; + + // settings + const QString m_settingsPath; + QString m_uuid; + +private: + // internal slots + void incomingConnection(); + void readSettings(); + void writeSettings(); + QSharedPointer initDevice(const DeviceInfo &deviceInfo = DeviceInfo(), + const DeviceSettings &deviceSettings = DeviceSettings()); + + // device signals + void deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo); + void deviceDisconnected(const QString &deviceId); + + QSharedPointer findDevice(const QString &deviceId) const; + +signals: + void deviceAdded(const DeviceInfo &deviceInfo); + void deviceRemoved(const DeviceInfo &deviceInfo); + void deviceOnline(const DeviceInfo &deviceInfo); + void deviceOffline(const DeviceInfo &deviceInfo); + void projectStarted(const DeviceInfo &deviceInfo); + void projectStopped(const DeviceInfo &deviceInfo); + void projectLogsReceived(const DeviceInfo &deviceInfo, const QString &logs); +}; + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp new file mode 100644 index 00000000000..6614cb3cf48 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp @@ -0,0 +1,162 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "devicemanagermodel.h" +#include "devicemanager.h" + +namespace QmlDesigner::DeviceShare { + +DeviceManagerModel::DeviceManagerModel(DeviceManager &deviceManager, QObject *parent) + : QAbstractTableModel(parent) + , m_deviceManager(deviceManager) +{ + connect(&m_deviceManager, &DeviceManager::deviceAdded, this, [this](const DeviceInfo &) { + endResetModel(); + }); + connect(&m_deviceManager, &DeviceManager::deviceRemoved, this, [this](const DeviceInfo &) { + endResetModel(); + }); + + connect(&m_deviceManager, &DeviceManager::deviceOnline, this, [this](const DeviceInfo &) { + endResetModel(); + }); + + connect(&m_deviceManager, &DeviceManager::deviceOffline, this, [this](const DeviceInfo &) { + endResetModel(); + }); +} + +int DeviceManagerModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_deviceManager.devices().size(); +} + +int DeviceManagerModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return DeviceColumns::COLUMN_COUNT; +} + +QVariant DeviceManagerModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) { + const auto deviceInfo = m_deviceManager.devices()[index.row()]->deviceInfo(); + const auto deviceSettings = m_deviceManager.devices()[index.row()]->deviceSettings(); + bool isConnected = m_deviceManager.devices()[index.row()]->isConnected(); + switch (index.column()) { + case DeviceColumns::Active: + return deviceSettings.active(); + case DeviceColumns::Status: + return static_cast(isConnected); + case DeviceColumns::Alias: + return deviceSettings.alias(); + case DeviceColumns::IPv4Addr: + return deviceSettings.ipAddress(); + case DeviceColumns::OS: + return deviceInfo.os(); + case DeviceColumns::OSVersion: + return deviceInfo.osVersion(); + case DeviceColumns::Architecture: + return deviceInfo.architecture(); + case DeviceColumns::ScreenSize: + return QString("%1x%2").arg(deviceInfo.screenWidth()).arg(deviceInfo.screenHeight()); + case DeviceColumns::AppVersion: + return deviceInfo.appVersion(); + case DeviceColumns::DeviceId: + return deviceInfo.deviceId(); + } + } + + if (role == Qt::EditRole) { + const auto deviceSettings = m_deviceManager.devices()[index.row()]->deviceSettings(); + switch (index.column()) { + case DeviceColumns::Alias: + return deviceSettings.alias(); + case DeviceColumns::Active: + return deviceSettings.active(); + } + } + + return QVariant(); +} + +QVariant DeviceManagerModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case DeviceColumns::Active: + return "Active"; + case DeviceColumns::Status: + return "Status"; + case DeviceColumns::Alias: + return "Alias"; + case DeviceColumns::IPv4Addr: + return "IPv4 Address"; + case DeviceColumns::OS: + return "OS"; + case DeviceColumns::OSVersion: + return "OS Version"; + case DeviceColumns::Architecture: + return "Architecture"; + case DeviceColumns::ScreenSize: + return "Screen Size"; + case DeviceColumns::AppVersion: + return "App Version"; + case DeviceColumns::DeviceId: + return "Device ID"; + case DeviceColumns::Remove: + return "Remove"; + } + } + + return QVariant(); +} + +bool DeviceManagerModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || role != Qt::EditRole) { + qCWarning(deviceSharePluginLog) << "Invalid index or role"; + return false; + } + + auto deviceInfo = m_deviceManager.devices()[index.row()]->deviceInfo(); + switch (index.column()) { + case DeviceColumns::Alias: + m_deviceManager.setDeviceAlias(deviceInfo.deviceId(), value.toString()); + break; + case DeviceColumns::Active: + m_deviceManager.setDeviceActive(deviceInfo.deviceId(), value.toBool()); + break; + case DeviceColumns::IPv4Addr: + m_deviceManager.setDeviceIP(deviceInfo.deviceId(), value.toString()); + break; + } + emit dataChanged(index, index, {role}); + return true; +} + +Qt::ItemFlags DeviceManagerModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if (index.column() == DeviceColumns::Active || index.column() == DeviceColumns::Alias) + flags |= Qt::ItemIsEditable; + + return flags; +} + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h new file mode 100644 index 00000000000..c3c676d02bd --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h @@ -0,0 +1,47 @@ +// Copyright (C) 2024 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::DeviceShare { + +class DeviceManager; +class DeviceManagerModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit DeviceManagerModel(DeviceManager &deviceManager, QObject *parent = nullptr); + + enum DeviceStatus { Offline, Online }; + Q_ENUM(DeviceStatus) + + enum DeviceColumns { + Active, + Status, + Alias, + IPv4Addr, + OS, + OSVersion, + Architecture, + ScreenSize, + AppVersion, + DeviceId, + Remove, + COLUMN_COUNT + }; + Q_ENUM(DeviceColumns) + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, 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; + +private: + DeviceManager &m_deviceManager; +}; + +} // namespace QmlDesigner::DeviceShare From fa5d5b4e0ddc2028913ad107f36a1337265a9f01 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Oct 2024 07:29:22 +0200 Subject: [PATCH 006/322] Fix warnings for unused parameter Change-Id: I55e7567da723e4ecafc456093aa80b271e89157b Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian --- src/plugins/effectcomposer/effectcomposermodel.cpp | 2 +- src/plugins/studiowelcome/fieldhelper.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 6f7a2612c98..d7a94a7278f 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -1574,7 +1574,7 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform) } // Return name for the image property Image element -QString EffectComposerModel::getImageElementName(const Uniform &uniform, bool localFiles) +QString EffectComposerModel::getImageElementName(const Uniform &uniform, bool) { QString simplifiedName = uniform.name().simplified(); simplifiedName = simplifiedName.remove(' '); diff --git a/src/plugins/studiowelcome/fieldhelper.cpp b/src/plugins/studiowelcome/fieldhelper.cpp index 5fdf3532be3..fea36813d31 100644 --- a/src/plugins/studiowelcome/fieldhelper.cpp +++ b/src/plugins/studiowelcome/fieldhelper.cpp @@ -43,7 +43,7 @@ QString ComboBoxHelper::text(int index) const return model->item(index)->text(); } -int ComboBoxHelper::indexOf(const QString &text) const +int ComboBoxHelper::indexOf(const QString &) const { QTC_ASSERT(m_field, return -1); From 18ca16cb2582a080fcc72daf44ddcf7f6ffdb7a0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 21 Oct 2024 08:54:07 +0200 Subject: [PATCH 007/322] QmlDesigner: Fallback to application Qt If the kit has no Qt it would return an empty path. That leads to errors later. In that case the application Qt is used. Change-Id: Ie5e755bb1f1ada80ea0e565756689a4acc3499fe Reviewed-by: Thomas Hartmann --- .../qmldesigner/qmldesignerprojectmanager.cpp | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 9c04f9b1bd3..3ca5e479a05 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -354,26 +354,13 @@ void QmlDesignerProjectManager::editorsClosed(const QList<::Core::IEditor *> &) namespace { -QtSupport::QtVersion *getQtVersion(::ProjectExplorer::Target *target) -{ - if (target) - return QtSupport::QtKitAspect::qtVersion(target->kit()); - - return {}; -} - -[[maybe_unused]] QtSupport::QtVersion *getQtVersion(::ProjectExplorer::Project *project) -{ - return getQtVersion(project->activeTarget()); -} - -Utils::FilePath qmlPath(::ProjectExplorer::Target *target) +QString qmlPath(::ProjectExplorer::Target *target) { auto qt = QtSupport::QtKitAspect::qtVersion(target->kit()); if (qt) - return qt->qmlPath(); + return qt->qmlPath().path(); - return {}; + return QLibraryInfo::path(QLibraryInfo::QmlImportsPath); } [[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) @@ -388,7 +375,7 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) [[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { - auto qmlRootPath = qmlPath(target).toString(); + auto qmlRootPath = qmlPath(target); qmldirPaths.push_back(qmlRootPath + "/QtQml"); qmldirPaths.push_back(qmlRootPath + "/QtQuick"); qmldirPaths.push_back(qmlRootPath + "/QtQuick3D"); @@ -443,7 +430,7 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(2); - const QString qmlRootPath = qmlPath(target).toString(); + const QString qmlRootPath = qmlPath(target); qmldirPaths.append(qmlRootPath + "/builtins.qmltypes"); qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes"); From 09e5e133bb27bc08eb7a1b9ccfc8a493bf6d2a35 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 4 Sep 2024 18:58:12 +0200 Subject: [PATCH 008/322] Add rewriter testing Adding only the framework to write the rewriter tests. Task-number: QDS-13406 Change-Id: If4a7476d09595624c3a752411d516f4d9f3d601a Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../libs/designercore/include/rewriterview.h | 9 +++- .../libs/designercore/include/textmodifier.h | 6 +-- .../designercore/rewriter/rewriterview.cpp | 25 ++++++++--- tests/unit/tests/mocks/CMakeLists.txt | 1 + tests/unit/tests/mocks/textmodifiermock.h | 31 +++++++++++++ .../unit/tests/unittests/model/CMakeLists.txt | 1 + .../unittests/model/rewriterview-test.cpp | 45 +++++++++++++++++++ 8 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 tests/unit/tests/mocks/textmodifiermock.h create mode 100644 tests/unit/tests/unittests/model/rewriterview-test.cpp diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index e19d2e3c226..a4d41a5877a 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -10,6 +10,7 @@ option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDI add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "") option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE}) +add_feature_info("Use project storage" ${USE_PROJECTSTORAGE} "") option(DETACH_DISABLED_VIEWS "Detach disabled views" OFF) env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF) diff --git a/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h b/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h index 1fdb6837238..5960e8fb0a3 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h @@ -42,6 +42,8 @@ struct QmlTypeData bool isCppType = false; }; +enum class InstantQmlTextUpdate { No, Yes }; + class QMLDESIGNERCORE_EXPORT RewriterView : public AbstractView { Q_OBJECT @@ -54,7 +56,8 @@ public: public: RewriterView(ExternalDependenciesInterface &externalDependencies, - DifferenceHandling differenceHandling = RewriterView::Amend); + DifferenceHandling differenceHandling = RewriterView::Amend, + InstantQmlTextUpdate instantQmlTextUpdate = InstantQmlTextUpdate::Yes); ~RewriterView() override; void modelAttached(Model *model) override; @@ -189,8 +192,10 @@ protected: // functions private: //variables ModelNode nodeAtTextCursorPositionHelper(const ModelNode &root, int cursorPosition) const; void setupCanonicalHashes() const; +#ifndef QDS_USE_PROJECTSTORAGE void handleLibraryInfoUpdate(); void handleProjectUpdate(); +#endif bool inErrorState() const { return !m_rewritingErrorMessage.isEmpty(); } QPointer m_textModifier; @@ -209,7 +214,7 @@ private: //variables QString m_rewritingErrorMessage; QString m_lastCorrectQmlSource; QTimer m_amendTimer; - bool m_instantQmlTextUpdate = false; + InstantQmlTextUpdate m_instantQmlTextUpdate = InstantQmlTextUpdate::No; std::function m_setWidgetStatusCallback; bool m_hasIncompleteTypeInformation = false; bool m_restoringAuxData = false; diff --git a/src/plugins/qmldesigner/libs/designercore/include/textmodifier.h b/src/plugins/qmldesigner/libs/designercore/include/textmodifier.h index e361d806a16..e7ac2a23ce5 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/textmodifier.h +++ b/src/plugins/qmldesigner/libs/designercore/include/textmodifier.h @@ -23,8 +23,8 @@ class QMLDESIGNERCORE_EXPORT TextModifier: public QObject Q_OBJECT private: - TextModifier(const TextModifier &); - TextModifier &operator=(const TextModifier &); + TextModifier(const TextModifier &) = delete; + TextModifier &operator=(const TextModifier &) = delete; public: struct MoveInfo { @@ -42,7 +42,7 @@ public: public: TextModifier() = default; - ~TextModifier() override = 0; + ~TextModifier(); virtual void replace(int offset, int length, const QString& replacement) = 0; virtual void move(const MoveInfo &moveInfo) = 0; diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp index dd55bd2ef67..f7d4b5bc243 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp @@ -53,20 +53,25 @@ bool debugQmlPuppet(const DesignerSettings &settings) } RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, - DifferenceHandling differenceHandling) + DifferenceHandling differenceHandling, + InstantQmlTextUpdate instantQmlTextUpdate) : AbstractView{externalDependencies} , m_differenceHandling(differenceHandling) , m_positionStorage(std::make_unique()) , m_modelToTextMerger(std::make_unique(this)) , m_textToModelMerger(std::make_unique(this)) + , m_instantQmlTextUpdate(instantQmlTextUpdate) { setKind(Kind::Rewriter); m_amendTimer.setSingleShot(true); - m_amendTimer.setInterval(800); - connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText); + if (m_instantQmlTextUpdate == InstantQmlTextUpdate::No) { + m_amendTimer.setInterval(800); + connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText); + } +#ifndef QDS_USE_PROJECTSTORAGE QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); connect(modelManager, &QmlJS::ModelManagerInterface::libraryInfoUpdated, @@ -83,6 +88,7 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, this, &RewriterView::handleLibraryInfoUpdate, Qt::QueuedConnection); +#endif } RewriterView::~RewriterView() = default; @@ -112,7 +118,9 @@ void RewriterView::modelAttached(Model *model) if (!(m_errors.isEmpty() && m_warnings.isEmpty())) notifyErrorsAndWarnings(m_errors); - if (hasIncompleteTypeInformation()) { + if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes) { + restoreAuxiliaryData(); + } else if (hasIncompleteTypeInformation()) { m_modelAttachPending = true; QTimer::singleShot(1000, this, [this, model]() { modelAttached(model); @@ -884,6 +892,7 @@ void RewriterView::setupCanonicalHashes() const } } +#ifndef QDS_USE_PROJECTSTORAGE void RewriterView::handleLibraryInfoUpdate() { // Trigger dummy amend to reload document when library info changes @@ -898,6 +907,7 @@ void RewriterView::handleProjectUpdate() { emit modelInterfaceProjectUpdated(); } +#endif ModelNode RewriterView::nodeAtTextCursorPosition(int cursorPosition) const { @@ -913,8 +923,8 @@ bool RewriterView::renameId(const QString &oldId, const QString &newId) && rootModelNode().hasBindingProperty(propertyName) && rootModelNode().bindingProperty(propertyName).isAliasExport(); - bool instant = m_instantQmlTextUpdate; - m_instantQmlTextUpdate = true; + auto instant = m_instantQmlTextUpdate; + m_instantQmlTextUpdate = InstantQmlTextUpdate::Yes; bool refactoring = textModifier()->renameId(oldId, newId); @@ -1158,7 +1168,8 @@ void RewriterView::qmlTextChanged() } case Amend: { - if (m_instantQmlTextUpdate || externalDependencies().instantQmlTextUpdate()) { + if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes + || externalDependencies().instantQmlTextUpdate()) { amendQmlText(); } else { if (externalDependencies().viewManagerUsesRewriterView(this)) { diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index 0fdfa639c09..432a2ca2c54 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -40,4 +40,5 @@ add_qtc_library(TestMocks OBJECT sqlitetransactionbackendmock.h sqlitewritestatementmock.cpp sqlitewritestatementmock.h + textmodifiermock.h ) diff --git a/tests/unit/tests/mocks/textmodifiermock.h b/tests/unit/tests/mocks/textmodifiermock.h new file mode 100644 index 00000000000..773011a4571 --- /dev/null +++ b/tests/unit/tests/mocks/textmodifiermock.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 "../utils/googletest.h" + +#include + +class TextModifierMock : public QmlDesigner::TextModifier +{ + MOCK_METHOD(void, replace, (int offset, int length, const QString &replacement), (override)); + MOCK_METHOD(void, move, (const MoveInfo &moveInfo), (override)); + MOCK_METHOD(void, indent, (int offset, int length), (override)); + MOCK_METHOD(void, indentLines, (int startLine, int endLine), (override)); + MOCK_METHOD(TextEditor::TabSettings, tabSettings, (), (const, override)); + MOCK_METHOD(void, startGroup, (), (override)); + MOCK_METHOD(void, flushGroup, (), (override)); + MOCK_METHOD(void, commitGroup, (), (override)); + MOCK_METHOD(QTextDocument *, textDocument, (), (const, override)); + MOCK_METHOD(QString, text, (), (const, override)); + MOCK_METHOD(QTextCursor, textCursor, (), (const, override)); + MOCK_METHOD(void, deactivateChangeSignals, (), (override)); + MOCK_METHOD(void, reactivateChangeSignals, (), (override)); + MOCK_METHOD(bool, renameId, (const QString &oldId, const QString &newId), (override)); + MOCK_METHOD(QStringList, + autoComplete, + (QTextDocument * textDocument, int positio, bool explicitComplete), + (override)); + MOCK_METHOD(bool, moveToComponent, (int nodeOffset, const QString &importData), (override)); +}; diff --git a/tests/unit/tests/unittests/model/CMakeLists.txt b/tests/unit/tests/unittests/model/CMakeLists.txt index b29eda3995f..37289ae6ddf 100644 --- a/tests/unit/tests/unittests/model/CMakeLists.txt +++ b/tests/unit/tests/unittests/model/CMakeLists.txt @@ -7,4 +7,5 @@ extend_qtc_test(unittest modelnode-test.cpp modelresourcemanagement-test.cpp nodelistproperty-test.cpp + rewriterview-test.cpp ) diff --git a/tests/unit/tests/unittests/model/rewriterview-test.cpp b/tests/unit/tests/unittests/model/rewriterview-test.cpp new file mode 100644 index 00000000000..8c62accd1d8 --- /dev/null +++ b/tests/unit/tests/unittests/model/rewriterview-test.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2024 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 + +namespace { + +using QmlDesigner::AbstractView; + +class RewriterView : public ::testing::Test +{ +protected: + RewriterView() + { + rewriter.setTextModifier(&textModifierMock); + } + + ~RewriterView() { model.setRewriterView(nullptr); } + +protected: + NiceMock externalDependenciesMock; + NiceMock textModifierMock; + NiceMock pathCacheMock{"/path/foo.qml"}; + NiceMock projectStorageMock{pathCacheMock.sourceId, "/path"}; + NiceMock resourceManagementMock; + QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")}; + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, + "Item", + imports, + QUrl::fromLocalFile(pathCacheMock.path.toQString()), + std::make_unique( + resourceManagementMock)}; + QmlDesigner::RewriterView rewriter{externalDependenciesMock}; + NiceMock view; +}; + +} // namespace From 31953e1b7eb799d687861eee21dcb352eaaf0c7d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 21 Oct 2024 18:25:17 +0200 Subject: [PATCH 009/322] QmlDesigner: Add view management tests Cannot really test the reweiter because it expects a rewriter view. So no mock can be used. Task-number: QDS-13406 Change-Id: I0191e3681ed288322dbd339d76288fa22bf0766a Reviewed-by: Thomas Hartmann --- .../libs/designercore/model/model.cpp | 14 +- .../designercore/rewriter/rewriterview.cpp | 8 +- tests/unit/tests/mocks/abstractviewmock.h | 15 +- .../unit/tests/unittests/model/model-test.cpp | 142 ++++++++++++++++-- 4 files changed, 160 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index b100cd678ab..8bd53b8b201 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -928,13 +928,16 @@ void ModelPrivate::attachView(AbstractView *view) if (!view->isEnabled()) return; - if (m_viewList.contains(view)) - return; + if (view->isAttached()) { + if (view->model() == m_model) + return; + else + view->model()->detachView(view); + } m_viewList.append(view); - if (!view->isAttached()) - view->modelAttached(m_model); + view->modelAttached(m_model); } void ModelPrivate::detachView(AbstractView *view, bool notifyView) @@ -1641,6 +1644,9 @@ void ModelPrivate::setNodeInstanceView(AbstractView *nodeInstanceView) if (nodeInstanceView && nodeInstanceView->kind() != AbstractView::Kind::NodeInstance) return; + if (nodeInstanceView && nodeInstanceView->isAttached()) + nodeInstanceView->model()->setNodeInstanceView(nullptr); + if (m_nodeInstanceView) m_nodeInstanceView->modelAboutToBeDetached(m_model); diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp index f7d4b5bc243..a1ba0848713 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp @@ -105,11 +105,13 @@ Internal::TextToModelMerger *RewriterView::textToModelMerger() const void RewriterView::modelAttached(Model *model) { - QTC_ASSERT(m_textModifier, return); - m_modelAttachPending = false; - AbstractView::modelAttached(model); + if (!m_textModifier) + return; + + m_modelAttachPending = false; + ModelAmender differenceHandler(m_textToModelMerger.get()); const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index d7eab46acce..862ec56a8ce 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -14,7 +14,15 @@ class AbstractViewMock : public QmlDesigner::AbstractView public: AbstractViewMock(QmlDesigner::ExternalDependenciesInterface *externalDependencies = nullptr) : QmlDesigner::AbstractView{*externalDependencies} - {} + { + ON_CALL(*this, modelAttached).WillByDefault([this](QmlDesigner::Model *model) { + this->QmlDesigner::AbstractView::modelAttached(model); + }); + + ON_CALL(*this, modelAboutToBeDetached).WillByDefault([this](QmlDesigner::Model *model) { + this->QmlDesigner::AbstractView::modelAboutToBeDetached(model); + }); + } MOCK_METHOD(void, nodeOrderChanged, (const QmlDesigner::NodeListProperty &listProperty), (override)); MOCK_METHOD(void, variantPropertiesChanged, @@ -57,4 +65,9 @@ public: (override)); MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override)); MOCK_METHOD(void, refreshMetaInfos, (const QmlDesigner::TypeIds &), (override)); + + MOCK_METHOD(void, modelAttached, (QmlDesigner::Model *), (override)); + MOCK_METHOD(void, modelAboutToBeDetached, (QmlDesigner::Model *), (override)); + + using AbstractView::setKind; }; diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index a374eb321c7..2a6fa2fb543 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -5,24 +5,27 @@ #include #include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { using QmlDesigner::AbstractProperty; +using QmlDesigner::AbstractView; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; using QmlDesigner::ModelResourceSet; @@ -119,13 +122,13 @@ protected: NiceMock projectStorageMock{pathCacheMock.sourceId, "/path"}; NiceMock resourceManagementMock; QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")}; + NiceMock viewMock; QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", imports, QUrl::fromLocalFile(pathCacheMock.path.toQString()), std::make_unique( resourceManagementMock)}; - NiceMock viewMock; QmlDesigner::SourceId filePathId = pathCacheMock.sourceId; QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); @@ -1201,4 +1204,121 @@ TEST_F(Model_TypeAnnotation, item_library_entries) ElementsAre(u"/extra/file/path")))); } +class Model_ViewManagement : public Model +{ +protected: + NiceMock viewMock; +}; + +TEST_F(Model_ViewManagement, set_rewriter) +{ + NiceMock externalDependenciesMock; + QmlDesigner::RewriterView rewriter{externalDependenciesMock}; + + model.setRewriterView(&rewriter); + + ASSERT_THAT(model.rewriterView(), Eq(&rewriter)); +} + +TEST_F(Model_ViewManagement, attach_rewriter) +{ + NiceMock externalDependenciesMock; + QmlDesigner::RewriterView rewriter{externalDependenciesMock}; + + model.attachView(&rewriter); + + ASSERT_THAT(model.rewriterView(), Eq(&rewriter)); +} + +TEST_F(Model_ViewManagement, set_node_instance_view) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + + model.setNodeInstanceView(&viewMock); + + ASSERT_THAT(model.nodeInstanceView(), Eq(&viewMock)); +} + +TEST_F(Model_ViewManagement, call_modelAttached_if_node_instance_view_is_set) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, dont_call_modelAttached_if_node_instance_view_is_already_set) +{ + viewMock.setKind(AbstractView::Kind::NodeInstance); + model.setNodeInstanceView(&viewMock); + + EXPECT_CALL(viewMock, modelAttached(&model)).Times(0); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, detach_node_instance_view_from_other_model_before_attach_to_new_model) +{ + InSequence s; + QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, + "Item", + imports, + QUrl::fromLocalFile(pathCacheMock.path.toQString()), + std::make_unique( + resourceManagementMock)}; + viewMock.setKind(AbstractView::Kind::NodeInstance); + otherModel.setNodeInstanceView(&viewMock); + + EXPECT_CALL(viewMock, modelAboutToBeDetached(&otherModel)); + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, call_modelAboutToBeDetached_for_already_set_node_instance_view) +{ + NiceMock otherViewMock; + otherViewMock.setKind(AbstractView::Kind::NodeInstance); + viewMock.setKind(AbstractView::Kind::NodeInstance); + model.setNodeInstanceView(&otherViewMock); + + EXPECT_CALL(otherViewMock, modelAboutToBeDetached(&model)); + + model.setNodeInstanceView(&viewMock); +} + +TEST_F(Model_ViewManagement, attach_view_is_calling_modelAttached) +{ + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.attachView(&viewMock); +} + +TEST_F(Model_ViewManagement, attach_view_is_not_calling_modelAttached_if_it_is_already_attached) +{ + model.attachView(&viewMock); + + EXPECT_CALL(viewMock, modelAttached(&model)).Times(0); + + model.attachView(&viewMock); +} + +TEST_F(Model_ViewManagement, view_is_detached_before_it_is_attached_ot_new_model) +{ + InSequence s; + QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, + "Item", + imports, + QUrl::fromLocalFile(pathCacheMock.path.toQString()), + std::make_unique( + resourceManagementMock)}; + otherModel.attachView(&viewMock); + + EXPECT_CALL(viewMock, modelAboutToBeDetached(&otherModel)); + EXPECT_CALL(viewMock, modelAttached(&model)); + + model.attachView(&viewMock); +} + } // namespace From 48a61d4d36c404916da9a602341ace6fa08bf145 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Oct 2024 15:30:59 +0200 Subject: [PATCH 010/322] QmlDesigner: Add test for creation of model from other model Task-number: QDS-13406 Change-Id: I2676824050eebc69234b8d9584dcc174935ba5cc Reviewed-by: Thomas Hartmann --- .../unit/tests/unittests/model/model-test.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 2a6fa2fb543..398ae95610f 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -203,6 +203,43 @@ TEST_F(Model_Creation, imports) ASSERT_THAT(model->imports(), UnorderedElementsAreArray(imports)); } +class Model_CreationFromOtherModel : public Model +{}; + +TEST_F(Model_CreationFromOtherModel, root_node_has_object_type_name) +{ + auto newModel = model.createModel("QtObject"); + + ASSERT_THAT(newModel->rootModelNode().type(), Eq("QtObject")); +} + +TEST_F(Model_CreationFromOtherModel, root_node_has_object_meta_info) +{ + auto newModel = model.createModel("QtObject"); + + ASSERT_THAT(newModel->rootModelNode().metaInfo(), newModel->qmlQtObjectMetaInfo()); +} + +TEST_F(Model_CreationFromOtherModel, file_url) +{ + auto newModel = model.createModel("QtObject"); + + ASSERT_THAT(newModel->fileUrl().toLocalFile(), Eq(pathCacheMock.path.toQString())); +} + +TEST_F(Model_CreationFromOtherModel, file_url_source_id) +{ + auto newModel = model.createModel("QtObject"); + ASSERT_THAT(newModel->fileUrlSourceId(), pathCacheMock.sourceId); +} + +TEST_F(Model_CreationFromOtherModel, imports) +{ + auto newModel = model.createModel("QtObject"); + + ASSERT_THAT(newModel->imports(), UnorderedElementsAreArray(imports)); +} + class Model_ResourceManagment : public Model {}; From 53feac48159a3b1567d979aa7d451c80a3c6dd71 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Oct 2024 07:28:57 +0200 Subject: [PATCH 011/322] Sqlite: Fix tracing Change-Id: I0a74f395a207b6f6289143af0ed9718b78ed5cc2 Reviewed-by: Thomas Hartmann --- src/libs/sqlite/sqliteids.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 4816cd67d7c..70bf23caafa 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -146,8 +146,8 @@ public: template friend void convertToString(String &string, CompoundBasicId id) { - convertToString(string, id.id); - convertToString(string, id.contextId); + convertToString(string, id.mainId()); + convertToString(string, id.contextId()); } friend bool compareId(CompoundBasicId first, CompoundBasicId second) From 68e0b5a3f246639564e1c53010bad66845f9677e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 23 Oct 2024 09:27:02 +0300 Subject: [PATCH 012/322] StudioWelcome: Fix the bug for the index of the ComboBoxHelper Change-Id: Ic6795ddcc0f25f461bfea212d48ee430d4da672b Reviewed-by: Mahmoud Badri --- src/plugins/studiowelcome/fieldhelper.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/studiowelcome/fieldhelper.cpp b/src/plugins/studiowelcome/fieldhelper.cpp index fea36813d31..d99296c53d2 100644 --- a/src/plugins/studiowelcome/fieldhelper.cpp +++ b/src/plugins/studiowelcome/fieldhelper.cpp @@ -43,16 +43,14 @@ QString ComboBoxHelper::text(int index) const return model->item(index)->text(); } -int ComboBoxHelper::indexOf(const QString &) const +int ComboBoxHelper::indexOf(const QString &text) const { QTC_ASSERT(m_field, return -1); const QStandardItemModel *model = m_field->model(); for (int i = 0; i < model->rowCount(); ++i) { const QStandardItem *item = model->item(i, 0); - const QString text = item->text(); - - if (text == text) + if (text == item->text()) return i; } From 51000a531f6b3d3a9c0bd7226667af9ec58a85aa Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 23 Oct 2024 09:45:14 +0300 Subject: [PATCH 013/322] EffectComposer: Remove unused parameter from getImageElementName Change-Id: I43d52c722806421243dfe395ae9ce5c25a878e8c Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposermodel.cpp | 8 ++++---- src/plugins/effectcomposer/effectcomposermodel.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index d7a94a7278f..32dce7cef65 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -1494,7 +1494,7 @@ QString EffectComposerModel::valueAsString(const Uniform &uniform) QVector4D v4 = uniform.value().value(); return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x()).arg(v4.y()).arg(v4.z()).arg(v4.w()); } else if (uniform.type() == Uniform::Type::Sampler) { - return getImageElementName(uniform, true); + return getImageElementName(uniform); } else if (uniform.type() == Uniform::Type::Color) { return QString("\"%1\"").arg(uniform.value().toString()); } else if (uniform.type() == Uniform::Type::Channel) { @@ -1537,7 +1537,7 @@ QString EffectComposerModel::valueAsBinding(const Uniform &uniform) 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::Sampler) { - return getImageElementName(uniform, false); + return getImageElementName(uniform); } else { qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); return QString(); @@ -1574,7 +1574,7 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform) } // Return name for the image property Image element -QString EffectComposerModel::getImageElementName(const Uniform &uniform, bool) +QString EffectComposerModel::getImageElementName(const Uniform &uniform) const { QString simplifiedName = uniform.name().simplified(); simplifiedName = simplifiedName.remove(' '); @@ -2067,7 +2067,7 @@ QString EffectComposerModel::getQmlImagesString(bool localFiles) .arg(uniform->name(), imagePath); } imagesString += " Image {\n"; - QString simplifiedName = getImageElementName(*uniform, localFiles); + QString simplifiedName = getImageElementName(*uniform); imagesString += QString(" id: %1\n").arg(simplifiedName); imagesString += " anchors.fill: parent\n"; // File paths are absolute, return as local when requested diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 09aeb724532..fc04255f111 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -182,7 +182,7 @@ private: QString valueAsString(const Uniform &uniform); QString valueAsBinding(const Uniform &uniform); QString valueAsVariable(const Uniform &uniform); - QString getImageElementName(const Uniform &uniform, bool localFiles); + QString getImageElementName(const Uniform &uniform) const; const QString getConstVariables(); const QString getDefineProperties(); int getTagIndex(const QStringList &code, const QString &tag); From 5cbcd454b2257a0f9c777b3502ed397f8901a458 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 19 Sep 2024 15:35:51 +0200 Subject: [PATCH 014/322] DesignSystem: R/W design system module from the library Task-number: QDS-13713 Change-Id: Ibc13272ac1e0b26352b84e74216b2fbfcd69ff4e Reviewed-by: Marco Bubke Reviewed-by: Thomas Hartmann --- .../libs/designsystem/CMakeLists.txt | 3 +- .../qmldesigner/libs/designsystem/dsstore.cpp | 265 ++++++++++++++++++ .../qmldesigner/libs/designsystem/dsstore.h | 48 ++++ .../libs/designsystem/dsthemegroup.cpp | 79 +++--- .../libs/designsystem/dsthemegroup.h | 14 +- .../libs/designsystem/dsthememanager.cpp | 159 +++++++++-- .../libs/designsystem/dsthememanager.h | 14 +- 7 files changed, 513 insertions(+), 69 deletions(-) create mode 100644 src/plugins/qmldesigner/libs/designsystem/dsstore.cpp create mode 100644 src/plugins/qmldesigner/libs/designsystem/dsstore.h diff --git a/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt b/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt index 05fde136802..b85e4a0dc22 100644 --- a/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt +++ b/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt @@ -1,9 +1,10 @@ add_qtc_library(DesignSystem STATIC PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} DEPENDS - Qt::Core Qt::Widgets QmlDesignerCore + Qt::Core Qt::Widgets QmlDesignerCore TextEditorSupport SOURCES dsconstants.h + dsstore.h dsstore.cpp dsthemegroup.h dsthemegroup.cpp dsthememanager.h dsthememanager.cpp ) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp new file mode 100644 index 00000000000..26ab0f9001c --- /dev/null +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp @@ -0,0 +1,265 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "dsstore.h" +#include "dsthememanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +constexpr char DesignModuleName[] = "DesignSystem"; + +QString capitalize(QStringView str) +{ + if (str.isEmpty()) + return QString(); + QString tmp = str.toString(); + tmp[0] = str[0].toUpper(); + return tmp; +} + +std::optional dsModuleDir(QmlDesigner::ExternalDependenciesInterface &ed) +{ + auto componentsPath = QmlDesigner::GeneratedComponentUtils(ed).generatedComponentsPath(); + if (componentsPath.exists()) + return componentsPath.pathAppended(DesignModuleName); + + return {}; +} + +static QByteArray reformatQml(const QString &content) +{ + auto document = QmlJS::Document::create({}, QmlJS::Dialect::QmlQtQuick2); + document->setSource(content); + document->parseQml(); + if (document->isParsedCorrectly()) + return QmlJS::reformat(document).toUtf8(); + + return content.toUtf8(); +} + +std::optional modelSerializeHelper(QmlDesigner::ExternalDependenciesInterface &ed, + std::function callback, + const Utils::FilePath &targetDir, + const QString &typeName, + bool isSingelton = false) +{ + QString qmlText{"import QtQuick\nQtObject {}\n"}; + if (isSingelton) + qmlText.prepend("pragma Singleton\n"); + + QmlDesigner::ModelPointer model(QmlDesigner::Model::create("QtObject")); + QPlainTextEdit editor; + editor.setPlainText(qmlText); + QmlDesigner::NotIndentingTextEditModifier modifier(&editor); + QmlDesigner::RewriterView view(ed, QmlDesigner::RewriterView::Validate); + view.setPossibleImportsEnabled(false); + view.setCheckSemanticErrors(false); + view.setTextModifier(&modifier); + model->attachView(&view); + + try { + callback(model.get()); + } catch (const QmlDesigner::RewritingException &e) { + return e.description(); + } + + Utils::FileSaver saver(targetDir / (typeName + ".qml"), QIODevice::Text); + saver.write(reformatQml(modifier.text())); + if (!saver.finalize()) + return saver.errorString(); + + return {}; +} + +} // namespace + +namespace QmlDesigner { + +DSStore::DSStore(ExternalDependenciesInterface &ed) + : m_ed(ed) +{} + +DSStore::~DSStore() {} + +QString DSStore::moduleImportStr() const +{ + auto prefix = GeneratedComponentUtils(m_ed).generatedComponentTypePrefix(); + if (!prefix.isEmpty()) + return QString("%1.%2").arg(prefix).arg(DesignModuleName); + + return DesignModuleName; +} + +std::optional DSStore::load() +{ + if (auto moduleDir = dsModuleDir(m_ed)) + return load(*moduleDir); + + return tr("Can not locate design system module"); +} + +std::optional DSStore::load(const Utils::FilePath &dsModuleDirPath) +{ + // read qmldir + const auto qmldirFile = dsModuleDirPath / "qmldir"; + const Utils::expected_str contents = qmldirFile.fileContents(); + if (!contents) + return tr("Can not read Design System qmldir"); + + m_collectionTypeNames.clear(); + m_collections.clear(); + + // Parse qmldir + QString qmldirData = QString::fromUtf8(*contents); + QmlDirParser qmlDirParser; + qmlDirParser.parse(qmldirData); + + // load collections + QStringList collectionErrors; + auto addCollectionErr = [&collectionErrors](const QString &name, const QString &e) { + collectionErrors << QString("Error loading collection %1. %2").arg(name, e); + }; + for (auto component : qmlDirParser.components()) { + if (!component.fileName.isEmpty()) { + const auto collectionPath = dsModuleDirPath.pathAppended(component.fileName); + if (auto err = loadCollection(component.typeName, collectionPath)) + addCollectionErr(component.typeName, *err); + } else { + addCollectionErr(component.typeName, tr("Can not find component file.")); + } + } + + if (!collectionErrors.isEmpty()) + return collectionErrors.join("\n"); + + return {}; +} + +std::optional DSStore::save(bool mcuCompatible) const +{ + if (auto moduleDir = dsModuleDir(m_ed)) + return save(*moduleDir, mcuCompatible); + + return tr("Can not locate design system module"); +} + +std::optional DSStore::save(const Utils::FilePath &moduleDirPath, bool mcuCompatible) const +{ + if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString())) + return tr("Can not create design system module directory %1.").arg(moduleDirPath.toString()); + + // dump collections + QStringList singletons; + QStringList errors; + for (auto &[typeName, collection] : m_collections) { + if (auto err = writeQml(collection, typeName, moduleDirPath, mcuCompatible)) + errors << *err; + singletons << QString("singleton %1 1.0 %1.qml").arg(typeName); + } + + // Write qmldir + Utils::FileSaver saver(moduleDirPath / "qmldir", QIODevice::Text); + const QString qmldirContents = QString("Module %1\n%2").arg(moduleImportStr(), singletons.join("\n")); + saver.write(qmldirContents.toUtf8()); + if (!saver.finalize()) + errors << tr("Can not write design system qmldir. %1").arg(saver.errorString()); + + if (!errors.isEmpty()) + return errors.join("\n"); + + return {}; +} + +DSThemeManager *DSStore::addCollection(const QString &qmlTypeName) +{ + const QString uniqueTypeName = UniqueName::generateId(qmlTypeName, + "designSystem", + [this](const QString &t) { + return m_collections.contains(t); + }); + + const QString componentType = capitalize(uniqueTypeName); + auto [itr, success] = m_collections.try_emplace(componentType, DSThemeManager{}); + if (success) { + m_collectionTypeNames.insert({&itr->second, itr->first}); + return &itr->second; + } + return nullptr; +} + +std::optional DSStore::typeName(DSThemeManager *collection) const +{ + auto itr = m_collectionTypeNames.find(collection); + if (itr != m_collectionTypeNames.end()) + return itr->second; + + return {}; +} + +std::optional DSStore::loadCollection(const QString &typeName, + const Utils::FilePath &qmlFilePath) +{ + Utils::FileReader reader; + if (!reader.fetch(qmlFilePath, QFile::Text)) + return reader.errorString(); + + ModelPointer model(QmlDesigner::Model::create("QtObject")); + + QPlainTextEdit editor; + QString qmlContent = QString::fromUtf8(reader.data()); + editor.setPlainText(qmlContent); + + QmlDesigner::NotIndentingTextEditModifier modifier(&editor); + RewriterView view(m_ed, QmlDesigner::RewriterView::Validate); + // QDS-8366 + view.setPossibleImportsEnabled(false); + view.setCheckSemanticErrors(false); + view.setTextModifier(&modifier); + model->attachView(&view); + + if (auto dsMgr = addCollection(typeName)) + return dsMgr->load(model->rootModelNode()); + + return {}; +} + +std::optional DSStore::writeQml(const DSThemeManager &mgr, + const QString &typeName, + const Utils::FilePath &targetDir, + bool mcuCompatible) const +{ + if (mgr.themeCount() == 0) + return {}; + + const QString themeInterfaceType = mcuCompatible ? QString("%1Theme").arg(typeName) : "QtObject"; + if (mcuCompatible) { + auto decorateInterface = [&mgr](Model *interfaceModel) { + mgr.decorateThemeInterface(interfaceModel->rootModelNode()); + }; + + if (auto error = modelSerializeHelper(m_ed, decorateInterface, targetDir, themeInterfaceType)) + return tr("Can not write theme interface %1.\n%2").arg(themeInterfaceType, *error); + } + + auto decorateCollection = [&](Model *collectionModel) { + mgr.decorate(collectionModel->rootModelNode(), themeInterfaceType.toUtf8(), mcuCompatible); + }; + + if (auto error = modelSerializeHelper(m_ed, decorateCollection, targetDir, typeName, true)) + return tr("Can not write collection %1.\n%2").arg(typeName, *error); + + return {}; +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.h b/src/plugins/qmldesigner/libs/designsystem/dsstore.h new file mode 100644 index 00000000000..585a38c39e0 --- /dev/null +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.h @@ -0,0 +1,48 @@ +// Copyright (C) 2024 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 DSThemeManager; +class ExternalDependenciesInterface; + +using DSCollections = std::map; + +class DSStore +{ + Q_DECLARE_TR_FUNCTIONS(DSStore); + +public: + DSStore(ExternalDependenciesInterface &ed); + ~DSStore(); + + QString moduleImportStr() const; + + std::optional load(); + std::optional load(const Utils::FilePath &dsModuleDirPath); + + std::optional save(bool mcuCompatible = false) const; + std::optional save(const Utils::FilePath &moduleDirPath, bool mcuCompatible = false) const; + + size_t collectionCount() const { return m_collections.size(); } + + DSThemeManager *addCollection(const QString &qmlTypeName); + std::optional typeName(DSThemeManager *collection) const; + +private: + std::optional loadCollection(const QString &typeName, const Utils::FilePath &qmlFilePath); + std::optional writeQml(const DSThemeManager &mgr, + const QString &typeName, + const Utils::FilePath &targetDir, + bool mcuCompatible) const; + +private: + ExternalDependenciesInterface &m_ed; + DSCollections m_collections; + std::map m_collectionTypeNames; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp index ec55d234a91..aa867a13e19 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp @@ -3,10 +3,12 @@ #include "dsthemegroup.h" +#include #include +#include #include -#include #include +#include #include #include @@ -142,64 +144,73 @@ void DSThemeGroup::removeTheme(ThemeId theme) void DSThemeGroup::duplicateValues(ThemeId from, ThemeId to) { - for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) { - auto &[propName, values] = *itr; - auto fromValueItr = values.find(from); + for (auto &[propName, values] : m_values) { + ThemeValues::iterator fromValueItr = values.find(from); if (fromValueItr != values.end()) values[to] = fromValueItr->second; } } -void DSThemeGroup::decorate(ThemeId theme, ModelNode themeNode, DECORATION_CONTEXT decorationContext) +void DSThemeGroup::decorate(ThemeId theme, ModelNode themeNode, bool wrapInGroups) { if (!count(theme)) return; // No props for this theme in this group. - ModelNode *targetNode = &themeNode; + ModelNode targetNode = themeNode; const auto typeName = groupTypeName(m_type); - if (decorationContext == DECORATION_CONTEXT::MPU) { + if (wrapInGroups) { // Create a group node const auto groupName = GroupId(m_type); auto groupNode = themeNode.model()->createModelNode("QtObject"); - auto groupProperty = themeNode.nodeProperty(groupName); + NodeProperty groupProperty = themeNode.nodeProperty(groupName); if (!groupProperty || !typeName || !groupNode) { qCDebug(dsLog) << "Adding group node failed." << groupName << theme; return; } groupProperty.setDynamicTypeNameAndsetModelNode("QtObject", groupNode); - targetNode = &groupNode; + targetNode = groupNode; } // Add properties - for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) { - auto &[propName, values] = *itr; + for (auto &[propName, values] : m_values) { auto themeValue = values.find(theme); - if (themeValue != values.end()) { - auto &propData = themeValue->second; - if (propData.isBinding) { - auto bindingProp = targetNode->bindingProperty(propName); - if (!bindingProp) - continue; - - if (decorationContext == DECORATION_CONTEXT::MCU) - bindingProp.setExpression(propData.value.toString()); - else - bindingProp.setDynamicTypeNameAndExpression(*typeName, propData.value.toString()); - - } else { - auto nodeProp = targetNode->variantProperty(propName); - if (!nodeProp) - continue; - - if (decorationContext == DECORATION_CONTEXT::MCU) - nodeProp.setValue(propData.value); - else - nodeProp.setDynamicTypeNameAndValue(*typeName, propData.value); - } - } + if (themeValue != values.end()) + addProperty(targetNode, propName, themeValue->second); } +} +void DSThemeGroup::decorateComponent(ModelNode node) +{ + const auto typeName = groupTypeName(m_type); + // Add properties with type to the node + for (auto &[propName, values] : m_values) { + auto nodeProp = node.variantProperty(propName); + nodeProp.setDynamicTypeNameAndValue(*typeName, nodeProp.value()); + } +} + +void DSThemeGroup::addProperty(ModelNode n, PropertyNameView propName, const PropertyData &data) const +{ + auto metaInfo = n.model()->metaInfo(n.type()); + const bool propDefined = metaInfo.property(propName).isValid(); + + const auto typeName = groupTypeName(m_type); + if (data.isBinding) { + if (propDefined) + n.bindingProperty(propName).setExpression(data.value.toString()); + else if (auto bindingProp = n.bindingProperty(propName)) + bindingProp.setDynamicTypeNameAndExpression(*typeName, data.value.toString()); + else + qCDebug(dsLog) << "Assigning invalid binding" << propName << n.id(); + } else { + if (propDefined) + n.variantProperty(propName).setValue(data.value); + else if (auto nodeProp = n.variantProperty(propName)) + nodeProp.setDynamicTypeNameAndValue(*typeName, data.value); + else + qCDebug(dsLog) << "Assigning invalid variant property" << propName << n.id(); + } } } diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.h b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.h index e61a915623e..d745da080ed 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.h @@ -12,12 +12,6 @@ #include namespace QmlDesigner { -enum class DECORATION_CONTEXT { - MCU, - MPU, - COMPONENT_THEME, -}; - class DESIGNSYSTEM_EXPORT DSThemeGroup { struct PropertyData @@ -46,8 +40,6 @@ public: void updateProperty(ThemeId theme, PropertyName newName, const ThemeProperty &prop); void removeProperty(const PropertyName &name); - GroupType type() const { return m_type; } - size_t count(ThemeId theme) const; size_t count() const; bool isEmpty() const; @@ -55,7 +47,11 @@ public: void removeTheme(ThemeId theme); void duplicateValues(ThemeId from, ThemeId to); - void decorate(ThemeId theme, ModelNode themeNode, DECORATION_CONTEXT decorationContext); + void decorate(ThemeId theme, ModelNode themeNode, bool wrapInGroups = true); + void decorateComponent(ModelNode node); + +private: + void addProperty(ModelNode n, PropertyNameView propName, const PropertyData &data) const; private: const GroupType m_type; diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp index 8e6513392d4..d86174902bd 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp @@ -5,21 +5,39 @@ #include "dsconstants.h" #include "dsthemegroup.h" +#include "variantproperty.h" -#include #include +#include #include #include #include +#include + namespace { Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg) + +std::optional typeToGroupType(const QmlDesigner::TypeName type) +{ + if (type == "color") + return QmlDesigner::GroupType::Colors; + if (type == "bool") + return QmlDesigner::GroupType::Flags; + if (type == "real") + return QmlDesigner::GroupType::Numbers; + if (type == "string") + return QmlDesigner::GroupType::Strings; + + return {}; +} } namespace QmlDesigner { -DSThemeManager::DSThemeManager() {} +DSThemeManager::DSThemeManager() +{} DSThemeManager::~DSThemeManager() {} @@ -31,7 +49,8 @@ std::optional DSThemeManager::addTheme(const ThemeName &themeName) } const ThemeId newThemeId = m_themes.empty() ? 1 : m_themes.rbegin()->first + 1; - m_themes.insert({newThemeId, themeName}); + if (!m_themes.try_emplace(newThemeId, themeName).second) + return {}; // Copy the new theme properties from an old theme(first one). if (m_themes.size() > 1) @@ -59,16 +78,16 @@ void DSThemeManager::removeTheme(ThemeId id) if (!m_themes.contains(id)) return; - for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) - groupItr->second->removeTheme(id); + for (auto &[gt, group] : m_groups) + group->removeTheme(id); m_themes.erase(id); } void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to) { - for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) - groupItr->second->duplicateValues(from, to); + for (auto &[gt, group] : m_groups) + group->duplicateValues(from, to); } std::optional DSThemeManager::property(ThemeId themeId, @@ -88,7 +107,7 @@ std::optional DSThemeManager::property(ThemeId themeId, bool DSThemeManager::addProperty(GroupType gType, const ThemeProperty &p) { if (!m_themes.size()) { - qCDebug(dsLog) << "Can not add proprty. Themes empty"; + qCDebug(dsLog) << "Can not add property. Themes empty"; return false; } @@ -141,44 +160,41 @@ void DSThemeManager::decorate(ModelNode rootNode, const QByteArray &nodeType, bo addGroupAliases(rootNode); auto model = rootNode.model(); - for (auto itr = m_themes.begin(); itr != m_themes.end(); ++itr) { + for (auto &[themeId, themeName] : m_themes) { auto themeNode = model->createModelNode(nodeType); - auto themeProperty = model->rootModelNode().nodeProperty(itr->second); + auto themeProperty = model->rootModelNode().nodeProperty(themeName); themeProperty.setDynamicTypeNameAndsetModelNode(nodeType, themeNode); // Add property groups - for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) - groupItr->second->decorate(itr->first, themeNode, isMCU ? DECORATION_CONTEXT::MCU : DECORATION_CONTEXT::MPU); + for (auto &[gt, group] : m_groups) + group->decorate(themeId, themeNode, !isMCU); } } -void DSThemeManager::decorateThemeComponent(ModelNode rootNode) const +void DSThemeManager::decorateThemeInterface(ModelNode rootNode) const { if (!m_themes.size()) return; - auto itr = m_themes.begin(); - for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) - groupItr->second->decorate(itr->first, rootNode, DECORATION_CONTEXT::COMPONENT_THEME); + for (auto &[gt, group] : m_groups) + group->decorateComponent(rootNode); } DSThemeGroup *DSThemeManager::propertyGroup(GroupType type) { auto itr = m_groups.find(type); if (itr == m_groups.end()) - itr = m_groups.insert({type, std::make_unique(type)}).first; + itr = m_groups.try_emplace(type, std::make_unique(type)).first; return itr->second.get(); } void DSThemeManager::addGroupAliases(ModelNode rootNode) const { - QSet groupNames; - for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) { - DSThemeGroup *group = groupItr->second.get(); - const PropertyName groupName = GroupId(group->type()); + std::set groupNames; + for (auto &[groupType, group] : m_groups) { if (group->count()) - groupNames.insert(groupName); + groupNames.emplace(GroupId(groupType)); } for (const auto &name : groupNames) { @@ -187,4 +203,103 @@ void DSThemeManager::addGroupAliases(ModelNode rootNode) const p.setDynamicTypeNameAndExpression("QtObject", binding); } } + +std::optional DSThemeManager::load(ModelNode rootModelNode) +{ + // We need all properties under the theme node and its child nodes. + // The properties must have a unique name. + auto propWithSameName = [](const AbstractProperty &p1, const AbstractProperty &p2) { + return p1.name() == p2.name(); + }; + using PropMap = std::set; + using ThemeProps = std::map; + auto getAllProps = [](const ModelNode &n) -> PropMap { + PropMap props; + auto nodesUnderTheme = n.allSubModelNodesAndThisNode(); + for (auto &n : nodesUnderTheme) { + for (const AbstractProperty &p : n.properties()) { + if (!props.insert(p).second) + qCDebug(dsLog) << "Duplicate Property, Skipping" << n << p; + } + } + return props; + }; + + // First level child nodes are assumed to be the theme nodes. + QList themes = rootModelNode.nodeProperties(); + if (themes.isEmpty()) + return tr("No themes objects in the collection."); + + // Collect properties under each theme node. + ThemeProps themeProps; + for (auto &themeNodeProp : themes) { + ModelNode themeNode = themeNodeProp.modelNode(); + if (auto themeId = addTheme(themeNodeProp.name().toByteArray())) + themeProps.insert({*themeId, getAllProps(themeNode)}); + } + + // Get properties from the first theme. We expect other theme nodes + // have prpperties with same name and type. If not we don't consider those properties. + auto themeItr = themeProps.begin(); + // Add default properties + const PropMap &baseProps = themeItr->second; + for (const AbstractProperty &baseNodeProp : baseProps) { + GroupType basePropGroupType; + ThemeProperty basethemeProp; + if (findPropertyType(baseNodeProp, &basethemeProp, &basePropGroupType)) + addProperty(basePropGroupType, basethemeProp); + else + continue; + + // Update values for rest of the themes. + for (auto otherTheme = std::next(themeItr); otherTheme != themeProps.end(); ++otherTheme) { + const PropMap &otherThemeProps = otherTheme->second; + auto otherThemePropItr = otherThemeProps.find(baseNodeProp); + if (otherThemePropItr == otherThemeProps.end()) { + qCDebug(dsLog) << "Can't find expected prop" << baseNodeProp.name() << "in theme" + << otherTheme->first; + continue; + } + + GroupType otherGroup; + ThemeProperty otherThemeProp; + if (findPropertyType(*otherThemePropItr, &otherThemeProp, &otherGroup) + && otherGroup == basePropGroupType) { + updateProperty(otherTheme->first, basePropGroupType, otherThemeProp); + } else { + qCDebug(dsLog) << "Incompatible property" << baseNodeProp.name() + << " found in theme" << otherTheme->first; + } + } + } + + return {}; +} + +bool DSThemeManager::findPropertyType(const AbstractProperty &p, + ThemeProperty *themeProp, + GroupType *gt) const +{ + auto group = typeToGroupType(p.dynamicTypeName()); + if (!group) { + qCDebug(dsLog) << "Can't find suitable group for the property" << p.name(); + return false; + } + + *gt = *group; + PropertyName pName = p.name().toByteArray(); + if (auto variantProp = p.toVariantProperty()) { + themeProp->value = variantProp.value(); + themeProp->isBinding = false; + } else if (auto binding = p.toBindingProperty()) { + themeProp->value = binding.expression(); + themeProp->isBinding = true; + } else { + qCDebug(dsLog) << "Property type not supported for design system" << pName; + return false; + } + + themeProp->name = pName; + return true; +} } diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h index 0972dcd7893..9028af1c5a6 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h @@ -8,8 +8,11 @@ #include "dsconstants.h" #include "dsthemegroup.h" +#include #include +#include + namespace QmlDesigner { using ThemeName = PropertyName; @@ -18,6 +21,7 @@ class DSTheme; class DESIGNSYSTEM_EXPORT DSThemeManager { + Q_DECLARE_TR_FUNCTIONS(DSThemeManager); public: DSThemeManager(); @@ -27,7 +31,7 @@ public: DSThemeManager& operator=(const DSThemeManager&) = delete; DSThemeManager(DSThemeManager&&) = default; - DSThemeManager& operator=(DSThemeManager&&) = default; + DSThemeManager &operator=(DSThemeManager &&) = default; std::optional addTheme(const ThemeName &themeName); std::optional themeId(const ThemeName &themeName) const; @@ -44,13 +48,17 @@ public: void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p); void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p, const PropertyName &newName); - void decorate(ModelNode rootNode, const QByteArray& nodeType, bool isMCU) const; - void decorateThemeComponent(ModelNode rootNode) const; + void decorate(ModelNode rootNode, const QByteArray &nodeType = "QtObject", bool isMCU = false) const; + void decorateThemeInterface(ModelNode rootNode) const; + + std::optional load(ModelNode rootModelNode); private: DSThemeGroup *propertyGroup(GroupType type); void addGroupAliases(ModelNode rootNode) const; + bool findPropertyType(const AbstractProperty &p, ThemeProperty *themeProp, GroupType *gt) const; + private: std::map m_themes; std::map> m_groups; From e376992d0150dcc3a98fdddbc8d0239e97b87ec6 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 17 May 2024 14:31:44 +0200 Subject: [PATCH 015/322] UnitTests: Tests for headless QML theme generator Task-number: QDS-11956 Change-Id: I5b76b62153436aa2ee3811702beca06af611308a Reviewed-by: Marco Bubke Reviewed-by: Thomas Hartmann --- .../tests/printers/gtest-creator-printing.cpp | 26 ++ .../tests/printers/gtest-creator-printing.h | 6 + tests/unit/tests/unittests/CMakeLists.txt | 1 + .../unittests/designsystem/CMakeLists.txt | 8 + .../designsystem/dsthemegroup-test.cpp | 237 ++++++++++++++++ .../designsystem/dsthememgr-test.cpp | 266 ++++++++++++++++++ .../designsystem/dsthemeqml-test.cpp | 255 +++++++++++++++++ 7 files changed, 799 insertions(+) create mode 100644 tests/unit/tests/unittests/designsystem/CMakeLists.txt create mode 100644 tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp create mode 100644 tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp create mode 100644 tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index d7a63fe6900..1e2981ce171 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -26,6 +26,8 @@ #include +#include + namespace std { template ostream &operator<<(ostream &out, const QVector &vector) { @@ -435,6 +437,30 @@ const char *sourceTypeToText(SourceType sourceType) } // namespace +std::ostream &operator<<(std::ostream &out, const ThemeProperty &prop) +{ + out << "{name: " << prop.name.toStdString() << ", value: " << prop.value + << ", isBinding: " << prop.isBinding << "}"; + + return out; +} + +void PrintTo(const ThemeProperty &prop, std::ostream *os) +{ + *os << prop; +} + +std::ostream &operator<<(std::ostream &out, const GroupType &group) +{ + out << "ThemeGroup{ " << static_cast(group) << ", " << GroupId(group) << "}"; + return out; +} + +void PrintTo(const GroupType &group, std::ostream *os) +{ + *os << group; +} + std::ostream &operator<<(std::ostream &out, const FileStatus &fileStatus) { return out << "(" << fileStatus.sourceId << ", " << fileStatus.size << ", " diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index 23c76832974..1438c889b8c 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -133,11 +133,17 @@ class FileStatus; class Import; class NodeMetaInfo; class PropertyMetaInfo; +class ThemeProperty; +enum class GroupType; struct CompoundPropertyMetaInfo; enum class FlagIs : unsigned int; template class BasicAuxiliaryDataKey; +void PrintTo(const ThemeProperty &prop, std::ostream *os); +std::ostream &operator<<(std::ostream &out, const ThemeProperty &prop); +void PrintTo(const GroupType &group, std::ostream *os); +std::ostream &operator<<(std::ostream &out, const GroupType &group); std::ostream &operator<<(std::ostream &out, const ModelNode &node); std::ostream &operator<<(std::ostream &out, const VariantProperty &property); std::ostream &operator<<(std::ostream &out, const AbstractProperty &property); diff --git a/tests/unit/tests/unittests/CMakeLists.txt b/tests/unit/tests/unittests/CMakeLists.txt index 1aabede38f2..78cd94c2d9a 100644 --- a/tests/unit/tests/unittests/CMakeLists.txt +++ b/tests/unit/tests/unittests/CMakeLists.txt @@ -46,6 +46,7 @@ endfunction(unittest_copy_data_folder) add_subdirectory(componentcore) add_subdirectory(designercoreutils) +add_subdirectory(designsystem) add_subdirectory(listmodeleditor) add_subdirectory(imagecache) add_subdirectory(metainfo) diff --git a/tests/unit/tests/unittests/designsystem/CMakeLists.txt b/tests/unit/tests/unittests/designsystem/CMakeLists.txt new file mode 100644 index 00000000000..113381a5554 --- /dev/null +++ b/tests/unit/tests/unittests/designsystem/CMakeLists.txt @@ -0,0 +1,8 @@ +extend_qtc_test(unittest + DEPENDS + QmlDesignerUtils DesignSystem + SOURCES + dsthemegroup-test.cpp + dsthememgr-test.cpp + dsthemeqml-test.cpp +) diff --git a/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp b/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp new file mode 100644 index 00000000000..bf4c0f14788 --- /dev/null +++ b/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp @@ -0,0 +1,237 @@ +// Copyright (C) 2024 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 + +using QmlDesigner::DSThemeGroup; +using QmlDesigner::ThemeProperty; + +namespace { +constexpr const char testPropertyNameFoo[] = "propFoo"; +constexpr const char testPropertyNameBar[] = "propBar"; + +constexpr QmlDesigner::ThemeId themeId1 = 0; +constexpr QmlDesigner::ThemeId themeId2 = 1; + +MATCHER_P3(HasPropertyCount, themeId, themePropCount, totalPropsCount, "") +{ + const DSThemeGroup &group = arg; + return group.count() == totalPropsCount && group.count(themeId) == themePropCount; +} + +MATCHER_P2(HasThemeProperty, themeId, themeProp, "") +{ + const DSThemeGroup &group = arg; + const std::optional prop = group.propertyValue(themeId, themeProp.name); + + return prop && themeProp.name == prop->name && themeProp.value == prop->value + && themeProp.isBinding == prop->isBinding; +} + +class DesignGroupTest : public testing::TestWithParam +{ +protected: + DesignGroupTest() + : group(groupType) + { + } + + QmlDesigner::GroupType groupType = GetParam(); + QmlDesigner::DSThemeGroup group; +}; + +INSTANTIATE_TEST_SUITE_P(DesignSystem, + DesignGroupTest, + testing::Values(QmlDesigner::GroupType::Colors, + QmlDesigner::GroupType::Flags, + QmlDesigner::GroupType::Numbers, + QmlDesigner::GroupType::Strings)); + +TEST_P(DesignGroupTest, add_property) +{ + // arrange + ThemeProperty testProp{testPropertyNameFoo, "test", false}; + + // act + group.addProperty(themeId1, testProp); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), HasThemeProperty(themeId1, testProp))); +} + +TEST_P(DesignGroupTest, add_multiple_properties) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + ThemeProperty testPropBar{testPropertyNameBar, "#bbddee", false}; + + // act + group.addProperty(themeId1, testPropFoo); + group.addProperty(themeId1, testPropBar); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 2u, 2u), + HasThemeProperty(themeId1, testPropFoo), + HasThemeProperty(themeId1, testPropBar))); +} + +TEST_P(DesignGroupTest, add_property_with_empty_property_name) +{ + // arrange + ThemeProperty testProp{"", "test", false}; + + // act + group.addProperty(themeId1, testProp); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 0u, 0u), Not(HasThemeProperty(themeId1, testProp)))); +} + +TEST_P(DesignGroupTest, add_binding_property) +{ + // arrange + ThemeProperty testProp{testPropertyNameFoo, "root.width", true}; + + // act + group.addProperty(themeId1, testProp); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), HasThemeProperty(themeId1, testProp))); +} + +TEST_P(DesignGroupTest, add_property_with_duplicate_name) +{ + // arrange + ThemeProperty testPropA{testPropertyNameFoo, "#aaccff", false}; + ThemeProperty testPropB{testPropertyNameFoo, "#bbddee", false}; + group.addProperty(themeId1, testPropA); + + // act + group.addProperty(themeId1, testPropB); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), + HasThemeProperty(themeId1, testPropA), + Not(HasThemeProperty(themeId1, testPropB)))); +} + +TEST_P(DesignGroupTest, remove_property) +{ + // arrange + ThemeProperty testProp{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testProp); + + // act + group.removeProperty(testPropertyNameFoo); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 0u, 0u), Not(HasThemeProperty(themeId1, testProp)))); +} + +TEST_P(DesignGroupTest, remove_nonexistent_property_have_no_side_effect) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + + // act + group.removeProperty(testPropertyNameBar); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), HasThemeProperty(themeId1, testPropFoo))); +} + +TEST_P(DesignGroupTest, remove_theme_with_multiple_properties) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + ThemeProperty testPropBar{testPropertyNameBar, "#bbddee", false}; + group.addProperty(themeId1, testPropBar); + + // act + group.removeTheme(themeId1); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 0u, 0u), + Not(HasThemeProperty(themeId1, testPropFoo)), + Not(HasThemeProperty(themeId1, testPropBar)))); +} + +TEST_P(DesignGroupTest, remove_theme_from_group_having_multiple_themes) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + ThemeProperty testPropBar{testPropertyNameBar, "#bbddee", false}; + group.addProperty(themeId2, testPropBar); + + // act + group.removeTheme(themeId1); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 0u, 1u), + Not(HasThemeProperty(themeId1, testPropFoo)), + HasThemeProperty(themeId2, testPropBar))); +} + +TEST_P(DesignGroupTest, remove_nonexistent_theme_have_no_side_effect) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + + // act + group.removeTheme(themeId2); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), HasThemeProperty(themeId1, testPropFoo))); +} + +TEST_P(DesignGroupTest, duplicate_theme) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + ThemeProperty testPropBar{testPropertyNameBar, "#bbddee", false}; + group.addProperty(themeId1, testPropBar); + + // act + group.duplicateValues(themeId1, themeId2); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId2, 2u, 2u), + HasThemeProperty(themeId2, testPropFoo), + HasThemeProperty(themeId2, testPropBar))); +} + +TEST_P(DesignGroupTest, duplicate_nonexistent_have_no_side_effect) +{ + // arrange + ThemeProperty testPropFoo{testPropertyNameFoo, "#aaccff", false}; + group.addProperty(themeId1, testPropFoo); + + // act + group.duplicateValues(themeId2, themeId1); + + //assert + ASSERT_THAT(group, + AllOf(HasPropertyCount(themeId1, 1u, 1u), HasThemeProperty(themeId1, testPropFoo))); +} +} // namespace diff --git a/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp b/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp new file mode 100644 index 00000000000..02a2e56cabc --- /dev/null +++ b/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp @@ -0,0 +1,266 @@ +// Copyright (C) 2024 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 + +using QmlDesigner::DSThemeManager; +using QmlDesigner::GroupType; +using QmlDesigner::Import; +using QmlDesigner::ModelNode; +using QmlDesigner::ThemeProperty; + +namespace { +constexpr const char testPropNameFoo[] = "propFoo"; +constexpr const char testPropNameBar[] = "propBar"; + +constexpr const char darkThemeName[] = "dark"; +constexpr const char lightThemeName[] = "light"; + +MATCHER_P3(HasProperty, + themeId, + group, + themeProp, + std::format("Collection {} have a property {}", + (negation ? "Does't " : "Does "), + PrintToString(themeProp))) +{ + const DSThemeManager &mgr = arg; + const std::optional prop = mgr.property(themeId, group, themeProp.name); + + return prop && themeProp.name == prop->name && themeProp.value == prop->value + && themeProp.isBinding == prop->isBinding; +} + +class DesignSystemManagerTest : public testing::TestWithParam +{ +protected: + QmlDesigner::GroupType groupType = GetParam(); + DSThemeManager mgr; +}; + +INSTANTIATE_TEST_SUITE_P(DesignSystem, + DesignSystemManagerTest, + testing::Values(QmlDesigner::GroupType::Colors, + QmlDesigner::GroupType::Flags, + QmlDesigner::GroupType::Numbers, + QmlDesigner::GroupType::Strings)); + +TEST(DesignSystemManagerTest, add_theme) +{ + // arrange + DSThemeManager mgr; + + // act + const auto themeId = mgr.addTheme(darkThemeName); + + // assert + ASSERT_THAT(themeId, Optional(A())); +} + +TEST(DesignSystemManagerTest, add_theme_with_empty_name_fails) +{ + // arrange + DSThemeManager mgr; + + // act + const auto themeId = mgr.addTheme(""); + + // assert + ASSERT_THAT(themeId, Eq(std::nullopt)); +} + +TEST(DesignSystemManagerTest, add_theme_generates_valid_theme_id) +{ + // arrange + DSThemeManager mgr; + + // act + const auto themeId = mgr.addTheme(darkThemeName); + + // assert + ASSERT_THAT(mgr.themeId(darkThemeName), Optional(themeId)); +} + +TEST(DesignSystemManagerTest, remove_theme) +{ + // arrange + DSThemeManager mgr; + const auto themeId = mgr.addTheme(darkThemeName); + + // act + mgr.removeTheme(*themeId); + + // assert + ASSERT_THAT(mgr, Property(&DSThemeManager::themeCount, 0)); +} + +TEST(DesignSystemManagerTest, remove_theme_with_properties) +{ + // arrange + DSThemeManager mgr; + const auto themeId = mgr.addTheme(darkThemeName); + ThemeProperty testProp{testPropNameFoo, "#aaccbb", false}; + mgr.addProperty(GroupType::Colors, testProp); + + // act + mgr.removeTheme(*themeId); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 0), + Not(HasProperty(*themeId, GroupType::Colors, testProp)))); +} + +TEST_P(DesignSystemManagerTest, add_property_without_theme) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + + // act + mgr.addProperty(groupType, testProp); + + //assert + ASSERT_THAT(mgr, Property(&DSThemeManager::themeCount, 0)); +} + +TEST_P(DesignSystemManagerTest, add_property) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + const auto themeId = mgr.addTheme(darkThemeName); + + // act + mgr.addProperty(groupType, testProp); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + HasProperty(*themeId, groupType, testProp))); +} + +TEST_P(DesignSystemManagerTest, adding_invalid_property_fails) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, {}, false}; + const auto themeId = mgr.addTheme(darkThemeName); + + // act + const bool result = mgr.addProperty(groupType, testProp); + + // assert + ASSERT_FALSE(result); + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + Not(HasProperty(*themeId, groupType, testProp)))); +} + +TEST_P(DesignSystemManagerTest, adding_property_adds_property_to_all_themes) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + const auto themeIdDark = mgr.addTheme(darkThemeName); + const auto themeIdLight = mgr.addTheme(lightThemeName); + + // act + mgr.addProperty(groupType, testProp); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 2), + HasProperty(*themeIdDark, groupType, testProp), + HasProperty(*themeIdLight, groupType, testProp))); +} + +TEST_P(DesignSystemManagerTest, update_property_value) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + ThemeProperty testPropUpdated{testPropNameFoo, "foo", false}; + const auto themeId = mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + + // act + mgr.updateProperty(*themeId, groupType, testPropUpdated); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + HasProperty(*themeId, groupType, testPropUpdated))); +} + +TEST_P(DesignSystemManagerTest, update_property_name) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + ThemeProperty testPropUpdated{testPropNameBar, "test", false}; + const auto themeId = mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + + // act + mgr.updateProperty(*themeId, groupType, testProp, testPropUpdated.name); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + HasProperty(*themeId, groupType, testPropUpdated))); +} + +TEST_P(DesignSystemManagerTest, updating_invalid_property_fails) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + ThemeProperty testPropUpdated{testPropNameFoo, {}, false}; + const auto themeId = mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + + // act + mgr.updateProperty(*themeId, groupType, testProp, testPropUpdated.name); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + HasProperty(*themeId, groupType, testProp))); +} + +TEST_P(DesignSystemManagerTest, remove_property) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + const auto themeId = mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + + // act + mgr.removeProperty(groupType, testPropNameFoo); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + Not(HasProperty(*themeId, groupType, testProp)))); +} + +TEST_P(DesignSystemManagerTest, remove_absent_property_fails) +{ + // arrange + ThemeProperty testProp{testPropNameFoo, "test", false}; + const auto themeId = mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + + // act + mgr.removeProperty(groupType, testPropNameBar); + + // assert + ASSERT_THAT(mgr, + AllOf(Property(&DSThemeManager::themeCount, 1), + HasProperty(*themeId, groupType, testProp))); +} +} // namespace diff --git a/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp b/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp new file mode 100644 index 00000000000..602a1b356d8 --- /dev/null +++ b/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp @@ -0,0 +1,255 @@ +// Copyright (C) 2024 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 + +using QmlDesigner::DSThemeManager; +using QmlDesigner::GroupType; +using QmlDesigner::Import; +using QmlDesigner::ModelNode; +using QmlDesigner::ThemeProperty; + +template<> +struct std::formatter : std::formatter +{ + template + auto format(const QByteArray &ba, FormatContext &ctx) const + { + return std::formatter::format(ba.toStdString(), ctx); + } +}; + +namespace { + +static std::string formatedPropStr(const char tag[], const char name[], const QVariant &v) +{ + return std::format("{}Property({}, {})", tag, name, v.toString().toStdString()); +} + +static auto bindingPropStr = std::bind(&formatedPropStr, + "Binding", + std::placeholders::_1, + std::placeholders::_2); +static auto variantPropStr = std::bind(&formatedPropStr, + "Variant", + std::placeholders::_1, + std::placeholders::_2); + +constexpr const char testPropertyName1[] = "prop1"; +constexpr const char darkThemeName[] = "dark"; +constexpr QmlDesigner::ThemeId testThemeId = 1; + +MATCHER_P2(HasNodeProperty, + name, + typeName, + std::format("{} have node {} with type {})", (negation ? "Does't " : "Does "), name, typeName)) +{ + ModelNode n = arg; + return n.hasNodeProperty(name) && n.nodeProperty(name).modelNode().isValid() + && n.nodeProperty(name).modelNode().type() == typeName; +} + +MATCHER_P2(HasBindingProperty, + name, + value, + std::format("{} have {})", (negation ? "Does't " : "Does "), bindingPropStr(name, value))) +{ + ModelNode n = arg; + return n.hasBindingProperty(name) && n.bindingProperty(name).expression() == value; +} + +MATCHER_P2(HasVariantProperty, + name, + value, + std::format("{} have {})", (negation ? "Does't " : "Does "), variantPropStr(name, value))) +{ + ModelNode n = arg; + return n.hasVariantProperty(name) && n.variantProperty(name).value() == value; +} + +MATCHER_P2(HasGroupVariantProperty, + groupName, + themeProp, + std::format("{} have node {} with {})", + (negation ? "Does't " : "Does "), + groupName.constData(), + PrintToString(themeProp))) +{ + ModelNode n = arg; + + ModelNode groupNode = n.nodeProperty(groupName).modelNode(); + + return groupNode.isValid() && groupNode.hasVariantProperty(themeProp.name) + && groupNode.variantProperty(themeProp.name).value() == themeProp.value; +} + +MATCHER_P2(HasGroupBindingProperty, + groupName, + themeProp, + std::format("{} have node {} with {})", + (negation ? "Does't " : "Does "), + groupName.constData(), + PrintToString(themeProp))) +{ + ModelNode n = arg; + + ModelNode groupNode = n.nodeProperty(groupName).modelNode(); + + return groupNode.isValid() && groupNode.hasBindingProperty(themeProp.name) + && groupNode.bindingProperty(themeProp.name).expression() == themeProp.value.toString(); +} + +class DesignSystemQmlTest : public testing::TestWithParam +{ +protected: + DesignSystemQmlTest() + : group(groupType) + {} + + const QmlDesigner::GroupType groupType = GetParam(); + const QmlDesigner::PropertyName groupName = GroupId(groupType); + QmlDesigner::DSThemeGroup group; + NiceMock pathCacheMock{"/path/model.qm"}; + NiceMock projectStorageMock{pathCacheMock.sourceId, "/path"}; + QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, + "QtObject", + {Import::createLibraryImport("QM"), + Import::createLibraryImport("QtQuick")}, + QUrl::fromLocalFile(pathCacheMock.path.toQString())}; +}; + +INSTANTIATE_TEST_SUITE_P(DesignSystem, + DesignSystemQmlTest, + testing::Values(QmlDesigner::GroupType::Colors, + QmlDesigner::GroupType::Flags, + QmlDesigner::GroupType::Numbers, + QmlDesigner::GroupType::Strings)); + +TEST_P(DesignSystemQmlTest, group_aliase_properties_are_generated) +{ + // arrange + ThemeProperty testProp{testPropertyName1, "test", false}; + DSThemeManager mgr; + mgr.addTheme(darkThemeName); + mgr.addProperty(groupType, testProp); + ModelNode rootNode = model.rootModelNode(); + QString binding = QString("currentTheme.%1").arg(QString::fromLatin1(groupName)); + + // act + mgr.decorate(rootNode); + + // assert + ASSERT_THAT(rootNode, + AllOf(Property(&ModelNode::type, Eq("QtObject")), + HasBindingProperty(groupName, binding), + HasBindingProperty("currentTheme", darkThemeName), + HasNodeProperty(darkThemeName, "QtObject"))); +} + +TEST_P(DesignSystemQmlTest, empty_groups_generate_no_group_aliase_properties) +{ + // arrange + DSThemeManager mgr; + ModelNode rootNode = model.rootModelNode(); + QString binding = QString("currentTheme.%1").arg(QString::fromLatin1(groupName)); + + // act + mgr.decorate(rootNode); + + // assert + ASSERT_THAT(rootNode, + AllOf(Property(&ModelNode::type, Eq("QtObject")), + Not(HasBindingProperty(groupName, binding)), + Not(HasBindingProperty("currentTheme", darkThemeName)), + Not(HasNodeProperty(darkThemeName, "QtObject")))); +} + +TEST_P(DesignSystemQmlTest, decorate_appends_binding_property_to_group_node) +{ + // arrange + ThemeProperty testProp{testPropertyName1, "width", true}; + group.addProperty(testThemeId, testProp); + ModelNode rootNode = model.rootModelNode(); + + // act + group.decorate(testThemeId, rootNode); + + // assert + ASSERT_THAT(rootNode, + AllOf(HasNodeProperty(groupName, "QtObject"), + HasGroupBindingProperty(groupName, testProp))); +} + +TEST_P(DesignSystemQmlTest, mcu_flag_decorate_appends_binding_property_to_root_node) +{ + // arrange + ThemeProperty testProp{testPropertyName1, "width", true}; + group.addProperty(testThemeId, testProp); + ModelNode rootNode = model.rootModelNode(); + + // act + group.decorate(testThemeId, rootNode, false); + + // assert + ASSERT_THAT(rootNode, + AllOf(Not(HasNodeProperty(groupName, "QtObject")), + HasBindingProperty(testProp.name, testProp.value))); +} + +TEST_P(DesignSystemQmlTest, decorate_appends_variant_property_to_group_node) +{ + // arrange + ThemeProperty testProp{testPropertyName1, 5, false}; + group.addProperty(testThemeId, testProp); + ModelNode rootNode = model.rootModelNode(); + + // act + group.decorate(testThemeId, rootNode); + + // assert + ASSERT_THAT(rootNode, + AllOf(HasNodeProperty(groupName, "QtObject"), + HasGroupVariantProperty(groupName, testProp))); +} + +TEST_P(DesignSystemQmlTest, mcu_flag_decorate_appends_variant_property_to_root_node) +{ + // arrange + ThemeProperty testProp{testPropertyName1, 5, false}; + group.addProperty(testThemeId, testProp); + ModelNode rootNode = model.rootModelNode(); + + // act + group.decorate(testThemeId, rootNode, false); + + // assert + ASSERT_THAT(rootNode, + AllOf(Not(HasNodeProperty(groupName, "QtObject")), + HasVariantProperty(testProp.name, testProp.value))); +} + +TEST_P(DesignSystemQmlTest, empty_group_decorate_adds_no_property) +{ + // arrange + ModelNode rootNode = model.rootModelNode(); + + // act + group.decorate(testThemeId, rootNode); + + // assert + ASSERT_THAT(rootNode, Not(HasNodeProperty(groupName, "QtObject"))); +} +} // namespace From 2af197a2b8f8716cac3e8e56283f2ed669f4a65b Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Fri, 18 Oct 2024 18:30:27 +0200 Subject: [PATCH 016/322] QmlProjectItem: ability to extend existing qml module list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-13811 Change-Id: I63be5d7c8640df340d266b57924cb46b3b23bdc3 Reviewed-by: Sivert Krøvel Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 1 + .../projectitem/qmlprojectitem.cpp | 47 +++++++++++++++++++ .../buildsystem/projectitem/qmlprojectitem.h | 3 ++ .../buildsystem/qmlbuildsystem.cpp | 10 ++++ .../buildsystem/qmlbuildsystem.h | 6 ++- .../qmlprojectmanager/projectitem-test.cpp | 32 +++++++++++++ 6 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 51ee81c7d54..92afbe27e45 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -530,6 +530,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) QStringList qmlProjectDependencies; qmlProjectDependencies.append(qmlprojectsFromImportPaths(importPaths, projectRootPath)); qmlProjectDependencies.append(qmlprojectsFromFilesNodes(fileGroupsObject, projectRootPath)); + qmlProjectDependencies.removeDuplicates(); qmlProjectDependencies.sort(); rootObject.insert("qmlprojectDependencies", QJsonArray::fromStringList(qmlProjectDependencies)); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 65c310ce497..63380fb5b9d 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -238,6 +238,28 @@ QStringList QmlProjectItem::qmlProjectModules() const return m_project["qmlprojectDependencies"].toVariant().toStringList(); } +void QmlProjectItem::setQmlProjectModules(const QStringList &paths) +{ + if (qmlProjectModules() == paths) + return; + + auto jsonArray = QJsonArray::fromStringList(paths); + updateFileGroup("Module", "files", jsonArray); + insertAndUpdateProjectFile("qmlprojectDependencies", jsonArray); +} + +void QmlProjectItem::addQmlProjectModule(const QString &modulePath) +{ + QJsonArray qmlModules = m_project["qmlprojectDependencies"].toArray(); + + if (qmlModules.contains(modulePath)) + return; + + qmlModules.append(modulePath); + updateFileGroup("Module", "files", qmlModules); + insertAndUpdateProjectFile("qmlprojectDependencies", qmlModules); +} + QStringList QmlProjectItem::fileSelectors() const { return m_project["runConfig"].toObject()["fileSelectors"].toVariant().toStringList(); @@ -444,10 +466,35 @@ void QmlProjectItem::addShaderToolFile(const QString &file) void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonValue &value) { m_project[key] = value; + if (!m_skipRewrite) m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8()); } +void QmlProjectItem::updateFileGroup(const QString &groupType, + const QString &property, + const QJsonValue &value) +{ + auto arr = m_project["fileGroups"].toArray(); + auto found = std::find_if(arr.begin(), arr.end(), [groupType](const QJsonValue &elem) { + return elem["type"].toString() == groupType; + }); + if (found == arr.end()) { + qWarning() << "fileGroups - unable to find group:" << groupType; + return; + } + + auto obj = found->toObject(); + obj[property] = value; + + arr.removeAt(std::distance(arr.begin(), found)); + arr.append(obj); + m_project["fileGroups"] = arr; + + m_content.clear(); + setupFileFilters(); +} + bool QmlProjectItem::enableCMakeGeneration() const { return m_project["deployment"].toObject()["enableCMakeGeneration"].toBool(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index fa6d700291a..6bfc351edff 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -50,6 +50,8 @@ public: void setMockImports(const QStringList &paths); QStringList qmlProjectModules() const; + void setQmlProjectModules(const QStringList &paths); + void addQmlProjectModule(const QString &modulePath); QStringList fileSelectors() const; void setFileSelectors(const QStringList &selectors); @@ -121,6 +123,7 @@ private: // file update functions void insertAndUpdateProjectFile(const QString &key, const QJsonValue &value); + void updateFileGroup(const QString &groupType, const QString &property, const QJsonValue &value); }; } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 701da75dd57..509bff6bcf7 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -446,6 +446,11 @@ QmlBuildSystem *QmlBuildSystem::getStartupBuildSystem() return nullptr; } +void QmlBuildSystem::addQmlProjectModule(const Utils::FilePath &path) +{ + m_projectItem->addQmlProjectModule(path.toFSPathString()); +} + Utils::FilePath QmlBuildSystem::mainFilePath() const { const QString fileName = mainFile(); @@ -727,6 +732,11 @@ QStringList QmlBuildSystem::importPaths() const return m_projectItem->importPaths(); } +void QmlBuildSystem::addImportPath(const Utils::FilePath &path) +{ + m_projectItem->addImportPath(path.toFSPathString()); +} + QStringList QmlBuildSystem::mockImports() const { return m_projectItem->mockImports(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index e76d8ba75c9..b2012ec858d 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -74,12 +74,14 @@ public: Utils::EnvironmentItems environment() const; QStringList allImports() const; - QStringList importPaths() const; QStringList mockImports() const; QStringList absoluteImportPaths() const; QStringList targetImportPaths() const; QStringList fileSelectors() const; + QStringList importPaths() const; + void addImportPath(const Utils::FilePath &path); + bool multilanguageSupport() const; QStringList supportedLanguages() const; void setSupportedLanguages(QStringList languages); @@ -115,6 +117,8 @@ public: static QmlBuildSystem *getStartupBuildSystem(); + void addQmlProjectModule(const Utils::FilePath &path); + signals: void projectChanged(); diff --git a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp index f3088a56bee..ab12a6e886e 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp @@ -762,6 +762,38 @@ TEST_F(QmlProjectItem, qmlproject_modules) "../converter/test-set-mcu-2/testfile.qmlproject")); } +TEST_F(QmlProjectItem, set_empty_qmlproject_modules) +{ + projectItemSetters->setQmlProjectModules({}); + + auto qmlProjectModules = projectItemSetters->qmlProjectModules(); + + ASSERT_THAT(qmlProjectModules, IsEmpty()); +} + +TEST_F(QmlProjectItem, set_qmlproject_modules) +{ + projectItemSetters->setQmlProjectModules({"testModule.qmlproject", "testModule2.qmlproject"}); + + auto qmlProjectModules = projectItemSetters->qmlProjectModules(); + + ASSERT_THAT(qmlProjectModules, + UnorderedElementsAre("testModule.qmlproject", "testModule2.qmlproject")); +} + +TEST_F(QmlProjectItem, add_qmlproject_module) +{ + auto currentModules = projectItemSetters->qmlProjectModules(); + projectItemSetters->addQmlProjectModule("test.qmlproject"); + projectItemSetters->addQmlProjectModule("test2.qmlproject"); + + auto qmlProjectModules = projectItemSetters->qmlProjectModules(); + + ASSERT_THAT(qmlProjectModules, + UnorderedElementsAreArray(currentModules + + QStringList{"test.qmlproject", "test2.qmlproject"})); +} + TEST_F(QmlProjectItem, no_qmlproject_modules) { auto qmlProjectModules = projectItemEmpty->qmlProjectModules(); From be45236bdf77e5025f38e4ec2bfe4b44ffffc397 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 22 Oct 2024 15:55:31 +0200 Subject: [PATCH 017/322] QmlDesigner: Test Model file url handling Change-Id: Ifb16b62bb4f8fa40240a6c44a65fb1173f11cea9 Reviewed-by: Thomas Hartmann --- tests/unit/tests/mocks/abstractviewmock.h | 2 + .../unit/tests/unittests/model/model-test.cpp | 51 ++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/unit/tests/mocks/abstractviewmock.h b/tests/unit/tests/mocks/abstractviewmock.h index 862ec56a8ce..7a2ea1b48c2 100644 --- a/tests/unit/tests/mocks/abstractviewmock.h +++ b/tests/unit/tests/mocks/abstractviewmock.h @@ -69,5 +69,7 @@ public: MOCK_METHOD(void, modelAttached, (QmlDesigner::Model *), (override)); MOCK_METHOD(void, modelAboutToBeDetached, (QmlDesigner::Model *), (override)); + MOCK_METHOD(void, fileUrlChanged, (const QUrl &, const QUrl &), (override)); + using AbstractView::setKind; }; diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 398ae95610f..396206e561c 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -29,6 +29,7 @@ using QmlDesigner::AbstractView; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; using QmlDesigner::ModelResourceSet; +using QmlDesigner::SourceId; using QmlDesigner::Storage::ModuleKind; MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) @@ -123,6 +124,7 @@ protected: NiceMock resourceManagementMock; QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")}; NiceMock viewMock; + QUrl fileUrl = QUrl::fromLocalFile(pathCacheMock.path.toQString()); QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", imports, @@ -148,7 +150,7 @@ TEST_F(Model_Creation, root_node_has_item_type_name) auto model = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)); @@ -160,7 +162,7 @@ TEST_F(Model_Creation, root_node_has_item_meta_info) auto model = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)); @@ -172,7 +174,7 @@ TEST_F(Model_Creation, file_url) auto model = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)); @@ -184,7 +186,7 @@ TEST_F(Model_Creation, file_url_source_id) auto model = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)); @@ -196,7 +198,7 @@ TEST_F(Model_Creation, imports) auto model = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)); @@ -1301,7 +1303,7 @@ TEST_F(Model_ViewManagement, detach_node_instance_view_from_other_model_before_a QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)}; viewMock.setKind(AbstractView::Kind::NodeInstance); @@ -1347,7 +1349,7 @@ TEST_F(Model_ViewManagement, view_is_detached_before_it_is_attached_ot_new_model QmlDesigner::Model otherModel{{projectStorageMock, pathCacheMock}, "Item", imports, - QUrl::fromLocalFile(pathCacheMock.path.toQString()), + fileUrl, std::make_unique( resourceManagementMock)}; otherModel.attachView(&viewMock); @@ -1358,4 +1360,39 @@ TEST_F(Model_ViewManagement, view_is_detached_before_it_is_attached_ot_new_model model.attachView(&viewMock); } +class Model_FileUrl : public Model +{ +protected: + QmlDesigner::SourcePath barFilePath = "/path/bar.qml"; + QUrl barFilePathUrl = barFilePath.toQString(); + SourceId barSourceId = pathCacheMock.createSourceId(barFilePath); +}; + +TEST_F(Model_FileUrl, set_file_url) +{ + model.setFileUrl(barFilePathUrl); + + ASSERT_THAT(model.fileUrl(), barFilePathUrl); +} + +TEST_F(Model_FileUrl, set_file_url_sets_source_id_too) +{ + model.setFileUrl(barFilePathUrl); + + ASSERT_THAT(model.fileUrlSourceId(), barSourceId); +} + +TEST_F(Model_FileUrl, notifies_change) +{ + EXPECT_CALL(viewMock, fileUrlChanged(Eq(fileUrl), Eq(barFilePathUrl))); + + model.setFileUrl(barFilePathUrl); +} + +TEST_F(Model_FileUrl, do_not_notify_if_there_is_no_change) +{ + EXPECT_CALL(viewMock, fileUrlChanged(_, _)).Times(0); + + model.setFileUrl(fileUrl); +} } // namespace From 44fcb093dcaa0276eb3a57e7164cd5b1a90e9c57 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Oct 2024 18:02:00 +0200 Subject: [PATCH 018/322] QmlDesigner: Fix build The design system tests introduced a dpendency to QmlDesignerCore. But that is not working. Only if designer core will use only the project storage, we can use it in the tests. Change-Id: I0fdec9eac8f417593aa4f201177749c69dbf384f Reviewed-by: Marco Bubke --- .../unittests/designsystem/CMakeLists.txt | 14 ++++- .../designsystem/dsthemegroup-test.cpp | 17 +++++-- .../designsystem/dsthememgr-test.cpp | 6 +-- .../designsystem/dsthemeqml-test.cpp | 51 ++++++------------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/tests/unit/tests/unittests/designsystem/CMakeLists.txt b/tests/unit/tests/unittests/designsystem/CMakeLists.txt index 113381a5554..bf2fcf2da24 100644 --- a/tests/unit/tests/unittests/designsystem/CMakeLists.txt +++ b/tests/unit/tests/unittests/designsystem/CMakeLists.txt @@ -1,8 +1,18 @@ extend_qtc_test(unittest - DEPENDS - QmlDesignerUtils DesignSystem SOURCES dsthemegroup-test.cpp dsthememgr-test.cpp dsthemeqml-test.cpp ) + +extend_qtc_test(unittest + SOURCES_PREFIX "${QML_DESIGNER_DIRECTORY}/libs/designsystem" + INCLUDES "${QML_DESIGNER_DIRECTORY}/libs/designsystem" + DEFINES DESIGNSYSTEM_STATIC_LIBRARY + DEPENDS + Qt::Core Qt::Widgets QmlDesignerUtils TestDesignerCore + SOURCES + dsconstants.h + dsthemegroup.h dsthemegroup.cpp + dsthememanager.h dsthememanager.cpp +) diff --git a/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp b/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp index bf4c0f14788..8908ce38338 100644 --- a/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp +++ b/tests/unit/tests/unittests/designsystem/dsthemegroup-test.cpp @@ -12,19 +12,28 @@ using QmlDesigner::DSThemeGroup; using QmlDesigner::ThemeProperty; namespace { -constexpr const char testPropertyNameFoo[] = "propFoo"; -constexpr const char testPropertyNameBar[] = "propBar"; +QByteArray testPropertyNameFoo = "propFoo"; +QByteArray testPropertyNameBar = "propBar"; constexpr QmlDesigner::ThemeId themeId1 = 0; constexpr QmlDesigner::ThemeId themeId2 = 1; -MATCHER_P3(HasPropertyCount, themeId, themePropCount, totalPropsCount, "") +MATCHER_P3(HasPropertyCount, + themeId, + themePropCount, + totalPropsCount, + std::string(negation ? "hasn't " : "has ") + "total property count " + + PrintToString(totalPropsCount) + " and theme property count " + + PrintToString(themePropCount)) { const DSThemeGroup &group = arg; return group.count() == totalPropsCount && group.count(themeId) == themePropCount; } -MATCHER_P2(HasThemeProperty, themeId, themeProp, "") +MATCHER_P2(HasThemeProperty, + themeId, + themeProp, + std::string(negation ? "hasn't " : "has ") + PrintToString(themeProp)) { const DSThemeGroup &group = arg; const std::optional prop = group.propertyValue(themeId, themeProp.name); diff --git a/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp b/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp index 02a2e56cabc..638e31b9853 100644 --- a/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp +++ b/tests/unit/tests/unittests/designsystem/dsthememgr-test.cpp @@ -11,8 +11,6 @@ #include -#include - using QmlDesigner::DSThemeManager; using QmlDesigner::GroupType; using QmlDesigner::Import; @@ -30,9 +28,7 @@ MATCHER_P3(HasProperty, themeId, group, themeProp, - std::format("Collection {} have a property {}", - (negation ? "Does't " : "Does "), - PrintToString(themeProp))) + std::string(negation ? "hasn't " : "has ") + PrintToString(themeProp)) { const DSThemeManager &mgr = arg; const std::optional prop = mgr.property(themeId, group, themeProp.name); diff --git a/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp b/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp index 602a1b356d8..5951ee830c0 100644 --- a/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp +++ b/tests/unit/tests/unittests/designsystem/dsthemeqml-test.cpp @@ -13,8 +13,9 @@ #include #include -#include #include +#include +#include using QmlDesigner::DSThemeManager; using QmlDesigner::GroupType; @@ -22,40 +23,24 @@ using QmlDesigner::Import; using QmlDesigner::ModelNode; using QmlDesigner::ThemeProperty; -template<> -struct std::formatter : std::formatter -{ - template - auto format(const QByteArray &ba, FormatContext &ctx) const - { - return std::formatter::format(ba.toStdString(), ctx); - } -}; - namespace { - -static std::string formatedPropStr(const char tag[], const char name[], const QVariant &v) +std::string formatedPropStr(std::string tag, const QByteArray &name, const QVariant &value) { - return std::format("{}Property({}, {})", tag, name, v.toString().toStdString()); + return tag + "Property(" + name.toStdString() + ", " + value.toString().toStdString() + ")"; } -static auto bindingPropStr = std::bind(&formatedPropStr, - "Binding", - std::placeholders::_1, - std::placeholders::_2); -static auto variantPropStr = std::bind(&formatedPropStr, - "Variant", - std::placeholders::_1, - std::placeholders::_2); +auto bindingPropStr = std::bind_front(&formatedPropStr, "Binding"); +auto variantPropStr = std::bind_front(&formatedPropStr, "Variant"); -constexpr const char testPropertyName1[] = "prop1"; -constexpr const char darkThemeName[] = "dark"; +QByteArray testPropertyName1 = "prop1"; +QByteArray darkThemeName = "dark"; constexpr QmlDesigner::ThemeId testThemeId = 1; MATCHER_P2(HasNodeProperty, name, typeName, - std::format("{} have node {} with type {})", (negation ? "Does't " : "Does "), name, typeName)) + std::string(negation ? "hasn't node " : "has node ") + name.toStdString() + + std::string(" with type ") + typeName) { ModelNode n = arg; return n.hasNodeProperty(name) && n.nodeProperty(name).modelNode().isValid() @@ -65,7 +50,7 @@ MATCHER_P2(HasNodeProperty, MATCHER_P2(HasBindingProperty, name, value, - std::format("{} have {})", (negation ? "Does't " : "Does "), bindingPropStr(name, value))) + std::string(negation ? "hasn't " : "has ") + bindingPropStr(name, value)) { ModelNode n = arg; return n.hasBindingProperty(name) && n.bindingProperty(name).expression() == value; @@ -74,7 +59,7 @@ MATCHER_P2(HasBindingProperty, MATCHER_P2(HasVariantProperty, name, value, - std::format("{} have {})", (negation ? "Does't " : "Does "), variantPropStr(name, value))) + std::string(negation ? "hasn't " : "has ") + variantPropStr(name, value)) { ModelNode n = arg; return n.hasVariantProperty(name) && n.variantProperty(name).value() == value; @@ -83,10 +68,8 @@ MATCHER_P2(HasVariantProperty, MATCHER_P2(HasGroupVariantProperty, groupName, themeProp, - std::format("{} have node {} with {})", - (negation ? "Does't " : "Does "), - groupName.constData(), - PrintToString(themeProp))) + std::string(negation ? "hasn't node " : "has node ") + groupName.toStdString() + " with " + + PrintToString(themeProp)) { ModelNode n = arg; @@ -99,10 +82,8 @@ MATCHER_P2(HasGroupVariantProperty, MATCHER_P2(HasGroupBindingProperty, groupName, themeProp, - std::format("{} have node {} with {})", - (negation ? "Does't " : "Does "), - groupName.constData(), - PrintToString(themeProp))) + std::string(negation ? "hasn't node " : "has node ") + groupName.toStdString() + " with " + + PrintToString(themeProp)) { ModelNode n = arg; From 3eb89de6452134697a3710c5760cf5d86eb2f3f5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Oct 2024 20:30:31 +0200 Subject: [PATCH 019/322] QmlDesigner: Add project storage to dsstore Change-Id: I8717986b6943970ba4df9b30f72fb9fb1626d666 Reviewed-by: Vikas Pachdha --- .../libs/designsystem/CMakeLists.txt | 2 + .../qmldesigner/libs/designsystem/dsstore.cpp | 51 ++++++++++++++----- .../qmldesigner/libs/designsystem/dsstore.h | 11 ++-- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt b/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt index b85e4a0dc22..880fe74e02f 100644 --- a/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt +++ b/src/plugins/qmldesigner/libs/designsystem/CMakeLists.txt @@ -2,6 +2,8 @@ add_qtc_library(DesignSystem STATIC PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} DEPENDS Qt::Core Qt::Widgets QmlDesignerCore TextEditorSupport + PUBLIC_DEFINES + $<$:QDS_USE_PROJECTSTORAGE> SOURCES dsconstants.h dsstore.h dsstore.cpp diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp index 26ab0f9001c..10ed7d11de4 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp @@ -5,6 +5,7 @@ #include "dsthememanager.h" #include +#include #include #include #include @@ -49,17 +50,28 @@ static QByteArray reformatQml(const QString &content) return content.toUtf8(); } -std::optional modelSerializeHelper(QmlDesigner::ExternalDependenciesInterface &ed, - std::function callback, - const Utils::FilePath &targetDir, - const QString &typeName, - bool isSingelton = false) +std::optional modelSerializeHelper( + QmlDesigner::ProjectStorageDependencies &projectStorageDependencies, + QmlDesigner::ExternalDependenciesInterface &ed, + std::function callback, + const Utils::FilePath &targetDir, + const QString &typeName, + bool isSingelton = false) { QString qmlText{"import QtQuick\nQtObject {}\n"}; if (isSingelton) qmlText.prepend("pragma Singleton\n"); - QmlDesigner::ModelPointer model(QmlDesigner::Model::create("QtObject")); +#ifdef QDS_USE_PROJECTSTORAGE + auto model = QmlDesigner::Model::create(projectStorageDependencies, + "QtObject", + {QmlDesigner::Import::createLibraryImport("QtQtuick")}, + QUrl::fromLocalFile( + "/path/dummy.qml")); // the dummy file will most probably not work +#else + auto model = QmlDesigner::Model::create("QtObject"); +#endif + QPlainTextEdit editor; editor.setPlainText(qmlText); QmlDesigner::NotIndentingTextEditModifier modifier(&editor); @@ -87,8 +99,10 @@ std::optional modelSerializeHelper(QmlDesigner::ExternalDependenciesInt namespace QmlDesigner { -DSStore::DSStore(ExternalDependenciesInterface &ed) +DSStore::DSStore(ExternalDependenciesInterface &ed, + ProjectStorageDependencies projectStorageDependencies) : m_ed(ed) + , m_projectStorageDependencies(projectStorageDependencies) {} DSStore::~DSStore() {} @@ -147,7 +161,7 @@ std::optional DSStore::load(const Utils::FilePath &dsModuleDirPath) return {}; } -std::optional DSStore::save(bool mcuCompatible) const +std::optional DSStore::save(bool mcuCompatible) { if (auto moduleDir = dsModuleDir(m_ed)) return save(*moduleDir, mcuCompatible); @@ -155,7 +169,7 @@ std::optional DSStore::save(bool mcuCompatible) const return tr("Can not locate design system module"); } -std::optional DSStore::save(const Utils::FilePath &moduleDirPath, bool mcuCompatible) const +std::optional DSStore::save(const Utils::FilePath &moduleDirPath, bool mcuCompatible) { if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString())) return tr("Can not create design system module directory %1.").arg(moduleDirPath.toString()); @@ -215,8 +229,15 @@ std::optional DSStore::loadCollection(const QString &typeName, if (!reader.fetch(qmlFilePath, QFile::Text)) return reader.errorString(); - ModelPointer model(QmlDesigner::Model::create("QtObject")); - +#ifdef QDS_USE_PROJECTSTORAGE + auto model = QmlDesigner::Model::create(m_projectStorageDependencies, + "QtObject", + {QmlDesigner::Import::createLibraryImport("QtQtuick")}, + QUrl::fromLocalFile( + "/path/dummy.qml")); // the dummy file will most probably not work +#else + auto model = QmlDesigner::Model::create("QtObject"); +#endif QPlainTextEdit editor; QString qmlContent = QString::fromUtf8(reader.data()); editor.setPlainText(qmlContent); @@ -238,7 +259,7 @@ std::optional DSStore::loadCollection(const QString &typeName, std::optional DSStore::writeQml(const DSThemeManager &mgr, const QString &typeName, const Utils::FilePath &targetDir, - bool mcuCompatible) const + bool mcuCompatible) { if (mgr.themeCount() == 0) return {}; @@ -249,7 +270,8 @@ std::optional DSStore::writeQml(const DSThemeManager &mgr, mgr.decorateThemeInterface(interfaceModel->rootModelNode()); }; - if (auto error = modelSerializeHelper(m_ed, decorateInterface, targetDir, themeInterfaceType)) + if (auto error = modelSerializeHelper( + m_projectStorageDependencies, m_ed, decorateInterface, targetDir, themeInterfaceType)) return tr("Can not write theme interface %1.\n%2").arg(themeInterfaceType, *error); } @@ -257,7 +279,8 @@ std::optional DSStore::writeQml(const DSThemeManager &mgr, mgr.decorate(collectionModel->rootModelNode(), themeInterfaceType.toUtf8(), mcuCompatible); }; - if (auto error = modelSerializeHelper(m_ed, decorateCollection, targetDir, typeName, true)) + if (auto error = modelSerializeHelper( + m_projectStorageDependencies, m_ed, decorateCollection, targetDir, typeName, true)) return tr("Can not write collection %1.\n%2").arg(typeName, *error); return {}; diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.h b/src/plugins/qmldesigner/libs/designsystem/dsstore.h index 585a38c39e0..03e8e1ddc21 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.h @@ -14,10 +14,10 @@ using DSCollections = std::map; class DSStore { - Q_DECLARE_TR_FUNCTIONS(DSStore); + Q_DECLARE_TR_FUNCTIONS(DSStore) public: - DSStore(ExternalDependenciesInterface &ed); + DSStore(ExternalDependenciesInterface &ed, ProjectStorageDependencies projectStorageDependencies); ~DSStore(); QString moduleImportStr() const; @@ -25,8 +25,8 @@ public: std::optional load(); std::optional load(const Utils::FilePath &dsModuleDirPath); - std::optional save(bool mcuCompatible = false) const; - std::optional save(const Utils::FilePath &moduleDirPath, bool mcuCompatible = false) const; + std::optional save(bool mcuCompatible = false); + std::optional save(const Utils::FilePath &moduleDirPath, bool mcuCompatible = false); size_t collectionCount() const { return m_collections.size(); } @@ -38,10 +38,11 @@ private: std::optional writeQml(const DSThemeManager &mgr, const QString &typeName, const Utils::FilePath &targetDir, - bool mcuCompatible) const; + bool mcuCompatible); private: ExternalDependenciesInterface &m_ed; + ProjectStorageDependencies m_projectStorageDependencies; DSCollections m_collections; std::map m_collectionTypeNames; }; From 6d0bb3d1193d520e6f1f6730d7464cd7854503d4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 24 Oct 2024 12:45:19 +0200 Subject: [PATCH 020/322] QmlDesigner: Fix unused variable Change-Id: I154676e214e49166d5f3309416310dc15fcb7978 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/libs/designsystem/dsstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp index 10ed7d11de4..4fd0a17d164 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp @@ -51,7 +51,7 @@ static QByteArray reformatQml(const QString &content) } std::optional modelSerializeHelper( - QmlDesigner::ProjectStorageDependencies &projectStorageDependencies, + [[maybe_unused]] QmlDesigner::ProjectStorageDependencies &projectStorageDependencies, QmlDesigner::ExternalDependenciesInterface &ed, std::function callback, const Utils::FilePath &targetDir, From 5e3c0ffcc558f2971ce086e203c49b41b55faef7 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 23 Oct 2024 17:27:25 +0300 Subject: [PATCH 021/322] QmlDesigner: Add generated components when adding to content lib Also few relevant fixes and tweaks. Fixes: QDS-13746 Change-Id: I2a22d535591d6908ca3960145cb01682844aee38 Reviewed-by: Miikka Heikkinen Reviewed-by: Ali Kianian --- .../components/componentcore/bundlehelper.cpp | 36 ++++++++---------- .../components/componentcore/bundlehelper.h | 7 ++-- .../contentlibraryusermodel.cpp | 4 +- .../contentlibrary/contentlibraryview.cpp | 37 +++++++++++-------- .../contentlibrary/useritemcategory.cpp | 2 +- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp index 1db2622e345..b7a17ce07f0 100644 --- a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp @@ -38,6 +38,11 @@ Utils::FilePath AssetPath::absFilPath() const return basePath.pathAppended(relativePath); } +QByteArray AssetPath::fileContent() const +{ + return absFilPath().fileContents().value_or(""); +} + BundleHelper::BundleHelper(AbstractView *view, QWidget *widget) : m_view(view) , m_widget(widget) @@ -186,7 +191,7 @@ void BundleHelper::importBundleToProject() } QString typePrefix = compUtils.userBundleType(bundleId); - TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.section('.', 0, 0)).toLatin1(); QString err = m_importer->importComponent(bundlePath.toFSPathString(), type, qml, files); @@ -211,12 +216,6 @@ void BundleHelper::exportComponent(const ModelNode &node) if (exportPath.isEmpty()) return; - // targetPath is a temp path for collecting and zipping assets, actual export target is where - // the user chose to export (i.e. exportPath) - QTemporaryDir tempDir; - QTC_ASSERT(tempDir.isValid(), return); - auto targetPath = Utils::FilePath::fromString(tempDir.path()); - m_zipWriter = std::make_unique(exportPath); Utils::FilePath compFilePath = Utils::FilePath::fromString(ModelUtils::componentFilePath(node)); @@ -224,14 +223,14 @@ void BundleHelper::exportComponent(const ModelNode &node) QString compBaseName = compFilePath.completeBaseName(); QString compFileName = compFilePath.fileName(); - QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); + m_iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); const QSet compDependencies = getComponentDependencies(compFilePath, compDir); QStringList filesList; for (const AssetPath &asset : compDependencies) { Utils::FilePath assetAbsPath = asset.absFilPath(); - QByteArray assetContent = assetAbsPath.fileContents().value_or(""); + QByteArray assetContent = asset.fileContent(); // remove imports of sub components for (const QString &import : std::as_const(asset.importsToRemove)) { @@ -252,7 +251,7 @@ void BundleHelper::exportComponent(const ModelNode &node) itemsArr.append(QJsonObject { {"name", node.simplifiedTypeName()}, {"qml", compFileName}, - {"icon", iconPath}, + {"icon", m_iconPath}, {"files", QJsonArray::fromStringList(filesList)} }); @@ -262,12 +261,9 @@ void BundleHelper::exportComponent(const ModelNode &node) jsonObj["id"] = compUtils.user3DBundleId(); jsonObj["version"] = BUNDLE_VERSION; - Utils::FilePath jsonFilePath = targetPath.pathAppended(Constants::BUNDLE_JSON_FILENAME); - m_zipWriter->addFile(jsonFilePath.fileName(), QJsonDocument(jsonObj).toJson()); + m_zipWriter->addFile(Constants::BUNDLE_JSON_FILENAME, QJsonDocument(jsonObj).toJson()); // add icon - m_iconSavePath = targetPath.pathAppended(iconPath); - m_iconSavePath.parentDir().ensureWritableDir(); getImageFromCache(compFilePath.path(), [&](const QImage &image) { addIconAndCloseZip(image); }); @@ -293,6 +289,7 @@ void BundleHelper::exportNode(const ModelNode &node, const QPixmap &iconPixmap) QString qml = nodeNameToComponentFileName(name); QString iconBaseName = UniqueName::generateId(name); + m_iconPath = QLatin1String("icons/%1.png").arg(iconBaseName); // generate and save Qml file auto [qmlString, depAssets] = modelNodeToQmlString(node); @@ -307,15 +304,13 @@ void BundleHelper::exportNode(const ModelNode &node, const QPixmap &iconPixmap) QTC_ASSERT_EXPECTED(result, return); m_zipWriter->addFile(qmlFilePath.fileName(), qmlString.toUtf8()); - QString iconPath = QLatin1String("icons/%1.png").arg(iconBaseName); - // add the item to the bundle json QJsonObject jsonObj; QJsonArray itemsArr; itemsArr.append(QJsonObject { {"name", name}, {"qml", qml}, - {"icon", iconPath}, + {"icon", m_iconPath}, {"files", QJsonArray::fromStringList(depAssetsRelativePaths)} }); @@ -331,7 +326,7 @@ void BundleHelper::exportNode(const ModelNode &node, const QPixmap &iconPixmap) // add item's dependency assets to the bundle zip and target path (for icon generation) for (const AssetPath &assetPath : depAssetsList) { - auto assetContent = assetPath.absFilPath().fileContents().value_or(""); + QByteArray assetContent = assetPath.fileContent(); m_zipWriter->addFile(assetPath.relativePath, assetContent); Utils::FilePath assetTargetPath = targetPath.pathAppended(assetPath.relativePath); @@ -352,7 +347,6 @@ void BundleHelper::exportNode(const ModelNode &node, const QPixmap &iconPixmap) iconPixmapToSave = iconPixmap; } - m_iconSavePath = targetPath.pathAppended(iconPath); if (iconPixmapToSave.isNull()) { getImageFromCache(qmlFilePath.toFSPathString(), [&](const QImage &image) { addIconAndCloseZip(image); @@ -556,7 +550,7 @@ void BundleHelper::addIconAndCloseZip(const auto &image) { // auto: QImage or QP buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); - m_zipWriter->addFile("icons/" + m_iconSavePath.fileName(), iconByteArray); + m_zipWriter->addFile(m_iconPath, iconByteArray); m_zipWriter->close(); }; @@ -664,7 +658,7 @@ Utils::FilePath getComponentFilePath(const QString &nodeType, const Utils::FileP } // namespace QSet BundleHelper::getComponentDependencies(const Utils::FilePath &filePath, - const Utils::FilePath &mainCompDir) + const Utils::FilePath &mainCompDir) const { QSet depList; AssetPath compAssetPath = {mainCompDir, filePath.relativePathFrom(mainCompDir).toFSPathString()}; diff --git a/src/plugins/qmldesigner/components/componentcore/bundlehelper.h b/src/plugins/qmldesigner/components/componentcore/bundlehelper.h index f853afea579..bb5da36bff0 100644 --- a/src/plugins/qmldesigner/components/componentcore/bundlehelper.h +++ b/src/plugins/qmldesigner/components/componentcore/bundlehelper.h @@ -32,6 +32,7 @@ public: {} Utils::FilePath absFilPath() const; + QByteArray fileContent() const; bool operator==(const AssetPath &other) const { @@ -62,6 +63,8 @@ public: QString nodeNameToComponentFileName(const QString &name) const; QPair> modelNodeToQmlString(const ModelNode &node, int depth = 0); QString getImportPath() const; + QSet getComponentDependencies(const Utils::FilePath &filePath, + const Utils::FilePath &mainCompDir) const; private: void createImporter(); @@ -71,8 +74,6 @@ private: void addIconAndCloseZip(const auto &image); Utils::FilePath componentPath(const NodeMetaInfo &metaInfo) const; QSet getBundleComponentDependencies(const ModelNode &node) const; - QSet getComponentDependencies(const Utils::FilePath &filePath, - const Utils::FilePath &mainCompDir); void exportComponent(const ModelNode &node); void exportNode(const ModelNode &node, const QPixmap &iconPixmap = QPixmap()); @@ -81,7 +82,7 @@ private: Utils::UniqueObjectPtr m_importer; std::unique_ptr m_zipWriter; std::unique_ptr m_tempDir; - Utils::FilePath m_iconSavePath; + QString m_iconPath; static constexpr char BUNDLE_VERSION[] = "1.0"; }; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 4c5161c3c2e..47b06781123 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -84,7 +84,7 @@ void ContentLibraryUserModel::addItem(const QString &bundleId, const QString &na auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); QString typePrefix = compUtils.userBundleType(bundleId); - TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.section('.', 0, 0)).toLatin1(); SectionIndex sectionIndex = bundleIdToSectionIndex(bundleId); @@ -308,7 +308,7 @@ void ContentLibraryUserModel::updateImportedState(const QStringList &importedIte bool changed = false; for (QObject *item : items) { ContentLibraryItem *castedItem = qobject_cast(item); - changed |= castedItem->setImported(importedItems.contains(castedItem->qml().chopped(4))); + changed |= castedItem->setImported(importedItems.contains(castedItem->qml().section('.', 0, 0))); } if (changed) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 55c96dd9b4b..dda4fa6b5fb 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -570,17 +570,18 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) m_widget->userModel()->addTextures(targetPathsToAdd); } +// TODO: combine this method with BundleHelper::exportComponent() void ContentLibraryView::addLib3DComponent(const ModelNode &node) { auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); m_bundleId = compUtils.user3DBundleId(); - QString compBaseName = node.simplifiedTypeName(); - QString compFileName = compBaseName + ".qml"; - - auto compDir = Utils::FilePath::fromString(ModelUtils::componentFilePath(node)).parentDir(); - auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + Utils::FilePath compFilePath = Utils::FilePath::fromString(ModelUtils::componentFilePath(node)); + Utils::FilePath compDir = compFilePath.parentDir(); + QString compBaseName = compFilePath.completeBaseName(); + QString compFileName = compFilePath.fileName(); // confirm overwrite if an item with same name exists if (bundlePath.pathAppended(compFileName).exists()) { @@ -599,24 +600,28 @@ void ContentLibraryView::addLib3DComponent(const ModelNode &node) m_iconSavePath = bundlePath.pathAppended(iconPath); m_iconSavePath.parentDir().ensureWritableDir(); - const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories}); - const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"}; + const QSet compDependencies = m_bundleHelper->getComponentDependencies(compFilePath, compDir); + QStringList filesList; // 3D component's assets (dependencies) + for (const AssetPath &asset : compDependencies) { + Utils::FilePath assetAbsPath = asset.absFilPath(); + QByteArray assetContent = asset.fileContent(); - for (const Utils::FilePath &sourcePath : sourceFiles) { - Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir); - if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene")) - continue; + // remove imports of sub components + for (const QString &import : std::as_const(asset.importsToRemove)) { + int removeIdx = assetContent.indexOf(QByteArray("import " + import.toLatin1())); + int removeLen = assetContent.indexOf('\n', removeIdx) - removeIdx; + assetContent.remove(removeIdx, removeLen); + } - Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path()); + Utils::FilePath targetPath = bundlePath.pathAppended(asset.relativePath); targetPath.parentDir().ensureWritableDir(); - // copy item from project to user bundle - auto result = sourcePath.copyFile(targetPath); + auto result = targetPath.writeFileContents(assetContent); QTC_ASSERT_EXPECTED(result,); - if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies) - filesList.append(relativePath.path()); + if (assetAbsPath.fileName() != compFileName) // skip component file (only collect dependencies) + filesList.append(asset.relativePath); } // add the item to the bundle json diff --git a/src/plugins/qmldesigner/components/contentlibrary/useritemcategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/useritemcategory.cpp index c6983a92525..62851b33836 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/useritemcategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/useritemcategory.cpp @@ -78,7 +78,7 @@ void UserItemCategory::loadBundle(bool force) QString name = itemObj.value("name").toString(); QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.section('.', 0, 0)).toLatin1(); QUrl icon = m_bundlePath.pathAppended(itemObj.value("icon").toString()).toUrl(); QStringList files = itemObj.value("files").toVariant().toStringList(); From e61351d98966bd825bc8412fc3b39ab2ad23228d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 24 Oct 2024 17:20:06 +0300 Subject: [PATCH 022/322] QmlDesigner: Add float as supported type for "real" editor This will make float type custom properties show up in property editor. Fixes: QDS-13751 Change-Id: I2ff4ce8e2df64b6ce02b2a921fe0f3c60dd16412 Reviewed-by: Mahmoud Badri --- .../PropertyTemplates/TemplateTypes.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml index 03e42a974fe..e1cd20c9c91 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml @@ -13,7 +13,7 @@ AutoTypes { sourceFile: "IntEditorTemplate.template" } Type { - typeNames: ["real", "double", "qreal"] + typeNames: ["real", "double", "qreal", "float"] module: "QML" sourceFile: "RealEditorTemplate.template" } From 529fb8eca7057bddb639f3c23076f567e4f445a1 Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Thu, 24 Oct 2024 21:44:46 +0200 Subject: [PATCH 023/322] DSStore: Expose module dir path Task-number: QDS-13811 Change-Id: I621fde180baa8ef8958c487ceceb4422ddc55cdc Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/libs/designsystem/dsstore.cpp | 5 +++++ src/plugins/qmldesigner/libs/designsystem/dsstore.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp index 4fd0a17d164..4c9aedf2f97 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.cpp @@ -222,6 +222,11 @@ std::optional DSStore::typeName(DSThemeManager *collection) const return {}; } +std::optional DSStore::moduleDirPath() const +{ + return dsModuleDir(m_ed); +} + std::optional DSStore::loadCollection(const QString &typeName, const Utils::FilePath &qmlFilePath) { diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.h b/src/plugins/qmldesigner/libs/designsystem/dsstore.h index 03e8e1ddc21..6beb22b87a3 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.h @@ -33,6 +33,8 @@ public: DSThemeManager *addCollection(const QString &qmlTypeName); std::optional typeName(DSThemeManager *collection) const; + std::optional moduleDirPath() const; + private: std::optional loadCollection(const QString &typeName, const Utils::FilePath &qmlFilePath); std::optional writeQml(const DSThemeManager &mgr, From 4f97a18f973f61b38bec287ac75ed4a9e9beac08 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 18 Oct 2024 16:55:53 +0300 Subject: [PATCH 024/322] EffectComposer: Add ItemFilterComboBox for sampler properties Composed effects property sheet will now show ItemFilterComboBox in addition to UrlChooser for sampler properties. Selecting an item will override the chosen url image for the sampler. Items as samplers are not supported in the effect preview, as effect preview is not part of the main scene. Fixes: QDS-11996 Change-Id: I35b3426803694e000de4618a241fb06cde44b5c9 Reviewed-by: Mats Honkamaa Reviewed-by: Mahmoud Badri --- .../effectcomposer/effectcomposermodel.cpp | 44 ++++++++++++++++--- .../effectcomposer/effectcomposermodel.h | 2 +- src/plugins/effectcomposer/uniform.cpp | 20 ++++++++- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 32dce7cef65..3063283bffc 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -933,6 +933,7 @@ R"( s += " property int animatedFrame: frameAnimation.currentFrame\n"; } + QString imageFixerTag{"___ecImagefixer___"}; QString parentChanged{ R"( function setupParentLayer() @@ -960,6 +961,7 @@ R"( } parent.update() } +%9 } onParentChanged: setupParentLayer() @@ -995,9 +997,12 @@ R"( m_extraMargin ? QString("\n connectSource(true)\n") : QString(), mipmap1, mipmap2, - mipmap3); + mipmap3, + imageFixerTag); } else { - parentChanged = parentChanged.arg(QString(), QString(), QString()); + parentChanged = parentChanged.arg(QString(), QString(), QString(), + QString(), QString(), QString(), + QString(), QString(), QString()); } s += parentChanged; @@ -1015,7 +1020,8 @@ R"( s += '\n'; } - QString customImagesString = getQmlImagesString(true); + QString imageFixerStr; + QString customImagesString = getQmlImagesString(true, imageFixerStr); if (!customImagesString.isEmpty()) s += customImagesString; @@ -1024,6 +1030,9 @@ R"( s += getQmlComponentString(true); s += " }\n"; s += "}\n"; + + s.replace(imageFixerTag, imageFixerStr); + return s; } @@ -2053,14 +2062,38 @@ void EffectComposerModel::setHasValidTarget(bool validTarget) emit hasValidTargetChanged(); } -QString EffectComposerModel::getQmlImagesString(bool localFiles) +QString EffectComposerModel::getQmlImagesString(bool localFiles, QString &outImageFixerStr) { + const QString imageItemChanged{ +R"( + property var old%2: null + function %3 + { + if (old%2) { + old%2.layer.enabled = false + old%2 = null + } + if (%1 != imageItem%1) { + %1.layer.enabled = true + old%2 = %1 + } + } + on%2Changed: %3 +)" + }; + QString imagesString; const QList uniforms = allUniforms(); for (Uniform *uniform : uniforms) { if (uniform->type() == Uniform::Type::Sampler) { QString imagePath = uniform->value().toString(); if (localFiles) { + QString capitalName = uniform->name(); + if (!capitalName.isEmpty()) + capitalName[0] = capitalName[0].toUpper(); + QString funcName = "setupLayer_" + uniform->name() + "()"; + outImageFixerStr += "\n " + funcName; + imagesString += imageItemChanged.arg(uniform->name(), capitalName, funcName); QFileInfo fi(imagePath); imagePath = fi.fileName(); imagesString += QString(" property url %1Url: \"%2\"\n") @@ -2140,7 +2173,8 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles) s += localFiles ? m_exportedEffectPropertiesString : m_previewEffectPropertiesString; if (!localFiles) { - QString customImagesString = getQmlImagesString(false); + QString dummyStr; + QString customImagesString = getQmlImagesString(false, dummyStr); if (!customImagesString.isEmpty()) s += '\n' + customImagesString; } diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index fc04255f111..0fe4aeb84f9 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -202,7 +202,7 @@ private: void bakeShaders(); void saveResources(const QString &name); - QString getQmlImagesString(bool localFiles); + QString getQmlImagesString(bool localFiles, QString &outImageFixerStr); QString getQmlComponentString(bool localFiles); QString getGeneratedMessage() const; QString getDesignerSpecifics() const; diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index c42f11063d6..c0129f85557 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -346,9 +346,27 @@ R"( R"( UrlChooser { backendValue: backendValues.%1 + enabled: comboBox_%1.currentIndex === 0 + } + ExpandingSpacer {} + } + + PropertyLabel { + text: "%3" + tooltip: "%4" + } + + SecondColumnLayout { + ItemFilterComboBox { + id: comboBox_%1 + backendValue: backendValues.%2 } )"; - specs += typeSpec.arg(m_name + "Url"); + specs += typeSpec.arg(m_name + "Url") + .arg(m_name) + .arg(m_displayName + tr(" Item")) + .arg(tr("Set this to use an item in the scene as %1 instead of the above image.") + .arg(m_displayName)); break; } case Type::Define: From b2052cf8074d58a0b4054f5a006af69bf90486d4 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 22 Oct 2024 17:18:53 +0200 Subject: [PATCH 025/322] Add link support to the output pane The user can now click on issues to jump into source files and open web pages. Links have to be wrapped into a html href attribute. Fixes: QDS-13722 Change-Id: If66f2c61adaad0ab7a9b949912264b583011071e Reviewed-by: Thomas Hartmann --- .../qmldesigner/statusbar/IssuesPanel.qml | 21 +++++++++++ .../components/componentcore/viewmanager.cpp | 5 +++ .../components/componentcore/viewmanager.h | 3 ++ .../components/texteditor/texteditorview.cpp | 5 +++ .../components/toolbar/messagemodel.cpp | 36 +++++++++++++------ .../components/toolbar/messagemodel.h | 1 + 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml b/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml index 36e90d024a6..67e219a3d0f 100644 --- a/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml +++ b/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml @@ -77,6 +77,12 @@ ScrollView { hoverEnabled: true cursorShape: mouseArea.containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + Connections { + target: mouseArea + function onClicked() { + messageModel.jumpToCode(index) + } + } } } @@ -84,11 +90,26 @@ ScrollView { id: labelInfo color: (type == "Warning") ? StudioTheme.Values.themeAmberLight : StudioTheme.Values.themeRedLight + + linkColor: StudioTheme.Values.themeInteraction text: message font.pixelSize: StudioTheme.Values.baseFontSize verticalAlignment: Text.AlignTop wrapMode: Text.WordWrap width: row.width - labelIcon.width - labelLocation.width - row.spacing * 2 + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: labelInfo.hoveredLink === "" ? Qt.ArrowCursor : Qt.PointingHandCursor + } + + Connections { + target: labelInfo + function onLinkActivated(link) { + messageModel.openLink(link) + } + } } } } diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 9694aa4802d..45b7abc0e90 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -511,6 +511,11 @@ const AbstractView *ViewManager::view() const return &d->nodeInstanceView; } +TextEditorView *ViewManager::textEditorView() +{ + return &d->textEditorView; +} + void ViewManager::emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data) { diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index e2bb2c833fc..15fd555a11e 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -25,6 +25,7 @@ class DesignerActionManager; class NodeInstanceView; class RewriterView; class Edit3DView; +class TextEditorView; namespace Internal { class DesignModeWidget; } @@ -71,6 +72,8 @@ public: void nextFileIsCalledInternally(); const AbstractView *view() const; + TextEditorView *textEditorView(); + void emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data); diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 3d9cc2cf6ac..a516a8ef7ad 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -168,6 +168,11 @@ void TextEditorView::qmlJSEditorContextHelp(const Core::IContext::HelpCallback & #endif } +TextEditor::BaseTextEditor *TextEditorView::textEditor() +{ + return m_widget->textEditor(); +} + void TextEditorView::nodeIdChanged(const ModelNode& /*node*/, const QString &/*newId*/, const QString &/*oldId*/) { } diff --git a/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp b/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp index 49ff3b8aaff..8a2ace645fa 100644 --- a/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp +++ b/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp @@ -4,6 +4,11 @@ #include #include +#include +#include + +#include +#include MessageModel::MessageModel(QObject *parent) : QAbstractListModel(parent) @@ -47,20 +52,31 @@ void MessageModel::jumpToCode(const QVariant &index) bool ok = false; if (int idx = index.toInt(&ok); ok) { if (idx >= 0 && std::cmp_less(idx, m_tasks.size())) { - // TODO: - // - Check why this does not jump to line/column - // - Only call this when sure that the task, file row etc are valid. - ProjectExplorer::Task task = m_tasks.at(idx); + ProjectExplorer::Task task = m_tasks.at(static_cast(idx)); const int column = task.column ? task.column - 1 : 0; - Utils::Link link(task.file, task.line, column); - Core::EditorManager::openEditorAt(link, - {}, - Core::EditorManager::SwitchSplitIfAlreadyVisible); + if (Core::EditorManager::openEditor(task.file, + Utils::Id(), + Core::EditorManager::DoNotMakeVisible)) { + + auto &viewManager = QmlDesigner::QmlDesignerPlugin::instance()->viewManager(); + if (auto *editorView = viewManager.textEditorView()) { + if (TextEditor::BaseTextEditor *editor = editorView->textEditor()) { + editor->gotoLine(task.line, column); + editor->widget()->setFocus(); + editor->editorWidget()->updateFoldingHighlight(QTextCursor()); + } + } + } } } } +void MessageModel::openLink(const QVariant &url) +{ + QDesktopServices::openUrl(QUrl::fromUserInput(url.toString())); +} + int MessageModel::rowCount(const QModelIndex &) const { return static_cast(m_tasks.size()); @@ -78,7 +94,7 @@ QHash MessageModel::roleNames() const QVariant MessageModel::data(const QModelIndex &index, int role) const { if (index.isValid() && index.row() < rowCount()) { - int row = index.row(); + size_t row = static_cast(index.row()); if (role == MessageRole) { return m_tasks.at(row).description(); } else if (role == FileNameRole) { @@ -140,7 +156,7 @@ void MessageModel::removeTask(const ProjectExplorer::Task &task) void MessageModel::clearTasks(const Utils::Id &categoryId) { beginResetModel(); - std::erase_if(m_tasks, [categoryId](const ProjectExplorer::Task& task) { + std::erase_if(m_tasks, [categoryId](const ProjectExplorer::Task &task) { return task.category == categoryId; }); endResetModel(); diff --git a/src/plugins/qmldesigner/components/toolbar/messagemodel.h b/src/plugins/qmldesigner/components/toolbar/messagemodel.h index ccba4f66b68..716a5c6f9f2 100644 --- a/src/plugins/qmldesigner/components/toolbar/messagemodel.h +++ b/src/plugins/qmldesigner/components/toolbar/messagemodel.h @@ -34,6 +34,7 @@ public: int warningCount() const; Q_INVOKABLE void resetModel(); Q_INVOKABLE void jumpToCode(const QVariant &index); + Q_INVOKABLE void openLink(const QVariant &url); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QHash roleNames() const override; From cec3838fc460eb3b675db229fe785fc53c8f4f61 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 23 Oct 2024 07:12:24 +0200 Subject: [PATCH 026/322] NanoTrace: Remove user literal workaround Since C++ 20 consteval we can enforce compile time evaluation. So the workaround is not anymore needed. Change-Id: Icfe254431e2d1c364846107474bab911efbf5641 Reviewed-by: Thomas Hartmann --- src/libs/nanotrace/nanotracehr.h | 7 +- src/libs/sqlite/sqlitebasestatement.cpp | 52 +-- src/libs/sqlite/sqlitebasestatement.h | 20 +- src/libs/sqlite/sqliteexception.cpp | 4 +- src/libs/sqlite/sqlitetracing.cpp | 4 +- .../imagecachecollector.cpp | 2 +- .../imagecache/asynchronousimagecache.cpp | 24 +- .../imagecache/asynchronousimagefactory.cpp | 4 +- .../libs/designercore/imagecache/taskqueue.h | 6 +- .../designercore/metainfo/nodemetainfo.cpp | 280 +++++++-------- .../model/internalbindingproperty.cpp | 2 +- .../libs/designercore/model/internalnode_p.h | 2 +- .../model/internalnodelistproperty.cpp | 10 +- .../model/internalnodeproperty.cpp | 8 +- .../designercore/model/internalproperty.cpp | 4 +- .../model/internalsignalhandlerproperty.cpp | 4 +- .../model/internalvariantproperty.cpp | 2 +- .../libs/designercore/model/model.cpp | 32 +- .../libs/designercore/model/model_p.h | 2 +- .../projectstorage/projectstorage.cpp | 328 +++++++++--------- .../projectstorage/projectstorage.h | 8 +- .../projectstorageexceptions.cpp | 28 +- .../projectstorage/projectstorageupdater.cpp | 104 +++--- .../projectstorage/qmldocumentparser.cpp | 4 +- .../projectstorage/qmltypesparser.cpp | 16 +- .../tracing/qmldesignertracing.cpp | 10 +- 26 files changed, 481 insertions(+), 486 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index cf260adab28..b6a30ec43cd 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -62,7 +62,6 @@ struct TracerLiteral : text{text} {} - friend consteval TracerLiteral operator""_t(const char *text, size_t size); constexpr operator std::string_view() const { return text; } @@ -74,10 +73,6 @@ private: std::string_view text; }; -consteval TracerLiteral operator""_t(const char *text, size_t size) -{ - return {std::string_view{text, size}}; -} } // namespace Literals using namespace Literals; @@ -333,7 +328,7 @@ template { static_assert( !std::is_same_v, - R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); + R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral ("").)"); if constexpr (std::is_same_v) eventArguments = {}; diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 8557bf6ad2c..3dc7102c24e 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -82,7 +82,7 @@ void BaseStatement::waitForUnlockNotify() const void BaseStatement::reset() const noexcept { - NanotraceHR::Tracer tracer{"reset"_t, + NanotraceHR::Tracer tracer{"reset", sqliteLowLevelCategory(), keyValue("sqlite statement", handle())}; @@ -91,7 +91,7 @@ void BaseStatement::reset() const noexcept bool BaseStatement::next() const { - NanotraceHR::Tracer tracer{"next"_t, + NanotraceHR::Tracer tracer{"next", sqliteLowLevelCategory(), keyValue("sqlite statement", handle())}; int resultCode; @@ -120,7 +120,7 @@ void BaseStatement::step() const void BaseStatement::bindNull(int index) { - NanotraceHR::Tracer tracer{"bind null"_t, + NanotraceHR::Tracer tracer{"bind null", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index)}; @@ -137,7 +137,7 @@ void BaseStatement::bind(int index, NullValue) void BaseStatement::bind(int index, int value) { - NanotraceHR::Tracer tracer{"bind int"_t, + NanotraceHR::Tracer tracer{"bind int", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -150,7 +150,7 @@ void BaseStatement::bind(int index, int value) void BaseStatement::bind(int index, long long value) { - NanotraceHR::Tracer tracer{"bind long long"_t, + NanotraceHR::Tracer tracer{"bind long long", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -163,7 +163,7 @@ void BaseStatement::bind(int index, long long value) void BaseStatement::bind(int index, double value) { - NanotraceHR::Tracer tracer{"bind double"_t, + NanotraceHR::Tracer tracer{"bind double", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -176,7 +176,7 @@ void BaseStatement::bind(int index, double value) void BaseStatement::bind(int index, void *pointer) { - NanotraceHR::Tracer tracer{"bind pointer"_t, + NanotraceHR::Tracer tracer{"bind pointer", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -189,7 +189,7 @@ void BaseStatement::bind(int index, void *pointer) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind int span"_t, + NanotraceHR::Tracer tracer{"bind int span", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -208,7 +208,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind long long span"_t, + NanotraceHR::Tracer tracer{"bind long long span", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -227,7 +227,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind double span"_t, + NanotraceHR::Tracer tracer{"bind double span", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -246,7 +246,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind const char* span"_t, + NanotraceHR::Tracer tracer{"bind const char* span", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -265,7 +265,7 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::SmallStringView text) { - NanotraceHR::Tracer tracer{"bind string"_t, + NanotraceHR::Tracer tracer{"bind string", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -282,7 +282,7 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) void BaseStatement::bind(int index, BlobView blobView) { - NanotraceHR::Tracer tracer{"bind blob"_t, + NanotraceHR::Tracer tracer{"bind blob", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("index", index), @@ -308,7 +308,7 @@ void BaseStatement::bind(int index, BlobView blobView) void BaseStatement::bind(int index, const Value &value) { NanotraceHR::Tracer tracer{ - "bind value"_t, + "bind value", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), }; @@ -335,7 +335,7 @@ void BaseStatement::bind(int index, const Value &value) void BaseStatement::bind(int index, ValueView value) { NanotraceHR::Tracer tracer{ - "bind value"_t, + "bind value", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), }; @@ -361,7 +361,7 @@ void BaseStatement::bind(int index, ValueView value) void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { - NanotraceHR::Tracer tracer{"prepare"_t, + NanotraceHR::Tracer tracer{"prepare", sqliteLowLevelCategory(), keyValue("sql statement", sqlStatement)}; @@ -380,7 +380,7 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement) m_compiledStatement.reset(sqliteStatement); if (resultCode == SQLITE_LOCKED) { - tracer.tick("wait for unlock"_t); + tracer.tick("wait for unlock"); waitForUnlockNotify(); } @@ -468,7 +468,7 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) Type BaseStatement::fetchType(int column) const { - NanotraceHR::Tracer tracer{"fetch type"_t, + NanotraceHR::Tracer tracer{"fetch type", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -493,7 +493,7 @@ Type BaseStatement::fetchType(int column) const int BaseStatement::fetchIntValue(int column) const { - NanotraceHR::Tracer tracer{"fetch int"_t, + NanotraceHR::Tracer tracer{"fetch int", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -524,7 +524,7 @@ long BaseStatement::fetchValue(int column) const long long BaseStatement::fetchLongLongValue(int column) const { - NanotraceHR::Tracer tracer{"fetch long long"_t, + NanotraceHR::Tracer tracer{"fetch long long", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -544,7 +544,7 @@ long long BaseStatement::fetchValue(int column) const double BaseStatement::fetchDoubleValue(int column) const { - NanotraceHR::Tracer tracer{"fetch double"_t, + NanotraceHR::Tracer tracer{"fetch double", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -558,7 +558,7 @@ double BaseStatement::fetchDoubleValue(int column) const BlobView BaseStatement::fetchBlobValue(int column) const { - NanotraceHR::Tracer tracer{"fetch blob"_t, + NanotraceHR::Tracer tracer{"fetch blob", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -575,7 +575,7 @@ double BaseStatement::fetchValue(int column) const template StringType BaseStatement::fetchValue(int column) const { - NanotraceHR::Tracer tracer{"fetch string value"_t, + NanotraceHR::Tracer tracer{"fetch string value", sqliteLowLevelCategory(), keyValue("sqlite statement", handle()), keyValue("column", column)}; @@ -596,7 +596,7 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue(statement)), }; diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index a41be5f4822..306f8bada3d 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -154,7 +154,7 @@ public: { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{ - "execute"_t, + "execute", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle()), }; @@ -167,7 +167,7 @@ public: void bindValues(const ValueType &...values) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"bind"_t, + NanotraceHR::Tracer tracer{"bind", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -181,7 +181,7 @@ public: void write(const ValueType&... values) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"write"_t, + NanotraceHR::Tracer tracer{"write", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -218,7 +218,7 @@ public: auto values(const QueryTypes &...queryValues) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"values"_t, + NanotraceHR::Tracer tracer{"values", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -252,7 +252,7 @@ public: auto value(const QueryTypes &...queryValues) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"value"_t, + NanotraceHR::Tracer tracer{"value", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -271,7 +271,7 @@ public: auto optionalValue(const QueryTypes &...queryValues) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"optionalValue"_t, + NanotraceHR::Tracer tracer{"optionalValue", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -290,7 +290,7 @@ public: static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()}; + NanotraceHR::Tracer tracer{"toValue", sqliteHighLevelCategory()}; StatementImplementation statement(sqlStatement, database); @@ -305,7 +305,7 @@ public: void readCallback(Callable &&callable, const QueryTypes &...queryValues) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"readCallback"_t, + NanotraceHR::Tracer tracer{"readCallback", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -325,7 +325,7 @@ public: void readTo(Container &container, const QueryTypes &...queryValues) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"readTo"_t, + NanotraceHR::Tracer tracer{"readTo", sqliteHighLevelCategory(), keyValue("sqlite statement", BaseStatement::handle())}; @@ -427,7 +427,7 @@ public: using TracerCategory = std::decay_t; StatementImplementation &m_statement; NanotraceHR::Tracer tracer{ - "range"_t, + "range", sqliteHighLevelCategory(), NanotraceHR::keyValue("sqlite statement", m_statement.handle())}; }; diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp index bb4a474adb0..5a54cf94b8f 100644 --- a/src/libs/sqlite/sqliteexception.cpp +++ b/src/libs/sqlite/sqliteexception.cpp @@ -38,7 +38,7 @@ void ExceptionWithMessage::printWarning() const StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage) : ExceptionWithMessage{std::move(sqliteErrorMessage)} { - sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t, + sqliteHighLevelCategory().threadEvent("StatementIsBusy", keyValue("error message", std::string_view{what()})); } @@ -55,7 +55,7 @@ const char *DatabaseIsBusy::what() const noexcept StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage) : ExceptionWithMessage{std::move(sqliteErrorMessage)} { - sqliteHighLevelCategory().threadEvent("StatementHasError"_t, + sqliteHighLevelCategory().threadEvent("StatementHasError", keyValue("error message", std::string_view{what()})); } diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp index 700546f1467..709e05b233b 100644 --- a/src/libs/sqlite/sqlitetracing.cpp +++ b/src/libs/sqlite/sqlitetracing.cpp @@ -23,14 +23,14 @@ thread_local NanotraceHR::EventQueue &sqliteLowLevelCategory() { thread_local NanotraceHR::StringViewWithStringArgumentsCategory - sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; + sqliteLowLevelCategory_{"sqlite low level", eventQueue, sqliteLowLevelCategory}; return sqliteLowLevelCategory_; } NanotraceHR::StringViewWithStringArgumentsCategory &sqliteHighLevelCategory() { thread_local NanotraceHR::StringViewWithStringArgumentsCategory - sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; + sqliteHighLevelCategory_{"sqlite high level", eventQueue, sqliteHighLevelCategory}; return sqliteHighLevelCategory_; } diff --git a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp index 97148e664f1..18ff61a1d0c 100644 --- a/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/imagecachecollectors/imagecachecollector.cpp @@ -83,7 +83,7 @@ void ImageCacheCollector::start(Utils::SmallStringView name, using namespace NanotraceHR::Literals; auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( - "generate image in standard collector"_t); + "generate image in standard collector"); RewriterView rewriterView{m_externalDependencies, RewriterView::Amend}; NodeInstanceView nodeInstanceView{m_connectionManager, m_externalDependencies}; diff --git a/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagecache.cpp index fdd06d19a16..b9a9001db15 100644 --- a/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagecache.cpp @@ -20,7 +20,7 @@ using namespace NanotraceHR::Literals; namespace ImageCache { namespace { -thread_local Category category_{"image cache"_t, +thread_local Category category_{"image cache", QmlDesigner::Tracing::eventQueueWithStringArguments(), category}; } // namespace @@ -63,15 +63,15 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, using namespace std::literals::string_view_literals; auto [durationToken, flowToken] = traceToken.beginDurationWithFlow( - "AsynchronousImageCache works on the image request"_t, + "AsynchronousImageCache works on the image request", keyValue("name", name), keyValue("extra id", extraId)); - auto timeStrampToken = durationToken.beginDuration("getting timestamp"_t); + auto timeStrampToken = durationToken.beginDuration("getting timestamp"); const auto timeStamp = timeStampProvider.timeStamp(name); timeStrampToken.end(keyValue("time stamp", timeStamp.value)); - auto storageTraceToken = durationToken.beginDuration("fetching image from storage"_t, + auto storageTraceToken = durationToken.beginDuration("fetching image from storage", keyValue("storage id", id)); auto requestImageFromStorage = [&](RequestType requestType) { switch (requestType) { @@ -92,7 +92,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, if (entry) { if (entry->isNull()) { - storageTraceToken.tick("there was an null image in storage"_t); + storageTraceToken.tick("there was an null image in storage"); abortCallback(ImageCache::AbortReason::Failed); } else { captureCallback(*entry); @@ -106,7 +106,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, const QImage &midSizeImage, const QImage &smallImage, ImageCache::TraceToken traceToken) { - auto token = traceToken.beginDuration("call capture callback"_t); + auto token = traceToken.beginDuration("call capture callback"); auto selectImage = [](RequestType requestType, const QImage &image, const QImage &midSizeImage, @@ -130,11 +130,11 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, auto imageGenerationAbortedCallback = [abortCallback = std::move(abortCallback)](ImageCache::AbortReason reason, ImageCache::TraceToken traceToken) { - traceToken.tick("image could not be created"_t); + traceToken.tick("image could not be created"); abortCallback(reason); }; - traceToken.tick("call the generator"_t); + traceToken.tick("call the generator"); generator.generateImage(name, extraId, @@ -153,7 +153,7 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name, ImageCache::AuxiliaryData auxiliaryData) { auto [trace, flowToken] = ImageCache::category().beginDurationWithFlow( - "request image in asynchronous image cache"_t); + "request image in asynchronous image cache"); m_taskQueue.addTask(trace.createToken(), std::move(name), std::move(extraId), @@ -171,7 +171,7 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, ImageCache::AuxiliaryData auxiliaryData) { auto [traceToken, flowToken] = ImageCache::category().beginDurationWithFlow( - "request mid size image in asynchronous image cache"_t); + "request mid size image in asynchronous image cache"); m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), @@ -189,7 +189,7 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, ImageCache::AuxiliaryData auxiliaryData) { auto [traceToken, flowtoken] = ImageCache::category().beginDurationWithFlow( - "request small size image in asynchronous image cache"_t); + "request small size image in asynchronous image cache"); m_taskQueue.addTask(traceToken.createToken(), std::move(name), std::move(extraId), @@ -226,7 +226,7 @@ void AsynchronousImageCache::Clean::operator()(Entry &entry) { using namespace NanotraceHR::Literals; - entry.traceToken.tick("cleaning up in the cache"_t); + entry.traceToken.tick("cleaning up in the cache"); entry.abortCallback(ImageCache::AbortReason::Abort); } diff --git a/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagefactory.cpp index 83fd238f1db..fbeb0bab382 100644 --- a/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/libs/designercore/imagecache/asynchronousimagefactory.cpp @@ -27,7 +27,7 @@ void AsynchronousImageFactory::generate(Utils::SmallStringView name, ImageCache::AuxiliaryData auxiliaryData) { auto [trace, flowToken] = ImageCache::category().beginDurationWithFlow( - "request image in asynchronous image factory"_t); + "request image in asynchronous image factory"); m_taskQueue.addTask(trace.createToken(), name, extraId, @@ -45,7 +45,7 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, ImageCacheCollectorInterface &collector, ImageCache::TraceToken traceToken) { - auto [storageTracer, flowToken] = traceToken.beginDurationWithFlow("starte image generator"_t); + auto [storageTracer, flowToken] = traceToken.beginDurationWithFlow("starte image generator"); const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); diff --git a/src/plugins/qmldesigner/libs/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/libs/designercore/imagecache/taskqueue.h index dc5ed3de23f..19054e947f3 100644 --- a/src/plugins/qmldesigner/libs/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/libs/designercore/imagecache/taskqueue.h @@ -124,10 +124,10 @@ private: return; auto [threadCreateToken, flowToken] = traceToken.beginDurationWithFlow( - "thread is created in the task queue"_t); + "thread is created in the task queue"); m_backgroundThread = std::thread{[this](auto traceToken) { auto duration = traceToken.beginDuration( - "thread is ready"_t); + "thread is ready"); while (true) { auto [lock, abort] = waitForTasks(); @@ -137,7 +137,7 @@ private: return; auto getTaskToken = duration.beginDuration( - "get task from queue"_t); + "get task from queue"); if (auto task = getTask(std::move(lock)); task) { getTaskToken.end(); m_dispatchCallback(*task); diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp index 56836119d06..43bdbcdd875 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp @@ -1504,7 +1504,7 @@ MetaInfoType NodeMetaInfo::type() const if constexpr (useProjectStorage()) { if (isValid()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get type", category(), keyValue("type id", m_typeId)}; auto kind = typeData().traits.kind; tracer.end(keyValue("type kind", kind)); @@ -1531,7 +1531,7 @@ bool NodeMetaInfo::isFileComponent() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is file component"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is file component", category(), keyValue("type id", m_typeId)}; auto isFileComponent = typeData().traits.isFileComponent; @@ -1551,7 +1551,7 @@ FlagIs NodeMetaInfo::canBeContainer() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"can be container"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"can be container", category(), keyValue("type id", m_typeId)}; auto canBeContainer = typeData().traits.canBeContainer; @@ -1570,7 +1570,7 @@ FlagIs NodeMetaInfo::forceClip() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"force clip"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"force clip", category(), keyValue("type id", m_typeId)}; auto forceClip = typeData().traits.forceClip; @@ -1589,7 +1589,7 @@ FlagIs NodeMetaInfo::doesLayoutChildren() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"does layout children"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"does layout children", category(), keyValue("type id", m_typeId)}; auto doesLayoutChildren = typeData().traits.doesLayoutChildren; @@ -1608,7 +1608,7 @@ FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"can be dropped in form editor"_t, + NanotraceHR::Tracer tracer{"can be dropped in form editor", category(), keyValue("type id", m_typeId)}; @@ -1629,7 +1629,7 @@ FlagIs NodeMetaInfo::canBeDroppedInNavigator() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"can be dropped in navigator"_t, + NanotraceHR::Tracer tracer{"can be dropped in navigator", category(), keyValue("type id", m_typeId)}; @@ -1650,7 +1650,7 @@ FlagIs NodeMetaInfo::canBeDroppedInView3D() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"can be dropped in view3d"_t, + NanotraceHR::Tracer tracer{"can be dropped in view3d", category(), keyValue("type id", m_typeId)}; @@ -1671,7 +1671,7 @@ FlagIs NodeMetaInfo::isMovable() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is movable"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is movable", category(), keyValue("type id", m_typeId)}; auto isMovable = typeData().traits.isMovable; @@ -1690,7 +1690,7 @@ FlagIs NodeMetaInfo::isResizable() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is resizable"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is resizable", category(), keyValue("type id", m_typeId)}; auto isResizable = typeData().traits.isResizable; @@ -1709,7 +1709,7 @@ FlagIs NodeMetaInfo::hasFormEditorItem() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"has form editor item"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"has form editor item", category(), keyValue("type id", m_typeId)}; auto hasFormEditorItem = typeData().traits.hasFormEditorItem; @@ -1728,7 +1728,7 @@ FlagIs NodeMetaInfo::isStackedContainer() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is stacked container"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is stacked container", category(), keyValue("type id", m_typeId)}; auto isStackedContainer = typeData().traits.isStackedContainer; @@ -1747,7 +1747,7 @@ FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"takes over rendering of children"_t, + NanotraceHR::Tracer tracer{"takes over rendering of children", category(), keyValue("type id", m_typeId)}; @@ -1768,7 +1768,7 @@ FlagIs NodeMetaInfo::visibleInNavigator() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"visible in navigator"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"visible in navigator", category(), keyValue("type id", m_typeId)}; auto visibleInNavigator = typeData().traits.visibleInNavigator; @@ -1799,7 +1799,7 @@ FlagIs NodeMetaInfo::visibleInLibrary() const return FlagIs::False; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"visible in library"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"visible in library", category(), keyValue("type id", m_typeId)}; auto visibleInLibrary = typeData().traits.visibleInLibrary; @@ -1818,7 +1818,7 @@ namespace { Utils::SmallStringView propertyName) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get combound property id"_t, + NanotraceHR::Tracer tracer{"get combound property id", category(), keyValue("type id", typeId), keyValue("property name", propertyName)}; @@ -1858,7 +1858,7 @@ bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"has property"_t, + NanotraceHR::Tracer tracer{"has property", category(), keyValue("type id", m_typeId), keyValue("property name", propertyName)}; @@ -1883,7 +1883,7 @@ PropertyMetaInfos NodeMetaInfo::properties() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get properties"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get properties", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->propertyDeclarationIds(m_typeId), PropertyMetaInfo::bind(m_projectStorage)); @@ -1908,7 +1908,7 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get local properties"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get local properties", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->localPropertyDeclarationIds( m_typeId), @@ -1934,7 +1934,7 @@ PropertyMetaInfo NodeMetaInfo::property(PropertyNameView propertyName) const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property"_t, + NanotraceHR::Tracer tracer{"get property", category(), keyValue("type id", m_typeId), keyValue("property name", propertyName)}; @@ -1955,7 +1955,7 @@ PropertyNameList NodeMetaInfo::signalNames() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get signal names"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get signal names", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->signalDeclarationNames(m_typeId), &Utils::SmallString::toQByteArray); @@ -1972,7 +1972,7 @@ PropertyNameList NodeMetaInfo::slotNames() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get slot names"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get slot names", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->functionDeclarationNames(m_typeId), &Utils::SmallString::toQByteArray); } else { @@ -1987,7 +1987,7 @@ PropertyName NodeMetaInfo::defaultPropertyName() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get default property name"_t, + NanotraceHR::Tracer tracer{"get default property name", category(), keyValue("type id", m_typeId)}; if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { @@ -2009,7 +2009,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get default property"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get default property", category(), keyValue("type id", m_typeId)}; auto id = defaultPropertyDeclarationId(); @@ -2027,7 +2027,7 @@ bool NodeMetaInfo::hasDefaultProperty() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"has default property"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"has default property", category(), keyValue("type id", m_typeId)}; auto hasDefaultProperty = bool(defaultPropertyDeclarationId()); tracer.end(keyValue("has default property", hasDefaultProperty)); @@ -2044,7 +2044,7 @@ std::vector NodeMetaInfo::selfAndPrototypes() const #ifdef QDS_USE_PROJECTSTORAGE using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get self and prototypes"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get self and prototypes", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->prototypeAndSelfIds(m_typeId), NodeMetaInfo::bind(m_projectStorage)); @@ -2071,7 +2071,7 @@ NodeMetaInfos NodeMetaInfo::prototypes() const #ifdef QDS_USE_PROJECTSTORAGE using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get prototypes"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get prototypes", category(), keyValue("type id", m_typeId)}; return Utils::transform(m_projectStorage->prototypeIds(m_typeId), NodeMetaInfo::bind(m_projectStorage)); @@ -2172,7 +2172,7 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get all exported type names"_t, + NanotraceHR::Tracer tracer{"get all exported type names", category(), keyValue("type id", m_typeId)}; @@ -2189,7 +2189,7 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get exported type names for source id"_t, + NanotraceHR::Tracer tracer{"get exported type names for source id", category(), keyValue("type id", m_typeId), keyValue("source id", sourceId)}; @@ -2207,7 +2207,7 @@ Storage::Info::TypeHints NodeMetaInfo::typeHints() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type hints"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get type hints", category(), keyValue("type id", m_typeId)}; auto hints = m_projectStorage->typeHints(m_typeId); @@ -2226,7 +2226,7 @@ Utils::PathString NodeMetaInfo::iconPath() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get icon path"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get icon path", category(), keyValue("type id", m_typeId)}; auto iconPath = m_projectStorage->typeIconPath(m_typeId); @@ -2245,7 +2245,7 @@ Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries"_t, + NanotraceHR::Tracer tracer{"get item library entries", category(), keyValue("type id", m_typeId)}; @@ -2266,7 +2266,7 @@ SourceId NodeMetaInfo::sourceId() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get source id"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"get source id", category(), keyValue("type id", m_typeId)}; auto id = typeData().sourceId; @@ -2323,7 +2323,7 @@ SourceId NodeMetaInfo::propertyEditorPathId() const if (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property editor path id"_t, + NanotraceHR::Tracer tracer{"get property editor path id", category(), keyValue("type id", m_typeId)}; @@ -2398,7 +2398,7 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is suitable for mouse area fill"_t, + NanotraceHR::Tracer tracer{"is suitable for mouse area fill", category(), keyValue("type id", m_typeId)}; @@ -2432,7 +2432,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId), keyValue("meta info type id", metaInfo.m_typeId)}; @@ -2454,7 +2454,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo & return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId); #else @@ -2477,7 +2477,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, @@ -2506,7 +2506,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, @@ -2535,7 +2535,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, @@ -2567,7 +2567,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, @@ -2602,7 +2602,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is based on", category(), keyValue("type id", m_typeId)}; return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, @@ -2633,7 +2633,7 @@ bool NodeMetaInfo::isGraphicalItem() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is graphical item"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is graphical item", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId(); @@ -2657,7 +2657,7 @@ bool NodeMetaInfo::isQtObject() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is Qt object"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is Qt object", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2673,7 +2673,7 @@ bool NodeMetaInfo::isQtQmlConnections() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is Qt Qml connections"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is Qt Qml connections", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2689,7 +2689,7 @@ bool NodeMetaInfo::isLayoutable() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is layoutable"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is layoutable", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto positionerId = m_projectStorage->commonTypeId(); @@ -2712,7 +2712,7 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout", category(), keyValue("type id", m_typeId)}; @@ -2730,7 +2730,7 @@ bool NodeMetaInfo::isView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is view"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is view", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2751,7 +2751,7 @@ bool NodeMetaInfo::usesCustomParser() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"uses custom parser"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"uses custom parser", category(), keyValue("type id", m_typeId)}; return typeData().traits.usesCustomParser; #else @@ -2783,7 +2783,7 @@ bool NodeMetaInfo::isVector2D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is vector2d"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is vector2d", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isTypeId(m_typeId, m_projectStorage->commonTypeId()); @@ -2804,7 +2804,7 @@ bool NodeMetaInfo::isVector3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is vector3d"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is vector3d", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isTypeId(m_typeId, m_projectStorage->commonTypeId()); @@ -2825,7 +2825,7 @@ bool NodeMetaInfo::isVector4D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is vector4d"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is vector4d", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isTypeId(m_typeId, m_projectStorage->commonTypeId()); @@ -2846,7 +2846,7 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges"_t, + NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges", category(), keyValue("type id", m_typeId)}; @@ -2865,7 +2865,7 @@ bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture"_t, + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture", category(), keyValue("type id", m_typeId)}; @@ -2883,7 +2883,7 @@ bool NodeMetaInfo::isQtSafeRendererSafePicture() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture"_t, + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture", category(), keyValue("type id", m_typeId)}; @@ -2901,7 +2901,7 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframe() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe", category(), keyValue("type id", m_typeId)}; @@ -2920,7 +2920,7 @@ bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation", category(), keyValue("type id", m_typeId)}; @@ -2938,7 +2938,7 @@ bool NodeMetaInfo::isQtQuickTimelineTimeline() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline", category(), keyValue("type id", m_typeId)}; @@ -2956,7 +2956,7 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup", category(), keyValue("type id", m_typeId)}; @@ -2974,7 +2974,7 @@ bool NodeMetaInfo::isListOrGridView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is list or grid view"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is list or grid view", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2992,7 +2992,7 @@ bool NodeMetaInfo::isNumber() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is number"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is number", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto intId = m_projectStorage->builtinTypeId(); @@ -3017,7 +3017,7 @@ bool NodeMetaInfo::isQtQuickExtrasPicture() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture", category(), keyValue("type id", m_typeId)}; @@ -3035,7 +3035,7 @@ bool NodeMetaInfo::isQtQuickImage() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Image"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Image", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; @@ -3052,7 +3052,7 @@ bool NodeMetaInfo::isQtQuickBorderImage() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.BorderImage"_t, + NanotraceHR::Tracer tracer{"is QtQuick.BorderImage", category(), keyValue("type id", m_typeId)}; @@ -3071,7 +3071,7 @@ bool NodeMetaInfo::isAlias() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is alias"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is alias", category(), keyValue("type id", m_typeId)}; return false; // all types are already resolved } else { @@ -3086,7 +3086,7 @@ bool NodeMetaInfo::isQtQuickPositioner() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Positioner"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Positioner", category(), keyValue("type id", m_typeId)}; @@ -3105,7 +3105,7 @@ bool NodeMetaInfo::isQtQuickPropertyAnimation() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation"_t, + NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation", category(), keyValue("type id", m_typeId)}; @@ -3124,7 +3124,7 @@ bool NodeMetaInfo::isQtQuickRectangle() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Rectange"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Rectange", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3140,7 +3140,7 @@ bool NodeMetaInfo::isQtQuickRepeater() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Repeater"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Repeater", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3156,7 +3156,7 @@ bool NodeMetaInfo::isQtQuickControlsTabBar() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar", category(), keyValue("type id", m_typeId)}; @@ -3174,7 +3174,7 @@ bool NodeMetaInfo::isQtQuickControlsLabel() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView", category(), keyValue("type id", m_typeId)}; @@ -3192,7 +3192,7 @@ bool NodeMetaInfo::isQtQuickControlsSwipeView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView", category(), keyValue("type id", m_typeId)}; @@ -3210,7 +3210,7 @@ bool NodeMetaInfo::isQtQuick3DCamera() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Camera"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Camera", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3226,7 +3226,7 @@ bool NodeMetaInfo::isQtQuick3DBakedLightmap() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap", category(), keyValue("type id", m_typeId)}; @@ -3244,7 +3244,7 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3260,7 +3260,7 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry", category(), keyValue("type id", m_typeId)}; @@ -3278,7 +3278,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Light"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Light", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3294,7 +3294,7 @@ bool NodeMetaInfo::isQtQmlModelsListElement() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement"_t, + NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement", category(), keyValue("type id", m_typeId)}; @@ -3312,7 +3312,7 @@ bool NodeMetaInfo::isQtQuickListModel() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.ListModel"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.ListModel", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3328,7 +3328,7 @@ bool NodeMetaInfo::isQtQuickListView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.ListView"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.ListView", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3344,7 +3344,7 @@ bool QmlDesigner::NodeMetaInfo::isQtQuickGridView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.GridView"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.GridView", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3360,7 +3360,7 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList", category(), keyValue("type id", m_typeId)}; @@ -3378,7 +3378,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D", category(), keyValue("type id", m_typeId)}; @@ -3396,7 +3396,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D", category(), keyValue("type id", m_typeId)}; @@ -3415,7 +3415,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D", category(), keyValue("type id", m_typeId)}; @@ -3433,7 +3433,7 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape", category(), keyValue("type id", m_typeId)}; @@ -3452,7 +3452,7 @@ bool NodeMetaInfo::isQtQuickItem() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Item"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Item", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3468,7 +3468,7 @@ bool NodeMetaInfo::isQtQuickPath() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Path"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Path", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3484,7 +3484,7 @@ bool NodeMetaInfo::isQtQuickPauseAnimation() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation"_t, + NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation", category(), keyValue("type id", m_typeId)}; @@ -3502,7 +3502,7 @@ bool NodeMetaInfo::isQtQuickTransition() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Transition"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Transition", category(), keyValue("type id", m_typeId)}; @@ -3520,7 +3520,7 @@ bool NodeMetaInfo::isQtQuickWindowWindow() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Window.Window"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Window.Window", category(), keyValue("type id", m_typeId)}; @@ -3538,7 +3538,7 @@ bool NodeMetaInfo::isQtQuickLoader() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Loader"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Loader", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3554,7 +3554,7 @@ bool NodeMetaInfo::isQtQuickState() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.State"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.State", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3570,7 +3570,7 @@ bool NodeMetaInfo::isQtQuickStateOperation() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.StateOperation"_t, + NanotraceHR::Tracer tracer{"is QtQuick.StateOperation", category(), keyValue("type id", m_typeId)}; @@ -3589,7 +3589,7 @@ bool NodeMetaInfo::isQtQuickText() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Text"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick.Text", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3605,7 +3605,7 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect"_t, + NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect", category(), keyValue("type id", m_typeId)}; @@ -3623,7 +3623,7 @@ bool NodeMetaInfo::isFlowViewItem() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.ViewItem"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is FlowView.ViewItem", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto flowItemId = m_projectStorage->commonTypeId(); @@ -3644,7 +3644,7 @@ bool NodeMetaInfo::isFlowViewFlowItem() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.FlowItem"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is FlowView.FlowItem", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3660,7 +3660,7 @@ bool NodeMetaInfo::isFlowViewFlowView() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.FlowView"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is FlowView.FlowView", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3686,7 +3686,7 @@ bool NodeMetaInfo::isFlowViewFlowTransition() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.FlowTransition"_t, + NanotraceHR::Tracer tracer{"is FlowView.FlowTransition", category(), keyValue("type id", m_typeId)}; @@ -3704,7 +3704,7 @@ bool NodeMetaInfo::isFlowViewFlowDecision() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.FlowDecision"_t, + NanotraceHR::Tracer tracer{"is FlowView.FlowDecision", category(), keyValue("type id", m_typeId)}; @@ -3722,7 +3722,7 @@ bool NodeMetaInfo::isFlowViewFlowWildcard() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard"_t, + NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard", category(), keyValue("type id", m_typeId)}; @@ -3740,7 +3740,7 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem", category(), keyValue("type id", m_typeId)}; @@ -3758,7 +3758,7 @@ bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel"_t, + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel", category(), keyValue("type id", m_typeId)}; @@ -3777,7 +3777,7 @@ bool NodeMetaInfo::isQmlComponent() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QML.Component"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QML.Component", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3798,7 +3798,7 @@ bool NodeMetaInfo::isFont() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is font"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is font", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); @@ -3814,7 +3814,7 @@ bool NodeMetaInfo::isColor() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is color"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is color", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3835,7 +3835,7 @@ bool NodeMetaInfo::isBool() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is bool"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is bool", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3856,7 +3856,7 @@ bool NodeMetaInfo::isInteger() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is integer"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is integer", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3877,7 +3877,7 @@ bool NodeMetaInfo::isFloat() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is float"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is float", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto floatId = m_projectStorage->builtinTypeId(); @@ -3901,7 +3901,7 @@ bool NodeMetaInfo::isVariant() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is variant"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is variant", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3922,7 +3922,7 @@ bool NodeMetaInfo::isString() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is string"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is string", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3943,7 +3943,7 @@ bool NodeMetaInfo::isUrl() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is url"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is url", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); @@ -3964,7 +3964,7 @@ bool NodeMetaInfo::isQtQuick3DTexture() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Texture"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Texture", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3981,7 +3981,7 @@ bool NodeMetaInfo::isQtQuick3DShader() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Shader"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Shader", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3997,7 +3997,7 @@ bool NodeMetaInfo::isQtQuick3DPass() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Pass"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Pass", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4013,7 +4013,7 @@ bool NodeMetaInfo::isQtQuick3DCommand() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Command"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Command", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4029,7 +4029,7 @@ bool NodeMetaInfo::isQtQuick3DDefaultMaterial() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial", category(), keyValue("type id", m_typeId)}; @@ -4057,7 +4057,7 @@ bool NodeMetaInfo::isQtQuick3DModel() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Model"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Model", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4073,7 +4073,7 @@ bool NodeMetaInfo::isQtQuick3DNode() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Node"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Node", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4089,7 +4089,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D", category(), keyValue("type id", m_typeId)}; @@ -4107,7 +4107,7 @@ bool NodeMetaInfo::isQtQuick3DView3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.View3D"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.View3D", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4123,7 +4123,7 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial", category(), keyValue("type id", m_typeId)}; @@ -4141,7 +4141,7 @@ bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial", category(), keyValue("type id", m_typeId)}; @@ -4159,7 +4159,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D", category(), keyValue("type id", m_typeId)}; @@ -4178,7 +4178,7 @@ bool NodeMetaInfo::isQtQuick3DTextureInput() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput", category(), keyValue("type id", m_typeId)}; @@ -4196,7 +4196,7 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture", category(), keyValue("type id", m_typeId)}; @@ -4216,7 +4216,7 @@ bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment"_t, + NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment", category(), keyValue("type id", m_typeId)}; @@ -4234,7 +4234,7 @@ bool NodeMetaInfo::isQtQuick3DEffect() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is QtQuick3D.Effect"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is QtQuick3D.Effect", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -4250,7 +4250,7 @@ bool NodeMetaInfo::isEnumeration() const return false; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is enumeration"_t, category(), keyValue("type id", m_typeId)}; + NanotraceHR::Tracer tracer{"is enumeration", category(), keyValue("type id", m_typeId)}; return typeData().traits.isEnum; } @@ -4282,7 +4282,7 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property type"_t, + NanotraceHR::Tracer tracer{"get property type", category(), keyValue("property declaration id", m_id)}; @@ -4305,7 +4305,7 @@ NodeMetaInfo PropertyMetaInfo::type() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property owner type "_t, + NanotraceHR::Tracer tracer{"get property owner type ", category(), keyValue("property declaration id", m_id)}; @@ -4322,7 +4322,7 @@ PropertyName PropertyMetaInfo::name() const if constexpr (useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property name"_t, + NanotraceHR::Tracer tracer{"get property name", category(), keyValue("property declaration id", m_id)}; @@ -4339,7 +4339,7 @@ bool PropertyMetaInfo::isWritable() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is property writable"_t, + NanotraceHR::Tracer tracer{"is property writable", category(), keyValue("property declaration id", m_id)}; @@ -4356,7 +4356,7 @@ bool PropertyMetaInfo::isReadOnly() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is property read only"_t, + NanotraceHR::Tracer tracer{"is property read only", category(), keyValue("property declaration id", m_id)}; @@ -4373,7 +4373,7 @@ bool PropertyMetaInfo::isListProperty() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is list property"_t, + NanotraceHR::Tracer tracer{"is list property", category(), keyValue("property declaration id", m_id)}; @@ -4390,7 +4390,7 @@ bool PropertyMetaInfo::isEnumType() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is enum type"_t, + NanotraceHR::Tracer tracer{"is enum type", category(), keyValue("property has enumeration type", m_id)}; @@ -4407,7 +4407,7 @@ bool PropertyMetaInfo::isPrivate() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is private property"_t, + NanotraceHR::Tracer tracer{"is private property", category(), keyValue("property declaration id", m_id)}; @@ -4424,7 +4424,7 @@ bool PropertyMetaInfo::isPointer() const return {}; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is pointer property"_t, + NanotraceHR::Tracer tracer{"is pointer property", category(), keyValue("property declaration id", m_id)}; @@ -4449,7 +4449,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const if constexpr (!useProjectStorage()) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"cast value"_t, + NanotraceHR::Tracer tracer{"cast value", category(), keyValue("property declaration id", m_id)}; diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalbindingproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalbindingproperty.cpp index 8c99041b08d..44b2dce30ec 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalbindingproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalbindingproperty.cpp @@ -24,7 +24,7 @@ QString InternalBindingProperty::expression() const void InternalBindingProperty::setExpression(const QString &expression) { - traceToken.tick("expression"_t, keyValue("expression", expression)); + traceToken.tick("expression", keyValue("expression", expression)); m_expression = expression; } diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/libs/designercore/model/internalnode_p.h index a850ead3ae1..8160e149e60 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/libs/designercore/model/internalnode_p.h @@ -63,7 +63,7 @@ public: , minorVersion(minorVersion) , isValid(true) , internalId(internalId) - , traceToken(flowTraceToken.beginAsynchronous("InternalNode"_t, + , traceToken(flowTraceToken.beginAsynchronous("InternalNode", keyValue("type", typeName), keyValue("internal id", internalId))) {} diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalnodelistproperty.cpp index a7ed51b18c3..d99f0116419 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalnodelistproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalnodelistproperty.cpp @@ -41,8 +41,8 @@ void InternalNodeListProperty::add(const InternalNode::Pointer &internalNode) { Q_ASSERT(!m_nodes.contains(internalNode)); - auto flowToken = traceToken.tickWithFlow("add node"_t); - internalNode->traceToken.tick(flowToken, "node added"_t); + auto flowToken = traceToken.tickWithFlow("add node"); + internalNode->traceToken.tick(flowToken, "node added"); m_nodes.append(internalNode); } @@ -51,8 +51,8 @@ void InternalNodeListProperty::remove(const InternalNodePointer &internalNode) { Q_ASSERT(m_nodes.contains(internalNode)); - auto flowToken = traceToken.tickWithFlow("remove node"_t); - internalNode->traceToken.tick(flowToken, "node removed"_t); + auto flowToken = traceToken.tickWithFlow("remove node"); + internalNode->traceToken.tick(flowToken, "node removed"); m_nodes.removeAll(internalNode); } @@ -64,7 +64,7 @@ const InternalNodeListProperty::FewNodes &InternalNodeListProperty::nodeList() c void InternalNodeListProperty::slide(int from, int to) { - traceToken.tick("slide"_t, keyValue("from", from), keyValue("to", to)); + traceToken.tick("slide", keyValue("from", from), keyValue("to", to)); InternalNode::Pointer internalNode = m_nodes.at(from); m_nodes.remove(from); diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalnodeproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalnodeproperty.cpp index 235f8855fd0..f87d5135672 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalnodeproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalnodeproperty.cpp @@ -43,8 +43,8 @@ void InternalNodeProperty::remove([[maybe_unused]] const InternalNode::Pointer & { Q_ASSERT(m_node == node); - auto flowToken = traceToken.tickWithFlow("remove node"_t); - node->traceToken.tick(flowToken, "node removed"_t); + auto flowToken = traceToken.tickWithFlow("remove node"); + node->traceToken.tick(flowToken, "node removed"); m_node.reset(); } @@ -54,8 +54,8 @@ void InternalNodeProperty::add(const InternalNode::Pointer &node) Q_ASSERT(node); Q_ASSERT(node->parentProperty()); - auto flowToken = traceToken.tickWithFlow("add node"_t); - node->traceToken.tick(flowToken, "node added"_t); + auto flowToken = traceToken.tickWithFlow("add node"); + node->traceToken.tick(flowToken, "node added"); m_node = node; } diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalproperty.cpp index 7f8417a0ad6..f805e0c019b 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalproperty.cpp @@ -69,14 +69,14 @@ TypeName InternalProperty::dynamicTypeName() const void InternalProperty::setDynamicTypeName(const TypeName &name) { - traceToken.tick("dynamic type name"_t, keyValue("name", name)); + traceToken.tick("dynamic type name", keyValue("name", name)); m_dynamicType = name; } void InternalProperty::resetDynamicTypeName() { - traceToken.tick("reset dynamic type name"_t); + traceToken.tick("reset dynamic type name"); m_dynamicType.clear(); } diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalsignalhandlerproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalsignalhandlerproperty.cpp index 61197feedb2..0a9753246bb 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalsignalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalsignalhandlerproperty.cpp @@ -23,7 +23,7 @@ QString InternalSignalHandlerProperty::source() const } void InternalSignalHandlerProperty::setSource(const QString &source) { - traceToken.tick("source"_t, keyValue("source", source)); + traceToken.tick("source", keyValue("source", source)); m_source = source; } @@ -40,7 +40,7 @@ QString InternalSignalDeclarationProperty::signature() const void InternalSignalDeclarationProperty::setSignature(const QString &signature) { - traceToken.tick("signature"_t, keyValue("signature", signature)); + traceToken.tick("signature", keyValue("signature", signature)); m_signature = signature; } diff --git a/src/plugins/qmldesigner/libs/designercore/model/internalvariantproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/internalvariantproperty.cpp index c031f73ba8c..ec7987c38ea 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/internalvariantproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/internalvariantproperty.cpp @@ -18,7 +18,7 @@ QVariant InternalVariantProperty::value() const void InternalVariantProperty::setValue(const QVariant &value) { - traceToken.tick("value"_t, keyValue("value", value)); + traceToken.tick("value", keyValue("value", value)); m_value = value; } diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index 8bd53b8b201..7dcea5e098a 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -152,7 +152,7 @@ ModelPrivate::~ModelPrivate() void ModelPrivate::detachAllViews() { - auto tracer = traceToken.begin("detach all views"_t); + auto tracer = traceToken.begin("detach all views"); for (const QPointer &view : std::as_const(m_viewList)) detachView(view.data(), true); @@ -197,7 +197,7 @@ Storage::Imports createStorageImports(const Imports &imports, void ModelPrivate::changeImports(Imports toBeAddedImports, Imports toBeRemovedImports) { - auto tracer = traceToken.begin("change imports"_t); + auto tracer = traceToken.begin("change imports"); std::sort(toBeAddedImports.begin(), toBeAddedImports.end()); std::sort(toBeRemovedImports.begin(), toBeRemovedImports.end()); @@ -274,7 +274,7 @@ void ModelPrivate::setDocumentMessages(const QList &errors, void ModelPrivate::setFileUrl(const QUrl &fileUrl) { - auto tracer = traceToken.begin("file url"_t); + auto tracer = traceToken.begin("file url"); QUrl oldPath = m_fileUrl; @@ -327,7 +327,7 @@ InternalNodePointer ModelPrivate::createNode(TypeNameView typeName, majorVersion, minorVersion, internalId, - traceToken.tickWithFlow("create node"_t)); + traceToken.tickWithFlow("create node")); setTypeId(newNode.get(), typeName); @@ -538,7 +538,7 @@ void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString & const QString oldId = node->id; node->id = id; - node->traceToken.tick("id"_t, std::forward_as_tuple("id", id)); + node->traceToken.tick("id", std::forward_as_tuple("id", id)); if (!oldId.isEmpty()) m_idNodeHash.remove(oldId); if (!id.isEmpty()) @@ -1175,7 +1175,7 @@ void ModelPrivate::setSelectedNodes(const FewNodes &selectedNodeList) if (sortedSelectedList == m_selectedInternalNodes) return; - auto flowToken = traceToken.tickWithFlow("selected model nodes"_t); + auto flowToken = traceToken.tickWithFlow("selected model nodes"); if constexpr (decltype(traceToken)::categoryIsActive()) { // the compiler should optimize it away but to be sure std::set_difference(sortedSelectedList.begin(), @@ -1183,7 +1183,7 @@ void ModelPrivate::setSelectedNodes(const FewNodes &selectedNodeList) m_selectedInternalNodes.begin(), m_selectedInternalNodes.end(), Utils::make_iterator([&](const auto &node) { - node->traceToken.tick(flowToken, "select model node"_t); + node->traceToken.tick(flowToken, "select model node"); })); } @@ -1196,7 +1196,7 @@ void ModelPrivate::setSelectedNodes(const FewNodes &selectedNodeList) m_selectedInternalNodes.begin(), m_selectedInternalNodes.end(), Utils::make_iterator([&](const auto &node) { - node->traceToken.tick(flowToken, "deselect model node"_t); + node->traceToken.tick(flowToken, "deselect model node"); })); } @@ -1205,7 +1205,7 @@ void ModelPrivate::setSelectedNodes(const FewNodes &selectedNodeList) void ModelPrivate::clearSelectedNodes() { - auto tracer = traceToken.begin("clear selected model nodes"_t); + auto tracer = traceToken.begin("clear selected model nodes"); auto lastSelectedNodeList = m_selectedInternalNodes; m_selectedInternalNodes.clear(); @@ -1573,7 +1573,7 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in { Q_ASSERT(rootNode()); - m_rootInternalNode->traceToken.tick("type name"_t, keyValue("type name", type)); + m_rootInternalNode->traceToken.tick("type name", keyValue("type name", type)); m_rootInternalNode->typeName = type; m_rootInternalNode->majorVersion = majorVersion; @@ -1584,7 +1584,7 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QStringList &scriptFunctionList) { - m_rootInternalNode->traceToken.tick("script function"_t); + m_rootInternalNode->traceToken.tick("script function"); node->scriptFunctions = scriptFunctionList; @@ -1593,7 +1593,7 @@ void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QSt void ModelPrivate::setNodeSource(const InternalNodePointer &node, const QString &nodeSource) { - m_rootInternalNode->traceToken.tick("node source"_t); + m_rootInternalNode->traceToken.tick("node source"); node->nodeSource = nodeSource; notifyNodeSourceChanged(node, nodeSource); @@ -1858,7 +1858,7 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) #ifndef QDS_USE_PROJECTSTORAGE void Model::setPossibleImports(Imports possibleImports) { - auto tracer = d->traceToken.begin("possible imports"_t); + auto tracer = d->traceToken.begin("possible imports"); std::sort(possibleImports.begin(), possibleImports.end()); @@ -1872,7 +1872,7 @@ void Model::setPossibleImports(Imports possibleImports) #ifndef QDS_USE_PROJECTSTORAGE void Model::setUsedImports(Imports usedImports) { - auto tracer = d->traceToken.begin("used imports"_t); + auto tracer = d->traceToken.begin("used imports"); std::sort(usedImports.begin(), usedImports.end()); @@ -2847,7 +2847,7 @@ The view is informed that it has been registered within the model by a call to A */ void Model::attachView(AbstractView *view) { - auto traceToken = d->traceToken.begin("attachView"_t, + auto traceToken = d->traceToken.begin("attachView", keyValue("name", std::string_view{view->metaObject()->className()})); @@ -2877,7 +2877,7 @@ void Model::attachView(AbstractView *view) */ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify) { - auto traceToken = d->traceToken.begin("detachView"_t, + auto traceToken = d->traceToken.begin("detachView", keyValue("name", std::string_view{view->metaObject()->className()})); diff --git a/src/plugins/qmldesigner/libs/designercore/model/model_p.h b/src/plugins/qmldesigner/libs/designercore/model/model_p.h index feb9777e150..38384a416c5 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/libs/designercore/model/model_p.h @@ -345,7 +345,7 @@ private: public: NotNullPointer projectStorage = nullptr; NotNullPointer pathCache = nullptr; - ModelTracing::AsynchronousToken traceToken = ModelTracing::category().beginAsynchronous("Model"_t); + ModelTracing::AsynchronousToken traceToken = ModelTracing::category().beginAsynchronous("Model"); private: Model *m_model = nullptr; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 37dc5189379..98c5e6dc8e8 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -1276,7 +1276,7 @@ ProjectStorage::ProjectStorage(Database &database, , moduleCache{ModuleStorageAdapter{*this}} , s{std::make_unique(database)} { - NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"initialize", projectStorageCategory()}; exclusiveTransaction.commit(); @@ -1289,7 +1289,7 @@ ProjectStorage::~ProjectStorage() = default; void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackage package) { - NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize", projectStorageCategory()}; TypeIds deletedTypeIds; ExportedTypesChanged exportedTypesChanged = ExportedTypesChanged::No; @@ -1361,7 +1361,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize document imports"_t, + NanotraceHR::Tracer tracer{"synchronize document imports", projectStorageCategory(), keyValue("imports", imports), keyValue("source id", sourceId)}; @@ -1390,20 +1390,20 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source void ProjectStorage::addObserver(ProjectStorageObserver *observer) { - NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"add observer", projectStorageCategory()}; observers.push_back(observer); } void ProjectStorage::removeObserver(ProjectStorageObserver *observer) { - NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"remove observer", projectStorageCategory()}; observers.removeOne(observer); } ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get module id"_t, + NanotraceHR::Tracer tracer{"get module id", projectStorageCategory(), keyValue("module name", moduleName)}; @@ -1417,7 +1417,7 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo Storage::Module ProjectStorage::module(ModuleId moduleId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get module name"_t, + NanotraceHR::Tracer tracer{"get module name", projectStorageCategory(), keyValue("module id", moduleId)}; @@ -1437,7 +1437,7 @@ TypeId ProjectStorage::typeId(ModuleId moduleId, Storage::Version version) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type id by exported name"_t, + NanotraceHR::Tracer tracer{"get type id by exported name", projectStorageCategory(), keyValue("module id", moduleId), keyValue("exported type name", exportedTypeName), @@ -1466,7 +1466,7 @@ TypeId ProjectStorage::typeId(ModuleId moduleId, TypeId ProjectStorage::typeId(ImportedTypeNameId typeNameId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, + NanotraceHR::Tracer tracer{"get type id by imported type name", projectStorageCategory(), keyValue("imported type name id", typeNameId)}; @@ -1480,7 +1480,7 @@ TypeId ProjectStorage::typeId(ImportedTypeNameId typeNameId) const QVarLengthArray ProjectStorage::typeIds(ModuleId moduleId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type ids by module id"_t, + NanotraceHR::Tracer tracer{"get type ids by module id", projectStorageCategory(), keyValue("module id", moduleId)}; @@ -1510,7 +1510,7 @@ SmallTypeIds<256> ProjectStorage::singletonTypeIds(SourceId sourceId) const Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, + NanotraceHR::Tracer tracer{"get exported type names by type id", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1525,7 +1525,7 @@ Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId, SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get exported type names by source id"_t, + NanotraceHR::Tracer tracer{"get exported type names by source id", projectStorageCategory(), keyValue("type id", typeId), keyValue("source id", sourceId)}; @@ -1542,7 +1542,7 @@ Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId ImportId ProjectStorage::importId(const Storage::Import &import) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get import id by import"_t, + NanotraceHR::Tracer tracer{"get import id by import", projectStorageCategory(), keyValue("import", import)}; @@ -1559,7 +1559,7 @@ ImportedTypeNameId ProjectStorage::importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, + NanotraceHR::Tracer tracer{"get imported type name id by import id", projectStorageCategory(), keyValue("import id", importId), keyValue("imported type name", typeName)}; @@ -1579,7 +1579,7 @@ ImportedTypeNameId ProjectStorage::importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, + NanotraceHR::Tracer tracer{"get imported type name id by source id", projectStorageCategory(), keyValue("source id", sourceId), keyValue("imported type name", typeName)}; @@ -1598,7 +1598,7 @@ ImportedTypeNameId ProjectStorage::importedTypeNameId(SourceId sourceId, QVarLengthArray ProjectStorage::propertyDeclarationIds(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration ids"_t, + NanotraceHR::Tracer tracer{"get property declaration ids", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1616,7 +1616,7 @@ QVarLengthArray ProjectStorage::propertyDeclarationI QVarLengthArray ProjectStorage::localPropertyDeclarationIds(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, + NanotraceHR::Tracer tracer{"get local property declaration ids", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1633,7 +1633,7 @@ PropertyDeclarationId ProjectStorage::propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration id"_t, + NanotraceHR::Tracer tracer{"get property declaration id", projectStorageCategory(), keyValue("type id", typeId), keyValue("property name", propertyName)}; @@ -1651,7 +1651,7 @@ PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get local property declaration id"_t, + NanotraceHR::Tracer tracer{"get local property declaration id", projectStorageCategory(), keyValue("type id", typeId), keyValue("property name", propertyName)}; @@ -1668,7 +1668,7 @@ PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId, PropertyDeclarationId ProjectStorage::defaultPropertyDeclarationId(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get default property declaration id"_t, + NanotraceHR::Tracer tracer{"get default property declaration id", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1685,7 +1685,7 @@ std::optional ProjectStorage::propertyDeclar PropertyDeclarationId propertyDeclarationId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration"_t, + NanotraceHR::Tracer tracer{"get property declaration", projectStorageCategory(), keyValue("property declaration id", propertyDeclarationId)}; @@ -1701,7 +1701,7 @@ std::optional ProjectStorage::propertyDeclar std::optional ProjectStorage::type(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + NanotraceHR::Tracer tracer{"get type", projectStorageCategory(), keyValue("type id", typeId)}; auto type = s->selectInfoTypeByTypeIdStatement.optionalValueWithTransaction( typeId); @@ -1714,7 +1714,7 @@ std::optional ProjectStorage::type(TypeId typeId) const Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type icon path"_t, + NanotraceHR::Tracer tracer{"get type icon path", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1728,7 +1728,7 @@ Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type hints"_t, + NanotraceHR::Tracer tracer{"get type hints", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1743,7 +1743,7 @@ Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, + NanotraceHR::Tracer tracer{"get type annotaion source ids", projectStorageCategory(), keyValue("source id", directoryId)}; @@ -1758,7 +1758,7 @@ SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"get type annotaion source ids", projectStorageCategory()}; auto sourceIds = s->selectTypeAnnotationDirectorySourceIdsStatement .valuesWithTransaction>(); @@ -1771,7 +1771,7 @@ SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, + NanotraceHR::Tracer tracer{"get item library entries by type id", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1806,7 +1806,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId importId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by import id"_t, + NanotraceHR::Tracer tracer{"get item library entries by import id", projectStorageCategory(), keyValue("import id", importId)}; @@ -1841,7 +1841,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by source id"_t, + NanotraceHR::Tracer tracer{"get item library entries by source id", projectStorageCategory(), keyValue("source id", sourceId)}; @@ -1876,7 +1876,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"get all item library entries", projectStorageCategory()}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -1909,7 +1909,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const std::vector ProjectStorage::signalDeclarationNames(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get signal names"_t, + NanotraceHR::Tracer tracer{"get signal names", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1924,7 +1924,7 @@ std::vector ProjectStorage::signalDeclarationNames(TypeId ty std::vector ProjectStorage::functionDeclarationNames(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get function names"_t, + NanotraceHR::Tracer tracer{"get function names", projectStorageCategory(), keyValue("type id", typeId)}; @@ -1940,7 +1940,7 @@ std::optional ProjectStorage::propertyName( PropertyDeclarationId propertyDeclarationId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property name"_t, + NanotraceHR::Tracer tracer{"get property name", projectStorageCategory(), keyValue("property declaration id", propertyDeclarationId)}; @@ -1955,7 +1955,7 @@ std::optional ProjectStorage::propertyName( SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)}; + NanotraceHR::Tracer tracer{"get prototypes", projectStorageCategory(), keyValue("type id", type)}; auto prototypeIds = s->selectPrototypeAndExtensionIdsStatement .valuesWithTransaction>(type); @@ -1968,7 +1968,7 @@ SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"get prototypes and self", projectStorageCategory()}; SmallTypeIds<16> prototypeAndSelfIds; prototypeAndSelfIds.push_back(typeId); @@ -1983,7 +1983,7 @@ SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const SmallTypeIds<64> ProjectStorage::heirIds(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"get heirs", projectStorageCategory()}; auto heirIds = s->selectHeirTypeIdsStatement.valuesWithTransaction>(typeId); @@ -2037,7 +2037,7 @@ bool ProjectStorage::isBasedOn( TypeId ProjectStorage::fetchTypeIdByExportedName(Utils::SmallStringView name) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, + NanotraceHR::Tracer tracer{"is based on", projectStorageCategory(), keyValue("exported type name", name)}; @@ -2052,7 +2052,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds Utils::SmallStringView name) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t, + NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name", projectStorageCategory(), keyValue("module ids", NanotraceHR::array(moduleIds)), keyValue("exported type name", name)}; @@ -2067,7 +2067,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by name"_t, + NanotraceHR::Tracer tracer{"fetch type id by name", projectStorageCategory(), keyValue("source id", sourceId), keyValue("internal type name", name)}; @@ -2083,7 +2083,7 @@ TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringVi Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type by type id"_t, + NanotraceHR::Tracer tracer{"fetch type by type id", projectStorageCategory(), keyValue("type id", typeId)}; @@ -2108,7 +2108,7 @@ Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) Storage::Synchronization::Types ProjectStorage::fetchTypes() { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"fetch types", projectStorageCategory()}; auto types = Sqlite::withDeferredTransaction(database, [&] { auto types = s->selectTypesStatement.values(); @@ -2131,7 +2131,7 @@ Storage::Synchronization::Types ProjectStorage::fetchTypes() FileStatuses ProjectStorage::fetchAllFileStatuses() const { - NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"fetch all file statuses", projectStorageCategory()}; return s->selectAllFileStatusesStatement.valuesWithTransaction(); } @@ -2139,7 +2139,7 @@ FileStatuses ProjectStorage::fetchAllFileStatuses() const FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch file status"_t, + NanotraceHR::Tracer tracer{"fetch file status", projectStorageCategory(), keyValue("source id", sourceId)}; @@ -2154,7 +2154,7 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const std::optional ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch directory info"_t, + NanotraceHR::Tracer tracer{"fetch directory info", projectStorageCategory(), keyValue("source id", sourceId)}; @@ -2170,7 +2170,7 @@ std::optional ProjectStorage::fetchDire Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch directory infos by source id"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source id", projectStorageCategory(), keyValue("source id", directorySourceId)}; @@ -2187,7 +2187,7 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( SourceId directorySourceId, Storage::Synchronization::FileType fileType) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type", projectStorageCategory(), keyValue("source id", directorySourceId), keyValue("file type", fileType)}; @@ -2205,7 +2205,7 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( const SourceIds &directorySourceIds) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch directory infos by source ids"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source ids", projectStorageCategory(), keyValue("source ids", directorySourceIds)}; @@ -2221,7 +2221,7 @@ Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( SmallSourceIds<32> ProjectStorage::fetchSubdirectorySourceIds(SourceId directorySourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch subdirectory source ids"_t, + NanotraceHR::Tracer tracer{"fetch subdirectory source ids", projectStorageCategory(), keyValue("source id", directorySourceId)}; @@ -2246,7 +2246,7 @@ void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"property editor path id"_t, + NanotraceHR::Tracer tracer{"property editor path id", projectStorageCategory(), keyValue("type id", typeId)}; @@ -2259,7 +2259,7 @@ SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const Storage::Imports ProjectStorage::fetchDocumentImports() const { - NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"fetch document imports", projectStorageCategory()}; return s->selectAllDocumentImportForSourceIdStatement.valuesWithTransaction(); } @@ -2276,7 +2276,7 @@ ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module id"_t, + NanotraceHR::Tracer tracer{"fetch module id", projectStorageCategory(), keyValue("module name", moduleName), keyValue("module kind", moduleKind)}; @@ -2293,7 +2293,7 @@ ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName, Storage::Module ProjectStorage::fetchModule(ModuleId id) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module name"_t, + NanotraceHR::Tracer tracer{"fetch module name", projectStorageCategory(), keyValue("module id", id)}; @@ -2307,7 +2307,7 @@ Storage::Module ProjectStorage::fetchModule(ModuleId id) ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const { - NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"fetch all modules", projectStorageCategory()}; return s->selectAllModulesStatement.valuesWithTransaction(); } @@ -2316,7 +2316,7 @@ void ProjectStorage::callRefreshMetaInfoCallback(TypeIds &deletedTypeIds, ExportedTypesChanged &exportedTypesChanged) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"call refresh meta info callback"_t, + NanotraceHR::Tracer tracer{"call refresh meta info callback", projectStorageCategory(), keyValue("type ids", deletedTypeIds)}; @@ -2352,7 +2352,7 @@ SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSou TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type ids"_t, + NanotraceHR::Tracer tracer{"fetch type ids", projectStorageCategory(), keyValue("source ids", sourceIds)}; @@ -2369,7 +2369,7 @@ void ProjectStorage::unique(SourceIds &sourceIds) void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize type traits"_t, + NanotraceHR::Tracer tracer{"synchronize type traits", projectStorageCategory(), keyValue("type id", typeId), keyValue("type traits", traits)}; @@ -2379,7 +2379,7 @@ void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits tr void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) { - NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"update type id in type annotations", projectStorageCategory()}; for (auto &annotation : typeAnnotations) { annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, @@ -2392,7 +2392,7 @@ void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::Typ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, const SourceIds &updatedTypeAnnotationSourceIds) { - NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize type annotations", projectStorageCategory()}; updateTypeIdInTypeAnnotations(typeAnnotations); @@ -2410,7 +2410,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert type annotations"_t, + NanotraceHR::Tracer tracer{"insert type annotations", projectStorageCategory(), keyValue("type annotation", annotation)}; @@ -2433,7 +2433,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson || annotationFromDatabase.hintsJson != annotation.hintsJson) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update type annotations"_t, + NanotraceHR::Tracer tracer{"update type annotations", projectStorageCategory(), keyValue("type annotation from database", annotationFromDatabase), @@ -2457,7 +2457,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove type annotations"_t, + NanotraceHR::Tracer tracer{"remove type annotations", projectStorageCategory(), keyValue("type annotation", annotationFromDatabase)}; @@ -2490,7 +2490,7 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, ExportedTypesChanged &exportedTypesChanged, const SourceIds &updatedSourceIds) { - NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize types", projectStorageCategory()}; Storage::Synchronization::ExportedTypes exportedTypes; exportedTypes.reserve(types.size() * 3); @@ -2542,7 +2542,7 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, const SourceIds &updatedDirectoryInfoSourceIds) { - NanotraceHR::Tracer tracer{"synchronize directory infos"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize directory infos", projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { return std::tie(first.directorySourceId, first.sourceId) @@ -2559,7 +2559,7 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert directory info"_t, + NanotraceHR::Tracer tracer{"insert directory info", projectStorageCategory(), keyValue("directory info", directoryInfo)}; @@ -2579,7 +2579,7 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo if (directoryInfoFromDatabase.fileType != directoryInfo.fileType || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update directory info"_t, + NanotraceHR::Tracer tracer{"update directory info", projectStorageCategory(), keyValue("directory info", directoryInfo), keyValue("directory info from database", @@ -2597,7 +2597,7 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove directory info"_t, + NanotraceHR::Tracer tracer{"remove directory info", projectStorageCategory(), keyValue("directory info", directoryInfo)}; @@ -2610,7 +2610,7 @@ void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::Directo void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) { - NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize file statuses", projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { return first.sourceId <=> second.sourceId; }; @@ -2621,7 +2621,7 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, auto insert = [&](const FileStatus &fileStatus) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert file status"_t, + NanotraceHR::Tracer tracer{"insert file status", projectStorageCategory(), keyValue("file status", fileStatus)}; @@ -2636,7 +2636,7 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, if (fileStatusFromDatabase.lastModified != fileStatus.lastModified || fileStatusFromDatabase.size != fileStatus.size) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update file status"_t, + NanotraceHR::Tracer tracer{"update file status", projectStorageCategory(), keyValue("file status", fileStatus), keyValue("file status from database", fileStatusFromDatabase)}; @@ -2652,7 +2652,7 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, auto remove = [&](const FileStatus &fileStatus) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove file status"_t, + NanotraceHR::Tracer tracer{"remove file status", projectStorageCategory(), keyValue("file status", fileStatus)}; @@ -2671,10 +2671,10 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { - NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize imports", projectStorageCategory()}; synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); - NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; + NanotraceHR::Tracer importTracer{"synchronize qml document imports", projectStorageCategory()}; synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import, @@ -2682,7 +2682,7 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports, relinkablePrototypes, relinkableExtensions); importTracer.end(); - NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, + NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies", projectStorageCategory()}; synchronizeDocumentImports(moduleDependencies, updatedModuleDependencySourceIds, @@ -2697,7 +2697,7 @@ void ProjectStorage::synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds) { - NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize module exported imports", projectStorageCategory()}; std::ranges::sort(moduleExportedImports, [](auto &&first, auto &&second) { return std::tie(first.moduleId, first.exportedModuleId) < std::tie(second.moduleId, second.exportedModuleId); @@ -2715,11 +2715,11 @@ void ProjectStorage::synchromizeModuleExportedImports( auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert module exported import"_t, + NanotraceHR::Tracer tracer{"insert module exported import", projectStorageCategory(), keyValue("module exported import", import), keyValue("module id", import.moduleId)}; - tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); + tracer.tick("exported module", keyValue("module id", import.exportedModuleId)); if (import.version.minor) { s->insertModuleExportedImportWithVersionStatement.write(import.moduleId, @@ -2746,11 +2746,11 @@ void ProjectStorage::synchromizeModuleExportedImports( auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove module exported import"_t, + NanotraceHR::Tracer tracer{"remove module exported import", projectStorageCategory(), keyValue("module exported import view", view), keyValue("module id", view.moduleId)}; - tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); + tracer.tick("exported module", keyValue("module id", view.exportedModuleId)); s->deleteModuleExportedImportStatement.write(view.moduleExportedImportId); }; @@ -2762,7 +2762,7 @@ ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::ModuleKind kind) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, + NanotraceHR::Tracer tracer{"fetch module id ungarded", projectStorageCategory(), keyValue("module name", name), keyValue("module kind", kind)}; @@ -2780,7 +2780,7 @@ ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::Module ProjectStorage::fetchModuleUnguarded(ModuleId id) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module ungarded"_t, + NanotraceHR::Tracer tracer{"fetch module ungarded", projectStorageCategory(), keyValue("module id", id)}; @@ -2799,7 +2799,7 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t, + NanotraceHR::Tracer tracer{"handle alias property declarations with property type", projectStorageCategory(), keyValue("type id", typeId), keyValue("relinkable alias property declarations", @@ -2834,7 +2834,7 @@ void ProjectStorage::handlePropertyDeclarationWithPropertyType( TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle property declarations with property type"_t, + NanotraceHR::Tracer tracer{"handle property declarations with property type", projectStorageCategory(), keyValue("type id", typeId), keyValue("relinkable property declarations", @@ -2850,7 +2850,7 @@ void ProjectStorage::handlePropertyDeclarationsWithExportedTypeNameAndTypeId( PropertyDeclarations &relinkablePropertyDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle property declarations with exported type name and type id"_t, + NanotraceHR::Tracer tracer{"handle property declarations with exported type name and type id", projectStorageCategory(), keyValue("type name", exportedTypeName), keyValue("type id", typeId), @@ -2868,7 +2868,7 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithExportedTypeNameAndTypeI AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle alias property declarations with exported type name and type id"_t, + NanotraceHR::Tracer tracer{"handle alias property declarations with exported type name and type id", projectStorageCategory(), keyValue("type name", exportedTypeName), keyValue("type id", typeId), @@ -2903,7 +2903,7 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithExportedTypeNameAndTypeI void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle prototypes"_t, + NanotraceHR::Tracer tracer{"handle prototypes", projectStorageCategory(), keyValue("type id", prototypeId), keyValue("relinkable prototypes", relinkablePrototypes)}; @@ -2920,7 +2920,7 @@ void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId( Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle invalid prototypes"_t, + NanotraceHR::Tracer tracer{"handle invalid prototypes", projectStorageCategory(), keyValue("type id", exportedTypeName), keyValue("relinkable prototypes", relinkablePrototypes)}; @@ -2937,7 +2937,7 @@ void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId( void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle extension"_t, + NanotraceHR::Tracer tracer{"handle extension", projectStorageCategory(), keyValue("type id", extensionId), keyValue("relinkable extensions", relinkableExtensions)}; @@ -2954,7 +2954,7 @@ void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId( Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle invalid extensions"_t, + NanotraceHR::Tracer tracer{"handle invalid extensions", projectStorageCategory(), keyValue("type id", exportedTypeName), keyValue("relinkable extensions", relinkableExtensions)}; @@ -2973,7 +2973,7 @@ void ProjectStorage::deleteType(TypeId typeId, Prototypes &relinkableExtensions) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"delete type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + NanotraceHR::Tracer tracer{"delete type", projectStorageCategory(), keyValue("type id", typeId)}; handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); @@ -2991,7 +2991,7 @@ void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations & const TypeIds &deletedTypeIds) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink alias properties"_t, + NanotraceHR::Tracer tracer{"relink alias properties", projectStorageCategory(), keyValue("alias property declarations", aliasPropertyDeclarations), keyValue("deleted type ids", deletedTypeIds)}; @@ -3034,7 +3034,7 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable const TypeIds &deletedTypeIds) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink property declarations"_t, + NanotraceHR::Tracer tracer{"relink property declarations", projectStorageCategory(), keyValue("relinkable property declarations", relinkablePropertyDeclaration), @@ -3071,7 +3071,7 @@ void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes, Callable updateStatement) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink prototypes"_t, + NanotraceHR::Tracer tracer{"relink prototypes", projectStorageCategory(), keyValue("relinkable prototypes", relinkablePrototypes), keyValue("deleted type ids", deletedTypeIds)}; @@ -3107,7 +3107,7 @@ void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, TypeIds &deletedTypeIds) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"delete not updated types"_t, + NanotraceHR::Tracer tracer{"delete not updated types", projectStorageCategory(), keyValue("updated type ids", updatedTypeIds), keyValue("updated source ids", updatedSourceIds), @@ -3135,7 +3135,7 @@ void ProjectStorage::relink(AliasPropertyDeclarations &relinkableAliasPropertyDe Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"relink", projectStorageCategory()}; std::ranges::sort(deletedTypeIds); @@ -3154,7 +3154,7 @@ PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId, Utils::SmallStringView aliasPropertyNameTail) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch alias id"_t, + NanotraceHR::Tracer tracer{"fetch alias id", projectStorageCategory(), keyValue("alias type id", aliasTypeId), keyValue("alias property name", aliasPropertyName), @@ -3176,7 +3176,7 @@ void ProjectStorage::linkAliasPropertyDeclarationAliasIds( const AliasPropertyDeclarations &aliasDeclarations, RaiseError raiseError) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, + NanotraceHR::Tracer tracer{"link alias property declarations alias ids", projectStorageCategory(), keyValue("alias property declarations", aliasDeclarations)}; @@ -3217,7 +3217,7 @@ void ProjectStorage::linkAliasPropertyDeclarationAliasIds( void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update alias property declarations"_t, + NanotraceHR::Tracer tracer{"update alias property declarations", projectStorageCategory(), keyValue("alias property declarations", aliasDeclarations)}; @@ -3232,7 +3232,7 @@ void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDec void ProjectStorage::checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t, + NanotraceHR::Tracer tracer{"check alias property declarations cycles", projectStorageCategory(), keyValue("alias property declarations", aliasDeclarations)}; for (const auto &aliasDeclaration : aliasDeclarations) @@ -3243,7 +3243,7 @@ void ProjectStorage::linkAliases(const AliasPropertyDeclarations &aliasPropertyD RaiseError raiseError) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"link aliases", projectStorageCategory()}; linkAliasPropertyDeclarationAliasIds(aliasPropertyDeclarationsToLink, raiseError); @@ -3255,7 +3255,7 @@ void ProjectStorage::linkAliases(const AliasPropertyDeclarations &aliasPropertyD void ProjectStorage::repairBrokenAliasPropertyDeclarations() { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"repair broken alias property declarations"_t, + NanotraceHR::Tracer tracer{"repair broken alias property declarations", projectStorageCategory()}; auto brokenAliasPropertyDeclarations = s->selectBrokenAliasPropertyDeclarationsStatement @@ -3273,7 +3273,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, ExportedTypesChanged &exportedTypesChanged) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize exported types", projectStorageCategory()}; std::ranges::sort(exportedTypes, [](auto &&first, auto &&second) { if (first.moduleId < second.moduleId) @@ -3302,7 +3302,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, auto insert = [&](const Storage::Synchronization::ExportedType &type) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert exported type"_t, + NanotraceHR::Tracer tracer{"insert exported type", projectStorageCategory(), keyValue("exported type", type), keyValue("type id", type.typeId), @@ -3347,7 +3347,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, auto update = [&](const Storage::Synchronization::ExportedTypeView &view, const Storage::Synchronization::ExportedType &type) { if (view.typeId != type.typeId) { - NanotraceHR::Tracer tracer{"update exported type"_t, + NanotraceHR::Tracer tracer{"update exported type", projectStorageCategory(), keyValue("exported type", type), keyValue("exported type view", view), @@ -3368,7 +3368,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, }; auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { - NanotraceHR::Tracer tracer{"remove exported type"_t, + NanotraceHR::Tracer tracer{"remove exported type", projectStorageCategory(), keyValue("exported type", view), keyValue("type id", view.typeId), @@ -3395,7 +3395,7 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property declaration to alias"_t, + NanotraceHR::Tracer tracer{"insert property declaration to alias", projectStorageCategory(), keyValue("property declaration", value)}; @@ -3493,7 +3493,7 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property declaration"_t, + NanotraceHR::Tracer tracer{"insert property declaration", projectStorageCategory(), keyValue("property declaration", value)}; @@ -3526,7 +3526,7 @@ void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias( SourceId sourceId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property declaration to alias"_t, + NanotraceHR::Tracer tracer{"update property declaration to alias", projectStorageCategory(), keyValue("property declaration", value), keyValue("property declaration view", view)}; @@ -3547,7 +3547,7 @@ Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProper PropertyDeclarationIds &propertyDeclarationIds) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property declaration"_t, + NanotraceHR::Tracer tracer{"update property declaration", projectStorageCategory(), keyValue("property declaration", value), keyValue("property declaration view", view)}; @@ -3589,7 +3589,7 @@ void ProjectStorage::synchronizePropertyDeclarations( AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarationIds &propertyDeclarationIds) { - NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize property declaration", projectStorageCategory()}; std::ranges::sort(propertyDeclarations, [](auto &&first, auto &&second) { return Sqlite::compare(first.name, second.name) < 0; @@ -3634,7 +3634,7 @@ void ProjectStorage::synchronizePropertyDeclarations( auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove property declaration"_t, + NanotraceHR::Tracer tracer{"remove property declaration", projectStorageCategory(), keyValue("property declaratio viewn", view)}; @@ -3656,7 +3656,7 @@ void ProjectStorage::synchronizePropertyDeclarations( void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( Storage::Synchronization::Type &type, PropertyDeclarationIds &propertyDeclarationIds) { - NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null", projectStorageCategory()}; if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) @@ -3683,7 +3683,7 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( auto remove = [&](const AliasPropertyDeclarationView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null", projectStorageCategory(), keyValue("alias property declaration view", view)}; @@ -3698,7 +3698,7 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( Storage::Synchronization::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { - NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"reset removed alias properties to null", projectStorageCategory()}; PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size()); @@ -3716,7 +3716,7 @@ void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceI Prototypes &relinkablePrototypes) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id", projectStorageCategory(), keyValue("source id", sourceId), keyValue("type id", prototypeId)}; @@ -3738,7 +3738,7 @@ void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId Prototypes &relinkableExtensions) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle prototypes with source id"_t, + NanotraceHR::Tracer tracer{"handle prototypes with source id", projectStorageCategory(), keyValue("source id", sourceId), keyValue("prototype id", prototypeId), @@ -3763,7 +3763,7 @@ void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceI Prototypes &relinkableExtensions) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id", projectStorageCategory(), keyValue("source id", sourceId), keyValue("type id", extensionId)}; @@ -3846,7 +3846,7 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, auto insert = [&](const Storage::Import &import) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert import"_t, + NanotraceHR::Tracer tracer{"insert import", projectStorageCategory(), keyValue("import", import), keyValue("import kind", importKind), @@ -3869,7 +3869,7 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, ? Storage::Synchronization::ImportKind::ModuleExportedImport : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; - NanotraceHR::Tracer tracer{"insert indirect import"_t, + NanotraceHR::Tracer tracer{"insert indirect import", projectStorageCategory(), keyValue("import", import), keyValue("import kind", exportedImportKind), @@ -3900,7 +3900,7 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, auto remove = [&](const Storage::Synchronization::ImportView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove import"_t, + NanotraceHR::Tracer tracer{"remove import", projectStorageCategory(), keyValue("import", view), keyValue("import id", view.importId), @@ -3923,7 +3923,7 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) { - NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"create json from parameter declarations", projectStorageCategory()}; Utils::PathString json; json.append("["); @@ -3955,7 +3955,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t, + NanotraceHR::Tracer tracer{"fetch type id by module id and exported name", projectStorageCategory(), keyValue("module id", moduleId), keyValue("exported name", name)}; @@ -3966,7 +3966,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, void ProjectStorage::addTypeIdToPropertyEditorQmlPaths( Storage::Synchronization::PropertyEditorQmlPaths &paths) { - NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"add type id to property editor qml paths", projectStorageCategory()}; for (auto &path : paths) path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); @@ -3987,7 +3987,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr auto insert = [&](const PropertyEditorQmlPath &path) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property editor paths"_t, + NanotraceHR::Tracer tracer{"insert property editor paths", projectStorageCategory(), keyValue("property editor qml path", path)}; @@ -3997,7 +3997,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property editor paths"_t, + NanotraceHR::Tracer tracer{"update property editor paths", projectStorageCategory(), keyValue("property editor qml path", value), keyValue("property editor qml path view", view)}; @@ -4014,7 +4014,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr auto remove = [&](const PropertyEditorQmlPathView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove property editor paths"_t, + NanotraceHR::Tracer tracer{"remove property editor paths", projectStorageCategory(), keyValue("property editor qml path view", view)}; @@ -4028,7 +4028,7 @@ void ProjectStorage::synchronizePropertyEditorQmlPaths( Storage::Synchronization::PropertyEditorQmlPaths &paths, SourceIds updatedPropertyEditorQmlPathsSourceIds) { - NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize property editor qml paths", projectStorageCategory()}; addTypeIdToPropertyEditorQmlPaths(paths); synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); @@ -4037,7 +4037,7 @@ void ProjectStorage::synchronizePropertyEditorQmlPaths( void ProjectStorage::synchronizeFunctionDeclarations( TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) { - NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize function declaration", projectStorageCategory()}; std::ranges::sort(functionsDeclarations, [](auto &&first, auto &&second) { auto compare = Sqlite::compare(first.name, second.name); @@ -4068,7 +4068,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert function declaration"_t, + NanotraceHR::Tracer tracer{"insert function declaration", projectStorageCategory(), keyValue("function declaration", value)}; @@ -4080,7 +4080,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, const Storage::Synchronization::FunctionDeclaration &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update function declaration"_t, + NanotraceHR::Tracer tracer{"update function declaration", projectStorageCategory(), keyValue("function declaration", value), keyValue("function declaration view", view)}; @@ -4099,7 +4099,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove function declaration"_t, + NanotraceHR::Tracer tracer{"remove function declaration", projectStorageCategory(), keyValue("function declaration view", view)}; @@ -4112,7 +4112,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( void ProjectStorage::synchronizeSignalDeclarations( TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations) { - NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize signal declaration", projectStorageCategory()}; std::ranges::sort(signalDeclarations, [](auto &&first, auto &&second) { auto compare = Sqlite::compare(first.name, second.name); @@ -4143,7 +4143,7 @@ void ProjectStorage::synchronizeSignalDeclarations( auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert signal declaration"_t, + NanotraceHR::Tracer tracer{"insert signal declaration", projectStorageCategory(), keyValue("signal declaration", value)}; @@ -4159,7 +4159,7 @@ void ProjectStorage::synchronizeSignalDeclarations( auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove signal declaration"_t, + NanotraceHR::Tracer tracer{"remove signal declaration", projectStorageCategory(), keyValue("signal declaration view", view)}; @@ -4173,7 +4173,7 @@ Utils::PathString ProjectStorage::createJson( const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"create json from enumerator declarations", projectStorageCategory()}; Utils::PathString json; json.append("{"); @@ -4201,7 +4201,7 @@ Utils::PathString ProjectStorage::createJson( void ProjectStorage::synchronizeEnumerationDeclarations( TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) { - NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize enumeration declaration", projectStorageCategory()}; std::ranges::sort(enumerationDeclarations, {}, &EnumerationDeclaration::name); @@ -4215,7 +4215,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert enumeration declaration"_t, + NanotraceHR::Tracer tracer{"insert enumeration declaration", projectStorageCategory(), keyValue("enumeration declaration", value)}; @@ -4227,7 +4227,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, const Storage::Synchronization::EnumerationDeclaration &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update enumeration declaration"_t, + NanotraceHR::Tracer tracer{"update enumeration declaration", projectStorageCategory(), keyValue("enumeration declaration", value), keyValue("enumeration declaration view", view)}; @@ -4246,7 +4246,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove enumeration declaration"_t, + NanotraceHR::Tracer tracer{"remove enumeration declaration", projectStorageCategory(), keyValue("enumeration declaration view", view)}; @@ -4267,7 +4267,7 @@ void ProjectStorage::extractExportedTypes(TypeId typeId, TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"declare type"_t, + NanotraceHR::Tracer tracer{"declare type", projectStorageCategory(), keyValue("source id", type.sourceId), keyValue("type name", type.typeName)}; @@ -4295,7 +4295,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarationIds &propertyDeclarationIds) { - NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize declaration per type", projectStorageCategory()}; if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) return; @@ -4314,7 +4314,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, AliasPropertyDeclarations &aliasPropertyDeclarationsToLink, PropertyDeclarations &relinkablePropertyDeclarations) { - NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize declaration", projectStorageCategory()}; PropertyDeclarationIds propertyDeclarationIds; propertyDeclarationIds.reserve(types.size() * 10); @@ -4329,7 +4329,7 @@ void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &types) { - NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize default properties", projectStorageCategory()}; auto range = s->selectTypesWithDefaultPropertyStatement.range(); @@ -4345,7 +4345,7 @@ void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &type auto update = [&](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize default properties by update"_t, + NanotraceHR::Tracer tracer{"synchronize default properties by update", projectStorageCategory(), keyValue("type id", value.typeId), keyValue("value", value), @@ -4383,7 +4383,7 @@ void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &type void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) { - NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"reset changed default properties", projectStorageCategory()}; auto range = s->selectTypesWithDefaultPropertyStatement.range(); @@ -4399,7 +4399,7 @@ void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::T auto update = [&](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, + NanotraceHR::Tracer tracer{"reset changed default properties by update", projectStorageCategory(), keyValue("type id", value.typeId), keyValue("value", value), @@ -4429,7 +4429,7 @@ void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::T void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t, + NanotraceHR::Tracer tracer{"check for prototype chain cycle", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4444,7 +4444,7 @@ void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check for alias chain cycle"_t, + NanotraceHR::Tracer tracer{"check for alias chain cycle", projectStorageCategory(), keyValue("property declaration id", propertyDeclarationId)}; auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { @@ -4460,7 +4460,7 @@ std::pair ProjectStorage::fetchImportedTypeNameIdAnd const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, + NanotraceHR::Tracer tracer{"fetch imported type name id and type id", projectStorageCategory(), keyValue("imported type name", importedTypeName), keyValue("source id", sourceId)}; @@ -4491,7 +4491,7 @@ void ProjectStorage::syncPrototypeAndExtension(Storage::Synchronization::Type &t return; using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, + NanotraceHR::Tracer tracer{"synchronize prototype and extension", projectStorageCategory(), keyValue("prototype", type.prototype), keyValue("extension", type.extension), @@ -4524,7 +4524,7 @@ void ProjectStorage::syncPrototypesAndExtensions(Storage::Synchronization::Types Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { - NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize prototypes and extensions", projectStorageCategory()}; TypeIds typeIds; typeIds.reserve(types.size()); @@ -4539,7 +4539,7 @@ void ProjectStorage::syncPrototypesAndExtensions(Storage::Synchronization::Types ImportId ProjectStorage::fetchImportId(SourceId sourceId, const Storage::Import &import) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + NanotraceHR::Tracer tracer{"fetch imported type name id", projectStorageCategory(), keyValue("import", import), keyValue("source id", sourceId)}; @@ -4569,7 +4569,7 @@ ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId( auto operator()(const Storage::Synchronization::ImportedType &importedType) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + NanotraceHR::Tracer tracer{"fetch imported type name id", projectStorageCategory(), keyValue("imported type name", importedType.name), keyValue("source id", sourceId), @@ -4583,7 +4583,7 @@ ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId( auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + NanotraceHR::Tracer tracer{"fetch imported type name id", projectStorageCategory(), keyValue("imported type name", importedType.name), keyValue("import", importedType.import), @@ -4609,7 +4609,7 @@ ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId( TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t, + NanotraceHR::Tracer tracer{"fetch type id with type name kind", projectStorageCategory(), keyValue("type name id", typeNameId)}; @@ -4637,7 +4637,7 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id"_t, + NanotraceHR::Tracer tracer{"fetch type id", projectStorageCategory(), keyValue("type name id", typeNameId), keyValue("type name kind", kind)}; @@ -4660,7 +4660,7 @@ ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, Utils::SmallStringView name) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, + NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded", projectStorageCategory(), keyValue("type id", typeId), keyValue("property name", name)}; @@ -4679,7 +4679,7 @@ PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameU TypeId typeId, Utils::SmallStringView name) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t, + NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded", projectStorageCategory(), keyValue("type id", typeId), keyValue("property name", name)}; @@ -4694,7 +4694,7 @@ PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameU Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch exported type"_t, + NanotraceHR::Tracer tracer{"fetch exported type", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4709,7 +4709,7 @@ Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeI Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDeclarations(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch property declarations"_t, + NanotraceHR::Tracer tracer{"fetch property declarations", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4724,7 +4724,7 @@ Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDecl Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDeclarations(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + NanotraceHR::Tracer tracer{"fetch signal declarations", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4749,7 +4749,7 @@ Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDecl Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarations(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + NanotraceHR::Tracer tracer{"fetch signal declarations", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4772,7 +4772,7 @@ Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarat Storage::Synchronization::EnumerationDeclarations ProjectStorage::fetchEnumerationDeclarations(TypeId typeId) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t, + NanotraceHR::Tracer tracer{"fetch enumeration declarations", projectStorageCategory(), keyValue("type id", typeId)}; @@ -4798,7 +4798,7 @@ template bool ProjectStorage::isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, + NanotraceHR::Tracer tracer{"is based on", projectStorageCategory(), keyValue("type id", typeId), keyValue("base type ids", NanotraceHR::array(baseTypeIds...))}; @@ -4827,7 +4827,7 @@ ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId(Storage::Synchronizat Utils::SmallStringView typeName) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + NanotraceHR::Tracer tracer{"fetch imported type name id", projectStorageCategory(), keyValue("imported type name", typeName), keyValue("kind", kind)}; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h index b3723bfa01d..caeb5377c54 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h @@ -132,7 +132,7 @@ public: TypeId commonTypeId() const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type id from common type cache"_t, + NanotraceHR::Tracer tracer{"get type id from common type cache", projectStorageCategory(), keyValue("module name", std::string_view{moduleName}), keyValue("type name", std::string_view{typeName})}; @@ -148,7 +148,7 @@ public: TypeId builtinTypeId() const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, + NanotraceHR::Tracer tracer{"get builtin type id from common type cache", projectStorageCategory()}; auto typeId = commonTypeCache_.builtinTypeId(); @@ -162,7 +162,7 @@ public: TypeId builtinTypeId() const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, + NanotraceHR::Tracer tracer{"get builtin type id from common type cache", projectStorageCategory()}; auto typeId = commonTypeCache_.builtinTypeId(); @@ -837,7 +837,7 @@ private: template void removeRelinkableEntries(std::vector &relinkables, auto &ids, auto projection) { - NanotraceHR::Tracer tracer{"remove relinkable entries"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"remove relinkable entries", projectStorageCategory()}; std::vector newRelinkables; newRelinkables.reserve(relinkables.size()); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageexceptions.cpp index 948a8d42d6d..619988510ab 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageexceptions.cpp @@ -19,7 +19,7 @@ auto &category() TypeHasInvalidSourceId::TypeHasInvalidSourceId() { - category().threadEvent("TypeHasInvalidSourceId"_t); + category().threadEvent("TypeHasInvalidSourceId"); } const char *TypeHasInvalidSourceId::what() const noexcept @@ -29,7 +29,7 @@ const char *TypeHasInvalidSourceId::what() const noexcept ModuleDoesNotExists::ModuleDoesNotExists() { - category().threadEvent("ModuleDoesNotExists"_t); + category().threadEvent("ModuleDoesNotExists"); } const char *ModuleDoesNotExists::what() const noexcept @@ -39,7 +39,7 @@ const char *ModuleDoesNotExists::what() const noexcept ModuleAlreadyExists::ModuleAlreadyExists() { - category().threadEvent("ModuleAlreadyExists"_t); + category().threadEvent("ModuleAlreadyExists"); } const char *ModuleAlreadyExists::what() const noexcept @@ -53,14 +53,14 @@ TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId Utils::SmallString::join( {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})} { - category().threadEvent("TypeNameDoesNotExists"_t, + category().threadEvent("TypeNameDoesNotExists", keyValue("type name", typeName), keyValue("source id", sourceId)); } PrototypeChainCycle::PrototypeChainCycle() { - category().threadEvent("PrototypeChainCycle"_t); + category().threadEvent("PrototypeChainCycle"); } const char *PrototypeChainCycle::what() const noexcept @@ -70,7 +70,7 @@ const char *PrototypeChainCycle::what() const noexcept AliasChainCycle::AliasChainCycle() { - category().threadEvent("AliasChainCycle"_t); + category().threadEvent("AliasChainCycle"); } const char *AliasChainCycle::what() const noexcept @@ -80,7 +80,7 @@ const char *AliasChainCycle::what() const noexcept CannotParseQmlTypesFile::CannotParseQmlTypesFile() { - category().threadEvent("CannotParseQmlTypesFile"_t); + category().threadEvent("CannotParseQmlTypesFile"); } const char *CannotParseQmlTypesFile::what() const noexcept @@ -90,7 +90,7 @@ const char *CannotParseQmlTypesFile::what() const noexcept CannotParseQmlDocumentFile::CannotParseQmlDocumentFile() { - category().threadEvent("CannotParseQmlDocumentFile"_t); + category().threadEvent("CannotParseQmlDocumentFile"); } const char *CannotParseQmlDocumentFile::what() const noexcept @@ -100,7 +100,7 @@ const char *CannotParseQmlDocumentFile::what() const noexcept DirectoryInfoHasInvalidProjectSourceId::DirectoryInfoHasInvalidProjectSourceId() { - category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"); } const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept @@ -110,7 +110,7 @@ const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept DirectoryInfoHasInvalidSourceId::DirectoryInfoHasInvalidSourceId() { - category().threadEvent("DirectoryInfoHasInvalidSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidSourceId"); } const char *DirectoryInfoHasInvalidSourceId::what() const noexcept @@ -120,7 +120,7 @@ const char *DirectoryInfoHasInvalidSourceId::what() const noexcept DirectoryInfoHasInvalidModuleId::DirectoryInfoHasInvalidModuleId() { - category().threadEvent("DirectoryInfoHasInvalidModuleId"_t); + category().threadEvent("DirectoryInfoHasInvalidModuleId"); } const char *DirectoryInfoHasInvalidModuleId::what() const noexcept @@ -130,7 +130,7 @@ const char *DirectoryInfoHasInvalidModuleId::what() const noexcept FileStatusHasInvalidSourceId::FileStatusHasInvalidSourceId() { - category().threadEvent("FileStatusHasInvalidSourceId"_t); + category().threadEvent("FileStatusHasInvalidSourceId"); } const char *FileStatusHasInvalidSourceId::what() const noexcept @@ -160,12 +160,12 @@ const char *ProjectStorageErrorWithMessage::what() const noexcept ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage) : ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage} { - category().threadEvent("ExportedTypeCannotBeInserted"_t, keyValue("error message", errorMessage)); + category().threadEvent("ExportedTypeCannotBeInserted", keyValue("error message", errorMessage)); } TypeAnnotationHasInvalidSourceId::TypeAnnotationHasInvalidSourceId() { - category().threadEvent("TypeAnnotationHasInvalidSourceId"_t); + category().threadEvent("TypeAnnotationHasInvalidSourceId"); } const char *TypeAnnotationHasInvalidSourceId::what() const noexcept diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 6f4fde65f61..542af30805c 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -183,7 +183,7 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im Storage::ModuleKind moduleKind, std::string_view exportedModuleName) { - NanotraceHR::Tracer tracer{"add module exported imports"_t, + NanotraceHR::Tracer tracer{"add module exported imports", category(), keyValue("module id", moduleId), keyValue("exported module id", exportedModuleId), @@ -208,7 +208,7 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i const QList &qmldirImports, ProjectStorageInterface &projectStorage) { - NanotraceHR::Tracer tracer{"add module exported imports"_t, + NanotraceHR::Tracer tracer{"add module exported imports", category(), keyValue("cpp module id", cppModuleId), keyValue("module id", moduleId)}; @@ -268,7 +268,7 @@ void ProjectStorageUpdater::update(Update update) const QString &propertyEditorResourcesPath = update.propertyEditorResourcesPath; const QStringList &typeAnnotationPaths = update.typeAnnotationPaths; - NanotraceHR::Tracer tracer{"update"_t, + NanotraceHR::Tracer tracer{"update", category(), keyValue("directories", directories), keyValue("qml types paths", qmlTypesPaths)}; @@ -307,14 +307,14 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, if (qmlTypesPaths.empty()) return; - NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; + NanotraceHR::Tracer tracer{"update qmltypes file", category()}; ModuleId moduleId = m_projectStorage.moduleId("QML", Storage::ModuleKind::CppLibrary); for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId); - tracer.tick("append watched qml types source id"_t, + tracer.tick("append watched qml types source id", keyValue("source id", sourceId), keyValue("qml types path", qmlTypesPath)); @@ -327,9 +327,9 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, notUpdatedSourceIds); if (state == FileState::Changed) { - tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + tracer.tick("append project data", keyValue("project data", directoryInfo)); package.directoryInfos.push_back(std::move(directoryInfo)); - tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); + tracer.tick("append updated project source ids", keyValue("source id", sourceId)); package.updatedDirectoryInfoSourceIds.push_back(sourceId); } } @@ -366,7 +366,7 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); if (qmldirState != FileState::NotChanged) { - tracer.tick("append updated source id"_t, keyValue("module id", qmldirSourceId)); + tracer.tick("append updated source id", keyValue("module id", qmldirSourceId)); package.updatedSourceIds.push_back(qmldirSourceId); } @@ -384,14 +384,14 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat moduleName, imports, m_projectStorage); - tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); + tracer.tick("append updated module id", keyValue("module id", moduleId)); package.updatedModuleIds.push_back(moduleId); const auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); - addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id"_t, tracer); + addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id", tracer); addSourceIds(package.updatedFileStatusSourceIds, qmlDirectoryInfos, - "append updated file status source id"_t, + "append updated file status source id", tracer); auto qmlTypes = filterMultipleEntries(parser.typeInfos()); @@ -415,7 +415,7 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat notUpdatedSourceIds, watchedSourceIdsIds, qmldirState); - tracer.tick("append updated project source id"_t, keyValue("module id", moduleId)); + tracer.tick("append updated project source id", keyValue("module id", moduleId)); package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); } @@ -424,7 +424,7 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { - NanotraceHR::Tracer tracer{"update directories"_t, category()}; + NanotraceHR::Tracer tracer{"update directories", category()}; for (const QString &directory : directories) updateDirectory({directory}, {}, package, notUpdatedSourceIds, watchedSourceIdsIds); @@ -532,7 +532,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { - NanotraceHR::Tracer tracer{"update directory"_t, category(), keyValue("directory", directoryPath)}; + NanotraceHR::Tracer tracer{"update directory", category(), keyValue("directory", directoryPath)}; SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); @@ -549,7 +549,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa switch (combineState(directoryState, qmldirState)) { case FileState::Changed: { - tracer.tick("update directory changed"_t); + tracer.tick("update directory changed"); updateDirectoryChanged(directoryPath, qmldirState, qmldirSourcePath, @@ -563,7 +563,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa break; } case FileState::NotChanged: { - tracer.tick("update directory not changed"_t); + tracer.tick("update directory not changed"); parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directorySourceId), package, @@ -572,7 +572,7 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa break; } case FileState::NotExists: { - tracer.tick("update directory don't exits"_t); + tracer.tick("update directory don't exits"); package.updatedFileStatusSourceIds.push_back(directorySourceId); package.updatedFileStatusSourceIds.push_back(qmldirSourceId); @@ -580,9 +580,9 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa package.updatedSourceIds.push_back(qmldirSourceId); auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) { - tracer.tick("append updated source id"_t, keyValue("source id", directoryInfo.sourceId)); + tracer.tick("append updated source id", keyValue("source id", directoryInfo.sourceId)); package.updatedSourceIds.push_back(directoryInfo.sourceId); - tracer.tick("append updated file status source id"_t, + tracer.tick("append updated file status source id", keyValue("source id", directoryInfo.sourceId)); package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId); } @@ -613,7 +613,7 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { - NanotraceHR::Tracer tracer{"update property editor paths"_t, + NanotraceHR::Tracer tracer{"update property editor paths", category(), keyValue("property editor resources path", propertyEditorResourcesPath)}; @@ -661,7 +661,7 @@ void ProjectStorageUpdater::updateTypeAnnotations(const QStringList &directoryPa Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { - NanotraceHR::Tracer tracer("update type annotations"_t, category()); + NanotraceHR::Tracer tracer("update type annotations", category()); std::map> updatedSourceIdsDictonary; @@ -680,7 +680,7 @@ void ProjectStorageUpdater::updateTypeAnnotations( NotUpdatedSourceIds ¬UpdatedSourceIds, std::map> &updatedSourceIdsDictonary) { - NanotraceHR::Tracer tracer("update type annotation directory"_t, + NanotraceHR::Tracer tracer("update type annotation directory", category(), keyValue("path", rootDirectoryPath)); @@ -753,7 +753,7 @@ void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath, SourceId directorySourceId, Storage::Synchronization::SynchronizationPackage &package) { - NanotraceHR::Tracer tracer{"update type annotation path"_t, + NanotraceHR::Tracer tracer{"update type annotation path", category(), keyValue("path", filePath), keyValue("directory path", directoryPath)}; @@ -776,12 +776,12 @@ void ProjectStorageUpdater::updatePropertyEditorPath( SourceId directorySourceId, long long pathOffset) { - NanotraceHR::Tracer tracer{"update property editor path"_t, + NanotraceHR::Tracer tracer{"update property editor path", category(), keyValue("directory path", directoryPath), keyValue("directory source id", directorySourceId)}; - tracer.tick("append updated property editor qml path source id"_t, + tracer.tick("append updated property editor qml path source id", keyValue("source id", directorySourceId)); package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId); auto dir = QDir{directoryPath}; @@ -796,7 +796,7 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( SourceId directorySourceId, long long pathOffset) { - NanotraceHR::Tracer tracer{"update property editor file path"_t, + NanotraceHR::Tracer tracer{"update property editor file path", category(), keyValue("directory path", path), keyValue("directory source id", directorySourceId)}; @@ -819,7 +819,7 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( typeName, pathId, directorySourceId); - tracer.tick("append property editor qml paths"_t, + tracer.tick("append property editor qml paths", keyValue("property editor qml paths", paths)); } } @@ -854,7 +854,7 @@ bool contains(const Container &container, Id id) void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &changedIdPaths) { - NanotraceHR::Tracer tracer{"paths with ids changed"_t, + NanotraceHR::Tracer tracer{"paths with ids changed", category(), keyValue("id paths", changedIdPaths)}; @@ -954,35 +954,35 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { - NanotraceHR::Tracer tracer{"parse type infos"_t, + NanotraceHR::Tracer tracer{"parse type infos", category(), keyValue("directory source id", directorySourceId), keyValue("directory path", directoryPath), keyValue("module id", moduleId)}; for (const QString &typeInfo : typeInfos) { - NanotraceHR::Tracer tracer{"parse type info"_t, category(), keyValue("type info", typeInfo)}; + NanotraceHR::Tracer tracer{"parse type info", category(), keyValue("type info", typeInfo)}; Utils::PathString qmltypesPath = Utils::PathString::join( {directoryPath, "/", Utils::SmallString{typeInfo}}); SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath}); - tracer.tick("append qmltypes source id"_t, keyValue("source id", sourceId)); + tracer.tick("append qmltypes source id", keyValue("source id", sourceId)); watchedSourceIds.qmltypesSourceIds.push_back(sourceId); addDependencies(package.moduleDependencies, sourceId, joinImports(qmldirDependencies, qmldirImports), m_projectStorage, - "append module dependency"_t, + "append module dependency", tracer); - tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); + tracer.tick("append module dependenct source source id", keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); - tracer.tick("append project data"_t, keyValue("source id", sourceId)); + tracer.tick("append project data", keyValue("source id", sourceId)); parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } @@ -994,7 +994,7 @@ void ProjectStorageUpdater::parseDirectoryInfos( NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { - NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; + NanotraceHR::Tracer tracer{"parse project datas", category()}; for (const Storage::Synchronization::DirectoryInfo &directoryInfo : directoryInfos) { switch (directoryInfo.fileType) { @@ -1022,14 +1022,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Direct Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { - NanotraceHR::Tracer tracer{"parse type info"_t, + NanotraceHR::Tracer tracer{"parse type info", category(), keyValue("qmltypes path", qmltypesPath)}; auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { - tracer.tick("append updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + tracer.tick("append updated source ids", keyValue("source id", directoryInfo.sourceId)); package.updatedSourceIds.push_back(directoryInfo.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); @@ -1037,7 +1037,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Direct break; } case FileState::NotChanged: { - tracer.tick("append not updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + tracer.tick("append not updated source ids", keyValue("source id", directoryInfo.sourceId)); notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId); break; } @@ -1059,7 +1059,7 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil WatchedSourceIdsIds &watchedSourceIds, FileState qmldirState) { - NanotraceHR::Tracer tracer{"parse qml component"_t, + NanotraceHR::Tracer tracer{"parse qml component", category(), keyValue("relative file path", relativeFilePath), keyValue("directory path", directoryPath), @@ -1076,18 +1076,18 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::Type type; auto state = fileState(sourceId, package, notUpdatedSourceIds); - tracer.tick("append watched qml source id"_t, keyValue("source id", sourceId)); + tracer.tick("append watched qml source id", keyValue("source id", sourceId)); watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { case FileState::NotChanged: if (qmldirState == FileState::NotExists) { - tracer.tick("append not updated source id"_t, keyValue("source id", sourceId)); + tracer.tick("append not updated source id", keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + tracer.tick("append project data", keyValue("project data", directoryInfo)); return; } @@ -1103,9 +1103,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + tracer.tick("append project data", keyValue("project data", directoryInfo)); - tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); + tracer.tick("append updated source id", keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); type.typeName = SourcePath{qmlFilePath}.name(); @@ -1121,13 +1121,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { - NanotraceHR::Tracer tracer{"parse qml component"_t, category(), keyValue("source id", sourceId)}; + NanotraceHR::Tracer tracer{"parse qml component", category(), keyValue("source id", sourceId)}; auto state = fileState(sourceId, package, notUpdatedSourceIds); if (state == FileState::NotChanged) return; - tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); + tracer.tick("append updated source id", keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); if (state == FileState::NotExists) @@ -1191,7 +1191,7 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState) { - NanotraceHR::Tracer tracer{"parse qml components"_t, + NanotraceHR::Tracer tracer{"parse qml components", category(), keyValue("directory source id", directorySourceId), keyValue("directory id", directoryId), @@ -1224,14 +1224,14 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) const { - NanotraceHR::Tracer tracer{"update property editor paths"_t, + NanotraceHR::Tracer tracer{"update property editor paths", category(), keyValue("source id", sourceId)}; auto currentFileStatus = m_fileStatusCache.find(sourceId); if (!currentFileStatus.isValid()) { - tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); + tracer.tick("append updated file status source id", keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); tracer.end(keyValue("state", FileState::NotExists)); @@ -1241,17 +1241,17 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId); if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) { - tracer.tick("append file status"_t, keyValue("file status", sourceId)); + tracer.tick("append file status", keyValue("file status", sourceId)); package.fileStatuses.push_back(currentFileStatus); - tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); + tracer.tick("append updated file status source id", keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); tracer.end(keyValue("state", FileState::Changed)); return FileState::Changed; } - tracer.tick("append not updated file status source id"_t, keyValue("source id", sourceId)); + tracer.tick("append not updated file status source id", keyValue("source id", sourceId)); notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); tracer.end(keyValue("state", FileState::NotChanged)); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/qmldocumentparser.cpp index 17aa8ca9617..5dbdb659c14 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/qmldocumentparser.cpp @@ -101,7 +101,7 @@ QualifiedImports createQualifiedImports(const QList &qmlImports, Utils::SmallStringView directoryPath, ProjectStorageType &storage) { - NanotraceHR::Tracer tracer{"create qualified imports"_t, + NanotraceHR::Tracer tracer{"create qualified imports", category(), keyValue("sourceId", sourceId), keyValue("directoryPath", directoryPath)}; @@ -324,7 +324,7 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon SourceId sourceId, Utils::SmallStringView directoryPath) { - NanotraceHR::Tracer tracer{"qml document parser parse"_t, + NanotraceHR::Tracer tracer{"qml document parser parse", category(), keyValue("sourceId", sourceId), keyValue("directoryPath", directoryPath)}; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/qmltypesparser.cpp index 052d38809d2..9b96dde68a6 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/qmltypesparser.cpp @@ -39,7 +39,7 @@ using Storage::TypeNameString; ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList &objects) { - NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()}; + NanotraceHR::Tracer tracer{"parse qmltypes file", category()}; ComponentWithoutNamespaces componentWithoutNamespaces; @@ -81,7 +81,7 @@ void addImports(Storage::Imports &imports, ModuleId cppModuleId) { NanotraceHR::Tracer tracer{ - "add imports"_t, + "add imports", category(), keyValue("source id", sourceId), keyValue("module id", cppModuleId), @@ -89,16 +89,16 @@ void addImports(Storage::Imports &imports, for (const QString &dependency : dependencies) { const auto &import = appendImports(imports, dependency, sourceId, storage); - tracer.tick("append import"_t, keyValue("import", import), keyValue("dependency", dependency)); + tracer.tick("append import", keyValue("import", import), keyValue("dependency", dependency)); } const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); - tracer.tick("append import"_t, keyValue("import", import)); + tracer.tick("append import", keyValue("import", import)); if (ModuleId qmlCppModuleId = storage.moduleId("QML", ModuleKind::CppLibrary); cppModuleId != qmlCppModuleId) { const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); - tracer.tick("append import"_t, keyValue("import", import)); + tracer.tick("append import", keyValue("import", import)); } } @@ -438,7 +438,7 @@ void addType(Storage::Synchronization::Types &types, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespace) { - NanotraceHR::Tracer tracer{"add type"_t, + NanotraceHR::Tracer tracer{"add type", category(), keyValue("source id", sourceId), keyValue("module id", cppModuleId)}; @@ -503,7 +503,7 @@ void addTypes(Storage::Synchronization::Types &types, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) { - NanotraceHR::Tracer tracer{"add types"_t, category()}; + NanotraceHR::Tracer tracer{"add types", category()}; types.reserve(Utils::usize(objects) + types.size()); const auto skipList = getSkipList(storage.module(directoryInfo.moduleId)); @@ -528,7 +528,7 @@ void QmlTypesParser::parse(const QString &sourceContent, Storage::Synchronization::Types &types, const Storage::Synchronization::DirectoryInfo &directoryInfo) { - NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()}; + NanotraceHR::Tracer tracer{"qmltypes parser parse", category()}; QQmlJSTypeDescriptionReader reader({}, sourceContent); QList components; diff --git a/src/plugins/qmldesigner/libs/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/libs/designercore/tracing/qmldesignertracing.cpp index e0bdcd8c860..bd6592d1f02 100644 --- a/src/plugins/qmldesigner/libs/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/libs/designercore/tracing/qmldesignertracing.cpp @@ -55,7 +55,7 @@ StringEventQueue &stringEventQueue() namespace ModelTracing { namespace { -thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category}; +thread_local Category category_{"model", Tracing::stringEventQueue(), category}; } // namespace @@ -70,7 +70,7 @@ namespace ProjectStorageTracing { Category &projectStorageCategory() { - thread_local Category category{"project storage"_t, + thread_local Category category{"project storage", Tracing::eventQueueWithStringArguments(), projectStorageCategory}; @@ -79,7 +79,7 @@ Category &projectStorageCategory() Category &projectStorageUpdaterCategory() { - thread_local Category category{"project storage updater"_t, + thread_local Category category{"project storage updater", Tracing::eventQueueWithStringArguments(), projectStorageCategory}; @@ -91,7 +91,7 @@ Category &projectStorageUpdaterCategory() namespace SourcePathStorageTracing { Category &category() { - thread_local Category category_{"project storage updater"_t, + thread_local Category category_{"project storage updater", Tracing::eventQueueWithStringArguments(), category}; @@ -102,7 +102,7 @@ Category &category() namespace MetaInfoTracing { Category &category() { - thread_local Category category_{"meta info"_t, Tracing::eventQueueWithStringArguments(), category}; + thread_local Category category_{"meta info", Tracing::eventQueueWithStringArguments(), category}; return category_; } From 42cf37a43ef40797b67fc9f859e1e86864354d78 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Thu, 24 Oct 2024 10:36:34 +0200 Subject: [PATCH 027/322] DeviceShare: Add UDP discovery for Design Studio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the network allows UDP broadcasts and if the IP address of the Qt UI Viewer has changed, we can update the device IP without expecting the user to update it. Change-Id: I50a89a9e2412220a3f8200aed807f691e5c9e377 Reviewed-by: Henning Gründl --- .../devicesharing/devicemanager.cpp | 70 ++++++++++++++++++- .../components/devicesharing/devicemanager.h | 6 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 79fefa2e010..98b82036077 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -6,9 +6,8 @@ #include #include #include -#include -#include -#include +#include +#include namespace QmlDesigner::DeviceShare { @@ -21,6 +20,71 @@ DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath) m_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); writeSettings(); } + initUdpDiscovery(); +} + +void DeviceManager::initUdpDiscovery() +{ + const QList interfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &interface : interfaces) { + if (interface.flags().testFlag(QNetworkInterface::IsUp) + && interface.flags().testFlag(QNetworkInterface::IsRunning) + && !interface.flags().testFlag(QNetworkInterface::IsLoopBack)) { + for (const QNetworkAddressEntry &entry : interface.addressEntries()) { + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { + QSharedPointer udpSocket = QSharedPointer::create(); + connect(udpSocket.data(), + &QUdpSocket::readyRead, + this, + &DeviceManager::incomingDatagram); + + bool retVal = udpSocket->bind(entry.ip(), 53452, QUdpSocket::ShareAddress); + + if (!retVal) { + qCWarning(deviceSharePluginLog) + << "UDP:: Failed to bind to UDP port 53452 on" << entry.ip().toString() + << "on interface" << interface.name() + << ". Error:" << udpSocket->errorString(); + continue; + } + + qCDebug(deviceSharePluginLog) << "UDP:: Listening on" << entry.ip().toString() + << "port" << udpSocket->localPort(); + m_udpSockets.append(udpSocket); + } + } + } + } +} + +void DeviceManager::incomingDatagram() +{ + const auto udpSocket = qobject_cast(sender()); + if (!udpSocket) + return; + + while (udpSocket->hasPendingDatagrams()) { + const QNetworkDatagram datagram = udpSocket->receiveDatagram(); + const QJsonDocument doc = QJsonDocument::fromJson(datagram.data()); + const QJsonObject message = doc.object(); + + if (message["name"].toString() != "__qtuiviewer__") { + continue; + } + + const QString id = message["id"].toString(); + const QString ip = datagram.senderAddress().toString(); + qCDebug(deviceSharePluginLog) << "Qt UI VIewer found at" << ip << "with id" << id; + + for (const auto &device : m_devices) { + if (device->deviceInfo().deviceId() == id) { + if (device->deviceSettings().ipAddress() != ip) { + qCDebug(deviceSharePluginLog) << "Updating IP address for device" << id; + setDeviceIP(id, ip); + } + } + } + } } void DeviceManager::writeSettings() diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index bd3484b6d18..5fec51ca194 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -3,8 +3,7 @@ #pragma once -#include -#include +#include #include #include "device.h" @@ -37,6 +36,7 @@ public: private: // Devices management QList> m_devices; + QList> m_udpSockets; // settings const QString m_settingsPath; @@ -44,6 +44,8 @@ private: private: // internal slots + void initUdpDiscovery(); + void incomingDatagram(); void incomingConnection(); void readSettings(); void writeSettings(); From ab5faee8b2e0ffa3ed22dc2d90ede54d2eb664ef Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 25 Oct 2024 15:03:04 +0200 Subject: [PATCH 028/322] QmlProject: Add link to warning Change-Id: I57586c91701bb591702134740e1dda263f71644c Reviewed-by: Thomas Hartmann Reviewed-by: Pranta Ghosh Dastider --- .../qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp index 050097f6514..c0dc80a0b6b 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp @@ -50,7 +50,9 @@ CMakeWriter::Ptr CMakeWriter::create(CMakeGenerator *parent) "The project was created with a Qt Design Studio version earlier than Qt Design Studio " "4.5. Due to limitations of the project structure in earlier Qt Design Studio versions, " "the resulting application might not display all the assets. Referring to " - "assets between different QML modules does not work in the compiled application.", + "assets between different QML modules does not work in the compiled application.
" + "See " + "the documentation for details.", buildSystem->projectFilePath()); return std::make_unique(parent); From 20c8e51d990e7b1b664dd416b2156d374e10a083 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 25 Oct 2024 17:39:51 +0300 Subject: [PATCH 029/322] QmlDesigner: Fix generated Image item path after import Fixes: QDS-13861 Change-Id: Ia1c085ebd4b929ba57ceb4a84c46f091ac51b357 Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/formeditor/formeditorwidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index e99ed6c615b..8d593ddd0ca 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -682,9 +682,12 @@ void FormEditorWidget::dropEvent(QDropEvent *dropEvent) const QStringList addedImages = addedAssets.value( ComponentCoreConstants::addImagesDisplayString); for (const QString &imgPath : addedImages) { + Utils::FilePath fp = Utils::FilePath::fromString(imgPath); + QmlItemNode::createQmlItemNodeFromImage( m_formEditorView, - imgPath, + ModelNodeOperations::getImagesDefaultDirectory().pathAppended(fp.fileName()) + .absoluteFilePath().toFSPathString(), {}, m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode(), false); From 2ea6b555e5f0a805cc250dc2f9f0255d8e0deb5d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 25 Oct 2024 14:32:24 +0300 Subject: [PATCH 030/322] EffectComposer: Fix custom preview issues - Custom preview saving and loading fixed when file is deleted - Custom preview not shown in combo when it's opened after file deletion - Fixed selected highlight on custom preview Fixes: QDS-13880 Change-Id: I8ef3fed5cfc220e3b37007b302f54fe3bf01899d Reviewed-by: Mahmoud Badri --- .../PreviewImagesComboBox.qml | 7 +++- .../effectcomposer/effectcomposermodel.cpp | 37 ++++++++++++++----- .../effectcomposer/effectcomposermodel.h | 1 + 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml index b75a04001e8..0fc5f26056b 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml @@ -69,6 +69,7 @@ StudioControls.ComboBox { target: root.popup function onAboutToShow() { + EffectComposerBackend.effectComposerModel.previewComboAboutToOpen() root.calculateWindowGeometry() window.show() @@ -170,8 +171,10 @@ StudioControls.ComboBox { required property var modelData color: "transparent" - border.color: root.selectedImage === modelData ? StudioTheme.Values.themeInteraction - : "transparent" + border.color: root.selectedImage === modelData + || index == 0 && root.selectedImage == EffectComposerBackend.effectComposerModel.customPreviewImage + ? StudioTheme.Values.themeInteraction + : "transparent" width: 200 height: 200 diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 3063283bffc..4319c9afd4d 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -294,6 +294,17 @@ void EffectComposerModel::chooseCustomPreviewImage() }); } +void EffectComposerModel::previewComboAboutToOpen() +{ + if (!m_customPreviewImage.isEmpty() && !Utils::FilePath::fromUrl(m_customPreviewImage).exists()) { + if (m_currentPreviewImage == m_customPreviewImage) { + m_customPreviewImage.clear(); + emit customPreviewImageChanged(); + setCurrentPreviewImage({}); + } + } +} + QString EffectComposerModel::fragmentShader() const { return m_fragmentShader; @@ -1067,8 +1078,12 @@ void EffectComposerModel::saveComposition(const QString &name) json.insert("tool", "EffectComposer"); Utils::FilePath customPreviewPath = Utils::FilePath::fromUrl(m_customPreviewImage); - if (m_customPreviewImage.isLocalFile()) - customPreviewPath = customPreviewPath.relativePathFrom(compositionDir); + if (m_customPreviewImage.isLocalFile()) { + if (customPreviewPath.exists()) + customPreviewPath = customPreviewPath.relativePathFrom(compositionDir); + else + customPreviewPath = {}; + } json.insert("customPreviewImage", customPreviewPath.toUrl().toString()); QUrl previewUrl = m_currentPreviewImage; @@ -1259,14 +1274,16 @@ void EffectComposerModel::openComposition(const QString &path) m_customPreviewImage.clear(); if (json.contains("customPreviewImage")) { QUrl imageUrl{json["customPreviewImage"].toString()}; - Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl); - if (imagePath.isAbsolutePath()) { - if (imagePath.exists()) - m_customPreviewImage = imageUrl; - } else { - imagePath = effectPath.absolutePath().resolvePath(imagePath); - if (imagePath.exists()) - m_customPreviewImage = imagePath.toUrl(); + if (!imageUrl.isEmpty()) { + Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl); + if (imagePath.isAbsolutePath()) { + if (imagePath.exists()) + m_customPreviewImage = imageUrl; + } else { + imagePath = effectPath.absolutePath().resolvePath(imagePath); + if (imagePath.exists()) + m_customPreviewImage = imagePath.toUrl(); + } } } diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 0fe4aeb84f9..a7ca05d124b 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -81,6 +81,7 @@ public: Q_INVOKABLE QString getUniqueEffectName() const; Q_INVOKABLE bool nameExists(const QString &name) const; Q_INVOKABLE void chooseCustomPreviewImage(); + Q_INVOKABLE void previewComboAboutToOpen(); bool shadersUpToDate() const; void setShadersUpToDate(bool newShadersUpToDate); From b5187e1cf059a0c0c354218da0c98577aef98c82 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 28 Oct 2024 11:24:39 +0100 Subject: [PATCH 031/322] qds: add ImageViewer plugin Task-number: QDS-13818 Change-Id: I27042fbcb13e7e83fba64e0f9e1a1dac0d92db20 Reviewed-by: Thomas Hartmann --- dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake index 59b57f98404..05428c39217 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -33,6 +33,7 @@ set(DESIGNSTUDIO_PLUGINS FakeVim Git Help + ImageViewer Insight LanguageClient McuSupport From 447c377267e44ec5fb02c9efccd367a2b0696447 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 23 Oct 2024 17:52:38 +0200 Subject: [PATCH 032/322] QmlDesigner: Fix file url for windows Change-Id: Ifb16b62bb4f8fa40240a6c44a65fb1173f11cfff Reviewed-by: Thomas Hartmann --- .../libs/designercore/model/model.cpp | 2 +- .../unit/tests/unittests/model/model-test.cpp | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index 7dcea5e098a..d9e5bc9deb3 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -281,7 +281,7 @@ void ModelPrivate::setFileUrl(const QUrl &fileUrl) if (oldPath != fileUrl) { m_fileUrl = fileUrl; if constexpr (useProjectStorage()) { - auto path = fileUrl.path(); + auto path = fileUrl.toString(QUrl::PreferLocalFile); m_sourceId = pathCache->sourceId(SourcePath{path}); auto found = std::find(path.rbegin(), path.rend(), u'/').base(); m_localPath = Utils::PathString{QStringView{path.begin(), std::prev(found)}}; diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 396206e561c..edd2539e7fe 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -909,6 +909,32 @@ TEST_F(Model_Imports, change_imports_is_synchronizing_imports_with_project_stora model.changeImports({qtQuickImport, qtQmlModelsImport}, {}); } +TEST_F(Model_Imports, change_imports_with_windows_file_url) +{ + QmlDesigner::SourcePath windowsFilePath = "c:/path/foo.qml"; + QUrl windowsFilePathUrl = windowsFilePath.toQString(); + SourceId windowsSourceId = pathCacheMock.createSourceId(windowsFilePath); + model.setFileUrl(windowsFilePathUrl); + QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); + ON_CALL(pathCacheMock, sourceId(Eq("c:/path/foo/."))).WillByDefault(Return(directoryPathId)); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); + auto localPathModuleId = projectStorageMock.createModule("c:/path", + QmlDesigner::Storage::ModuleKind::PathLibrary); + auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); + auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); + auto directoryImport = QmlDesigner::Import::createFileImport("foo"); + + EXPECT_CALL(projectStorageMock, + synchronizeDocumentImports( + UnorderedElementsAre(IsImport(qtQuickModuleId, windowsSourceId, 2, 1), + IsImport(qtQmlModelsModuleId, windowsSourceId, -1, -1), + IsImport(localPathModuleId, windowsSourceId, -1, -1)), + windowsSourceId)); + + model.changeImports({qtQuickImport, qtQmlModelsImport}, {}); +} + TEST_F(Model_Imports, change_imports_is_not_synchronizing_imports_with_project_storage_if_no_new_imports_are_added) { @@ -1366,6 +1392,9 @@ protected: QmlDesigner::SourcePath barFilePath = "/path/bar.qml"; QUrl barFilePathUrl = barFilePath.toQString(); SourceId barSourceId = pathCacheMock.createSourceId(barFilePath); + QmlDesigner::SourcePath windowsFilePath = "c:/path/bar.qml"; + QUrl windowsFilePathUrl = windowsFilePath.toQString(); + SourceId windowsSourceId = pathCacheMock.createSourceId(windowsFilePath); }; TEST_F(Model_FileUrl, set_file_url) @@ -1375,6 +1404,13 @@ TEST_F(Model_FileUrl, set_file_url) ASSERT_THAT(model.fileUrl(), barFilePathUrl); } +TEST_F(Model_FileUrl, set_windows_file_url) +{ + model.setFileUrl(windowsFilePathUrl); + + ASSERT_THAT(model.fileUrl(), windowsFilePathUrl); +} + TEST_F(Model_FileUrl, set_file_url_sets_source_id_too) { model.setFileUrl(barFilePathUrl); @@ -1382,6 +1418,13 @@ TEST_F(Model_FileUrl, set_file_url_sets_source_id_too) ASSERT_THAT(model.fileUrlSourceId(), barSourceId); } +TEST_F(Model_FileUrl, set_windows_file_url_sets_source_id_too) +{ + model.setFileUrl(windowsFilePathUrl); + + ASSERT_THAT(model.fileUrlSourceId(), windowsSourceId); +} + TEST_F(Model_FileUrl, notifies_change) { EXPECT_CALL(viewMock, fileUrlChanged(Eq(fileUrl), Eq(barFilePathUrl))); From eab20da68467268cdf66564eacf9638ac6eefd85 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 24 Oct 2024 15:35:30 +0200 Subject: [PATCH 033/322] QmlDesigner: Update local path module if file url is changed Change-Id: I9197148879b694fef46d44a530d8534177b26271 Reviewed-by: Thomas Hartmann --- .../qmldesigner/libs/designercore/model/model.cpp | 2 ++ tests/unit/tests/unittests/model/model-test.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index d9e5bc9deb3..4f4dfeed7fd 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -285,6 +285,8 @@ void ModelPrivate::setFileUrl(const QUrl &fileUrl) m_sourceId = pathCache->sourceId(SourcePath{path}); auto found = std::find(path.rbegin(), path.rend(), u'/').base(); m_localPath = Utils::PathString{QStringView{path.begin(), std::prev(found)}}; + auto imports = createStorageImports(m_imports, m_localPath, *projectStorage, m_sourceId); + projectStorage->synchronizeDocumentImports(std::move(imports), m_sourceId); } for (const QPointer &view : std::as_const(m_viewList)) diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index edd2539e7fe..15d2bb86aa8 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -1438,4 +1438,16 @@ TEST_F(Model_FileUrl, do_not_notify_if_there_is_no_change) model.setFileUrl(fileUrl); } + +TEST_F(Model_FileUrl, updated_local_path_module) +{ + auto localPathModuleId = projectStorageMock.moduleId("/path", ModuleKind::PathLibrary); + + EXPECT_CALL(projectStorageMock, + synchronizeDocumentImports(Contains(IsImport(localPathModuleId, barSourceId, -1, -1)), + barSourceId)); + + model.setFileUrl(barFilePathUrl); +} + } // namespace From d0a8de5694cc867637cfd06760843d819e4c80eb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 24 Oct 2024 18:28:34 +0200 Subject: [PATCH 034/322] QmlDesigner: Remove duplicate exports in qmldir Fixes: QDS-13909 Change-Id: I9f369024d832d210c2efaed6410c462ef8af8bd7 Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.cpp | 33 +++++++- .../projectstorageupdater-test.cpp | 76 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 542af30805c..17072441777 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -1167,6 +1167,32 @@ void rangeForTheSameFileName(const ProjectStorageUpdater::Components &components } } +namespace { + +void removeDuplicates(Storage::Synchronization::ExportedTypes &exportedTypes) +{ + using Storage::Synchronization::ExportedType; + + auto factory = [](auto... projections) { + return [=](auto compare) { + return [=](const auto &first, const auto &second) { + return compare(std::forward_as_tuple(std::invoke(projections, first)...), + std::forward_as_tuple(std::invoke(projections, second)...)); + }; + }; + }; + + auto compare = factory(&ExportedType::name, &ExportedType::version); + auto less = compare(std::ranges::less{}); + auto equal = compare(std::ranges::equal_to{}); + + std::ranges::sort(exportedTypes, less); + + auto duplicateExportedTypes = std::ranges::unique(exportedTypes, equal); + exportedTypes.erase(duplicateExportedTypes.begin(), duplicateExportedTypes.end()); +} +} // namespace + Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdater::ComponentRange components) { Storage::Synchronization::ExportedTypes exportedTypes; @@ -1178,6 +1204,8 @@ Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdate Storage::Version{component.majorVersion, component.minorVersion}); } + removeDuplicates(exportedTypes); + return exportedTypes; } @@ -1197,9 +1225,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, keyValue("directory id", directoryId), keyValue("qmldir state", qmldirState)}; - std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { - return first.fileName < second.fileName; - }); + std::ranges::sort(components, + [](auto &&first, auto &&second) { return first.fileName < second.fileName; }); auto directoryPath = m_pathCache.sourceContextPath(directoryId); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 60d617635da..3a51b997eba 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -830,6 +830,82 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) updater.update({.directories = directories}); } +TEST_F(ProjectStorageUpdater, skip_duplicate_qmldir_entries) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 1.0 First.qml + FirstType 2.0 First.qml + FirstType 2.2 First2.qml + SecondType 2.2 Second.qml)"}; + setContent(u"/path/qmldir", qmldir); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(exampleModuleId, "FirstType", 2, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), + AllOf(IsStorageType("Second.qml", + Storage::Synchronization::ImportedType{"Object3"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId1, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21), + IsFileStatus(qmlDocumentSourceId3, 1, 21))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(directoryPathSourceId)), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); + + updater.update({.directories = directories}); +} + TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) { QString qmldir{R"(module Example From a58cf3784ab6e20ddbb7eb0ec3eafbff643b1ace Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 24 Oct 2024 19:27:32 +0200 Subject: [PATCH 035/322] QmlDesigner: Add compare factory Instead of writing a complicated lambda a compare factory can be used to generate them. Instead of: std::ranges::sort(foo, [] (const Foo &first, const Foo &second) { return std::tie(first.name, first.version) < std::tie(second.name, second.version); }); you can write: std::ranges::sort(foos, makeLess(&Foo::name, &Foo::version)); Change-Id: I4daa5639f007a006a152d7b17688c189b3249cd8 Reviewed-by: Thomas Hartmann --- .../libs/designercore/CMakeLists.txt | 1 + .../designercoreutils/functional.h | 32 +++++++++++++++++++ .../projectstorage/projectstorageupdater.cpp | 12 ++----- 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 src/plugins/qmldesigner/libs/designercore/designercoreutils/functional.h diff --git a/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt b/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt index 1ebb8c48322..91612dad2f1 100644 --- a/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt +++ b/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt @@ -30,6 +30,7 @@ extend_qtc_library(QmlDesignerCore PUBLIC_INCLUDES designercoreutils SOURCES_PREFIX designercoreutils SOURCES + functional.h generatedcomponentutils.cpp generatedcomponentutils.h modelmerger.cpp diff --git a/src/plugins/qmldesigner/libs/designercore/designercoreutils/functional.h b/src/plugins/qmldesigner/libs/designercore/designercoreutils/functional.h new file mode 100644 index 00000000000..66a0485c2f4 --- /dev/null +++ b/src/plugins/qmldesigner/libs/designercore/designercoreutils/functional.h @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include + +namespace QmlDesigner { + +inline constexpr auto makeCompare = [](auto... projections) { + return [=](auto compare) { + return [=](const auto &first, const auto &second) { + return compare(std::forward_as_tuple(std::invoke(projections, first)...), + std::forward_as_tuple(std::invoke(projections, second)...)); + }; + }; +}; + +inline constexpr auto makeLess = [](auto... projections) { + return [=](const auto &first, const auto &second) { + return std::ranges::less(std::forward_as_tuple(std::invoke(projections, first)...), + std::forward_as_tuple(std::invoke(projections, second)...)); + }; +}; + +inline constexpr auto makeEqual = [](auto... projections) { + return [=](const auto &first, const auto &second) { + return std::ranges::equal_to(std::forward_as_tuple(std::invoke(projections, first)...), + std::forward_as_tuple(std::invoke(projections, second)...)); + }; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 17072441777..c366f7c1a9f 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -13,6 +13,7 @@ #include "sourcepathstorage/sourcepathcache.h" #include "typeannotationreader.h" +#include #include #include #include @@ -1173,16 +1174,7 @@ void removeDuplicates(Storage::Synchronization::ExportedTypes &exportedTypes) { using Storage::Synchronization::ExportedType; - auto factory = [](auto... projections) { - return [=](auto compare) { - return [=](const auto &first, const auto &second) { - return compare(std::forward_as_tuple(std::invoke(projections, first)...), - std::forward_as_tuple(std::invoke(projections, second)...)); - }; - }; - }; - - auto compare = factory(&ExportedType::name, &ExportedType::version); + auto compare = makeCompare(&ExportedType::name, &ExportedType::version); auto less = compare(std::ranges::less{}); auto equal = compare(std::ranges::equal_to{}); From 7b532499eea5ebf1f78e906c60cd8e57f4bf1ae0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 28 Oct 2024 12:24:34 +0100 Subject: [PATCH 036/322] QmlDesigner: Use correct path separators on Windows Task-number: QDS-13703 Change-Id: I1bf3f4dd7ede1c2d5649a8a19dec3d3d71a4be82 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 7878ba1c963..b873f76771a 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -481,7 +481,10 @@ QVariant ProjectModel::data(const QModelIndex &index, int role) const case FilePathRole: return data.filePath.toVariant(); case PrettyFilePathRole: - return data.filePath.absolutePath().withTildeHomePath(); + if (Utils::HostOsInfo::isWindowsHost()) + return data.filePath.absolutePath().nativePath(); + else + return data.filePath.absolutePath().withTildeHomePath(); case PreviewUrl: return QVariant( QStringLiteral("image://project_preview/") From 475f2dc7bcac41f965e14e1d42b8997c90684a0c Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 28 Oct 2024 10:03:19 +0100 Subject: [PATCH 037/322] DesignViewer: Add connector for the new Design Viewer backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5e0939c13a31f4bd0fcf3112354d6e60319565e5 Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/CMakeLists.txt | 11 +- .../components/designviewer/dvconnector.cpp | 637 ++++++++++++++++++ .../components/designviewer/dvconnector.h | 151 +++++ .../designviewer/resourcegeneratorproxy.cpp | 63 ++ .../designviewer/resourcegeneratorproxy.h | 28 + 5 files changed, 889 insertions(+), 1 deletion(-) create mode 100644 src/plugins/qmldesigner/components/designviewer/dvconnector.cpp create mode 100644 src/plugins/qmldesigner/components/designviewer/dvconnector.h create mode 100644 src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp create mode 100644 src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index a4d41a5877a..41173237254 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -35,7 +35,7 @@ add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") add_subdirectory(libs) -find_package(Qt6 REQUIRED COMPONENTS WebSockets) +find_package(Qt6 REQUIRED COMPONENTS WebSockets WebEngineWidgets) add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview @@ -742,6 +742,15 @@ extend_qtc_plugin(QmlDesigner devicemanagermodel.cpp devicemanagermodel.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/designviewer + DEPENDS + Qt::WebEngineWidgets + SOURCES + dvconnector.cpp dvconnector.h + resourcegeneratorproxy.cpp resourcegeneratorproxy.h +) + add_qtc_plugin(assetexporterplugin PLUGIN_CLASS AssetExporterPlugin CONDITION TARGET QmlDesigner diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp new file mode 100644 index 00000000000..8ec76bd4bc2 --- /dev/null +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -0,0 +1,637 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "dvconnector.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace QmlDesigner::DesignViewer { +Q_LOGGING_CATEGORY(deploymentPluginLog, "qtc.designer.deploymentPlugin", QtWarningMsg) + +namespace DVEndpoints { +using namespace Qt::Literals; +constexpr auto serviceUrl = "https://api-designviewer-staging.qt.io"_L1; +constexpr auto project = "/api/v2/project"_L1; +constexpr auto projectThumbnail = "/api/v2/project/image"_L1; +constexpr auto share = "/api/v2/share"_L1; +constexpr auto shareThumbnail = "/api/v2/share/image"_L1; +constexpr auto login = "/api/v2/auth/login"_L1; +constexpr auto logout = "/api/v2/auth/logout"_L1; +constexpr auto userInfo = "/api/v2/auth/userinfo"_L1; +}; // namespace DVEndpoints + +CustomWebEnginePage::CustomWebEnginePage(QWebEngineProfile *profile, QObject *parent) + : QWebEnginePage(profile, parent) +{} + +void CustomWebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, + const QString &message, + int lineNumber, + const QString &sourceID) +{ + // Suppress JavaScript console messages + if (level == QWebEnginePage::JavaScriptConsoleMessageLevel::InfoMessageLevel + || level == QWebEnginePage::JavaScriptConsoleMessageLevel::WarningMessageLevel + || level == QWebEnginePage::JavaScriptConsoleMessageLevel::ErrorMessageLevel) { + return; + } + QWebEnginePage::javaScriptConsoleMessage(level, message, lineNumber, sourceID); +} + +CustomCookieJar::CustomCookieJar(QObject *parent, const QString &cookieFilePath) + : QNetworkCookieJar(parent) + , m_cookieFilePath(cookieFilePath) +{} + +CustomCookieJar::~CustomCookieJar() +{ + saveCookies(); +} + +void CustomCookieJar::loadCookies() +{ + QFile file(m_cookieFilePath); + if (!file.open(QIODevice::ReadOnly)) { + qCDebug(deploymentPluginLog) << "Failed to open cookie file for reading:" << m_cookieFilePath; + return; + } + + QList cookies; + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + QList lineCookies = QNetworkCookie::parseCookies(line.toUtf8()); + cookies.append(lineCookies); + } + setAllCookies(cookies); + file.close(); +} + +void CustomCookieJar::saveCookies() +{ + QFile file(m_cookieFilePath); + if (!file.open(QIODevice::WriteOnly)) { + qCDebug(deploymentPluginLog) << "Failed to open cookie file for writing:" << m_cookieFilePath; + return; + } + + QTextStream out(&file); + QList cookies = allCookies(); + for (const QNetworkCookie &cookie : cookies) { + out << cookie.toRawForm() << "\n"; + } + file.close(); +} + +void CustomCookieJar::clearCookies() +{ + setAllCookies(QList()); + QFile file(m_cookieFilePath); + if (!file.open(QIODevice::WriteOnly)) { + qCDebug(deploymentPluginLog) << "Failed to open cookie file for writing:" << m_cookieFilePath; + return; + } + + file.close(); +} + +DVConnector::DVConnector(QObject *parent) + : QObject{parent} + , m_connectorStatus(ConnectorStatus::FetchingUserInfo) +{ + QLoggingCategory::setFilterRules("qtc.designer.deploymentPlugin.debug=true"); + m_webEngineProfile.reset(new QWebEngineProfile("DesignViewer", this)); + m_webEngineProfile->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); + m_webEnginePage.reset(new CustomWebEnginePage(m_webEngineProfile.data(), this)); + m_webEngineView.reset(new QWebEngineView); + m_webEngineView->setPage(m_webEnginePage.data()); + m_webEngineView->resize(1024, 750); + + m_networkCookieJar.reset( + new CustomCookieJar(this, m_webEngineProfile->persistentStoragePath() + "/dv_cookies.txt")); + m_networkAccessManager.reset(new QNetworkAccessManager(this)); + m_networkAccessManager->setCookieJar(m_networkCookieJar.data()); + m_networkCookieJar->loadCookies(); + + connect(m_webEngineProfile->cookieStore(), + &QWebEngineCookieStore::cookieAdded, + this, + [&](const QNetworkCookie &cookie) { + const QByteArray cookieName = cookie.name(); + qCDebug(deploymentPluginLog) + << "Cookie added: " << cookieName << ", value: " << cookie.value(); + if (cookieName != "jwt" && cookieName != "jwt_refresh") + return; + m_networkAccessManager->cookieJar()->insertCookie(cookie); + m_networkCookieJar->saveCookies(); + + if (cookieName == "jwt") { + qCDebug(deploymentPluginLog) << "Got JWT"; + m_webEngineView->hide(); + userInfo(); + } + }); + + userInfo(); +} + +DVConnector::ConnectorStatus DVConnector::connectorStatus() const +{ + return m_connectorStatus; +} + +void DVConnector::projectList() +{ + qCDebug(deploymentPluginLog) << "Fetching project list"; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::project); + QNetworkRequest request(url); + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Project list"; + evaluatorData.successCallback = [this](const QByteArray &reply) { + emit projectListReceived(reply); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::uploadProject(const QString &projectId, const QString &filePath) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_UPLOADED); + + QFileInfo fileInfo(filePath); + QFile *file = new QFile(filePath); + if (!file->open(QIODevice::ReadOnly)) { + qCWarning(deploymentPluginLog) << "File not found"; + return; + } + + const QString newProjectId = projectId.endsWith(".qmlrc") ? projectId : projectId + ".qmlrc"; + qCDebug(deploymentPluginLog) << "Uploading project:" << fileInfo.fileName() + << " with projectId: " << newProjectId; + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + file->setParent(multiPart); + + QHttpPart filePart; + QHttpPart displayNamePart; + QHttpPart descriptionPart; + QHttpPart qdsVersionPart; + + filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + + // fileName is not required by the server, but it is required by the QHttpPart. + // it's also useful to set custom file names for the uploaded files, such as uuids. + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"file\"; filename=\"" + newProjectId + "\"")); + + filePart.setBodyDevice(file); + displayNamePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"displayName\"")); + displayNamePart.setBody(fileInfo.fileName().toUtf8()); + + descriptionPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"description\"")); + descriptionPart.setBody("testDescription"); + + qdsVersionPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"qdsVersion\"")); + // qdsVersionPart.setBody(Core::ICore::versionString().toUtf8()); + qdsVersionPart.setBody("1.0.0"); + + multiPart->append(filePart); + multiPart->append(displayNamePart); + multiPart->append(descriptionPart); + multiPart->append(qdsVersionPart); + + QNetworkRequest request; + request.setUrl(QUrl(DVEndpoints::serviceUrl + DVEndpoints::project)); + request.setTransferTimeout(10000); + qCDebug(deploymentPluginLog) << "Sending request to: " << request.url().toString(); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->post(request, multiPart); + evaluatorData.description = "Upload project"; + evaluatorData.successCallback = [this](const QByteArray &) { + emit projectUploaded(); + // call userInfo to update storage info in the UI + userInfo(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit projectUploadError(errorCode, errorString); + }; + + multiPart->setParent(evaluatorData.reply); + emit projectUploadProgress(0.0); + connect(evaluatorData.reply, + &QNetworkReply::uploadProgress, + this, + [this](qint64 bytesSent, qint64 bytesTotal) { + emit projectUploadProgress(100.0 * (double) bytesSent / (double) bytesTotal); + ; + }); + evaluatorData.connectCallbacks(this); +} + +void DVConnector::uploadProjectThumbnail(const QString &projectId, const QString &filePath) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_THUMBNAIL_UPLOADED); + + QFileInfo fileInfo(filePath); + QFile *file = new QFile(filePath); + if (!file->open(QIODevice::ReadOnly)) { + qCWarning(deploymentPluginLog) << "File not found"; + return; + } + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + file->setParent(multiPart); + + QHttpPart filePart; + filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"file\"; filename=\"" + fileInfo.fileName() + "\"")); + + filePart.setBodyDevice(file); + + multiPart->append(filePart); + + QNetworkRequest request; + request.setUrl(QUrl(DVEndpoints::serviceUrl + DVEndpoints::projectThumbnail + "/" + projectId)); + request.setTransferTimeout(10000); + qCDebug(deploymentPluginLog) << "Sending request to: " << request.url().toString() + << "file: " << fileInfo.fileName(); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->post(request, multiPart); + evaluatorData.description = "Upload project thumbnail"; + evaluatorData.successCallback = [this](const QByteArray &) { + emit thumbnailUploaded(); + // call userInfo to update storage info in the UI + userInfo(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit thumbnailUploadError(errorCode, errorString); + }; + + multiPart->setParent(evaluatorData.reply); + emit thumbnailUploadProgress(0.0); + connect(evaluatorData.reply, + &QNetworkReply::uploadProgress, + this, + [this](qint64 bytesSent, qint64 bytesTotal) { + emit thumbnailUploadProgress(100.0 * (double) bytesSent / (double) bytesTotal); + }); + evaluatorData.connectCallbacks(this); +} + +void DVConnector::deleteProject(const QString &projectId) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_DELETED); + + qCDebug(deploymentPluginLog) << "Deleting project with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::project + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->deleteResource(request); + evaluatorData.description = "Delete project"; + evaluatorData.successCallback = [this](const QByteArray &) { + emit projectDeleted(); + // call userInfo to update storage info in the UI + userInfo(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit projectDeleteError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::deleteProjectThumbnail(const QString &projectId) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_THUMBNAIL_DELETED); + + qCDebug(deploymentPluginLog) << "Deleting project thumbnail with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::projectThumbnail + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->deleteResource(request); + evaluatorData.description = "Delete project thumbnail"; + evaluatorData.successCallback = [this](const QByteArray &) { + emit thumbnailDeleted(); + // call userInfo to update storage info in the UI + userInfo(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit thumbnailDeleteError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::downloadProject(const QString &projectId, const QString &filePath) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_DOWNLOADED); + + qCDebug(deploymentPluginLog) << "Downloading project with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::project + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Download project"; + evaluatorData.successCallback = [this, filePath](const QByteArray &reply) { + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(deploymentPluginLog) << "Failed to open file for writing:" << filePath; + return; + } + file.write(reply); + file.close(); + emit projectDownloaded(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit projectDownloadError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::downloadProjectThumbnail(const QString &projectId, const QString &filePath) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_THUMBNAIL_DOWNLOADED); + + qCDebug(deploymentPluginLog) << "Downloading project thumbnail with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::projectThumbnail + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Download project thumbnail"; + evaluatorData.successCallback = [this, filePath](const QByteArray &reply) { + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(deploymentPluginLog) << "Failed to open file for writing:" << filePath; + return; + } + file.write(reply); + file.close(); + emit thumbnailDownloaded(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit thumbnailDownloadError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::sharedProjectList() +{ + qCDebug(deploymentPluginLog) << "Fetching shared project list"; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::share); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Shared project list"; + evaluatorData.successCallback = [this](const QByteArray &reply) { + emit sharedProjectListReceived(reply); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::shareProject(const QString &projectId, + const QString &password, + const int ttlDays, + const QString &description) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_SHARED); + + qCDebug(deploymentPluginLog) << "Sharing project with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::share); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QJsonObject json; + json["fileName"] = projectId; + json["password"] = password; + json["ttlDays"] = ttlDays; + json["description"] = description; + + QJsonDocument doc(json); + QByteArray data = doc.toJson(); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->post(request, data); + evaluatorData.description = "Share project"; + evaluatorData.successCallback = [this, projectId](const QByteArray &reply) { + qCDebug(deploymentPluginLog) << "Project shared: " << reply; + const QString shareUuid = QJsonDocument::fromJson(reply).object()["id"].toString(); + emit projectShared(projectId, shareUuid); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit projectShareError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::unshareProject(const QString &shareUUID) +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_UNSHARED); + + qCDebug(deploymentPluginLog) << "Unsharing project with id: " << shareUUID; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::share + "/" + shareUUID); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->deleteResource(request); + evaluatorData.description = "Unshare project"; + evaluatorData.successCallback = [this](const QByteArray &) { emit projectUnshared(); }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit projectUnshareError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::unshareAllProjects() +{ + QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( + QmlDesigner::Constants::EVENT_DESIGNVIEWER_PROJECT_UNSHARED_ALL); + qCDebug(deploymentPluginLog) << "Unsharing all projects"; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::share); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->deleteResource(request); + evaluatorData.description = "Unshare all projects"; + evaluatorData.successCallback = [this](const QByteArray &) { emit allProjectsUnshared(); }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit allProjectsUnshareError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::downloadSharedProject(const QString &projectId, const QString &filePath) +{ + qCDebug(deploymentPluginLog) << "Downloading shared project with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::share + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Download shared project"; + evaluatorData.successCallback = [this, filePath](const QByteArray &reply) { + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(deploymentPluginLog) << "Failed to open file for writing:" << filePath; + return; + } + file.write(reply); + file.close(); + emit sharedProjectDownloaded(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit sharedProjectDownloadError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::downloadSharedProjectThumbnail(const QString &projectId, const QString &filePath) +{ + qCDebug(deploymentPluginLog) << "Downloading shared project thumbnail with id: " << projectId; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::shareThumbnail + "/" + projectId); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Download shared project thumbnail"; + evaluatorData.successCallback = [this, filePath](const QByteArray &reply) { + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(deploymentPluginLog) << "Failed to open file for writing:" << filePath; + return; + } + file.write(reply); + file.close(); + emit sharedProjectThumbnailDownloaded(); + }; + evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { + emit sharedProjectThumbnailDownloadError(errorCode, errorString); + }; + evaluatorData.connectCallbacks(this); +} + +void DVConnector::login() +{ + if (m_connectorStatus == ConnectorStatus::LoggedIn) { + qCDebug(deploymentPluginLog) << "Already logged in"; + return; + } + + qCDebug(deploymentPluginLog) << "Logging in"; + m_webEnginePage->load(QUrl(DVEndpoints::serviceUrl + DVEndpoints::login)); + m_webEngineView->show(); +} + +void DVConnector::logout() +{ + if (m_connectorStatus == ConnectorStatus::NotLoggedIn) { + qCDebug(deploymentPluginLog) << "Already logged out"; + return; + } + + qCDebug(deploymentPluginLog) << "Logging out"; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::logout); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.description = "Logout"; + evaluatorData.successCallback = [this](const QByteArray &) { + m_webEngineProfile->cookieStore()->deleteAllCookies(); + m_connectorStatus = ConnectorStatus::NotLoggedIn; + emit connectorStatusUpdated(m_connectorStatus); + }; + + evaluatorData.connectCallbacks(this); +} + +void DVConnector::userInfo() +{ + qCDebug(deploymentPluginLog) << "Fetching user info"; + QUrl url(DVEndpoints::serviceUrl + DVEndpoints::userInfo); + QNetworkRequest request(url); + + ReplyEvaluatorData evaluatorData; + evaluatorData.description = "User info"; + evaluatorData.reply = m_networkAccessManager->get(request); + evaluatorData.successCallback = [this](const QByteArray &reply) { + m_connectorStatus = ConnectorStatus::LoggedIn; + emit connectorStatusUpdated(m_connectorStatus); + emit userInfoReceived(reply); + }; + evaluatorData.errorCodeOtherCallback = [this](const int, const QString &) { + QTimer::singleShot(1000, this, &DVConnector::userInfo); + }; + + evaluatorData.connectCallbacks(this); +} + +void DVConnector::evaluateReply(const ReplyEvaluatorData &evaluatorData) +{ + if (evaluatorData.reply->error() == QNetworkReply::NoError) { + qCDebug(deploymentPluginLog) << evaluatorData.description << " request finished successfully"; + if (evaluatorData.successCallback) { + qCDebug(deploymentPluginLog) << "Executing success callback"; + evaluatorData.successCallback(evaluatorData.reply->readAll()); + } + return; + } + + qCDebug(deploymentPluginLog) << evaluatorData.description << "Request error. Return Code:" + << evaluatorData.reply + ->attribute(QNetworkRequest::HttpStatusCodeAttribute) + .toInt() + << ", Message:" << evaluatorData.reply->errorString(); + if (evaluatorData.errorPreCallback) { + qCDebug(deploymentPluginLog) << "Executing custom error pre callback"; + evaluatorData.errorPreCallback(evaluatorData.reply->error(), + evaluatorData.reply->errorString()); + } + + if (evaluatorData.reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401) { + if (evaluatorData.errorCodeUnauthorizedCallback) { + qCDebug(deploymentPluginLog) << "Executing custom unauthorized callback"; + evaluatorData.errorCodeUnauthorizedCallback( + evaluatorData.reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), + evaluatorData.reply->errorString()); + } else { + qCDebug(deploymentPluginLog) << "Executing default unauthorized callback"; + m_connectorStatus = ConnectorStatus::NotLoggedIn; + emit connectorStatusUpdated(m_connectorStatus); + } + } else { + if (evaluatorData.errorCodeOtherCallback) { + qCDebug(deploymentPluginLog) << "Executing custom error callback"; + evaluatorData.errorCodeOtherCallback( + evaluatorData.reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), + evaluatorData.reply->errorString()); + } + } + + evaluatorData.reply->deleteLater(); +} + +} // namespace QmlDesigner::DesignViewer diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h new file mode 100644 index 00000000000..63377075cde --- /dev/null +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -0,0 +1,151 @@ +// Copyright (C) 2024 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 + +namespace QmlDesigner::DesignViewer { + +class CustomWebEnginePage : public QWebEnginePage +{ +public: + CustomWebEnginePage(QWebEngineProfile *profile, QObject *parent = nullptr); + +protected: + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, + const QString &message, + int lineNumber, + const QString &sourceID) override; +}; + +class CustomCookieJar : public QNetworkCookieJar +{ + Q_OBJECT +public: + CustomCookieJar(QObject *parent = nullptr, const QString &cookieFilePath = "cookies.txt"); + ~CustomCookieJar(); + + void loadCookies(); + void saveCookies(); + void clearCookies(); + +private: + const QString m_cookieFilePath; +}; + +class DVConnector : public QObject +{ + Q_OBJECT + Q_PROPERTY(ConnectorStatus connectorStatus READ connectorStatus NOTIFY connectorStatusUpdated) +public: + explicit DVConnector(QObject *parent = nullptr); + + enum ConnectorStatus { FetchingUserInfo, NotLoggedIn, LoggedIn }; + Q_ENUM(ConnectorStatus) + +public: + ConnectorStatus connectorStatus() const; + + void projectList(); + void uploadProject(const QString &projectId, const QString &filePath); + void deleteProject(const QString &projectId); + void downloadProject(const QString &projectId, const QString &filePath); + + void uploadProjectThumbnail(const QString &projectId, const QString &filePath); + void deleteProjectThumbnail(const QString &projectId); + void downloadProjectThumbnail(const QString &projectId, const QString &filePath); + + void sharedProjectList(); + void shareProject(const QString &projectId, + const QString &password = {}, + const int ttlDays = 30, + const QString &description = {}); + void unshareProject(const QString &shareUUID); + void unshareAllProjects(); + void downloadSharedProject(const QString &projectId, const QString &filePath); + void downloadSharedProjectThumbnail(const QString &projectId, const QString &filePath); + + void login(); + void logout(); + void userInfo(); + +private: + // network + QScopedPointer m_networkAccessManager; + QScopedPointer m_networkCookieJar; + + // login + QScopedPointer m_webEngineProfile; + QScopedPointer m_webEnginePage; + QScopedPointer m_webEngineView; + + // status + ConnectorStatus m_connectorStatus; + + struct ReplyEvaluatorData + { + QNetworkReply *reply = nullptr; + QString description; + std::function successCallback = nullptr; + std::function errorPreCallback = nullptr; + std::function errorCodeUnauthorizedCallback = nullptr; + std::function errorCodeOtherCallback = nullptr; + + void connectCallbacks(DVConnector *connector) + { + ReplyEvaluatorData newData = *this; + QObject::connect(reply, &QNetworkReply::finished, connector, [newData, connector] { + connector->evaluateReply(newData); + }); + } + }; + +private: + void evaluateReply(const ReplyEvaluatorData &evaluator); + +signals: + // backend integration - project related signals + void projectListReceived(const QByteArray &reply); + void projectUploaded(); + void projectUploadError(const int errorCode, const QString &message); + void projectUploadProgress(const double progress); + void projectDeleted(); + void projectDeleteError(const int errorCode, const QString &message); + void projectDownloaded(); + void projectDownloadError(const int errorCode, const QString &message); + + // backend integration - project thumbnail related signals + void thumbnailUploaded(); + void thumbnailUploadError(const int errorCode, const QString &message); + void thumbnailUploadProgress(const double progress); + void thumbnailDeleted(); + void thumbnailDeleteError(const int errorCode, const QString &message); + void thumbnailDownloaded(); + void thumbnailDownloadError(const int errorCode, const QString &message); + + // backend integration - shared project related signals + void sharedProjectListReceived(const QByteArray &reply); + void projectShared(const QString &projectId, const QString &shareUUID); + void projectShareError(const int errorCode, const QString &message); + void projectUnshared(); + void projectUnshareError(const int errorCode, const QString &message); + void allProjectsUnshared(); + void allProjectsUnshareError(const int errorCode, const QString &message); + void sharedProjectDownloaded(); + void sharedProjectDownloadError(const int errorCode, const QString &message); + void sharedProjectThumbnailDownloaded(); + void sharedProjectThumbnailDownloadError(const int errorCode, const QString &message); + + // backend integration - login/user related signals + void userInfoReceived(const QByteArray &reply); + + // internal signals + void connectorStatusUpdated(const ConnectorStatus status); +}; + +} // namespace QmlDesigner::DesignViewer diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp new file mode 100644 index 00000000000..30bec8a550d --- /dev/null +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "resourcegeneratorproxy.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +namespace QmlDesigner::DesignViewer { + +ResourceGeneratorProxy::~ResourceGeneratorProxy() +{ + if (m_future.isRunning()) { + m_future.cancel(); + m_future.waitForFinished(); + } +} + +void ResourceGeneratorProxy::createResourceFileAsync() +{ + m_future = QtConcurrent::run([&]() { + const QString filePath = createResourceFileSync(); + + if (filePath.isEmpty()) { + emit errorOccurred("Failed to create resource file"); + return; + } + + emit resourceFileCreated(filePath); + }); +} + +QString ResourceGeneratorProxy::createResourceFileSync() +{ + const auto project = ProjectExplorer::ProjectManager::startupProject(); + const Utils::FilePath tempFilePath = project->projectDirectory().pathAppended("share.qmlrc"); + + const bool retVal = ResourceGenerator::createQmlrcFile(tempFilePath); + + if (!retVal || tempFilePath.fileSize() == 0) { + Core::MessageManager::writeDisrupting(tr("Failed to create resource file")); + return ""; + } + + return tempFilePath.toString(); +} + +} // namespace QmlDesigner::DesignViewer diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h new file mode 100644 index 00000000000..4afcad98a30 --- /dev/null +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 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::DesignViewer { + +class ResourceGeneratorProxy : public QObject +{ + Q_OBJECT +public: + ~ResourceGeneratorProxy(); + Q_INVOKABLE void createResourceFileAsync(); + Q_INVOKABLE QString createResourceFileSync(); + +private: + QFuture m_future; + +signals: + void errorOccurred(const QString &error); + void resourceFileCreated(const QString &filename); +}; + +} // namespace QmlDesigner::DesignViewer From 5f22234c9c0e3d6e0d5917cf36c0f53b9e43a1e2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 28 Oct 2024 15:23:59 +0100 Subject: [PATCH 038/322] QmlDesigner: InstantQmlTextUpdate::No is the default We always have to connect to m_amendTimer, since InstantQmlTextUpdate::No is set to false only temporarily. Restoring aux data has to be delayed in any case if type information is incomplete. Task-number: QDS-13947 Change-Id: I40b1a75e0efe6e746b171ec5abaf623ea1aea7cd Reviewed-by: Miikka Heikkinen --- .../libs/designercore/include/rewriterview.h | 2 +- .../libs/designercore/rewriter/rewriterview.cpp | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h b/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h index 5960e8fb0a3..e9eb4a97ea7 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/libs/designercore/include/rewriterview.h @@ -57,7 +57,7 @@ public: public: RewriterView(ExternalDependenciesInterface &externalDependencies, DifferenceHandling differenceHandling = RewriterView::Amend, - InstantQmlTextUpdate instantQmlTextUpdate = InstantQmlTextUpdate::Yes); + InstantQmlTextUpdate instantQmlTextUpdate = InstantQmlTextUpdate::No); ~RewriterView() override; void modelAttached(Model *model) override; diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp index a1ba0848713..533a199e8be 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/rewriterview.cpp @@ -66,10 +66,8 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, m_amendTimer.setSingleShot(true); - if (m_instantQmlTextUpdate == InstantQmlTextUpdate::No) { - m_amendTimer.setInterval(800); - connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText); - } + m_amendTimer.setInterval(800); + connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText); #ifndef QDS_USE_PROJECTSTORAGE QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); @@ -120,9 +118,7 @@ void RewriterView::modelAttached(Model *model) if (!(m_errors.isEmpty() && m_warnings.isEmpty())) notifyErrorsAndWarnings(m_errors); - if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes) { - restoreAuxiliaryData(); - } else if (hasIncompleteTypeInformation()) { + if (hasIncompleteTypeInformation()) { m_modelAttachPending = true; QTimer::singleShot(1000, this, [this, model]() { modelAttached(model); From d01232d1f4587284eb27286e1c817eb905caa55b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 28 Oct 2024 16:02:17 +0100 Subject: [PATCH 039/322] QmlDesigner: Fix DETACH_DISABLED_VIEWS We have to add the flag to the designer core too. Change-Id: I4428da79f6155e32e936c11994550d3b6ea5e9b9 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/libs/designercore/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt b/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt index 91612dad2f1..1ebe4bd288c 100644 --- a/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt +++ b/src/plugins/qmldesigner/libs/designercore/CMakeLists.txt @@ -21,6 +21,7 @@ add_qtc_library(QmlDesignerCore DEFINES $<$:QDS_USE_PROJECTSTORAGE> $<$:QTC_USE_QML_DESIGNER_LITE> + $<$:DETACH_DISABLED_VIEWS> PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR} include From a3683cd78aaf9e5137d417cb67892541b2d08dbd Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 28 Oct 2024 14:21:52 +0100 Subject: [PATCH 040/322] QmlDesigner: Add local file item library entries If the item library entry is queried by the source id the local files are added too. Change-Id: I8b11474bad2a20e54721f532dfdd0b21ca0a1bf8 Reviewed-by: Thomas Hartmann --- .../itemlibrary/itemlibrarymodel.cpp | 9 +++- .../metainfo/itemlibraryentry.cpp | 3 +- .../projectstorage/projectstorage.cpp | 49 ++++++++++++++----- .../projectstorage/projectstorageinfotypes.h | 13 +++++ .../tests/matchers/projectstorage-matcher.h | 18 +++++++ .../unittests/metainfo/nodemetainfo-test.cpp | 1 + .../projectstorage/projectstorage-test.cpp | 43 ++++++++++++++++ 7 files changed, 123 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 0997d06853e..bbf9ef00a23 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -370,8 +370,10 @@ void ItemLibraryModel::update(Model *model) itemLibImport->setImportExpanded(loadExpandedState(itemLibImport->importUrl())); } +#ifndef QDS_USE_PROJECTSTORAGE DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); const bool blockNewImports = document->inFileComponentModelActive(); +#endif const QList itemLibEntries = model->itemLibraryEntries(); for (const ItemLibraryEntry &entry : itemLibEntries) { NodeMetaInfo metaInfo; @@ -407,13 +409,18 @@ void ItemLibraryModel::update(Model *model) blocked = true; } +#ifndef QDS_USE_PROJECTSTORAGE Import import = entryToImport(entry); bool hasImport = model->hasImport(import, true, true); bool isImportPossible = false; if (!hasImport) isImportPossible = !blockNewImports && model->isImportPossible(import, true, true); +#else + bool hasImport = true; + bool isImportPossible = false; +#endif bool isUsable = (valid && (isItem || forceVisibility)) - && (entry.requiredImport().isEmpty() || hasImport); + && (entry.requiredImport().isEmpty() || hasImport); if (!blocked && (isUsable || isImportPossible)) { ItemLibraryImport *importSection = nullptr; QString catName = entry.category(); diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp index 745b46c91e3..d8b05aac6b7 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp @@ -74,7 +74,8 @@ ItemLibraryEntry ItemLibraryEntry::create(const Storage::Info::ItemLibraryEntry m_data->category = entry.category.toQString(); if (entry.iconPath.size()) m_data->libraryEntryIconPath = entry.iconPath.toQString(); - m_data->requiredImport = entry.import.toQString(); + if (entry.moduleKind == Storage::ModuleKind::QmlLibrary) + m_data->requiredImport = entry.import.toQString(); m_data->toolTip = entry.toolTip.toQString(); m_data->qmlSource = entry.templatePath.toQString(); m_data->properties.reserve(Utils::ssize(entry.properties)); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 98c5e6dc8e8..06e7be20185 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -864,6 +864,13 @@ struct ProjectStorage::Statements " USING(moduleId) " " WHERE di.sourceId=?)", database}; + mutable Sqlite::ReadStatement<3, 2> selectLocalFileItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, etn.name, m.name " + "FROM documentImports AS di " + " JOIN exportedTypeNames AS etn USING(moduleId) " + " JOIN modules AS m USING(moduleId) " + "WHERE di.sourceId=?1 AND m.kind = ?2", + database}; mutable Sqlite::ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; mutable Sqlite::ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ @@ -1838,6 +1845,13 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im return entries; } +namespace { +bool isCapitalLetter(char c) +{ + return c >= 'A' && c <= 'Z'; +} +} // namespace + Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId sourceId) const { using NanotraceHR::keyValue; @@ -1848,16 +1862,16 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; - auto callback = [&](TypeId typeId, - Utils::SmallStringView typeName, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { + auto typeAnnotationCallback = [&](TypeId typeId, + Utils::SmallStringView typeName, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { auto &last = entries.emplace_back( typeId, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) @@ -1866,7 +1880,20 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); }; - s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(typeAnnotationCallback, + sourceId); + + auto fileComponentCallback = + [&](TypeId typeId, Utils::SmallStringView typeName, Utils::SmallStringView import) { + if (!isCapitalLetter(typeName.front())) + return; + + auto &last = entries.emplace_back(typeId, typeName, typeName, "My Components", import); + last.moduleKind = Storage::ModuleKind::PathLibrary; + }; + + s->selectLocalFileItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction( + fileComponentCallback, sourceId, Storage::ModuleKind::PathLibrary); tracer.end(keyValue("item library entries", entries)); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h index 8b602ed65a9..c0fa895713a 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h @@ -413,6 +413,18 @@ struct ItemLibraryEntry , properties{std::move(properties)} {} + ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, + Utils::SmallStringView name, + Utils::SmallStringView category, + Utils::SmallStringView import) + : typeId{typeId} + , typeName{typeName} + , name{name} + , category{category} + , import{import} + {} + template friend void convertToString(String &string, const ItemLibraryEntry &entry) { @@ -438,6 +450,7 @@ struct ItemLibraryEntry Utils::PathString iconPath; Utils::SmallString category; Utils::SmallString import; + ModuleKind moduleKind = ModuleKind::QmlLibrary; ToolTipString toolTip; Utils::PathString templatePath; ItemLibraryProperties properties; diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h index 56b4ad9d6ae..65bdd3d930a 100644 --- a/tests/unit/tests/matchers/projectstorage-matcher.h +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -25,6 +25,7 @@ auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, Utils::SmallStringView iconPath, Utils::SmallStringView category, Utils::SmallStringView import, + QmlDesigner::Storage::ModuleKind moduleKind, Utils::SmallStringView toolTip, Utils::SmallStringView templatePath, PropertiesMatcher propertiesMatcher, @@ -37,12 +38,29 @@ auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, Field("iconPath", &ItemLibraryEntry::iconPath, iconPath), Field("category", &ItemLibraryEntry::category, category), Field("import", &ItemLibraryEntry::import, import), + Field("moduleKind", &ItemLibraryEntry::moduleKind, moduleKind), Field("toolTip", &ItemLibraryEntry::toolTip, toolTip), Field("templatePath", &ItemLibraryEntry::templatePath, templatePath), Field("properties", &ItemLibraryEntry::properties, propertiesMatcher), Field("extraFilePaths", &ItemLibraryEntry::extraFilePaths, extraFilePathsMatcher)); } +inline auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + Utils::SmallStringView typeName, + Utils::SmallStringView name, + Utils::SmallStringView category, + Utils::SmallStringView import, + QmlDesigner::Storage::ModuleKind moduleKind) +{ + using QmlDesigner::Storage::Info::ItemLibraryEntry; + return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId), + Field("typeName", &ItemLibraryEntry::typeName, typeName), + Field("name", &ItemLibraryEntry::name, name), + Field("category", &ItemLibraryEntry::category, category), + Field("import", &ItemLibraryEntry::import, import), + Field("moduleKind", &ItemLibraryEntry::moduleKind, moduleKind)); +} + MATCHER_P3(IsItemLibraryProperty, name, type, diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index f3f55136f27..6d22e03b72d 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -3168,6 +3168,7 @@ TEST_F(NodeMetaInfo, item_library_entries) "/icon/path", "Basic", "QtQuick", + ModuleKind::QmlLibrary, "An object", "", ElementsAre(IsItemLibraryProperty("x", "double", 1)), diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 45b7e146756..03b5defefcd 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -8344,6 +8344,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) "/path/icon", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Foo is a Item", "/path/templates/item.qml", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), @@ -8356,6 +8357,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) "/path/icon2", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Bar is a Item", "", UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), @@ -8366,6 +8368,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) "/path/icon3", "Advanced Items", "QtQuick", + ModuleKind::QmlLibrary, "Item is an Object", "", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), @@ -8407,6 +8410,7 @@ TEST_F(ProjectStorage, synchronize_updates_item_library_entries) "/path/icon", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Foo is a Item", "", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), @@ -8486,6 +8490,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) "/path/icon", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Foo is a Item", "/path/templates/item.qml", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), @@ -8498,6 +8503,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) "/path/icon2", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Bar is a Item", "", UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), @@ -8508,6 +8514,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) "/path/icon3", "Advanced Items", "QtQuick", + ModuleKind::QmlLibrary, "Item is an Object", "", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), @@ -8534,6 +8541,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries_handles_no_entries) "/path/icon3", "Advanced Items", "QtQuick", + ModuleKind::QmlLibrary, "Item is an Object", "", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), @@ -8561,6 +8569,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) "/path/icon", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Foo is a Item", "/path/templates/item.qml", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), @@ -8573,6 +8582,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) "/path/icon2", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Bar is a Item", "", UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), @@ -8626,6 +8636,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) "/path/icon", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Foo is a Item", "/path/templates/item.qml", UnorderedElementsAre(IsItemLibraryProperty("x", "double", 32.1), @@ -8638,12 +8649,44 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) "/path/icon2", "Basic Items", "QtQuick", + ModuleKind::QmlLibrary, "Bar is a Item", "", UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), IsEmpty()))); } +TEST_F(ProjectStorage, get_local_file_item_library_entries_by_source_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.types[1].exportedTypes.emplace_back(pathToModuleId, "Object"); + storage.synchronize(package); + + auto entries = storage.itemLibraryEntries(sourceId2); + + ASSERT_THAT(entries, + UnorderedElementsAre(IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", + "Object", + "My Components", + "/path/to", + ModuleKind::PathLibrary))); +} + +TEST_F(ProjectStorage, + dont_get_local_file_item_library_entries_by_source_id_if_type_name_is_not_capital) +{ + auto package{createSimpleSynchronizationPackage()}; + package.imports.emplace_back(pathToModuleId, Storage::Version{}, sourceId2); + package.types[1].exportedTypes.emplace_back(pathToModuleId, "object"); + storage.synchronize(package); + + auto entries = storage.itemLibraryEntries(sourceId2); + + ASSERT_THAT(entries, IsEmpty()); +} + TEST_F(ProjectStorage, get_no_item_library_entries_by_source_id_for_no_entries) { auto package{createSimpleSynchronizationPackage()}; From c3b159db191c4a1bc22a9ec90349cbdee8a9626a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 24 Oct 2024 14:45:00 +0300 Subject: [PATCH 041/322] EffectComposer: Make code editor visibility exclusive * Only one code editor is opened in a moment * The code editor is closed by detaching the model * A tooltip is added for code editor buttons Fixes: QDS-13835 Fixes: QDS-13836 Fixes: QDS-13913 Change-Id: I9dd7c988246383347e931fd271401293c26edc56 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../EffectComposer.qml | 6 -- .../EffectComposerTopBar.qml | 24 +++++- .../EffectCompositionNode.qml | 18 +++-- .../effectcomposer/compositionnode.cpp | 23 +++++- src/plugins/effectcomposer/compositionnode.h | 4 +- .../effectcomposer/effectcomposermodel.cpp | 78 ++++++++++++++++++- .../effectcomposer/effectcomposermodel.h | 11 ++- .../effectcomposer/effectcomposerview.cpp | 2 + .../effectshaderscodeeditor.cpp | 19 +++++ .../effectcomposer/effectshaderscodeeditor.h | 5 ++ 10 files changed, 165 insertions(+), 25 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 81c01d5f54e..16525678be8 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -195,10 +195,6 @@ Item { onAssignToSelectedClicked: { root.backendModel.assignToSelected() } - - onOpenShadersCodeEditor: { - root.backendModel.openMainShadersCodeEditor() - } } SplitView { @@ -370,8 +366,6 @@ Item { expanded = wasExpanded dragAnimation.enabled = true } - - onOpenShadersCodeEditor: (idx) => root.backendModel.openShadersCodeEditor(idx) } } // Repeater } // Column diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index c6e8abe11a3..a9e0d74587e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -19,7 +19,6 @@ Rectangle { signal saveClicked signal saveAsClicked signal assignToSelectedClicked - signal openShadersCodeEditor Row { spacing: 5 @@ -57,14 +56,31 @@ Rectangle { } HelperWidgets.AbstractButton { - style: StudioTheme.Values.viewBarButtonStyle + id: openCodeEditorButton + + property bool codeEditorOpen: root.backendModel + && (root.backendModel.codeEditorIndex + === root.backendModel.mainCodeEditorIndex) + property color buttonIconColor: openCodeEditorButton.codeEditorOpen + ? StudioTheme.Values.themeInteraction + : StudioTheme.Values.themeTextColor + style: StudioTheme.ViewBarButtonStyle { + icon: StudioTheme.ControlStyle.IconColors { + idle: openCodeEditorButton.buttonIconColor + hover: openCodeEditorButton.buttonIconColor + interaction: openCodeEditorButton.buttonIconColor + disabled: StudioTheme.Values.themeToolbarIcon_blocked + } + } + buttonIcon: StudioTheme.Constants.codeEditor_medium - tooltip: qsTr("Open Code") + tooltip: qsTr("Open code editor") enabled: root.backendModel ? root.backendModel.isEnabled && root.backendModel.currentComposition !== "" : false - onClicked: root.openShadersCodeEditor() + onClicked: root.backendModel.openMainCodeEditor() + } HelperWidgets.AbstractButton { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index ef59468ed60..fe19390a0f1 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -11,6 +11,9 @@ import EffectComposerBackend HelperWidgets.Section { id: root + readonly property var backendModel: EffectComposerBackend.effectComposerModel + readonly property bool codeEditorOpen: root.backendModel.codeEditorIndex === root.modelIndex + property int modelIndex: 0 caption: nodeName @@ -23,28 +26,31 @@ HelperWidgets.Section { visible: repeater.count > 0 || !isDependency onCloseButtonClicked: { - EffectComposerBackend.effectComposerModel.removeNode(root.modelIndex) + root.backendModel.removeNode(root.modelIndex) } showEyeButton: !isDependency eyeEnabled: nodeEnabled eyeButtonToolTip: qsTr("Enable/Disable Node") - signal openShadersCodeEditor(index: int) - onEyeButtonClicked: { nodeEnabled = root.eyeEnabled } icons: HelperWidgets.IconButton { + id: codeButton + icon: StudioTheme.Constants.codeEditor_medium transparentBg: true buttonSize: 21 iconSize: StudioTheme.Values.smallIconFontSize - iconColor: StudioTheme.Values.themeTextColor - iconScale: containsMouse ? 1.2 : 1 + iconColor: root.codeEditorOpen + ? StudioTheme.Values.themeInteraction + : StudioTheme.Values.themeTextColor + iconScale: codeButton.containsMouse ? 1.2 : 1 implicitWidth: width - onClicked: root.openShadersCodeEditor(index) + tooltip: qsTr("Open code editor") + onClicked: root.backendModel.openCodeEditor(index) } content: Label { diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 5426d55f2cb..86455fcc200 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -45,7 +45,10 @@ CompositionNode::CompositionNode(const QString &effectName, const QString &qenPa } } -CompositionNode::~CompositionNode() = default; +CompositionNode::~CompositionNode() +{ + closeCodeEditor(); +}; QString CompositionNode::fragmentCode() const { @@ -179,6 +182,12 @@ void CompositionNode::ensureShadersCodeEditor() &EffectShadersCodeEditor::rebakeRequested, this, &CompositionNode::rebakeRequested); + + connect( + m_shadersCodeEditor.get(), + &EffectShadersCodeEditor::openedChanged, + this, + &CompositionNode::codeEditorVisibilityChanged); } void CompositionNode::requestRebakeIfLiveUpdateMode() @@ -244,12 +253,22 @@ void CompositionNode::setVertexCode(const QString &vertexCode) requestRebakeIfLiveUpdateMode(); } -void CompositionNode::openShadersCodeEditor() +void CompositionNode::openCodeEditor() { ensureShadersCodeEditor(); + + if (m_shadersCodeEditor->isVisible()) + return; + m_shadersCodeEditor->showWidget(); } +void CompositionNode::closeCodeEditor() +{ + if (m_shadersCodeEditor) + m_shadersCodeEditor->close(); +} + QString CompositionNode::name() const { return m_name; diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index dcd66072afa..a57d4cb4eba 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -68,7 +68,8 @@ public: void setFragmentCode(const QString &fragmentCode); void setVertexCode(const QString &vertexCode); - void openShadersCodeEditor(); + void openCodeEditor(); + void closeCodeEditor(); signals: void uniformsModelChanged(); @@ -77,6 +78,7 @@ signals: void rebakeRequested(); void fragmentCodeChanged(); void vertexCodeChanged(); + void codeEditorVisibilityChanged(bool); private: void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 4319c9afd4d..cbc0889b9cd 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -42,6 +42,9 @@ enum class FileType Text }; +static constexpr int INVALID_CODE_EDITOR_INDEX = -1; +static constexpr int MAIN_CODE_EDITOR_INDEX = -2; + static bool writeToFile(const QByteArray &buf, const QString &filename, FileType fileType) { QDir().mkpath(QFileInfo(filename).path()); @@ -59,6 +62,7 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectComposerModel::EffectComposerModel(QObject *parent) : QAbstractListModel{parent} + , m_codeEditorIndex(INVALID_CODE_EDITOR_INDEX) , m_shaderDir(QDir::tempPath() + "/qds_ec_XXXXXX") { m_rebakeTimer.setSingleShot(true); @@ -184,6 +188,10 @@ void EffectComposerModel::removeNode(int idx) m_rebakeTimer.stop(); CompositionNode *node = m_nodes.takeAt(idx); + // Invalidate codeEditorIndex only if the index is the same as current index + if (m_codeEditorIndex == idx) + setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); + const QStringList reqNodes = node->requiredNodes(); for (const QString &reqId : reqNodes) { CompositionNode *depNode = findNodeById(reqId); @@ -221,6 +229,7 @@ void EffectComposerModel::clear(bool clearName) } setHasUnsavedChanges(!m_currentComposition.isEmpty()); + setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); setIsEmpty(true); emit nodesChanged(); @@ -1125,16 +1134,24 @@ void EffectComposerModel::saveComposition(const QString &name) setHasUnsavedChanges(false); } -void EffectComposerModel::openShadersCodeEditor(int idx) +void EffectComposerModel::openCodeEditor(int idx) { - if (m_nodes.size() < idx || idx < 0) + if (idx == MAIN_CODE_EDITOR_INDEX) + return openMainCodeEditor(); + + if (idx < 0 || idx >= m_nodes.size()) return; + if (m_codeEditorIndex == MAIN_CODE_EDITOR_INDEX && m_shadersCodeEditor) + m_shadersCodeEditor->close(); + else if (m_codeEditorIndex != idx && m_codeEditorIndex > -1 && m_codeEditorIndex < m_nodes.size()) + m_nodes.at(m_codeEditorIndex)->closeCodeEditor(); + CompositionNode *node = m_nodes.at(idx); - node->openShadersCodeEditor(); + node->openCodeEditor(); } -void EffectComposerModel::openMainShadersCodeEditor() +void EffectComposerModel::openMainCodeEditor() { if (!m_shadersCodeEditor) { m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr( @@ -1160,8 +1177,33 @@ void EffectComposerModel::openMainShadersCodeEditor() &EffectShadersCodeEditor::rebakeRequested, this, &EffectComposerModel::startRebakeTimer); + + connect( + m_shadersCodeEditor.get(), + &EffectShadersCodeEditor::openedChanged, + this, + [this](bool visible) { + if (visible) { + setCodeEditorIndex(MAIN_CODE_EDITOR_INDEX); + } else { + // Invalidate codeEditorIndex only if the index is the same as the current index + // It means that if the current code editor index belongs to another node, we + // shouldn't declare it closed. + // This condition prevents marking the new code editor closed, in the case that + // the user opens another editor deliberately while the old editor is opened. + if (m_codeEditorIndex == MAIN_CODE_EDITOR_INDEX) + setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); + } + }); } + + int oldIndex = m_codeEditorIndex; + m_shadersCodeEditor->showWidget(); + + // Close the old editor + if (oldIndex > -1 && oldIndex < m_nodes.size()) + m_nodes.at(oldIndex)->closeCodeEditor(); } void EffectComposerModel::openComposition(const QString &path) @@ -2243,6 +2285,20 @@ void EffectComposerModel::connectCompositionNode(CompositionNode *node) connect(node, &CompositionNode::rebakeRequested, this, &EffectComposerModel::startRebakeTimer); connect(node, &CompositionNode::fragmentCodeChanged, this, setUnsaved); connect(node, &CompositionNode::vertexCodeChanged, this, setUnsaved); + connect(node, &CompositionNode::codeEditorVisibilityChanged, this, [this, node](bool visible) { + int idx = m_nodes.indexOf(node); + if (idx < 0) + return; + if (visible) { + setCodeEditorIndex(idx); + } else { + // Invalidate codeEditorIndex only if the index is the same as current index + // It means that if current code editor index belongs to another node, we + // shouldn't declare it closed. + if (m_codeEditorIndex == idx) + setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); + } + }); } void EffectComposerModel::updateExtraMargin() @@ -2283,6 +2339,15 @@ QSet EffectComposerModel::getExposedProperties(const QByteArray &qml return returnSet; } +void EffectComposerModel::setCodeEditorIndex(int index) +{ + if (m_codeEditorIndex == index) + return; + + m_codeEditorIndex = index; + emit codeEditorIndexChanged(m_codeEditorIndex); +} + QString EffectComposerModel::currentComposition() const { return m_currentComposition; @@ -2321,6 +2386,11 @@ void EffectComposerModel::setCurrentPreviewImage(const QUrl &path) emit currentPreviewImageChanged(); } +int EffectComposerModel::mainCodeEditorIndex() const +{ + return MAIN_CODE_EDITOR_INDEX; +} + Utils::FilePath EffectComposerModel::compositionPath() const { return m_compositionPath; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index a7ca05d124b..b60e07f2530 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -49,6 +49,7 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(int codeEditorIndex MEMBER m_codeEditorIndex NOTIFY codeEditorIndexChanged) Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) @@ -56,6 +57,7 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) Q_PROPERTY(QUrl currentPreviewImage READ currentPreviewImage WRITE setCurrentPreviewImage NOTIFY currentPreviewImageChanged) Q_PROPERTY(QUrl customPreviewImage READ customPreviewImage NOTIFY customPreviewImageChanged) + Q_PROPERTY(int mainCodeEditorIndex READ mainCodeEditorIndex CONSTANT) public: EffectComposerModel(QObject *parent = nullptr); @@ -113,8 +115,8 @@ public: Q_INVOKABLE void saveComposition(const QString &name); - Q_INVOKABLE void openShadersCodeEditor(int idx); - Q_INVOKABLE void openMainShadersCodeEditor(); + Q_INVOKABLE void openCodeEditor(int idx); + Q_INVOKABLE void openMainCodeEditor(); void openComposition(const QString &path); @@ -124,6 +126,7 @@ public: QUrl customPreviewImage() const; QUrl currentPreviewImage() const; void setCurrentPreviewImage(const QUrl &path); + int mainCodeEditorIndex() const; Utils::FilePath compositionPath() const; void setCompositionPath(const Utils::FilePath &newCompositionPath); @@ -138,6 +141,7 @@ public: signals: void isEmptyChanged(); void selectedIndexChanged(int idx); + void codeEditorIndexChanged(int idx); void effectErrorChanged(); void shadersUpToDateChanged(); void isEnabledChanged(); @@ -214,9 +218,12 @@ private: void rebakeIfLiveUpdateMode(); QSet getExposedProperties(const QByteArray &qmlContent); + void setCodeEditorIndex(int index); + QList m_nodes; int m_selectedIndex = -1; + int m_codeEditorIndex = -1; bool m_isEmpty = false; // Init to false to force initial bake after setup bool m_hasUnsavedChanges = false; // True when shaders haven't changed since last baking diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index 76ff57d49d5..7d24fc66f8b 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -174,6 +174,8 @@ void EffectComposerView::modelAttached(QmlDesigner::Model *model) void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model) { AbstractView::modelAboutToBeDetached(model); + if (m_widget) + m_widget->effectComposerModel()->clear(true); } void EffectComposerView::selectedNodesChanged(const QList & selectedNodeList, diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 95fe30a54e6..5b0627a22ef 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -64,6 +64,8 @@ EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget * EffectShadersCodeEditor::~EffectShadersCodeEditor() { + if (isOpened()) + close(); m_fragmentEditor->deleteLater(); m_vertexEditor->deleteLater(); } @@ -74,6 +76,7 @@ void EffectShadersCodeEditor::showWidget() show(); raise(); m_vertexEditor->setFocus(); + setOpened(true); } void EffectShadersCodeEditor::showWidget(int x, int y) @@ -129,6 +132,11 @@ void EffectShadersCodeEditor::setLiveUpdate(bool liveUpdate) emit rebakeRequested(); } +bool EffectShadersCodeEditor::isOpened() const +{ + return m_opened; +} + EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() { static EffectCodeEditorFactory f; @@ -168,12 +176,23 @@ void EffectShadersCodeEditor::setupUIComponents() this->resize(660, 240); } +void EffectShadersCodeEditor::setOpened(bool value) +{ + if (m_opened == value) + return; + + m_opened = value; + emit openedChanged(m_opened); +} + void EffectShadersCodeEditor::closeEvent(QCloseEvent *event) { QWidget::closeEvent(event); if (!liveUpdate()) emit rebakeRequested(); + + setOpened(false); } void EffectShadersCodeEditor::writeLiveUpdateSettings() diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index f797315f4a6..a20f5e71fc2 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -33,16 +33,20 @@ public: bool liveUpdate() const; void setLiveUpdate(bool liveUpdate); + bool isOpened() const; + signals: void liveUpdateChanged(bool); void fragmentValueChanged(); void vertexValueChanged(); void rebakeRequested(); + void openedChanged(bool); protected: using QWidget::show; EffectCodeEditorWidget *createJSEditor(); void setupUIComponents(); + void setOpened(bool value); void closeEvent(QCloseEvent *event) override; @@ -56,6 +60,7 @@ private: QPointer m_vertexEditor; bool m_liveUpdate = false; + bool m_opened = false; }; } // namespace EffectComposer From a96349fdbbd284f4e795e540124e33d4d024b558 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 28 Oct 2024 14:04:58 +0200 Subject: [PATCH 042/322] QmlDesigner: Fix combobox popup background size This is a workaround for Window not propagating size changes to anchored children in timely fashion (QTBUG-130354). Fixes: QDS-13905 Change-Id: Ib22f8011e7291471bb803578b5c978a42239fc47 Reviewed-by: Mahmoud Badri --- .../imports/StudioControls/TopLevelComboBox.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 3ee49b56ad8..7b03ac97724 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -122,7 +122,8 @@ T.ComboBox { } Rectangle { - anchors.fill: parent + width: window.width + height: window.height color: control.style.popup.background } } From ae64cd1b0f71aca2d02a406734c55365e367bf78 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 29 Oct 2024 12:18:26 +0200 Subject: [PATCH 043/322] QmlDesigner: Skip automatic PopupDialog positioning if moved manually It's annoying to have the dialog pop back into its original position when its size changes if you have deliberately moved it elsewhere, so we skip the automatic positioning in that case. Fixes: QDS-13908 Change-Id: I30efe4ae550bf0bbfc8f8f5a9d74428cd9d918eb Reviewed-by: Mahmoud Badri --- .../imports/StudioControls/PopupDialog.qml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index 4ac288cd997..534e7270dfb 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -31,12 +31,14 @@ QtObject { //property alias chevronVisible: chevron.visible property rect __itemGlobal: Qt.rect(0, 0, 100, 100) + property bool __movedManually: false property bool keepOpen: false signal closing(close: var) function showGlobal() { + root.__movedManually = false var pos = WindowManager.globalCursorPosition() root.__itemGlobal = Qt.rect(pos.x, pos.y, 300, 20) //root.chevronVisible = false @@ -46,6 +48,7 @@ QtObject { } function show(target: Item): void { + root.__movedManually = false var originGlobal = target.mapToGlobal(0, 0) root.__itemGlobal = Qt.rect(originGlobal.x, originGlobal.y, target.width, target.height) //root.chevronVisible = true @@ -59,6 +62,9 @@ QtObject { } function layout() { + if (root.__movedManually) + return + let position = Qt.point(root.__itemGlobal.x, root.__itemGlobal.y) var screen = WindowManager.getScreenGeometry(position) @@ -423,8 +429,10 @@ QtObject { target: null grabPermissions: PointerHandler.CanTakeOverFromAnything onActiveChanged: { - if (dragHandler.active) + if (dragHandler.active) { + root.__movedManually = true window.startSystemMove() // QTBUG-102488 + } } } From 2131e92f3db23478466d00149168333e9ef4c92d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 29 Oct 2024 14:51:08 +0200 Subject: [PATCH 044/322] QmlDesigner: Fix target display on combobox model change Since backend.signal.id.currentIndex is wrapped into currentTypeIndex property on qml side, changed signals emitted for it do not trigger combobox currentIndex change unless backend.signal.id.currentIndex value actually changes. Model change resets combobox current index to 0, so we need to force target.currentIndex change in that case. Fixes: QDS-13904 Change-Id: I42dbfab5337d5a56d18c3c273169eb2baa54c0ec Reviewed-by: Mahmoud Badri --- .../qmldesigner/connectionseditor/ConnectionsDialog.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml index 299234127ad..81f26f6a090 100644 --- a/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml +++ b/share/qtcreator/qmldesigner/connectionseditor/ConnectionsDialog.qml @@ -39,6 +39,10 @@ StudioControls.PopupDialog { onActivated: backend.signal.id.activateIndex(target.currentIndex) property int currentTypeIndex: backend.signal.id.currentIndex ?? 0 onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex + onModelChanged: { + if (target.model) + target.currentIndex = target.currentTypeIndex + } } } From f13a1c682cd3cbf7a004f32b303b48444fac29d5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 29 Oct 2024 12:57:32 +0100 Subject: [PATCH 045/322] QmlDesigner: Ignore component wit non existing files Instead of throwing an exception we now silently ignoring non existing components. But that has the disadvantage that we maybe later miss the component if the the file is added later because the qmldir was not changed. If we want to handle that case too we have have to save more meta data. Task-number: QDS-13954 Change-Id: Ia17207dadfd2c130d19d3316c2643a5e357c7074 Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.cpp | 22 ++++++++++++++----- .../projectstorageupdater-test.cpp | 20 +++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index c366f7c1a9f..7a5ef587a09 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -84,7 +84,7 @@ QList joinImports(const QList &first } ProjectStorageUpdater::Components createComponents( - const QMultiHash &qmlDirParserComponents, + const QMultiHash &qmlDirParserComponentsDict, ModuleId moduleId, ModuleId pathModuleId, FileSystemInterface &fileSystem, @@ -93,8 +93,10 @@ ProjectStorageUpdater::Components createComponents( ProjectStorageUpdater::Components components; auto qmlFileNames = fileSystem.qmlFileNames(QString{directory}); + std::ranges::sort(qmlFileNames); - components.reserve(static_cast(qmlDirParserComponents.size() + qmlFileNames.size())); + components.reserve( + static_cast(qmlDirParserComponentsDict.size() + qmlFileNames.size())); for (const QString &qmlFileName : qmlFileNames) { Utils::PathString fileName{qmlFileName}; @@ -103,15 +105,25 @@ ProjectStorageUpdater::Components createComponents( ProjectStorageUpdater::Component{fileName, typeName, pathModuleId, -1, -1}); } - for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) { + auto qmlDirParserComponents = qmlDirParserComponentsDict.values(); + std::ranges::sort(qmlDirParserComponents, {}, &QmlDirParser::Component::fileName); + + auto addComponent = [&](const QmlDirParser::Component &qmlDirParserComponent) { if (qmlDirParserComponent.fileName.contains('/')) - continue; + return; components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName, qmlDirParserComponent.typeName, moduleId, qmlDirParserComponent.majorVersion, qmlDirParserComponent.minorVersion}); - } + }; + + Utils::set_greedy_intersection(qmlDirParserComponents, + qmlFileNames, + addComponent, + {}, + &QmlDirParser::Component::fileName, + {}); return components; } diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 3a51b997eba..1f4b115dc58 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -747,14 +747,26 @@ TEST_F(ProjectStorageUpdater, parse_qml_documents) updater.update({.directories = directories}); } -TEST_F(ProjectStorageUpdater, parse_qml_documents_with_non_existing_qml_document_throws) +TEST_F(ProjectStorageUpdater, non_existing_qml_documents_are_ignored) { QString qmldir{R"(module Example - NonexitingType 1.0 NonexitingType.qml)"}; + NonexitingType 1.0 NonexitingType.qml + FirstType 1.0 First.qml + FirstTypeV2 2.2 First2.qml + SecondType 2.2 Second.qml)"}; + QString qmlDocument1{"First{}"}; + QString qmlDocument2{"Second{}"}; + QString qmlDocument3{"Third{}"}; setContent(u"/path/qmldir", qmldir); + setContent(u"/path/First.qml", qmlDocument1); + setContent(u"/path/First2.qml", qmlDocument2); + setContent(u"/path/Second.qml", qmlDocument3); - ASSERT_THROW(updater.update({.directories = directories}), - QmlDesigner::CannotParseQmlDocumentFile); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _, _)); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _)); + EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _, _)); + + updater.update({.directories = directories}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents) From 811c8db630f66c073cac20c2304955ed342fd6b9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 29 Oct 2024 17:14:29 +0100 Subject: [PATCH 046/322] QmlDesigner: Add dummy types if qml document don't exists If there is an entry in the qmldir but there is no document, a dummy type is created. That can be later updated if the document is provided. Otherwise no type is added if the qml document is added after the qmldir entry was added. Task-number: QDS-13954 Change-Id: Iecde333cc5c99fff22cdaa6ac9783c1d1dba8581 Reviewed-by: Thomas Hartmann --- .../projectstorage/projectstorageupdater.cpp | 26 +++----- .../projectstorageupdater-test.cpp | 59 +++++++++++++++++-- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 7a5ef587a09..1a1083641a2 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -84,7 +84,7 @@ QList joinImports(const QList &first } ProjectStorageUpdater::Components createComponents( - const QMultiHash &qmlDirParserComponentsDict, + const QMultiHash &qmlDirParserComponents, ModuleId moduleId, ModuleId pathModuleId, FileSystemInterface &fileSystem, @@ -93,10 +93,8 @@ ProjectStorageUpdater::Components createComponents( ProjectStorageUpdater::Components components; auto qmlFileNames = fileSystem.qmlFileNames(QString{directory}); - std::ranges::sort(qmlFileNames); - components.reserve( - static_cast(qmlDirParserComponentsDict.size() + qmlFileNames.size())); + components.reserve(static_cast(qmlDirParserComponents.size() + qmlFileNames.size())); for (const QString &qmlFileName : qmlFileNames) { Utils::PathString fileName{qmlFileName}; @@ -105,25 +103,15 @@ ProjectStorageUpdater::Components createComponents( ProjectStorageUpdater::Component{fileName, typeName, pathModuleId, -1, -1}); } - auto qmlDirParserComponents = qmlDirParserComponentsDict.values(); - std::ranges::sort(qmlDirParserComponents, {}, &QmlDirParser::Component::fileName); - - auto addComponent = [&](const QmlDirParser::Component &qmlDirParserComponent) { + for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) { if (qmlDirParserComponent.fileName.contains('/')) - return; + continue; components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName, qmlDirParserComponent.typeName, moduleId, qmlDirParserComponent.majorVersion, qmlDirParserComponent.minorVersion}); - }; - - Utils::set_greedy_intersection(qmlDirParserComponents, - qmlFileNames, - addComponent, - {}, - &QmlDirParser::Component::fileName, - {}); + } return components; } @@ -1107,8 +1095,10 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil type.changeLevel = Storage::Synchronization::ChangeLevel::Minimal; break; case FileState::NotExists: - throw CannotParseQmlDocumentFile{}; + tracer.tick("file does not exits", keyValue("source id", sourceId)); + break; case FileState::Changed: + tracer.tick("update from qml document", keyValue("source id", sourceId)); const auto content = m_fileSystem.contentAsQString(QString{qmlFilePath}); type = m_qmlDocumentParser.parse(content, package.imports, sourceId, directoryPath); break; diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 1f4b115dc58..fae632adde0 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -2191,13 +2191,64 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) updater.update({.directories = directories}); } -TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_qml_document_does_not_exists) +TEST_F(ProjectStorageUpdater, + synchronize_qml_documents_without_parsed_type_if_qml_document_does_not_exists) { - setFilesDontExists({qmlDirPathSourceId, qmlDocumentSourceId1}); + setFilesDontExists({qmlDocumentSourceId1}); setFilesAdded({directoryPathSourceId}); + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml)"}; + setQmlFileNames(u"/path", {"First2.qml"}); + setContent(u"/path/qmldir", qmldir); - ASSERT_THROW(updater.update({.directories = directories}), - QmlDesigner::CannotParseQmlDocumentFile); + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import2)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::None, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(exampleModuleId, "FirstType", 1, 0)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre( + IsExportedType(pathModuleId, "First2", -1, -1), + IsExportedType(exampleModuleId, "FirstType", 2, 2)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(directoryPathSourceId, + qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), + IsFileStatus(qmlDirPathSourceId, 1, 21), + IsFileStatus(qmlDocumentSourceId2, 1, 21))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(directoryPathSourceId)), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); + + updater.update({.directories = directories}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) From b17525f839b71e206907110ec97355b5d9c6e0af Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 28 Oct 2024 17:14:58 +0100 Subject: [PATCH 047/322] QmlDesigner: Add component source path to item library entry After moving to the new code model we should simply use the source id. That will remove the dependency to the cache in the conversion functions. Change-Id: I8bd4a2d697279b36cfd48d2dc5393d73978bb85b Reviewed-by: Thomas Hartmann --- .../components/edit3d/edit3dview.cpp | 2 +- .../components/edit3d/edit3dwidget.cpp | 2 +- .../designercore/include/itemlibraryentry.h | 5 +++-- .../metainfo/itemlibraryentry.cpp | 14 +++++++++--- .../libs/designercore/model/model.cpp | 2 +- .../projectstorage/projectstorage.cpp | 22 +++++++++++-------- .../projectstorage/projectstorageinfotypes.h | 1 + .../tests/matchers/projectstorage-matcher.h | 6 +++-- .../projectstorage/projectstorage-test.cpp | 3 ++- 9 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 24cbc7670dc..af3fe34cfc7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -350,7 +350,7 @@ void Edit3DView::handleEntriesChanged() auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) { auto entries = metaInfo.itemLibrariesEntries(); if (entries.size()) - entriesMap[key].entryList.append(toItemLibraryEntries(entries)); + entriesMap[key].entryList.append(toItemLibraryEntries(model()->pathCache(), entries)); }; append(model()->qtQuick3DModelMetaInfo(), EK_primitives); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 933e7728354..266c17f34de 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -805,7 +805,7 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { - auto entry = ItemLibraryEntry::create(entries.front()); + auto entry = ItemLibraryEntry::create(m_view->model()->pathCache(), entries.front()); QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); } } diff --git a/src/plugins/qmldesigner/libs/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/libs/designercore/include/itemlibraryentry.h index f3d5ebd437f..d11119063f6 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/itemlibraryentry.h +++ b/src/plugins/qmldesigner/libs/designercore/include/itemlibraryentry.h @@ -48,7 +48,8 @@ public: ItemLibraryEntry &operator=(ItemLibraryEntry &&) = default; ~ItemLibraryEntry(); - static ItemLibraryEntry create(const Storage::Info::ItemLibraryEntry &entry); + static ItemLibraryEntry create(const PathCacheType &pathCache, + const Storage::Info::ItemLibraryEntry &entry); QString name() const; TypeName typeName() const; @@ -90,7 +91,7 @@ private: using ItemLibraryEntries = QList; QMLDESIGNERCORE_EXPORT QList toItemLibraryEntries( - const Storage::Info::ItemLibraryEntries &entries); + const PathCacheType &pathCache, const Storage::Info::ItemLibraryEntries &entries); } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp index d8b05aac6b7..6c4b626a508 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/itemlibraryentry.cpp @@ -7,12 +7,15 @@ #include #include +#include #include #include #include +#include + namespace QmlDesigner { namespace Internal { @@ -64,7 +67,8 @@ ItemLibraryEntry::ItemLibraryEntry() : m_data(std::make_shared()) {} -ItemLibraryEntry ItemLibraryEntry::create(const Storage::Info::ItemLibraryEntry &entry) +ItemLibraryEntry ItemLibraryEntry::create(const PathCacheType &pathCache, + const Storage::Info::ItemLibraryEntry &entry) { ItemLibraryEntry itemLibraryEntry; auto &m_data = itemLibraryEntry.m_data; @@ -72,6 +76,8 @@ ItemLibraryEntry ItemLibraryEntry::create(const Storage::Info::ItemLibraryEntry m_data->typeId = entry.typeId; m_data->typeName = entry.typeName.toQByteArray(); m_data->category = entry.category.toQString(); + if (entry.componentSourceId) + m_data->customComponentSource = pathCache.sourcePath(entry.componentSourceId).toQString(); if (entry.iconPath.size()) m_data->libraryEntryIconPath = entry.iconPath.toQString(); if (entry.moduleKind == Storage::ModuleKind::QmlLibrary) @@ -305,9 +311,11 @@ QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) return debug.space(); } -QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries) +QList toItemLibraryEntries(const PathCacheType &pathCache, + const Storage::Info::ItemLibraryEntries &entries) { - return Utils::transform>(entries, &ItemLibraryEntry::create); + auto create = std::bind_front(&ItemLibraryEntry::create, std::ref(pathCache)); + return Utils::transform>(entries, create); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index 4f4dfeed7fd..4742c1a8a81 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -2757,7 +2757,7 @@ QList Model::itemLibraryEntries() const { #ifdef QDS_USE_PROJECTSTORAGE using namespace Storage::Info; - return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId)); + return toItemLibraryEntries(*d->pathCache, d->projectStorage->itemLibraryEntries(d->m_sourceId)); #else return d->metaInfo().itemLibraryInfo()->entries(); #endif diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 06e7be20185..c5074932186 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -864,11 +864,12 @@ struct ProjectStorage::Statements " USING(moduleId) " " WHERE di.sourceId=?)", database}; - mutable Sqlite::ReadStatement<3, 2> selectLocalFileItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, etn.name, m.name " + mutable Sqlite::ReadStatement<4, 2> selectLocalFileItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, etn.name, m.name, t.sourceId " "FROM documentImports AS di " " JOIN exportedTypeNames AS etn USING(moduleId) " " JOIN modules AS m USING(moduleId) " + " JOIN types AS t USING(typeId)" "WHERE di.sourceId=?1 AND m.kind = ?2", database}; mutable Sqlite::ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ @@ -1883,14 +1884,17 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(typeAnnotationCallback, sourceId); - auto fileComponentCallback = - [&](TypeId typeId, Utils::SmallStringView typeName, Utils::SmallStringView import) { - if (!isCapitalLetter(typeName.front())) - return; + auto fileComponentCallback = [&](TypeId typeId, + Utils::SmallStringView typeName, + Utils::SmallStringView import, + SourceId componentSourceId) { + if (!isCapitalLetter(typeName.front())) + return; - auto &last = entries.emplace_back(typeId, typeName, typeName, "My Components", import); - last.moduleKind = Storage::ModuleKind::PathLibrary; - }; + auto &last = entries.emplace_back(typeId, typeName, typeName, "My Components", import); + last.moduleKind = Storage::ModuleKind::PathLibrary; + last.componentSourceId = componentSourceId; + }; s->selectLocalFileItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction( fileComponentCallback, sourceId, Storage::ModuleKind::PathLibrary); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h index c0fa895713a..e82e385d78c 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinfotypes.h @@ -455,6 +455,7 @@ struct ItemLibraryEntry Utils::PathString templatePath; ItemLibraryProperties properties; std::vector extraFilePaths; + SourceId componentSourceId; }; using ItemLibraryEntries = QVarLengthArray; diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h index 65bdd3d930a..3116530fceb 100644 --- a/tests/unit/tests/matchers/projectstorage-matcher.h +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -50,7 +50,8 @@ inline auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, Utils::SmallStringView name, Utils::SmallStringView category, Utils::SmallStringView import, - QmlDesigner::Storage::ModuleKind moduleKind) + QmlDesigner::Storage::ModuleKind moduleKind, + QmlDesigner::SourceId componentSourceId) { using QmlDesigner::Storage::Info::ItemLibraryEntry; return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId), @@ -58,7 +59,8 @@ inline auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, Field("name", &ItemLibraryEntry::name, name), Field("category", &ItemLibraryEntry::category, category), Field("import", &ItemLibraryEntry::import, import), - Field("moduleKind", &ItemLibraryEntry::moduleKind, moduleKind)); + Field("moduleKind", &ItemLibraryEntry::moduleKind, moduleKind), + Field("componentSourceId", &ItemLibraryEntry::componentSourceId, componentSourceId)); } MATCHER_P3(IsItemLibraryProperty, diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 03b5defefcd..49e9b996ebc 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -8671,7 +8671,8 @@ TEST_F(ProjectStorage, get_local_file_item_library_entries_by_source_id) "Object", "My Components", "/path/to", - ModuleKind::PathLibrary))); + ModuleKind::PathLibrary, + sourceId2))); } TEST_F(ProjectStorage, From f7556ab4b35c455b6a6b6a4193155437f22bccb5 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 30 Oct 2024 11:13:57 +0100 Subject: [PATCH 048/322] DeviceSharing: Return immediate response when device is not online MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0700502346eba6027bc79ed09da94989fc47e07b Reviewed-by: Henning Gründl --- .../components/devicesharing/device.cpp | 28 +++++++++++-------- .../components/devicesharing/device.h | 12 ++++---- .../devicesharing/devicemanager.cpp | 14 +++++----- .../components/devicesharing/devicemanager.h | 4 +-- .../devicesharing/devicemanagermodel.h | 2 +- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/device.cpp b/src/plugins/qmldesigner/components/devicesharing/device.cpp index 7c3ebe37ddc..3452ba62a57 100644 --- a/src/plugins/qmldesigner/components/devicesharing/device.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/device.cpp @@ -123,23 +123,24 @@ void Device::setDeviceSettings(const DeviceSettings &deviceSettings) reconnect(); } -void Device::sendDesignStudioReady(const QString &uuid) { - sendTextMessage(PackageToDevice::designStudioReady, uuid); +bool Device::sendDesignStudioReady(const QString &uuid) +{ + return sendTextMessage(PackageToDevice::designStudioReady, uuid); } -void Device::sendProjectNotification() +bool Device::sendProjectNotification() { - sendTextMessage(PackageToDevice::projectData); + return sendTextMessage(PackageToDevice::projectData); } -void Device::sendProjectData(const QByteArray &data) +bool Device::sendProjectData(const QByteArray &data) { - sendBinaryMessage(data); + return sendBinaryMessage(data); } -void Device::sendProjectStopped() +bool Device::sendProjectStopped() { - sendTextMessage(PackageToDevice::stopRunningProject); + return sendTextMessage(PackageToDevice::stopRunningProject); } bool Device::isConnected() const @@ -147,10 +148,10 @@ bool Device::isConnected() const return m_socket ? m_socket->state() == QAbstractSocket::ConnectedState : false; } -void Device::sendTextMessage(const QLatin1String &dataType, const QJsonValue &data) +bool Device::sendTextMessage(const QLatin1String &dataType, const QJsonValue &data) { if (!isConnected()) - return; + return false; QJsonObject message; message["dataType"] = dataType; @@ -158,14 +159,17 @@ void Device::sendTextMessage(const QLatin1String &dataType, const QJsonValue &da const QString jsonMessage = QString::fromLatin1( QJsonDocument(message).toJson(QJsonDocument::Compact)); m_socket->sendTextMessage(jsonMessage); + + return true; } -void Device::sendBinaryMessage(const QByteArray &data) +bool Device::sendBinaryMessage(const QByteArray &data) { if (!isConnected()) - return; + return false; m_socket->sendBinaryMessage(data); + return true; } void Device::processTextMessage(const QString &data) diff --git a/src/plugins/qmldesigner/components/devicesharing/device.h b/src/plugins/qmldesigner/components/devicesharing/device.h index 1d9042893de..08f76d04dc8 100644 --- a/src/plugins/qmldesigner/components/devicesharing/device.h +++ b/src/plugins/qmldesigner/components/devicesharing/device.h @@ -26,10 +26,10 @@ public: void setDeviceSettings(const DeviceSettings &deviceSettings); // device communication - void sendDesignStudioReady(const QString &uuid); - void sendProjectNotification(); - void sendProjectData(const QByteArray &data); - void sendProjectStopped(); + bool sendDesignStudioReady(const QString &uuid); + bool sendProjectNotification(); + bool sendProjectData(const QByteArray &data); + bool sendProjectStopped(); // socket bool isConnected() const; @@ -50,8 +50,8 @@ private: QTimer m_pongTimer; void initPingPong(); - void sendTextMessage(const QLatin1String &dataType, const QJsonValue &data = QJsonValue()); - void sendBinaryMessage(const QByteArray &data); + bool sendTextMessage(const QLatin1String &dataType, const QJsonValue &data = QJsonValue()); + bool sendBinaryMessage(const QByteArray &data); signals: void connected(const QString &deviceId); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 98b82036077..0032e3550c5 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -290,35 +290,35 @@ void DeviceManager::removeDeviceAt(int index) emit deviceRemoved(deviceInfo); } -void DeviceManager::sendProjectFile(const QString &deviceId, const QString &projectFile) +bool DeviceManager::sendProjectFile(const QString &deviceId, const QString &projectFile) { auto device = findDevice(deviceId); if (!device) - return; + return false; device->sendProjectNotification(); QFile file(projectFile); if (!file.open(QIODevice::ReadOnly)) { qCWarning(deviceSharePluginLog) << "Failed to open project file" << projectFile; - return; + return false; } qCDebug(deviceSharePluginLog) << "Sending project file to device" << deviceId; QByteArray projectData = file.readAll(); - device->sendProjectData(projectData); + return device->sendProjectData(projectData); qCDebug(deviceSharePluginLog) << "Project file sent to device" << deviceId; } -void DeviceManager::stopRunningProject(const QString &deviceId) +bool DeviceManager::stopRunningProject(const QString &deviceId) { auto device = findDevice(deviceId); if (!device) - return; + return false; - device->sendProjectStopped(); + return device->sendProjectStopped(); } } // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 5fec51ca194..a70d1e49ed1 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -30,8 +30,8 @@ public: void removeDevice(const QString &deviceId); void removeDeviceAt(int index); - void sendProjectFile(const QString &deviceId, const QString &projectFile); - void stopRunningProject(const QString &deviceId); + bool sendProjectFile(const QString &deviceId, const QString &projectFile); + bool stopRunningProject(const QString &deviceId); private: // Devices management diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h index c3c676d02bd..88f05f1215d 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h @@ -18,8 +18,8 @@ public: Q_ENUM(DeviceStatus) enum DeviceColumns { - Active, Status, + Active, Alias, IPv4Addr, OS, From 09554c65ac0955d3f191df579b8c207e456ef9ef Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 30 Oct 2024 12:05:21 +0100 Subject: [PATCH 049/322] DeviceSharing: Signals for device activation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I6b9a37eb424fac707d7fd2a87bdab8fe94ed7671 Reviewed-by: Henning Gründl --- .../qmldesigner/components/devicesharing/devicemanager.cpp | 5 +++++ .../qmldesigner/components/devicesharing/devicemanager.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 0032e3550c5..ff80f14c8e0 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -178,6 +178,11 @@ void DeviceManager::setDeviceActive(const QString &deviceId, const bool active) deviceSettings.setActive(active); device->setDeviceSettings(deviceSettings); writeSettings(); + + if (active) + emit deviceActivated(device->deviceInfo()); + else + emit deviceDeactivated(device->deviceInfo()); } void DeviceManager::setDeviceIP(const QString &deviceId, const QString &ip) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index a70d1e49ed1..066b766b2f9 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -63,6 +63,8 @@ signals: void deviceRemoved(const DeviceInfo &deviceInfo); void deviceOnline(const DeviceInfo &deviceInfo); void deviceOffline(const DeviceInfo &deviceInfo); + void deviceActivated(const DeviceInfo &deviceInfo); + void deviceDeactivated(const DeviceInfo &deviceInfo); void projectStarted(const DeviceInfo &deviceInfo); void projectStopped(const DeviceInfo &deviceInfo); void projectLogsReceived(const DeviceInfo &deviceInfo, const QString &logs); From f0598daec64b137fbcf7e851bec914fbd10a435d Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Mon, 28 Oct 2024 15:31:31 +0100 Subject: [PATCH 050/322] QmlProjectItem: don't write undefined properties Write enableCMakeGeneration and enablePythonGeneration properties to the qmlproject file, only if they are defined in the internal json object. Task-number: QDS-13936 Change-Id: I0e0b8597ff56d7bf528ecc4d619b81a073c2da6b Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 11 +++++++++-- .../data/converter/test-set-1/testfile.jsontoqml | 1 - .../data/converter/test-set-2/testfile.jsontoqml | 1 - .../data/converter/test-set-3/testfile.qmlproject | 1 + .../data/converter/test-set-3/testfile.qmltojson | 1 + .../data/converter/test-set-mcu-1/testfile.jsontoqml | 2 -- .../data/converter/test-set-mcu-2/testfile.jsontoqml | 2 -- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 92afbe27e45..9ec1c957135 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -60,6 +60,13 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendItem(key, QString::fromStdString(val ? "true" : "false"), false); }; + auto appendBoolOpt = [&appendBool](const QString &key, const QJsonObject &source) { + if (!source.keys().contains(key)) { + return; + } + appendBool(key, source[key].toBool()); + }; + auto appendStringArray = [&appendItem](const QString &key, const QStringList &vals) { if (vals.isEmpty()) return; @@ -143,8 +150,8 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("mainFile", runConfig["mainFile"].toString()); appendString("mainUiFile", runConfig["mainUiFile"].toString()); appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); - appendBool("enableCMakeGeneration", deploymentConfig["enableCMakeGeneration"].toBool()); - appendBool("enablePythonGeneration", deploymentConfig["enablePythonGeneration"].toBool()); + appendBoolOpt("enableCMakeGeneration", deploymentConfig); + appendBoolOpt("enablePythonGeneration", deploymentConfig); appendBool("widgetApp", runConfig["widgetApp"].toBool()); appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); appendStringArray("mockImports", rootObject["mockImports"].toVariant().toStringList()); diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml index ec698ec1d6a..90c980cda1b 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml @@ -8,7 +8,6 @@ Project { mainUiFile: "content/Screen01.ui.qml" targetDirectory: "/opt/UntitledProject13" enableCMakeGeneration: false - enablePythonGeneration: false widgetApp: true importPaths: [ "imports","asset_imports" ] 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 fbdc7d78980..16617e015dc 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 @@ -7,7 +7,6 @@ Project { mainFile: "fileSelectors.qml" targetDirectory: "/opt/fileSelectors" enableCMakeGeneration: false - enablePythonGeneration: false widgetApp: false importPaths: [ "imports" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject index faf115d609c..765bdf810ac 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject @@ -91,6 +91,7 @@ Project { QDS.targetDirectory: "/opt/UntitledProject13" QDS.enableCMakeGeneration: false + QDS.enablePythonGeneration: false QDS.qdsVersion: "4.0" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson index d6e453fbfb1..8ec06dde1c2 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -1,6 +1,7 @@ { "deployment": { "enableCMakeGeneration": false, + "enablePythonGeneration": false, "targetDirectory": "/opt/UntitledProject13" }, "environment": { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml index be7fde31218..9dff575fd1f 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml @@ -6,8 +6,6 @@ import QmlProject Project { mainFile: "Main.qml" targetDirectory: "/opt/UntitledProject13" - enableCMakeGeneration: false - enablePythonGeneration: false widgetApp: true importPaths: [ "imports","asset_imports","mcu-modules" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml index 1e6c1deb206..2e73146cdaf 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml @@ -4,8 +4,6 @@ import QmlProject Project { - enableCMakeGeneration: false - enablePythonGeneration: false widgetApp: false qt6Project: false From 5960986d5dc710336523197320181b0bffc1ed99 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 30 Oct 2024 17:54:26 +0100 Subject: [PATCH 051/322] DeviceSharing: Fix restarting connection in setting change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I627b6d92b3f268afe526aa02c99334e6dcdda0bd Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/components/devicesharing/device.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/device.cpp b/src/plugins/qmldesigner/components/devicesharing/device.cpp index 3452ba62a57..d0303128e49 100644 --- a/src/plugins/qmldesigner/components/devicesharing/device.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/device.cpp @@ -119,8 +119,10 @@ DeviceSettings Device::deviceSettings() const void Device::setDeviceSettings(const DeviceSettings &deviceSettings) { + QString oldIp = m_deviceSettings.ipAddress(); m_deviceSettings = deviceSettings; - reconnect(); + if (oldIp != m_deviceSettings.ipAddress()) + reconnect(); } bool Device::sendDesignStudioReady(const QString &uuid) From 1cf0f14fa712bae29254e23e20a499c9e7f92398 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 30 Oct 2024 07:53:50 +0100 Subject: [PATCH 052/322] Sqlite: Update to 3.47.0 Change-Id: I67db2661968c819e4722483a7ffc6b56b1a1a60f Reviewed-by: Thomas Hartmann --- src/libs/3rdparty/sqlite/sqlite3.c | 8303 ++++++++++++++++++---------- src/libs/3rdparty/sqlite/sqlite3.h | 193 +- 2 files changed, 5702 insertions(+), 2794 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index 946815f13ec..2886d04ae52 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.c +++ b/src/libs/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.46.1. By combining all the individual C code files into this +** version 3.47.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** c9c2ab54ba1f5f46360f1b4f35d849cd3f08. +** 03a9703e27c44437c39363d0baf82db4ebc9. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -256,10 +256,13 @@ /* ** Macro to disable warnings about missing "break" at the end of a "case". */ -#if GCC_VERSION>=7000000 -# define deliberate_fall_through __attribute__((fallthrough)); -#else -# define deliberate_fall_through +#if defined(__has_attribute) +# if __has_attribute(fallthrough) +# define deliberate_fall_through __attribute__((fallthrough)); +# endif +#endif +#if !defined(deliberate_fall_through) +# define deliberate_fall_through #endif /* @@ -459,9 +462,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.46.1" -#define SQLITE_VERSION_NUMBER 3046001 -#define SQLITE_SOURCE_ID "2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33" +#define SQLITE_VERSION "3.47.0" +#define SQLITE_VERSION_NUMBER 3047000 +#define SQLITE_SOURCE_ID "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1085,8 +1088,8 @@ struct sqlite3_file { ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, -** PENDING, or EXCLUSIVE lock on the file. It returns true -** if such a lock exists and false otherwise. +** PENDING, or EXCLUSIVE lock on the file. It returns, via its output +** pointer parameter, true if such a lock exists and false otherwise. ** ** The xFileControl() method is a generic interface that allows custom ** VFS implementations to directly control an open file using the @@ -3883,8 +3886,8 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** [[OPEN_EXRESCODE]] ^(
[SQLITE_OPEN_EXRESCODE]
**
The database connection comes up in "extended result code mode". -** In other words, the database behaves has if -** [sqlite3_extended_result_codes(db,1)] where called on the database +** In other words, the database behaves as if +** [sqlite3_extended_result_codes(db,1)] were called on the database ** connection as soon as the connection is created. In addition to setting ** the extended result code mode, this flag also causes [sqlite3_open_v2()] ** to return an extended result code.
@@ -4535,13 +4538,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the -** first zero terminator. ^If nByte is positive, then it is the -** number of bytes read from zSql. ^If nByte is zero, then no prepared +** first zero terminator. ^If nByte is positive, then it is the maximum +** number of bytes read from zSql. When nByte is positive, zSql is read +** up to the first zero terminator or until the nByte bytes have been read, +** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string including ** the nul-terminator. +** Note that nByte measure the length of the input in bytes, not +** characters, even for the UTF-16 interfaces. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -5912,7 +5919,7 @@ SQLITE_API int sqlite3_create_window_function( ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). -** SQL functions that invokes [sqlite3_value_subtype()] should have this +** All SQL functions that invoke [sqlite3_value_subtype()] should have this ** property. If the SQLITE_SUBTYPE property is omitted, then the return ** value from [sqlite3_value_subtype()] might sometimes be zero even though ** a non-zero subtype was specified by the function argument expression. @@ -5928,6 +5935,15 @@ SQLITE_API int sqlite3_create_window_function( ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. +** +** [[SQLITE_SELFORDER1]]
SQLITE_SELFORDER1
+** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate +** that internally orders the values provided to the first argument. The +** ordered-set aggregate SQL notation with a single ORDER BY term can be +** used to invoke this function. If the ordered-set aggregate notation is +** used on a function that lacks this flag, then an error is raised. Note +** that the ordered-set aggregate syntax is only available if SQLite is +** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. **
** */ @@ -5936,6 +5952,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 +#define SQLITE_SELFORDER1 0x002000000 /* ** CAPI3REF: Deprecated Functions @@ -6133,7 +6150,7 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. ** -** Every [application-defined SQL function] that invoke this interface +** Every [application-defined SQL function] that invokes this interface ** should include the [SQLITE_SUBTYPE] property in the text ** encoding argument when the function is [sqlite3_create_function|registered]. ** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() @@ -7740,9 +7757,11 @@ struct sqlite3_module { ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a -** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag - -** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite -** assumes that the strategy may visit at most one row. +** mask of SQLITE_INDEX_SCAN_* flags. One such flag is +** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN] +** output to show the idxNum has hex instead of as decimal. Another flag is +** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will +** return at most one row. ** ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then ** SQLite also assumes that if a call to the xUpdate() method is made as @@ -7806,7 +7825,9 @@ struct sqlite3_index_info { ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ -#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */ +#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */ +#define SQLITE_INDEX_SCAN_HEX 0x00000002 /* Display idxNum as hex */ + /* in EXPLAIN QUERY PLAN */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes @@ -8643,6 +8664,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ +#define SQLITE_TESTCTRL_GETOPT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 @@ -8662,7 +8684,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* @@ -9638,6 +9660,16 @@ typedef struct sqlite3_backup sqlite3_backup; ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. +** +** Alternatives To Using The Backup API +** +** Other techniques for safely creating a consistent backup of an SQLite +** database include: +** +**
    +**
  • The [VACUUM INTO] command. +**
  • The [sqlite3_rsync] utility program. +**
*/ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ @@ -10837,6 +10869,14 @@ typedef struct sqlite3_snapshot { ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** +** If a read-transaction is opened by this function, then it is guaranteed +** that the returned snapshot object may not be invalidated by a database +** writer or checkpointer until after the read-transaction is closed. This +** is not guaranteed if a read-transaction is already open when this +** function is called. In that case, any subsequent write or checkpoint +** operation on the database may invalidate the returned snapshot handle, +** even while the read-transaction remains open. +** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined @@ -11145,8 +11185,6 @@ SQLITE_API int sqlite3_deserialize( #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 -# undef SQLITE_OMIT_WAL -# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ # ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION # endif @@ -13349,6 +13387,10 @@ struct Fts5PhraseIter { ** (i.e. if it is a contentless table), then this API always iterates ** through an empty set (all calls to xPhraseFirst() set iCol to -1). ** +** In all cases, matches are visited in (column ASC, offset ASC) order. +** i.e. all those in column 0, sorted by offset, followed by those in +** column 1, etc. +** ** xPhraseNext() ** See xPhraseFirst above. ** @@ -13415,9 +13457,32 @@ struct Fts5PhraseIter { ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. +** +** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the locale associated +** with column iCol of the current row. Usually, there is no associated +** locale, and output parameters (*pzLocale) and (*pnLocale) are set +** to NULL and 0, respectively. However, if the fts5_locale() function +** was used to associate a locale with the value when it was inserted +** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated +** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) +** is set to the size in bytes of the buffer, not including the +** nul-terminator. +** +** If successful, SQLITE_OK is returned. Or, if an error occurs, an +** SQLite error code is returned. The final value of the output parameters +** is undefined in this case. +** +** xTokenize_v2: +** Tokenize text using the tokenizer belonging to the FTS5 table. This +** API is the same as the xTokenize() API, except that it allows a tokenizer +** locale to be specified. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); @@ -13459,6 +13524,15 @@ struct Fts5ExtensionApi { const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); + + /* Below this point are iVersion>=4 only */ + int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); + int (*xTokenize_v2)(Fts5Context*, + const char *pText, int nText, /* Text to tokenize */ + const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ + void *pCtx, /* Context passed to xToken() */ + int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ + ); }; /* @@ -13479,7 +13553,7 @@ struct Fts5ExtensionApi { ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) -** pointer provided by the application when the fts5_tokenizer object +** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the @@ -13503,7 +13577,7 @@ struct Fts5ExtensionApi { ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** -** The second argument indicates the reason that FTS5 is requesting +** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** @@ -13527,6 +13601,13 @@ struct Fts5ExtensionApi { ** on a columnsize=0 database. ** ** +** The sixth and seventh arguments passed to xTokenize() - pLocale and +** nLocale - are a pointer to a buffer containing the locale to use for +** tokenization (e.g. "en_US") and its size in bytes, respectively. The +** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in +** which case nLocale is always 0) to indicate that the tokenizer should +** use its default locale. +** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth @@ -13550,6 +13631,30 @@ struct Fts5ExtensionApi { ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** +** If the tokenizer is registered using an fts5_tokenizer_v2 object, +** then the xTokenize() method has two additional arguments - pLocale +** and nLocale. These specify the locale that the tokenizer should use +** for the current request. If pLocale and nLocale are both 0, then the +** tokenizer should use its default locale. Otherwise, pLocale points to +** an nLocale byte buffer containing the name of the locale to use as utf-8 +** text. pLocale is not nul-terminated. +** +** FTS5_TOKENIZER +** +** There is also an fts5_tokenizer object. This is an older, deprecated, +** version of fts5_tokenizer_v2. It is similar except that: +** +**
    +**
  • There is no "iVersion" field, and +**
  • The xTokenize() method does not take a locale argument. +**
+** +** Legacy fts5_tokenizer tokenizers must be registered using the +** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). +** +** Tokenizer implementations registered using either API may be retrieved +** using both xFindTokenizer() and xFindTokenizer_v2(). +** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a @@ -13658,6 +13763,33 @@ struct Fts5ExtensionApi { ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; +typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; +struct fts5_tokenizer_v2 { + int iVersion; /* Currently always 2 */ + + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); + void (*xDelete)(Fts5Tokenizer*); + int (*xTokenize)(Fts5Tokenizer*, + void *pCtx, + int flags, /* Mask of FTS5_TOKENIZE_* flags */ + const char *pText, int nText, + const char *pLocale, int nLocale, + int (*xToken)( + void *pCtx, /* Copy of 2nd argument to xTokenize() */ + int tflags, /* Mask of FTS5_TOKEN_* flags */ + const char *pToken, /* Pointer to buffer containing token */ + int nToken, /* Size of token in bytes */ + int iStart, /* Byte offset of token within input text */ + int iEnd /* Byte offset of end of token within input text */ + ) + ); +}; + +/* +** New code should use the fts5_tokenizer_v2 type to define tokenizer +** implementations. The following type is included for legacy applications +** that still use it. +*/ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); @@ -13677,6 +13809,7 @@ struct fts5_tokenizer { ); }; + /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 @@ -13696,7 +13829,7 @@ struct fts5_tokenizer { */ typedef struct fts5_api fts5_api; struct fts5_api { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( @@ -13723,6 +13856,25 @@ struct fts5_api { fts5_extension_function xFunction, void (*xDestroy)(void*) ); + + /* APIs below this point are only available if iVersion>=3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void *pUserData, + fts5_tokenizer_v2 *pTokenizer, + void (*xDestroy)(void*) + ); + + /* Find an existing tokenizer */ + int (*xFindTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void **ppUserData, + fts5_tokenizer_v2 **ppTokenizer + ); }; /* @@ -14532,132 +14684,132 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_OR 43 #define TK_AND 44 #define TK_IS 45 -#define TK_MATCH 46 -#define TK_LIKE_KW 47 -#define TK_BETWEEN 48 -#define TK_IN 49 -#define TK_ISNULL 50 -#define TK_NOTNULL 51 -#define TK_NE 52 -#define TK_EQ 53 -#define TK_GT 54 -#define TK_LE 55 -#define TK_LT 56 -#define TK_GE 57 -#define TK_ESCAPE 58 -#define TK_ID 59 -#define TK_COLUMNKW 60 -#define TK_DO 61 -#define TK_FOR 62 -#define TK_IGNORE 63 -#define TK_INITIALLY 64 -#define TK_INSTEAD 65 -#define TK_NO 66 -#define TK_KEY 67 -#define TK_OF 68 -#define TK_OFFSET 69 -#define TK_PRAGMA 70 -#define TK_RAISE 71 -#define TK_RECURSIVE 72 -#define TK_REPLACE 73 -#define TK_RESTRICT 74 -#define TK_ROW 75 -#define TK_ROWS 76 -#define TK_TRIGGER 77 -#define TK_VACUUM 78 -#define TK_VIEW 79 -#define TK_VIRTUAL 80 -#define TK_WITH 81 -#define TK_NULLS 82 -#define TK_FIRST 83 -#define TK_LAST 84 -#define TK_CURRENT 85 -#define TK_FOLLOWING 86 -#define TK_PARTITION 87 -#define TK_PRECEDING 88 -#define TK_RANGE 89 -#define TK_UNBOUNDED 90 -#define TK_EXCLUDE 91 -#define TK_GROUPS 92 -#define TK_OTHERS 93 -#define TK_TIES 94 -#define TK_GENERATED 95 -#define TK_ALWAYS 96 -#define TK_MATERIALIZED 97 -#define TK_REINDEX 98 -#define TK_RENAME 99 -#define TK_CTIME_KW 100 -#define TK_ANY 101 -#define TK_BITAND 102 -#define TK_BITOR 103 -#define TK_LSHIFT 104 -#define TK_RSHIFT 105 -#define TK_PLUS 106 -#define TK_MINUS 107 -#define TK_STAR 108 -#define TK_SLASH 109 -#define TK_REM 110 -#define TK_CONCAT 111 -#define TK_PTR 112 -#define TK_COLLATE 113 -#define TK_BITNOT 114 -#define TK_ON 115 -#define TK_INDEXED 116 -#define TK_STRING 117 -#define TK_JOIN_KW 118 -#define TK_CONSTRAINT 119 -#define TK_DEFAULT 120 -#define TK_NULL 121 -#define TK_PRIMARY 122 -#define TK_UNIQUE 123 -#define TK_CHECK 124 -#define TK_REFERENCES 125 -#define TK_AUTOINCR 126 -#define TK_INSERT 127 -#define TK_DELETE 128 -#define TK_UPDATE 129 -#define TK_SET 130 -#define TK_DEFERRABLE 131 -#define TK_FOREIGN 132 -#define TK_DROP 133 -#define TK_UNION 134 -#define TK_ALL 135 -#define TK_EXCEPT 136 -#define TK_INTERSECT 137 -#define TK_SELECT 138 -#define TK_VALUES 139 -#define TK_DISTINCT 140 -#define TK_DOT 141 -#define TK_FROM 142 -#define TK_JOIN 143 -#define TK_USING 144 -#define TK_ORDER 145 -#define TK_GROUP 146 -#define TK_HAVING 147 -#define TK_LIMIT 148 -#define TK_WHERE 149 -#define TK_RETURNING 150 -#define TK_INTO 151 -#define TK_NOTHING 152 -#define TK_FLOAT 153 -#define TK_BLOB 154 -#define TK_INTEGER 155 -#define TK_VARIABLE 156 -#define TK_CASE 157 -#define TK_WHEN 158 -#define TK_THEN 159 -#define TK_ELSE 160 -#define TK_INDEX 161 -#define TK_ALTER 162 -#define TK_ADD 163 -#define TK_WINDOW 164 -#define TK_OVER 165 -#define TK_FILTER 166 -#define TK_COLUMN 167 -#define TK_AGG_FUNCTION 168 -#define TK_AGG_COLUMN 169 -#define TK_TRUEFALSE 170 -#define TK_ISNOT 171 +#define TK_ISNOT 46 +#define TK_MATCH 47 +#define TK_LIKE_KW 48 +#define TK_BETWEEN 49 +#define TK_IN 50 +#define TK_ISNULL 51 +#define TK_NOTNULL 52 +#define TK_NE 53 +#define TK_EQ 54 +#define TK_GT 55 +#define TK_LE 56 +#define TK_LT 57 +#define TK_GE 58 +#define TK_ESCAPE 59 +#define TK_ID 60 +#define TK_COLUMNKW 61 +#define TK_DO 62 +#define TK_FOR 63 +#define TK_IGNORE 64 +#define TK_INITIALLY 65 +#define TK_INSTEAD 66 +#define TK_NO 67 +#define TK_KEY 68 +#define TK_OF 69 +#define TK_OFFSET 70 +#define TK_PRAGMA 71 +#define TK_RAISE 72 +#define TK_RECURSIVE 73 +#define TK_REPLACE 74 +#define TK_RESTRICT 75 +#define TK_ROW 76 +#define TK_ROWS 77 +#define TK_TRIGGER 78 +#define TK_VACUUM 79 +#define TK_VIEW 80 +#define TK_VIRTUAL 81 +#define TK_WITH 82 +#define TK_NULLS 83 +#define TK_FIRST 84 +#define TK_LAST 85 +#define TK_CURRENT 86 +#define TK_FOLLOWING 87 +#define TK_PARTITION 88 +#define TK_PRECEDING 89 +#define TK_RANGE 90 +#define TK_UNBOUNDED 91 +#define TK_EXCLUDE 92 +#define TK_GROUPS 93 +#define TK_OTHERS 94 +#define TK_TIES 95 +#define TK_GENERATED 96 +#define TK_ALWAYS 97 +#define TK_MATERIALIZED 98 +#define TK_REINDEX 99 +#define TK_RENAME 100 +#define TK_CTIME_KW 101 +#define TK_ANY 102 +#define TK_BITAND 103 +#define TK_BITOR 104 +#define TK_LSHIFT 105 +#define TK_RSHIFT 106 +#define TK_PLUS 107 +#define TK_MINUS 108 +#define TK_STAR 109 +#define TK_SLASH 110 +#define TK_REM 111 +#define TK_CONCAT 112 +#define TK_PTR 113 +#define TK_COLLATE 114 +#define TK_BITNOT 115 +#define TK_ON 116 +#define TK_INDEXED 117 +#define TK_STRING 118 +#define TK_JOIN_KW 119 +#define TK_CONSTRAINT 120 +#define TK_DEFAULT 121 +#define TK_NULL 122 +#define TK_PRIMARY 123 +#define TK_UNIQUE 124 +#define TK_CHECK 125 +#define TK_REFERENCES 126 +#define TK_AUTOINCR 127 +#define TK_INSERT 128 +#define TK_DELETE 129 +#define TK_UPDATE 130 +#define TK_SET 131 +#define TK_DEFERRABLE 132 +#define TK_FOREIGN 133 +#define TK_DROP 134 +#define TK_UNION 135 +#define TK_ALL 136 +#define TK_EXCEPT 137 +#define TK_INTERSECT 138 +#define TK_SELECT 139 +#define TK_VALUES 140 +#define TK_DISTINCT 141 +#define TK_DOT 142 +#define TK_FROM 143 +#define TK_JOIN 144 +#define TK_USING 145 +#define TK_ORDER 146 +#define TK_GROUP 147 +#define TK_HAVING 148 +#define TK_LIMIT 149 +#define TK_WHERE 150 +#define TK_RETURNING 151 +#define TK_INTO 152 +#define TK_NOTHING 153 +#define TK_FLOAT 154 +#define TK_BLOB 155 +#define TK_INTEGER 156 +#define TK_VARIABLE 157 +#define TK_CASE 158 +#define TK_WHEN 159 +#define TK_THEN 160 +#define TK_ELSE 161 +#define TK_INDEX 162 +#define TK_ALTER 163 +#define TK_ADD 164 +#define TK_WINDOW 165 +#define TK_OVER 166 +#define TK_FILTER 167 +#define TK_COLUMN 168 +#define TK_AGG_FUNCTION 169 +#define TK_AGG_COLUMN 170 +#define TK_TRUEFALSE 171 #define TK_FUNCTION 172 #define TK_UPLUS 173 #define TK_UMINUS 174 @@ -14680,6 +14832,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #include #include #include +#include /* ** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY. @@ -14700,7 +14853,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 -# define LONGDOUBLE_TYPE sqlite_int64 +# define fabs(X) ((X)<0?-(X):(X)) +# define sqlite3IsOverflow(X) 0 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif @@ -14875,9 +15029,6 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); # define INT8_TYPE signed char # endif #endif -#ifndef LONGDOUBLE_TYPE -# define LONGDOUBLE_TYPE long double -#endif typedef sqlite_int64 i64; /* 8-byte signed integer */ typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ @@ -15377,6 +15528,7 @@ typedef struct Savepoint Savepoint; typedef struct Select Select; typedef struct SQLiteThread SQLiteThread; typedef struct SelectDest SelectDest; +typedef struct Subquery Subquery; typedef struct SrcItem SrcItem; typedef struct SrcList SrcList; typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ @@ -16259,6 +16411,9 @@ SQLITE_PRIVATE int sqlite3BtreeCursor( ); SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor(Btree*,BtCursor*); +#endif SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned); #ifdef SQLITE_ENABLE_CURSOR_HINTS @@ -16477,6 +16632,19 @@ typedef struct Vdbe Vdbe; */ typedef struct sqlite3_value Mem; typedef struct SubProgram SubProgram; +typedef struct SubrtnSig SubrtnSig; + +/* +** A signature for a reusable subroutine that materializes the RHS of +** an IN operator. +*/ +struct SubrtnSig { + int selId; /* SELECT-id for the SELECT statement on the RHS */ + char *zAff; /* Affinity of the overall IN expression */ + int iTable; /* Ephemeral table generated by the subroutine */ + int iAddr; /* Subroutine entry address */ + int regReturn; /* Register used to hold return address */ +}; /* ** A single instruction of the virtual machine has an opcode @@ -16505,6 +16673,7 @@ struct VdbeOp { u32 *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ + SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */ #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif @@ -16572,6 +16741,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ #define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ +#define P4_SUBRTNSIG (-17) /* P4 is a SubrtnSig pointer */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -16663,16 +16833,16 @@ typedef struct VdbeOpList VdbeOpList; #define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ #define OP_Program 48 /* jump0 */ #define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ -#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ -#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ -#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ -#define OP_Eq 53 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ -#define OP_Gt 54 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ -#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ -#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ -#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */ -#define OP_IfPos 59 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ +#define OP_IfPos 50 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ +#define OP_IsNull 51 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ +#define OP_NotNull 52 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ +#define OP_Ne 53 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ +#define OP_Eq 54 /* jump, same as TK_EQ, synopsis: IF r[P3]==r[P1] */ +#define OP_Gt 55 /* jump, same as TK_GT, synopsis: IF r[P3]>r[P1] */ +#define OP_Le 56 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */ +#define OP_Lt 57 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ +#define OP_ElseEq 59 /* jump, same as TK_ESCAPE */ #define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ #define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */ #define OP_IncrVacuum 62 /* jump */ @@ -16715,23 +16885,23 @@ typedef struct VdbeOpList VdbeOpList; #define OP_ReadCookie 99 #define OP_SetCookie 100 #define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */ -#define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ -#define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ -#define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ -#define OP_Add 106 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ -#define OP_Subtract 107 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ -#define OP_Multiply 108 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ -#define OP_Divide 109 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ -#define OP_Remainder 110 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ -#define OP_Concat 111 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -#define OP_OpenRead 112 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenRead 102 /* synopsis: root=P2 iDb=P3 */ +#define OP_BitAnd 103 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 104 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 105 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 107 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 108 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 109 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 110 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 111 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 112 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ #define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */ -#define OP_BitNot 114 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ -#define OP_OpenDup 115 +#define OP_OpenDup 114 +#define OP_BitNot 115 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */ #define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */ -#define OP_String8 117 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_OpenEphemeral 118 /* synopsis: nColumn=P2 */ +#define OP_OpenEphemeral 117 /* synopsis: nColumn=P2 */ +#define OP_String8 118 /* same as TK_STRING, synopsis: r[P2]='P4' */ #define OP_SorterOpen 119 #define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */ #define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */ @@ -16766,8 +16936,8 @@ typedef struct VdbeOpList VdbeOpList; #define OP_LoadAnalysis 150 #define OP_DropTable 151 #define OP_DropIndex 152 -#define OP_Real 153 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_DropTrigger 154 +#define OP_DropTrigger 153 +#define OP_Real 154 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ #define OP_IntegrityCk 155 #define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */ #define OP_Param 157 @@ -16823,20 +16993,20 @@ typedef struct VdbeOpList VdbeOpList; /* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ /* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\ /* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ -/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ -/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ +/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\ +/* 56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x41,\ /* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ /* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ /* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\ -/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26, 0x26,\ +/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x40, 0x26,\ /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ -/* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\ +/* 112 */ 0x26, 0x00, 0x40, 0x12, 0x40, 0x40, 0x10, 0x00,\ /* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ /* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ /* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ -/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x06, 0x10, 0x00, 0x04,\ /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ /* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\ @@ -17897,6 +18067,7 @@ struct sqlite3 { #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ +#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -18884,9 +19055,15 @@ struct AggInfo { ** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg. ** The assert()s that are part of this macro verify that constraint. */ +#ifndef NDEBUG #define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I)) #define AggInfoFuncReg(A,I) \ (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I)) +#else +#define AggInfoColumnReg(A,I) ((A)->iFirstReg+(I)) +#define AggInfoFuncReg(A,I) \ + ((A)->iFirstReg+(A)->nColumn+(I)) +#endif /* ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. @@ -19067,7 +19244,7 @@ struct Expr { #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ #define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ - /* 0x80000000 // Available */ +#define EP_SubtArg 0x80000000 /* Is argument to SQLITE_SUBTYPE function */ /* The EP_Propagate mask is a set of properties that automatically propagate ** upwards into parent nodes. @@ -19238,6 +19415,16 @@ struct IdList { #define EU4_IDX 1 /* Uses IdList.a.u4.idx */ #define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ +/* +** Details of the implementation of a subquery. +*/ +struct Subquery { + Select *pSelect; /* A SELECT statement used in place of a table name */ + int addrFillSub; /* Address of subroutine to initialize a subquery */ + int regReturn; /* Register holding return address of addrFillSub */ + int regResult; /* Registers holding results of a co-routine */ +}; + /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. @@ -19250,29 +19437,40 @@ struct IdList { ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** -** Union member validity: +** Aggressive use of "union" helps keep the size of the object small. This +** has been shown to boost performance, in addition to saving memory. +** Access to union elements is gated by the following rules which should +** always be checked, either by an if-statement or by an assert(). ** -** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc -** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** Field Only access if this is true +** --------------- ----------------------------------- +** u1.zIndexedBy fg.isIndexedBy +** u1.pFuncArg fg.isTabFunc ** u1.nRow !fg.isTabFunc && !fg.isIndexedBy ** -** u2.pIBIndex fg.isIndexedBy && !fg.isCte -** u2.pCteUse fg.isCte && !fg.isIndexedBy +** u2.pIBIndex fg.isIndexedBy +** u2.pCteUse fg.isCte +** +** u3.pOn !fg.isUsing +** u3.pUsing fg.isUsing +** +** u4.zDatabase !fg.fixedSchema && !fg.isSubquery +** u4.pSchema fg.fixedSchema +** u4.pSubq fg.isSubquery +** +** See also the sqlite3SrcListDelete() routine for assert() statements that +** check invariants on the fields of this object, especially the flags +** inside the fg struct. */ struct SrcItem { - Schema *pSchema; /* Schema to which this item is fixed */ - char *zDatabase; /* Name of database holding this table */ char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ - Table *pTab; /* An SQL table corresponding to zName */ - Select *pSelect; /* A SELECT statement used in place of a table name */ - int addrFillSub; /* Address of subroutine to manifest a subquery */ - int regReturn; /* Register holding return address of addrFillSub */ - int regResult; /* Registers holding results of a co-routine */ + Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ struct { u8 jointype; /* Type of join between this table and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ + unsigned isSubquery :1; /* True if this term is a subquery */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned isMaterialized:1; /* This is a materialized view */ @@ -19286,12 +19484,10 @@ struct SrcItem { unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ + unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */ + unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ - union { - Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ - IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ - } u3; Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ union { char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ @@ -19302,6 +19498,15 @@ struct SrcItem { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; + union { + Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ + IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ + } u3; + union { + Schema *pSchema; /* Schema to which this item is fixed */ + char *zDatabase; /* Name of database holding this table */ + Subquery *pSubq; /* Description of a subquery */ + } u4; }; /* @@ -19433,7 +19638,7 @@ struct NameContext { #define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ #define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ #define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ -#define NC_Complex 0x002000 /* True if a function or subquery seen */ +/* 0x002000 // available for reuse */ #define NC_AllowWin 0x004000 /* Window functions are allowed here */ #define NC_HasWin 0x008000 /* One or more window functions seen */ #define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */ @@ -19561,8 +19766,10 @@ struct Select { #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ #define SF_Correlated 0x20000000 /* True if references the outer context */ -/* True if S exists and has SF_NestedFrom */ -#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) +/* True if SrcItem X is a subquery that has SF_NestedFrom */ +#define IsNestedFrom(X) \ + ((X)->fg.isSubquery && \ + ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0) /* ** The results of a SELECT can be distributed in several ways, as defined @@ -19592,7 +19799,11 @@ struct Select { ** SRT_Set The result must be a single column. Store each ** row of result as the key in table pDest->iSDParm. ** Apply the affinity pDest->affSdst before storing -** results. Used to implement "IN (SELECT ...)". +** results. if pDest->iSDParm2 is positive, then it is +** a register holding a Bloom filter for the IN operator +** that should be populated in addition to the +** pDest->iSDParm table. This SRT is used to +** implement "IN (SELECT ...)". ** ** SRT_EphemTab Create an temporary table pDest->iSDParm and store ** the result there. The cursor is left open after @@ -19800,6 +20011,7 @@ struct Parse { u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ u8 bHasWith; /* True if statement contains WITH */ + u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif @@ -20095,7 +20307,7 @@ struct Returning { }; /* -** An objected used to accumulate the text of a string where we +** An object used to accumulate the text of a string where we ** do not necessarily know how big the string will be in the end. */ struct sqlite3_str { @@ -20109,7 +20321,7 @@ struct sqlite3_str { }; #define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */ #define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */ -#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */ +#define SQLITE_PRINTF_MALLOCED 0x04 /* True if zText is allocated space */ #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) @@ -20187,7 +20399,6 @@ struct Sqlite3Config { u8 bUseCis; /* Use covering indices for full-scans */ u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ - u8 bUseLongDouble; /* Make use of long double */ #ifdef SQLITE_DEBUG u8 bJsonSelfcheck; /* Double-check JSON parsing */ #endif @@ -20562,15 +20773,6 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define SQLITE_ENABLE_FTS3 1 #endif -/* -** The ctype.h header is needed for non-ASCII systems. It is also -** needed by FTS3 when FTS3 is included in the amalgamation. -*/ -#if !defined(SQLITE_ASCII) || \ - (defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_AMALGAMATION)) -# include -#endif - /* ** The following macros mimic the standard library functions toupper(), ** isspace(), isalnum(), isdigit() and isxdigit(), respectively. The @@ -20949,6 +21151,9 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); +SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3*,Subquery*); +SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3*,SrcItem*); +SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery(Parse*, SrcItem*, Select*, int); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, OnOrUsing*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); @@ -20998,6 +21203,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); +SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg); SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); #ifndef SQLITE_OMIT_GENERATED_COLUMNS SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); @@ -21060,7 +21266,7 @@ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,i #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif -SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); +SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); @@ -21188,7 +21394,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 -SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); +SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nByte, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); @@ -22174,6 +22380,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC "ENABLE_OFFSET_SQL_FUNC", #endif +#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES + "ENABLE_ORDERED_SET_AGGREGATES", +#endif #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif @@ -22921,7 +23130,6 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ - sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ #ifdef SQLITE_DEBUG 0, /* bJsonSelfcheck */ #endif @@ -23644,6 +23852,7 @@ struct PreUpdate { Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ + sqlite3_value **apDflt; /* Array of default values, if required */ }; /* @@ -24490,8 +24699,8 @@ static void computeJD(DateTime *p){ Y--; M += 12; } - A = Y/100; - B = 2 - A + (A/4); + A = (Y+4800)/100; + B = 38 - A + (A/4); X1 = 36525*(Y+4716)/100; X2 = 306001*(M+1)/10000; p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); @@ -24675,7 +24884,7 @@ static int validJulianDay(sqlite3_int64 iJD){ ** Compute the Year, Month, and Day from the julian day number. */ static void computeYMD(DateTime *p){ - int Z, A, B, C, D, E, X1; + int Z, alpha, A, B, C, D, E, X1; if( p->validYMD ) return; if( !p->validJD ){ p->Y = 2000; @@ -24686,8 +24895,8 @@ static void computeYMD(DateTime *p){ return; }else{ Z = (int)((p->iJD + 43200000)/86400000); - A = (int)((Z - 1867216.25)/36524.25); - A = Z + 1 + A - (A/4); + alpha = (int)((Z + 32044.75)/36524.25) - 52; + A = Z + 1 + alpha - ((alpha+100)/4) + 25; B = A + 1524; C = (int)((B - 122.1)/365.25); D = (36525*(C&32767))/100; @@ -24886,8 +25095,8 @@ static const struct { /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, /* 3 */ { 3, "day", 5373485.0, 86400.0 }, - /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 }, - /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 }, + /* 4 */ { 5, "month", 176546.0, 2592000.0 }, + /* 5 */ { 4, "year", 14713.0, 31536000.0 }, }; /* @@ -29089,16 +29298,29 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ /* ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use inside assert() statements. +** +** Because these routines raise false-positive alerts in TSAN, disable +** them (make them always return 1) when compiling with TSAN. */ SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ +# if defined(__has_feature) +# if __has_feature(thread_sanitizer) + p = 0; +# endif +# endif assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld ); return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); } SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ +# if defined(__has_feature) +# if __has_feature(thread_sanitizer) + p = 0; +# endif +# endif assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld ); return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); } -#endif +#endif /* NDEBUG */ #endif /* !defined(SQLITE_MUTEX_OMIT) */ @@ -31986,16 +32208,19 @@ SQLITE_API void sqlite3_str_vappendf( if( pItem->zAlias && !flag_altform2 ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else if( pItem->zName ){ - if( pItem->zDatabase ){ - sqlite3_str_appendall(pAccum, pItem->zDatabase); + if( pItem->fg.fixedSchema==0 + && pItem->fg.isSubquery==0 + && pItem->u4.zDatabase!=0 + ){ + sqlite3_str_appendall(pAccum, pItem->u4.zDatabase); sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); - }else{ - Select *pSel = pItem->pSelect; - assert( pSel!=0 ); /* Because of tag-20240424-1 */ + }else if( ALWAYS(pItem->fg.isSubquery) ){/* Because of tag-20240424-1 */ + Select *pSel = pItem->u4.pSubq->pSelect; + assert( pSel!=0 ); if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); }else if( pSel->selFlags & SF_MultiValue ){ @@ -32777,9 +33002,9 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); - if( pItem->pTab ){ + if( pItem->pSTab ){ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", - pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, + pItem->pSTab->zName, pItem->pSTab->nCol, pItem->pSTab, pItem->colUsed, pItem->fg.rowidUsed ? "+rowid" : ""); } @@ -32810,25 +33035,30 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte"); if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom"); + if( pItem->fg.fixedSchema ) sqlite3_str_appendf(&x, " fixedSchema"); + if( pItem->fg.hadSchema ) sqlite3_str_appendf(&x, " hadSchema"); + if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery"); sqlite3StrAccumFinish(&x); sqlite3TreeViewItem(pView, zLine, inSrc-1); n = 0; - if( pItem->pSelect ) n++; + if( pItem->fg.isSubquery ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } - if( pItem->pSelect ){ - sqlite3TreeViewPush(&pView, i+1nSrc); - if( pItem->pTab ){ - Table *pTab = pItem->pTab; + if( pItem->fg.isSubquery ){ + assert( n==1 ); + if( pItem->pSTab ){ + Table *pTab = pItem->pSTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } - assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); - sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); + sqlite3TreeViewPush(&pView, 0); + sqlite3TreeViewLine(pView, "SUBQUERY"); sqlite3TreeViewPop(&pView); + sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); @@ -32870,7 +33100,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m n = 1000; }else{ n = 0; - if( p->pSrc && p->pSrc->nSrc ) n++; + if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ) n++; if( p->pWhere ) n++; if( p->pGroupBy ) n++; if( p->pHaving ) n++; @@ -32896,7 +33126,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m sqlite3TreeViewPop(&pView); } #endif - if( p->pSrc && p->pSrc->nSrc ){ + if( p->pSrc && p->pSrc->nSrc && p->pSrc->nAlloc ){ sqlite3TreeViewPush(&pView, (n--)>0); sqlite3TreeViewLine(pView, "FROM"); sqlite3TreeViewSrcList(pView, p->pSrc); @@ -33404,7 +33634,8 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m case OE_Ignore: zType = "ignore"; break; } assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); + sqlite3TreeViewLine(pView, "RAISE %s", zType); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; } #endif @@ -33484,9 +33715,10 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList( sqlite3TreeViewLine(pView, "%s", zLabel); for(i=0; inExpr; i++){ int j = pList->a[i].u.x.iOrderByCol; + u8 sortFlags = pList->a[i].fg.sortFlags; char *zName = pList->a[i].zEName; int moreToFollow = inExpr - 1; - if( j || zName ){ + if( j || zName || sortFlags ){ sqlite3TreeViewPush(&pView, moreToFollow); moreToFollow = 0; sqlite3TreeViewLine(pView, 0); @@ -33507,13 +33739,18 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList( } } if( j ){ - fprintf(stdout, "iOrderByCol=%d", j); + fprintf(stdout, "iOrderByCol=%d ", j); + } + if( sortFlags & KEYINFO_ORDER_DESC ){ + fprintf(stdout, "DESC "); + }else if( sortFlags & KEYINFO_ORDER_BIGNULL ){ + fprintf(stdout, "NULLS-LAST"); } fprintf(stdout, "\n"); fflush(stdout); } sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow); - if( j || zName ){ + if( j || zName || sortFlags ){ sqlite3TreeViewPop(&pView); } } @@ -34476,7 +34713,7 @@ static const unsigned char sqlite3Utf8Trans1[] = { c = *(zIn++); \ if( c>=0xc0 ){ \ c = sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ + while( zIn=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; + if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2; n++; } return (int)(z-(unsigned char const *)zIn) @@ -35448,6 +35687,8 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en int eValid = 1; /* True exponent is either not used or is well-formed */ int nDigit = 0; /* Number of digits processed */ int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ + double rr[2]; + u64 s2; assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); *pResult = 0.0; /* Default return value, in case of an error */ @@ -35559,68 +35800,41 @@ do_atof_calc: e++; } - if( e==0 ){ - *pResult = s; - }else if( sqlite3Config.bUseLongDouble ){ - LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s; - if( e>0 ){ - while( e>=100 ){ e-=100; r *= 1.0e+100L; } - while( e>=10 ){ e-=10; r *= 1.0e+10L; } - while( e>=1 ){ e-=1; r *= 1.0e+01L; } - }else{ - while( e<=-100 ){ e+=100; r *= 1.0e-100L; } - while( e<=-10 ){ e+=10; r *= 1.0e-10L; } - while( e<=-1 ){ e+=1; r *= 1.0e-01L; } - } - assert( r>=0.0 ); - if( r>+1.7976931348623157081452742373e+308L ){ -#ifdef INFINITY - *pResult = +INFINITY; -#else - *pResult = 1.0e308*10.0; + rr[0] = (double)s; + s2 = (u64)rr[0]; +#if defined(_MSC_VER) && _MSC_VER<1700 + if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } #endif - }else{ - *pResult = (double)r; + rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); + if( e>0 ){ + while( e>=100 ){ + e -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( e>=10 ){ + e -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( e>=1 ){ + e -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); } }else{ - double rr[2]; - u64 s2; - rr[0] = (double)s; - s2 = (u64)rr[0]; -#if defined(_MSC_VER) && _MSC_VER<1700 - if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } -#endif - rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); - if( e>0 ){ - while( e>=100 ){ - e -= 100; - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); - } - while( e>=10 ){ - e -= 10; - dekkerMul2(rr, 1.0e+10, 0.0); - } - while( e>=1 ){ - e -= 1; - dekkerMul2(rr, 1.0e+01, 0.0); - } - }else{ - while( e<=-100 ){ - e += 100; - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); - } - while( e<=-10 ){ - e += 10; - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); - } - while( e<=-1 ){ - e += 1; - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); - } + while( e<=-100 ){ + e += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( e<=-10 ){ + e += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( e<=-1 ){ + e += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); } - *pResult = rr[0]+rr[1]; - if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; } + *pResult = rr[0]+rr[1]; + if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; if( sign<0 ) *pResult = -*pResult; assert( !sqlite3IsNaN(*pResult) ); @@ -35924,10 +36138,13 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ ** Decode a floating-point value into an approximate decimal ** representation. ** -** Round the decimal representation to n significant digits if -** n is positive. Or round to -n signficant digits after the -** decimal point if n is negative. No rounding is performed if -** n is zero. +** If iRound<=0 then round to -iRound significant digits to the +** the left of the decimal point, or to a maximum of mxRound total +** significant digits. +** +** If iRound>0 round to min(iRound,mxRound) significant digits total. +** +** mxRound must be positive. ** ** The significant digits of the decimal representation are ** stored in p->z[] which is a often (but not always) a pointer @@ -35938,8 +36155,11 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou int i; u64 v; int e, exp = 0; + double rr[2]; + p->isSpecial = 0; p->z = p->zBuf; + assert( mxRound>0 ); /* Convert negative numbers to positive. Deal with Infinity, 0.0, and ** NaN. */ @@ -35966,62 +36186,45 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou /* Multiply r by powers of ten until it lands somewhere in between ** 1.0e+19 and 1.0e+17. + ** + ** Use Dekker-style double-double computation to increase the + ** precision. + ** + ** The error terms on constants like 1.0e+100 computed using the + ** decimal extension, for example as follows: + ** + ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); */ - if( sqlite3Config.bUseLongDouble ){ - LONGDOUBLE_TYPE rr = r; - if( rr>=1.0e+19 ){ - while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } - while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } - while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } - }else{ - while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } - while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } - while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } + rr[0] = r; + rr[1] = 0.0; + if( rr[0]>9.223372036854774784e+18 ){ + while( rr[0]>9.223372036854774784e+118 ){ + exp += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( rr[0]>9.223372036854774784e+28 ){ + exp += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( rr[0]>9.223372036854774784e+18 ){ + exp += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); } - v = (u64)rr; }else{ - /* If high-precision floating point is not available using "long double", - ** then use Dekker-style double-double computation to increase the - ** precision. - ** - ** The error terms on constants like 1.0e+100 computed using the - ** decimal extension, for example as follows: - ** - ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); - */ - double rr[2]; - rr[0] = r; - rr[1] = 0.0; - if( rr[0]>9.223372036854774784e+18 ){ - while( rr[0]>9.223372036854774784e+118 ){ - exp += 100; - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); - } - while( rr[0]>9.223372036854774784e+28 ){ - exp += 10; - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); - } - while( rr[0]>9.223372036854774784e+18 ){ - exp += 1; - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); - } - }else{ - while( rr[0]<9.223372036854774784e-83 ){ - exp -= 100; - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); - } - while( rr[0]<9.223372036854774784e+07 ){ - exp -= 10; - dekkerMul2(rr, 1.0e+10, 0.0); - } - while( rr[0]<9.22337203685477478e+17 ){ - exp -= 1; - dekkerMul2(rr, 1.0e+01, 0.0); - } + while( rr[0]<9.223372036854774784e-83 ){ + exp -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( rr[0]<9.223372036854774784e+07 ){ + exp -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( rr[0]<9.22337203685477478e+17 ){ + exp -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); } - v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; } - + v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; /* Extract significant digits. */ i = sizeof(p->zBuf)-1; @@ -36792,104 +36995,6 @@ SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nNam return 0; } -/* -** High-resolution hardware timer used for debugging and testing only. -*/ -#if defined(VDBE_PROFILE) \ - || defined(SQLITE_PERFORMANCE_TRACE) \ - || defined(SQLITE_ENABLE_STMT_SCANSTATUS) -/************** Include hwtime.h in the middle of util.c *********************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on Pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlite3Hwtime() routine. - ** - ** sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in util.c ***********************/ -#endif - /************** End of util.c ************************************************/ /************** Begin file hash.c ********************************************/ /* @@ -37227,16 +37332,16 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), /* 48 */ "Program" OpHelp(""), /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), - /* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), - /* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), - /* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), - /* 53 */ "Eq" OpHelp("IF r[P3]==r[P1]"), - /* 54 */ "Gt" OpHelp("IF r[P3]>r[P1]"), - /* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"), - /* 56 */ "Lt" OpHelp("IF r[P3]=r[P1]"), - /* 58 */ "ElseEq" OpHelp(""), - /* 59 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), + /* 50 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), + /* 51 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), + /* 52 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), + /* 53 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), + /* 54 */ "Eq" OpHelp("IF r[P3]==r[P1]"), + /* 55 */ "Gt" OpHelp("IF r[P3]>r[P1]"), + /* 56 */ "Le" OpHelp("IF r[P3]<=r[P1]"), + /* 57 */ "Lt" OpHelp("IF r[P3]=r[P1]"), + /* 59 */ "ElseEq" OpHelp(""), /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), /* 62 */ "IncrVacuum" OpHelp(""), @@ -37279,23 +37384,23 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 99 */ "ReadCookie" OpHelp(""), /* 100 */ "SetCookie" OpHelp(""), /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), - /* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), - /* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), - /* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), - /* 106 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), - /* 107 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), - /* 108 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), - /* 109 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), - /* 110 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), - /* 111 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), - /* 112 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 102 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 103 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 104 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 105 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 107 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 108 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 109 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 110 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 111 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 112 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), - /* 114 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), - /* 115 */ "OpenDup" OpHelp(""), + /* 114 */ "OpenDup" OpHelp(""), + /* 115 */ "BitNot" OpHelp("r[P2]= ~r[P1]"), /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"), - /* 117 */ "String8" OpHelp("r[P2]='P4'"), - /* 118 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 117 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 118 */ "String8" OpHelp("r[P2]='P4'"), /* 119 */ "SorterOpen" OpHelp(""), /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), @@ -37330,8 +37435,8 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 150 */ "LoadAnalysis" OpHelp(""), /* 151 */ "DropTable" OpHelp(""), /* 152 */ "DropIndex" OpHelp(""), - /* 153 */ "Real" OpHelp("r[P2]=P4"), - /* 154 */ "DropTrigger" OpHelp(""), + /* 153 */ "DropTrigger" OpHelp(""), + /* 154 */ "Real" OpHelp("r[P2]=P4"), /* 155 */ "IntegrityCk" OpHelp(""), /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), /* 157 */ "Param" OpHelp(""), @@ -38680,7 +38785,7 @@ static pid_t randomnessPid = 0; #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ #define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ -#ifndef SQLITE_DISABLE_DIRSYNC +#if !defined(SQLITE_DISABLE_DIRSYNC) && !defined(_AIX) # define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ #else # define UNIXFILE_DIRSYNC 0x00 @@ -40637,26 +40742,22 @@ static int nolockClose(sqlite3_file *id) { /* ** This routine checks if there is a RESERVED lock held on the specified -** file by this or any other process. If such a lock is held, set *pResOut -** to a non-zero value otherwise *pResOut is set to zero. The return value -** is set to SQLITE_OK unless an I/O error occurs during lock checking. -** -** In dotfile locking, either a lock exists or it does not. So in this -** variation of CheckReservedLock(), *pResOut is set to true if any lock -** is held on the file and false if the file is unlocked. +** file by this or any other process. If the caller holds a SHARED +** or greater lock when it is called, then it is assumed that no other +** client may hold RESERVED. Or, if the caller holds no lock, then it +** is assumed another client holds RESERVED if the lock-file exists. */ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { - int rc = SQLITE_OK; - int reserved = 0; unixFile *pFile = (unixFile*)id; - SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - assert( pFile ); - reserved = osAccess((const char*)pFile->lockingContext, 0)==0; - OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); - *pResOut = reserved; - return rc; + if( pFile->eFileLock>=SHARED_LOCK ){ + *pResOut = 0; + }else{ + *pResOut = osAccess((const char*)pFile->lockingContext, 0)==0; + } + OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, 0, *pResOut)); + return SQLITE_OK; } /* @@ -40826,54 +40927,33 @@ static int robust_flock(int fd, int op){ ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ - int rc = SQLITE_OK; - int reserved = 0; +#ifdef SQLITE_DEBUG unixFile *pFile = (unixFile*)id; +#else + UNUSED_PARAMETER(id); +#endif SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); + assert( pFile->eFileLock<=SHARED_LOCK ); - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - reserved = 1; - } + /* The flock VFS only ever takes exclusive locks (see function flockLock). + ** Therefore, if this connection is holding any lock at all, no other + ** connection may be holding a RESERVED lock. So set *pResOut to 0 + ** in this case. + ** + ** Or, this connection may be holding no lock. In that case, set *pResOut to + ** 0 as well. The caller will then attempt to take an EXCLUSIVE lock on the + ** db in order to roll the hot journal back. If there is another connection + ** holding a lock, that attempt will fail and an SQLITE_BUSY returned to + ** the user. With other VFS, we try to avoid this, in order to allow a reader + ** to proceed while a writer is preparing its transaction. But that won't + ** work with the flock VFS - as it always takes EXCLUSIVE locks - so it is + ** not a problem in this case. */ + *pResOut = 0; - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - /* attempt to get the lock */ - int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); - if( !lrc ){ - /* got the lock, unlock it */ - lrc = robust_flock(pFile->h, LOCK_UN); - if ( lrc ) { - int tErrno = errno; - /* unlock failed with an error */ - lrc = SQLITE_IOERR_UNLOCK; - storeLastErrno(pFile, tErrno); - rc = lrc; - } - } else { - int tErrno = errno; - reserved = 1; - /* someone else might have it reserved */ - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(lrc) ){ - storeLastErrno(pFile, tErrno); - rc = lrc; - } - } - } - OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); - -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (rc & 0xff) == SQLITE_IOERR ){ - rc = SQLITE_OK; - reserved=1; - } -#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - *pResOut = reserved; - return rc; + return SQLITE_OK; } /* @@ -42345,7 +42425,7 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ /* Forward declaration */ static int unixGetTempname(int nBuf, char *zBuf); -#ifndef SQLITE_OMIT_WAL +#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) static int unixFcntlExternalReader(unixFile*, int*); #endif @@ -42472,7 +42552,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ case SQLITE_FCNTL_EXTERNAL_READER: { -#ifndef SQLITE_OMIT_WAL +#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) return unixFcntlExternalReader((unixFile*)id, (int*)pArg); #else *(int*)pArg = 0; @@ -42561,7 +42641,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ pFile->sectorSize = fsInfo.f_bsize; pFile->deviceCharacteristics = /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind ** so it is ordered */ 0; @@ -42569,7 +42649,7 @@ static void setDeviceCharacteristics(unixFile *pFile){ pFile->sectorSize = fsInfo.f_bsize; pFile->deviceCharacteristics = /* full bitset of atomics from max sector size and smaller */ - ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | + (((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2) | SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind ** so it is ordered */ 0; @@ -42645,7 +42725,7 @@ static int unixGetpagesize(void){ #endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ -#ifndef SQLITE_OMIT_WAL +#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL) /* ** Object used to represent an shared memory buffer. @@ -54730,6 +54810,7 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( pPgHdr->pData = pPage->pBuf; pPgHdr->pExtra = (void *)&pPgHdr[1]; memset(pPgHdr->pExtra, 0, 8); + assert( EIGHT_BYTE_ALIGNMENT( pPgHdr->pExtra ) ); pPgHdr->pCache = pCache; pPgHdr->pgno = pgno; pPgHdr->flags = PGHDR_CLEAN; @@ -55476,7 +55557,8 @@ static int pcache1InitBulk(PCache1 *pCache){ do{ PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; pX->page.pBuf = zBulk; - pX->page.pExtra = &pX[1]; + pX->page.pExtra = (u8*)pX + ROUND8(sizeof(*pX)); + assert( EIGHT_BYTE_ALIGNMENT( pX->page.pExtra ) ); pX->isBulkLocal = 1; pX->isAnchor = 0; pX->pNext = pCache->pFree; @@ -55613,7 +55695,8 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ if( pPg==0 ) return 0; p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; p->page.pBuf = pPg; - p->page.pExtra = &p[1]; + p->page.pExtra = (u8*)p + ROUND8(sizeof(*p)); + assert( EIGHT_BYTE_ALIGNMENT( p->page.pExtra ) ); p->isBulkLocal = 0; p->isAnchor = 0; p->pLruPrev = 0; /* Initializing this saves a valgrind error */ @@ -61172,6 +61255,7 @@ static int pagerAcquireMapPage( return SQLITE_NOMEM_BKPT; } p->pExtra = (void *)&p[1]; + assert( EIGHT_BYTE_ALIGNMENT( p->pExtra ) ); p->flags = PGHDR_MMAP; p->nRef = 1; p->pPager = pPager; @@ -64955,7 +65039,7 @@ SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){ ** 28: Checksum-2 (second part of checksum for first 24 bytes of header). ** ** Immediately following the wal-header are zero or more frames. Each -** frame consists of a 24-byte frame-header followed by a bytes +** frame consists of a 24-byte frame-header followed by bytes ** of page data. The frame-header is six big-endian 32-bit unsigned ** integer values, as follows: ** @@ -65452,6 +65536,7 @@ struct Wal { #endif #ifdef SQLITE_ENABLE_SNAPSHOT WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ + int bGetSnapshot; /* Transaction opened for sqlite3_get_snapshot() */ #endif #ifdef SQLITE_ENABLE_SETLK_TIMEOUT sqlite3 *db; @@ -67344,7 +67429,7 @@ static int walHandleException(Wal *pWal){ /* ** Assert that the Wal.lockMask mask, which indicates the locks held -** by the connenction, is consistent with the Wal.readLock, Wal.writeLock +** by the connection, is consistent with the Wal.readLock, Wal.writeLock ** and Wal.ckptLock variables. To be used as: ** ** assert( walAssertLockmask(pWal) ); @@ -68008,7 +68093,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ SEH_INJECT_FAULT; if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame #ifdef SQLITE_ENABLE_SNAPSHOT - && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) + && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) #endif ){ /* The WAL has been completely backfilled (or it is empty). @@ -69408,7 +69493,20 @@ SQLITE_PRIVATE void sqlite3WalSnapshotOpen( Wal *pWal, sqlite3_snapshot *pSnapshot ){ - pWal->pSnapshot = (WalIndexHdr*)pSnapshot; + if( pSnapshot && ((WalIndexHdr*)pSnapshot)->iVersion==0 ){ + /* iVersion==0 means that this is a call to sqlite3_snapshot_get(). In + ** this case set the bGetSnapshot flag so that if the call to + ** sqlite3_snapshot_get() is about to read transaction on this wal + ** file, it does not take read-lock 0 if the wal file has been completely + ** checkpointed. Taking read-lock 0 would work, but then it would be + ** possible for a subsequent writer to destroy the snapshot even while + ** this connection is holding its read-transaction open. This is contrary + ** to user expectations, so we avoid it by not taking read-lock 0. */ + pWal->bGetSnapshot = 1; + }else{ + pWal->pSnapshot = (WalIndexHdr*)pSnapshot; + pWal->bGetSnapshot = 0; + } } /* @@ -75289,6 +75387,25 @@ SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ return ROUND8(sizeof(BtCursor)); } +#ifdef SQLITE_DEBUG +/* +** Return true if and only if the Btree object will be automatically +** closed with the BtCursor closes. This is used within assert() statements +** only. +*/ +SQLITE_PRIVATE int sqlite3BtreeClosesWithCursor( + Btree *pBtree, /* the btree object */ + BtCursor *pCur /* Corresponding cursor */ +){ + BtShared *pBt = pBtree->pBt; + if( (pBt->openFlags & BTREE_SINGLE)==0 ) return 0; + if( pBt->pCursor!=pCur ) return 0; + if( pCur->pNext!=0 ) return 0; + if( pCur->pBtree!=pBtree ) return 0; + return 1; +} +#endif + /* ** Initialize memory that will be converted into a BtCursor object. ** @@ -76532,7 +76649,7 @@ SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 && pIdxKey->errCode==SQLITE_OK ){ - pCur->curFlags &= ~BTCF_ValidOvfl; + pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast); if( !pCur->pPage->isInit ){ return SQLITE_CORRUPT_BKPT; } @@ -78110,7 +78227,8 @@ static int rebuildPage( if( j>(u32)usableSize ){ j = 0; } memcpy(&pTmp[j], &aData[j], usableSize - j); - for(k=0; ALWAYS(kixNx[k]<=i; k++){} + assert( pCArray->ixNx[NB*2-1]>i ); + for(k=0; pCArray->ixNx[k]<=i; k++){} pSrcEnd = pCArray->apEnd[k]; pData = pEnd; @@ -78193,7 +78311,8 @@ static int pageInsertArray( u8 *pEnd; /* Maximum extent of cell data */ assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ if( iEnd<=iFirst ) return 0; - for(k=0; ALWAYS(kixNx[k]<=i ; k++){} + assert( pCArray->ixNx[NB*2-1]>i ); + for(k=0; pCArray->ixNx[k]<=i ; k++){} pEnd = pCArray->apEnd[k]; while( 1 /*Exit by break*/ ){ int sz, rc; @@ -78478,6 +78597,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ b.szCell = &szCell; b.apEnd[0] = pPage->aDataEnd; b.ixNx[0] = 2; + b.ixNx[NB*2-1] = 0x7fffffff; rc = rebuildPage(&b, 0, 1, pNew); if( NEVER(rc) ){ releasePage(pNew); @@ -78713,7 +78833,9 @@ static int balance_nonroot( CellArray b; /* Parsed information on cells being balanced */ memset(abDone, 0, sizeof(abDone)); - memset(&b, 0, sizeof(b)); + assert( sizeof(b) - sizeof(b.ixNx) == offsetof(CellArray,ixNx) ); + memset(&b, 0, sizeof(b)-sizeof(b.ixNx[0])); + b.ixNx[NB*2-1] = 0x7fffffff; pBt = pParent->pBt; assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -79304,7 +79426,8 @@ static int balance_nonroot( iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); assert( iOvflSpace <= (int)pBt->pageSize ); - for(k=0; ALWAYS(kj ); + for(k=0; b.ixNx[k]<=j; k++){} pSrcEnd = b.apEnd[k]; if( SQLITE_OVERFLOW(pSrcEnd, pCell, pCell+sz) ){ rc = SQLITE_CORRUPT_BKPT; @@ -84316,7 +84439,8 @@ static int valueFromFunction( goto value_from_function_out; } for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); + rc = sqlite3Stat4ValueFromExpr(pCtx->pParse, pList->a[i].pExpr, aff, + &apVal[i]); if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; } } @@ -86250,6 +86374,12 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); break; } + case P4_SUBRTNSIG: { + SubrtnSig *pSig = (SubrtnSig*)p4; + sqlite3DbFree(db, pSig->zAff); + sqlite3DbFree(db, pSig); + break; + } } } @@ -86829,6 +86959,11 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ zP4 = pOp->p4.pTab->zName; break; } + case P4_SUBRTNSIG: { + SubrtnSig *pSig = pOp->p4.pSubrtnSig; + sqlite3_str_appendf(&x, "subrtnsig:%d,%s", pSig->selId, pSig->zAff); + break; + } default: { zP4 = pOp->p4.z; } @@ -89338,7 +89473,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem ** We must use separate SQLITE_NOINLINE functions here, since otherwise ** optimizer code movement causes gcov to become very confused. */ -#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) static int SQLITE_NOINLINE doubleLt(double a, double b){ return ar ); - testcase( x==r ); - return (xr); }else{ i64 y; if( r<-9223372036854775808.0 ) return +1; @@ -90369,6 +90497,13 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( } sqlite3DbNNFreeNN(db, preupdate.aNew); } + if( preupdate.apDflt ){ + int i; + for(i=0; inCol; i++){ + sqlite3ValueFree(preupdate.apDflt[i]); + } + sqlite3DbFree(db, preupdate.apDflt); + } } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -91997,6 +92132,17 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ ** ** The error code stored in database p->db is overwritten with the return ** value in any case. +** +** (tag-20240917-01) If vdbeUnbind(p,(u32)(i-1)) returns SQLITE_OK, +** that means all of the the following will be true: +** +** p!=0 +** p->pVar!=0 +** i>0 +** i<=p->nVar +** +** An assert() is normally added after vdbeUnbind() to help static analyzers +** realize this. */ static int vdbeUnbind(Vdbe *p, unsigned int i){ Mem *pVar; @@ -92054,6 +92200,7 @@ static int bindText( rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ if( zData!=0 ){ pVar = &p->aVar[i-1]; rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); @@ -92103,6 +92250,7 @@ SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue); sqlite3_mutex_leave(p->db->mutex); } @@ -92116,6 +92264,7 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue); sqlite3_mutex_leave(p->db->mutex); } @@ -92126,6 +92275,7 @@ SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ sqlite3_mutex_leave(p->db->mutex); } return rc; @@ -92141,6 +92291,7 @@ SQLITE_API int sqlite3_bind_pointer( Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor); sqlite3_mutex_leave(p->db->mutex); }else if( xDestructor ){ @@ -92222,6 +92373,7 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, (u32)(i-1)); if( rc==SQLITE_OK ){ + assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ #ifndef SQLITE_OMIT_INCRBLOB sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n); #else @@ -92581,7 +92733,30 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey1); }else if( iIdx>=p->pUnpacked->nField ){ - *ppValue = (sqlite3_value *)columnNullValue(); + /* This occurs when the table has been extended using ALTER TABLE + ** ADD COLUMN. The value to return is the default value of the column. */ + Column *pCol = &p->pTab->aCol[iIdx]; + if( pCol->iDflt>0 ){ + if( p->apDflt==0 ){ + int nByte = sizeof(sqlite3_value*)*p->pTab->nCol; + p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); + if( p->apDflt==0 ) goto preupdate_old_out; + } + if( p->apDflt[iIdx]==0 ){ + sqlite3_value *pVal = 0; + Expr *pDflt; + assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); + pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; + rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); + if( rc==SQLITE_OK && pVal==0 ){ + rc = SQLITE_CORRUPT_BKPT; + } + p->apDflt[iIdx] = pVal; + } + *ppValue = p->apDflt[iIdx]; + }else{ + *ppValue = (sqlite3_value *)columnNullValue(); + } }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ if( pMem->flags & (MEM_Int|MEM_IntReal) ){ testcase( pMem->flags & MEM_Int ); @@ -93134,6 +93309,104 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( /* #include "sqliteInt.h" */ /* #include "vdbeInt.h" */ +/* +** High-resolution hardware timer used for debugging and testing only. +*/ +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +/************** Include hwtime.h in the middle of vdbe.c *********************/ +/************** Begin file hwtime.h ******************************************/ +/* +** 2008 May 27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains inline asm code for retrieving "high-performance" +** counters for x86 and x86_64 class CPUs. +*/ +#ifndef SQLITE_HWTIME_H +#define SQLITE_HWTIME_H + +/* +** The following routine only works on Pentium-class (or newer) processors. +** It uses the RDTSC opcode to read the cycle count value out of the +** processor and returns that value. This can be used for high-res +** profiling. +*/ +#if !defined(__STRICT_ANSI__) && \ + (defined(__GNUC__) || defined(_MSC_VER)) && \ + (defined(i386) || defined(__i386__) || defined(_M_IX86)) + + #if defined(__GNUC__) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + + #elif defined(_MSC_VER) + + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ + __asm { + rdtsc + ret ; return value at EDX:EAX + } + } + + #endif + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned long long retval; + unsigned long junk; + __asm__ __volatile__ ("\n\ + 1: mftbu %1\n\ + mftb %L0\n\ + mftbu %0\n\ + cmpw %0,%1\n\ + bne 1b" + : "=r" (retval), "=r" (junk)); + return retval; + } + +#else + + /* + ** asm() is needed for hardware timing support. Without asm(), + ** disable the sqlite3Hwtime() routine. + ** + ** sqlite3Hwtime() is only used for some obscure debugging + ** and analysis configurations, not in any deliverable, so this + ** should not be a great loss. + */ +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } + +#endif + +#endif /* !defined(SQLITE_HWTIME_H) */ + +/************** End of hwtime.h **********************************************/ +/************** Continuing where we left off in vdbe.c ***********************/ +#endif + /* ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are @@ -94327,7 +94600,7 @@ case OP_HaltIfNull: { /* in3 */ /* no break */ deliberate_fall_through } -/* Opcode: Halt P1 P2 * P4 P5 +/* Opcode: Halt P1 P2 P3 P4 P5 ** ** Exit immediately. All open cursors, etc are closed ** automatically. @@ -94340,18 +94613,22 @@ case OP_HaltIfNull: { /* in3 */ ** then back out all changes that have occurred during this execution of the ** VDBE, but do not rollback the transaction. ** -** If P4 is not null then it is an error message string. +** If P3 is not zero and P4 is NULL, then P3 is a register that holds the +** text of an error message. ** -** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. +** If P3 is zero and P4 is not null then the error message string is held +** in P4. +** +** P5 is a value between 1 and 4, inclusive, then the P4 error message +** string is modified as follows: ** -** 0: (no change) ** 1: NOT NULL constraint failed: P4 ** 2: UNIQUE constraint failed: P4 ** 3: CHECK constraint failed: P4 ** 4: FOREIGN KEY constraint failed: P4 ** -** If P5 is not zero and P4 is NULL, then everything after the ":" is -** omitted. +** If P3 is zero and P5 is not zero and P4 is NULL, then everything after +** the ":" is omitted. ** ** There is an implied "Halt 0 0 0" instruction inserted at the very end of ** every program. So a jump past the last instruction of the program @@ -94364,6 +94641,9 @@ case OP_Halt: { #ifdef SQLITE_DEBUG if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } #endif + assert( pOp->p4type==P4_NOTUSED + || pOp->p4type==P4_STATIC + || pOp->p4type==P4_DYNAMIC ); /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates ** something is wrong with the code generator. Raise an assertion in order @@ -94394,7 +94674,12 @@ case OP_Halt: { p->errorAction = (u8)pOp->p2; assert( pOp->p5<=4 ); if( p->rc ){ - if( pOp->p5 ){ + if( pOp->p3>0 && pOp->p4type==P4_NOTUSED ){ + const char *zErr; + assert( pOp->p3<=(p->nMem + 1 - p->nCursor) ); + zErr = sqlite3ValueText(&aMem[pOp->p3], SQLITE_UTF8); + sqlite3VdbeError(p, "%s", zErr); + }else if( pOp->p5 ){ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", "FOREIGN KEY" }; testcase( pOp->p5==1 ); @@ -95197,7 +95482,7 @@ case OP_RealAffinity: { /* in1 */ } #endif -#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE) +#if !defined(SQLITE_OMIT_CAST) || !defined(SQLITE_OMIT_ANALYZE) /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** @@ -97437,23 +97722,23 @@ case OP_OpenWrite: if( pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = pDb->pSchema->file_format; } + if( pOp->p5 & OPFLAG_P2ISREG ){ + assert( p2>0 ); + assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); + pIn2 = &aMem[p2]; + assert( memIsValid(pIn2) ); + assert( (pIn2->flags & MEM_Int)!=0 ); + sqlite3VdbeMemIntegerify(pIn2); + p2 = (int)pIn2->u.i; + /* The p2 value always comes from a prior OP_CreateBtree opcode and + ** that opcode will always set the p2 value to 2 or more or else fail. + ** If there were a failure, the prepared statement would have halted + ** before reaching this instruction. */ + assert( p2>=2 ); + } }else{ wrFlag = 0; - } - if( pOp->p5 & OPFLAG_P2ISREG ){ - assert( p2>0 ); - assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); - assert( pOp->opcode==OP_OpenWrite ); - pIn2 = &aMem[p2]; - assert( memIsValid(pIn2) ); - assert( (pIn2->flags & MEM_Int)!=0 ); - sqlite3VdbeMemIntegerify(pIn2); - p2 = (int)pIn2->u.i; - /* The p2 value always comes from a prior OP_CreateBtree opcode and - ** that opcode will always set the p2 value to 2 or more or else fail. - ** If there were a failure, the prepared statement would have halted - ** before reaching this instruction. */ - assert( p2>=2 ); + assert( (pOp->p5 & OPFLAG_P2ISREG)==0 ); } if( pOp->p4type==P4_KEYINFO ){ pKeyInfo = pOp->p4.pKeyInfo; @@ -97631,7 +97916,10 @@ case OP_OpenEphemeral: { /* ncycle */ } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); if( rc ){ + assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); sqlite3BtreeClose(pCx->ub.pBtx); + }else{ + assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) ); } } } @@ -98409,6 +98697,7 @@ case OP_Found: { /* jump, in3, ncycle */ r.pKeyInfo = pC->pKeyInfo; r.default_rc = 0; #ifdef SQLITE_DEBUG + (void)sqlite3FaultSim(50); /* For use by --counter in TH3 */ for(ii=0; iip4type==P4_FUNCDEF ); n = pOp->p5; assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); - pCtx = sqlite3DbMallocRawNN(db, n*sizeof(sqlite3_value*) + - (sizeof(pCtx[0]) + sizeof(Mem) - sizeof(sqlite3_value*))); + + /* Allocate space for (a) the context object and (n-1) extra pointers + ** to append to the sqlite3_context.argv[1] array, and (b) a memory + ** cell in which to store the accumulation. Be careful that the memory + ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. + ** + ** Note: We could avoid this by using a regular memory cell from aMem[] for + ** the accumulator, instead of allocating one here. */ + nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) ); + pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); if( pCtx==0 ) goto no_mem; - pCtx->pMem = 0; - pCtx->pOut = (Mem*)&(pCtx->argv[n]); + pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); + assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); + sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null); + pCtx->pMem = 0; pCtx->pFunc = pOp->p4.pFunc; pCtx->iOp = (int)(pOp - aOp); pCtx->pVdbe = p; @@ -102116,14 +102416,29 @@ case OP_ReleaseReg: { /* Opcode: Noop * * * * * ** -** Do nothing. This instruction is often useful as a jump -** destination. +** Do nothing. Continue downward to the next opcode. */ -/* -** The magic Explain opcode are only inserted when explain==2 (which -** is to say when the EXPLAIN QUERY PLAN syntax is used.) -** This opcode records information from the optimizer. It is the -** the same as a no-op. This opcodesnever appears in a real VM program. +/* Opcode: Explain P1 P2 P3 P4 * +** +** This is the same as OP_Noop during normal query execution. The +** purpose of this opcode is to hold information about the query +** plan for the purpose of EXPLAIN QUERY PLAN output. +** +** The P4 value is human-readable text that describes the query plan +** element. Something like "SCAN t1" or "SEARCH t2 USING INDEX t2x1". +** +** The P1 value is the ID of the current element and P2 is the parent +** element for the case of nested query plan elements. If P2 is zero +** then this element is a top-level element. +** +** For loop elements, P3 is the estimated code of each invocation of this +** element. +** +** As with all opcodes, the meanings of the parameters for OP_Explain +** are subject to change from one release to the next. Applications +** should not attempt to interpret or use any of the information +** contained in the OP_Explain opcode. The information provided by this +** opcode is intended for testing and debugging use only. */ default: { /* This is really OP_Noop, OP_Explain */ assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain ); @@ -102450,6 +102765,11 @@ SQLITE_API int sqlite3_blob_open( pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } + if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){ + pTab = 0; + sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s", + zTable); + } #ifndef SQLITE_OMIT_VIEW if( pTab && IsView(pTab) ){ pTab = 0; @@ -103357,13 +103677,14 @@ static int vdbePmaReadBlob( while( nRem>0 ){ int rc; /* vdbePmaReadBlob() return code */ int nCopy; /* Number of bytes to copy */ - u8 *aNext; /* Pointer to buffer to copy data from */ + u8 *aNext = 0; /* Pointer to buffer to copy data from */ nCopy = nRem; if( nRem>p->nBuffer ) nCopy = p->nBuffer; rc = vdbePmaReadBlob(p, nCopy, &aNext); if( rc!=SQLITE_OK ) return rc; assert( aNext!=p->aAlloc ); + assert( aNext!=0 ); memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy); nRem -= nCopy; } @@ -106633,7 +106954,9 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ + if( pItem->fg.isSubquery + && sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect) + ){ return WRC_Abort; } if( pItem->fg.isTabFunc @@ -106939,7 +107262,7 @@ static void extendFJMatch( if( pNew ){ pNew->iTable = pMatch->iCursor; pNew->iColumn = iColumn; - pNew->y.pTab = pMatch->pTab; + pNew->y.pTab = pMatch->pSTab; assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); ExprSetProperty(pNew, EP_CanBeNull); *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew); @@ -107070,10 +107393,10 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ u8 hCol; - pTab = pItem->pTab; + pTab = pItem->pSTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 || pParse->nErr ); - assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem)); if( pItem->fg.isNestedFrom ){ /* In this case, pItem is a subquery that has been formed from a ** parenthesized subset of the FROM clause terms. Example: @@ -107082,8 +107405,12 @@ static int lookupName( ** This pItem -------------^ */ int hit = 0; - assert( pItem->pSelect!=0 ); - pEList = pItem->pSelect->pEList; + Select *pSel; + assert( pItem->fg.isSubquery ); + assert( pItem->u4.pSubq!=0 ); + pSel = pItem->u4.pSubq->pSelect; + assert( pSel!=0 ); + pEList = pSel->pEList; assert( pEList!=0 ); assert( pEList->nExpr==pTab->nCol ); for(j=0; jnExpr; j++){ @@ -107206,9 +107533,9 @@ static int lookupName( */ if( cntTab==0 || (cntTab==1 - && ALWAYS(pMatch!=0) - && ALWAYS(pMatch->pTab!=0) - && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0 + && pMatch!=0 + && ALWAYS(pMatch->pSTab!=0) + && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 && (pTab->tabFlags & TF_Ephemeral)==0) ){ cntTab = 1; @@ -107229,7 +107556,7 @@ static int lookupName( if( pMatch ){ pExpr->iTable = pMatch->iCursor; assert( ExprUseYTab(pExpr) ); - pExpr->y.pTab = pMatch->pTab; + pExpr->y.pTab = pMatch->pSTab; if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } @@ -107271,7 +107598,7 @@ static int lookupName( if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ - pTab = pUpsert->pUpsertSrc->a[0].pTab; + pTab = pUpsert->pUpsertSrc->a[0].pSTab; pExpr->iTable = EXCLUDED_TABLE_NUMBER; } } @@ -107354,11 +107681,11 @@ static int lookupName( && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) - && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) + && ALWAYS(VisibleRowid(pMatch->pSTab) || pMatch->fg.isNestedFrom) ){ cnt = cntTab; #if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 - if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){ + if( pMatch->pSTab!=0 && IsView(pMatch->pSTab) ){ eNewExprOp = TK_NULL; } #endif @@ -107595,7 +107922,7 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr SrcItem *pItem = &pSrc->a[iSrc]; Table *pTab; assert( ExprUseYTab(p) ); - pTab = p->y.pTab = pItem->pTab; + pTab = p->y.pTab = pItem->pSTab; p->iTable = pItem->iCursor; if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; @@ -107714,7 +108041,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pItem = pSrcList->a; pExpr->op = TK_COLUMN; assert( ExprUseYTab(pExpr) ); - pExpr->y.pTab = pItem->pTab; + pExpr->y.pTab = pItem->pSTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn--; pExpr->affExpr = SQLITE_AFF_INTEGER; @@ -107839,8 +108166,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Resolve function names */ case TK_FUNCTION: { - ExprList *pList = pExpr->x.pList; /* The argument list */ - int n = pList ? pList->nExpr : 0; /* Number of arguments */ + ExprList *pList; /* The argument list */ + int n; /* Number of arguments */ int no_such_func = 0; /* True if no such function exists */ int wrong_num_args = 0; /* True if wrong number of arguments */ int is_agg = 0; /* True if is an aggregate function */ @@ -107853,6 +108180,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); + pList = pExpr->x.pList; + n = pList ? pList->nExpr : 0; zId = pExpr->u.zToken; pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); if( pDef==0 ){ @@ -107901,6 +108230,24 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } } #endif + + /* If the function may call sqlite3_value_subtype(), then set the + ** EP_SubtArg flag on all of its argument expressions. This prevents + ** where.c from replacing the expression with a value read from an + ** index on the same expression, which will not have the correct + ** subtype. Also set the flag if the function expression itself is + ** an EP_SubtArg expression. In this case subtypes are required as + ** the function may return a value with a subtype back to its + ** caller using sqlite3_result_value(). */ + if( (pDef->funcFlags & SQLITE_SUBTYPE) + || ExprHasProperty(pExpr, EP_SubtArg) + ){ + int ii; + for(ii=0; iia[ii].pExpr, EP_SubtArg); + } + } + if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ /* For the purposes of the EP_ConstFunc flag, date and time ** functions and other functions that change slowly are considered @@ -108020,9 +108367,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); } #ifndef SQLITE_OMIT_WINDOWFUNC - if( pWin ){ + if( pWin && pParse->nErr==0 ){ Select *pSel = pNC->pWinSelect; - assert( pWin==0 || (ExprUseYWin(pExpr) && pWin==pExpr->y.pWin) ); + assert( ExprUseYWin(pExpr) && pWin==pExpr->y.pWin ); if( IN_RENAME_OBJECT==0 ){ sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); if( pParse->db->mallocFailed ) break; @@ -108229,7 +108576,7 @@ static int resolveOrderByTermToExprList( int rc; /* Return code from subprocedures */ u8 savedSuppErr; /* Saved value of db->suppressErr */ - assert( sqlite3ExprIsInteger(pE, &i)==0 ); + assert( sqlite3ExprIsInteger(pE, &i, 0)==0 ); pEList = pSelect->pEList; /* Resolve all names in the ORDER BY term expression @@ -108328,7 +108675,7 @@ static int resolveCompoundOrderBy( if( pItem->fg.done ) continue; pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); if( NEVER(pE==0) ) continue; - if( sqlite3ExprIsInteger(pE, &iCol) ){ + if( sqlite3ExprIsInteger(pE, &iCol, 0) ){ if( iCol<=0 || iCol>pEList->nExpr ){ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); return 1; @@ -108513,7 +108860,7 @@ static int resolveOrderGroupBy( continue; } } - if( sqlite3ExprIsInteger(pE2, &iCol) ){ + if( sqlite3ExprIsInteger(pE2, &iCol, 0) ){ /* The ORDER BY term is an integer constant. Again, set the column ** number so that sqlite3ResolveOrderGroupBy() will convert the ** order-by term to a copy of the result-set expression */ @@ -108604,7 +108951,11 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** moves the pOrderBy down to the sub-query. It will be moved back ** after the names have been resolved. */ if( p->selFlags & SF_Converted ){ - Select *pSub = p->pSrc->a[0].pSelect; + Select *pSub; + assert( p->pSrc->a[0].fg.isSubquery ); + assert( p->pSrc->a[0].u4.pSubq!=0 ); + pSub = p->pSrc->a[0].u4.pSubq->pSelect; + assert( pSub!=0 ); assert( p->pSrc->nSrc==1 && p->pOrderBy ); assert( pSub->pPrior && pSub->pOrderBy==0 ); pSub->pOrderBy = p->pOrderBy; @@ -108616,13 +108967,16 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; - assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ - if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ + assert( pItem->zName!=0 + || pItem->fg.isSubquery ); /* Test of tag-20240424-1*/ + if( pItem->fg.isSubquery + && (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0 + ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; if( pItem->zName ) pParse->zAuthContext = pItem->zName; - sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); + sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; if( pParse->nErr ) return WRC_Abort; assert( db->mallocFailed==0 ); @@ -108724,7 +109078,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** These integers will be replaced by copies of the corresponding result ** set expressions by the call to resolveOrderGroupBy() below. */ if( p->selFlags & SF_Converted ){ - Select *pSub = p->pSrc->a[0].pSelect; + Select *pSub; + assert( p->pSrc->a[0].fg.isSubquery ); + pSub = p->pSrc->a[0].u4.pSubq->pSelect; + assert( pSub!=0 ); p->pOrderBy = pSub->pOrderBy; pSub->pOrderBy = 0; } @@ -108991,7 +109348,7 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference( if( pTab ){ sSrc.nSrc = 1; sSrc.a[0].zName = pTab->zName; - sSrc.a[0].pTab = pTab; + sSrc.a[0].pSTab = pTab; sSrc.a[0].iCursor = -1; if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP @@ -109096,7 +109453,9 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){ op = pExpr->op; continue; } - if( op!=TK_REGISTER || (op = pExpr->op2)==TK_REGISTER ) break; + if( op!=TK_REGISTER ) break; + op = pExpr->op2; + if( NEVER( op==TK_REGISTER ) ) break; } return pExpr->affExpr; } @@ -110886,15 +111245,30 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla SrcItem *pNewItem = &pNew->a[i]; const SrcItem *pOldItem = &p->a[i]; Table *pTab; - pNewItem->pSchema = pOldItem->pSchema; - pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); + pNewItem->fg = pOldItem->fg; + if( pOldItem->fg.isSubquery ){ + Subquery *pNewSubq = sqlite3DbMallocRaw(db, sizeof(Subquery)); + if( pNewSubq==0 ){ + assert( db->mallocFailed ); + pNewItem->fg.isSubquery = 0; + }else{ + memcpy(pNewSubq, pOldItem->u4.pSubq, sizeof(*pNewSubq)); + pNewSubq->pSelect = sqlite3SelectDup(db, pNewSubq->pSelect, flags); + if( pNewSubq->pSelect==0 ){ + sqlite3DbFree(db, pNewSubq); + pNewSubq = 0; + pNewItem->fg.isSubquery = 0; + } + } + pNewItem->u4.pSubq = pNewSubq; + }else if( pOldItem->fg.fixedSchema ){ + pNewItem->u4.pSchema = pOldItem->u4.pSchema; + }else{ + pNewItem->u4.zDatabase = sqlite3DbStrDup(db, pOldItem->u4.zDatabase); + } pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); - pNewItem->fg = pOldItem->fg; pNewItem->iCursor = pOldItem->iCursor; - pNewItem->addrFillSub = pOldItem->addrFillSub; - pNewItem->regReturn = pOldItem->regReturn; - pNewItem->regResult = pOldItem->regResult; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); }else if( pNewItem->fg.isTabFunc ){ @@ -110907,11 +111281,10 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } - pTab = pNewItem->pTab = pOldItem->pTab; + pTab = pNewItem->pSTab = pOldItem->pSTab; if( pTab ){ pTab->nTabRef++; } - pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); if( pOldItem->fg.isUsing ){ assert( pNewItem->fg.isUsing ); pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing); @@ -110985,7 +111358,6 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int fla pp = &pNew->pPrior; pNext = pNew; } - return pRet; } #else @@ -111800,8 +112172,12 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){ ** to fit in a 32-bit integer, return 1 and put the value of the integer ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. +** +** If the pParse pointer is provided, then allow the expression p to be +** a parameter (TK_VARIABLE) that is bound to an integer. +** But if pParse is NULL, then p must be a pure integer literal. */ -SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue){ +SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue, Parse *pParse){ int rc = 0; if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */ @@ -111816,18 +112192,38 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue){ } switch( p->op ){ case TK_UPLUS: { - rc = sqlite3ExprIsInteger(p->pLeft, pValue); + rc = sqlite3ExprIsInteger(p->pLeft, pValue, 0); break; } case TK_UMINUS: { int v = 0; - if( sqlite3ExprIsInteger(p->pLeft, &v) ){ + if( sqlite3ExprIsInteger(p->pLeft, &v, 0) ){ assert( ((unsigned int)v)!=0x80000000 ); *pValue = -v; rc = 1; } break; } + case TK_VARIABLE: { + sqlite3_value *pVal; + if( pParse==0 ) break; + if( NEVER(pParse->pVdbe==0) ) break; + if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) break; + sqlite3VdbeSetVarmask(pParse->pVdbe, p->iColumn); + pVal = sqlite3VdbeGetBoundValue(pParse->pReprepare, p->iColumn, + SQLITE_AFF_BLOB); + if( pVal ){ + if( sqlite3_value_type(pVal)==SQLITE_INTEGER ){ + sqlite3_int64 vv = sqlite3_value_int64(pVal); + if( vv == (vv & 0x7fffffff) ){ /* non-negative numbers only */ + *pValue = (int)vv; + rc = 1; + } + } + sqlite3ValueFree(pVal); + } + break; + } default: break; } return rc; @@ -111981,8 +112377,8 @@ static Select *isCandidateForInOpt(const Expr *pX){ pSrc = p->pSrc; assert( pSrc!=0 ); if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ - if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */ - pTab = pSrc->a[0].pTab; + if( pSrc->a[0].fg.isSubquery) return 0;/* FROM is not a subquery or view */ + pTab = pSrc->a[0].pSTab; assert( pTab!=0 ); assert( !IsView(pTab) ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ @@ -112165,7 +112561,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ - pTab = p->pSrc->a[0].pTab; + pTab = p->pSrc->a[0].pSTab; /* Code an OP_Transaction and OP_TableLock for . */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -112405,6 +112801,49 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){ } } +#ifndef SQLITE_OMIT_SUBQUERY +/* +** Scan all previously generated bytecode looking for an OP_BeginSubrtn +** that is compatible with pExpr. If found, add the y.sub values +** to pExpr and return true. If not found, return false. +*/ +static int findCompatibleInRhsSubrtn( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* IN operator with RHS that we want to reuse */ + SubrtnSig *pNewSig /* Signature for the IN operator */ +){ + VdbeOp *pOp, *pEnd; + SubrtnSig *pSig; + Vdbe *v; + + if( pNewSig==0 ) return 0; + if( (pParse->mSubrtnSig & (1<<(pNewSig->selId&7)))==0 ) return 0; + assert( pExpr->op==TK_IN ); + assert( !ExprUseYSub(pExpr) ); + assert( ExprUseXSelect(pExpr) ); + assert( pExpr->x.pSelect!=0 ); + assert( (pExpr->x.pSelect->selFlags & SF_All)==0 ); + v = pParse->pVdbe; + assert( v!=0 ); + pOp = sqlite3VdbeGetOp(v, 1); + pEnd = sqlite3VdbeGetLastOp(v); + for(; pOpp4type!=P4_SUBRTNSIG ) continue; + assert( pOp->opcode==OP_BeginSubrtn ); + pSig = pOp->p4.pSubrtnSig; + assert( pSig!=0 ); + if( pNewSig->selId!=pSig->selId ) continue; + if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue; + pExpr->y.sub.iAddr = pSig->iAddr; + pExpr->y.sub.regReturn = pSig->regReturn; + pExpr->iTable = pSig->iTable; + ExprSetProperty(pExpr, EP_Subrtn); + return 1; + } + return 0; +} +#endif /* SQLITE_OMIT_SUBQUERY */ + #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate code that will construct an ephemeral table containing all terms @@ -112454,11 +112893,28 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( ** and reuse it many names. */ if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){ - /* Reuse of the RHS is allowed */ - /* If this routine has already been coded, but the previous code - ** might not have been invoked yet, so invoke it now as a subroutine. + /* Reuse of the RHS is allowed + ** + ** Compute a signature for the RHS of the IN operator to facility + ** finding and reusing prior instances of the same IN operator. */ - if( ExprHasProperty(pExpr, EP_Subrtn) ){ + SubrtnSig *pSig = 0; + assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 ); + if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){ + pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0])); + if( pSig ){ + pSig->selId = pExpr->x.pSelect->selId; + pSig->zAff = exprINAffinity(pParse, pExpr); + } + } + + /* Check to see if there is a prior materialization of the RHS of + ** this IN operator. If there is, then make use of that prior + ** materialization rather than recomputing it. + */ + if( ExprHasProperty(pExpr, EP_Subrtn) + || findCompatibleInRhsSubrtn(pParse, pExpr, pSig) + ){ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); if( ExprUseXSelect(pExpr) ){ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", @@ -112470,6 +112926,10 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( assert( iTab!=pExpr->iTable ); sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); sqlite3VdbeJumpHere(v, addrOnce); + if( pSig ){ + sqlite3DbFree(pParse->db, pSig->zAff); + sqlite3DbFree(pParse->db, pSig); + } return; } @@ -112480,7 +112940,13 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( pExpr->y.sub.regReturn = ++pParse->nMem; pExpr->y.sub.iAddr = sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; - + if( pSig ){ + pSig->iAddr = pExpr->y.sub.iAddr; + pSig->regReturn = pExpr->y.sub.regReturn; + pSig->iTable = iTab; + pParse->mSubrtnSig = 1 << (pSig->selId&7); + sqlite3VdbeChangeP4(v, -1, (const char*)pSig, P4_SUBRTNSIG); + } addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } @@ -112521,15 +112987,30 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( SelectDest dest; int i; int rc; + int addrBloom = 0; sqlite3SelectDestInit(&dest, SRT_Set, iTab); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; + if( addrOnce && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + int regBloom = ++pParse->nMem; + addrBloom = sqlite3VdbeAddOp2(v, OP_Blob, 10000, regBloom); + VdbeComment((v, "Bloom filter")); + dest.iSDParm2 = regBloom; + } testcase( pSelect->selFlags & SF_Distinct ); testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ pCopy = sqlite3SelectDup(pParse->db, pSelect, 0); rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest); sqlite3SelectDelete(pParse->db, pCopy); sqlite3DbFree(pParse->db, dest.zAffSdst); + if( addrBloom ){ + sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2; + if( dest.iSDParm2==0 ){ + sqlite3VdbeChangeToNoop(v, addrBloom); + }else{ + sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2; + } + } if( rc ){ sqlite3KeyInfoUnref(pKeyInfo); return; @@ -112827,9 +113308,7 @@ static void sqlite3ExprCodeIN( if( sqlite3ExprCheckIN(pParse, pExpr) ) return; zAff = exprINAffinity(pParse, pExpr); nVector = sqlite3ExprVectorSize(pExpr->pLeft); - aiMap = (int*)sqlite3DbMallocZero( - pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1 - ); + aiMap = (int*)sqlite3DbMallocZero(pParse->db, nVector*sizeof(int)); if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; /* Attempt to compute the RHS. After this step, if anything other than @@ -112972,6 +113451,15 @@ static void sqlite3ExprCodeIN( sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); if( destIfFalse==destIfNull ){ /* Combine Step 3 and Step 5 into a single opcode */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); + assert( pOp->opcode==OP_Once || pParse->nErr ); + if( pOp->opcode==OP_Once && pOp->p3>0 ){ + assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); + sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, + rLhs, nVector); VdbeCoverage(v); + } + } sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, rLhs, nVector); VdbeCoverage(v); goto sqlite3ExprCodeIN_finished; @@ -113254,13 +113742,17 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n ** register iReg. The caller must ensure that iReg already contains ** the correct value for the expression. */ -static void exprToRegister(Expr *pExpr, int iReg){ +SQLITE_PRIVATE void sqlite3ExprToRegister(Expr *pExpr, int iReg){ Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); if( NEVER(p==0) ) return; - p->op2 = p->op; - p->op = TK_REGISTER; - p->iTable = iReg; - ExprClearProperty(p, EP_Skip); + if( p->op==TK_REGISTER ){ + assert( p->iTable==iReg ); + }else{ + p->op2 = p->op; + p->op = TK_REGISTER; + p->iTable = iReg; + ExprClearProperty(p, EP_Skip); + } } /* @@ -113430,6 +113922,59 @@ static int exprCodeInlineFunction( return target; } +/* +** Expression Node callback for sqlite3ExprCanReturnSubtype(). +** +** Only a function call is able to return a subtype. So if the node +** is not a function call, return WRC_Prune immediately. +** +** A function call is able to return a subtype if it has the +** SQLITE_RESULT_SUBTYPE property. +** +** Assume that every function is able to pass-through a subtype from +** one of its argument (using sqlite3_result_value()). Most functions +** are not this way, but we don't have a mechanism to distinguish those +** that are from those that are not, so assume they all work this way. +** That means that if one of its arguments is another function and that +** other function is able to return a subtype, then this function is +** able to return a subtype. +*/ +static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ + int n; + FuncDef *pDef; + sqlite3 *db; + if( pExpr->op!=TK_FUNCTION ){ + return WRC_Prune; + } + assert( ExprUseXList(pExpr) ); + db = pWalker->pParse->db; + n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ + pWalker->eCode = 1; + return WRC_Prune; + } + return WRC_Continue; +} + +/* +** Return TRUE if expression pExpr is able to return a subtype. +** +** A TRUE return does not guarantee that a subtype will be returned. +** It only indicates that a subtype return is possible. False positives +** are acceptable as they only disable an optimization. False negatives, +** on the other hand, can lead to incorrect answers. +*/ +static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ + Walker w; + memset(&w, 0, sizeof(w)); + w.pParse = pParse; + w.xExprCallback = exprNodeCanReturnSubtype; + sqlite3WalkExpr(&w, pExpr); + return w.eCode; +} + + /* ** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr. ** If it is, then resolve the expression by reading from the index and @@ -113462,6 +114007,17 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup( continue; } + + /* Functions that might set a subtype should not be replaced by the + ** value taken from an expression index if they are themselves an + ** argument to another scalar function or aggregate. + ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */ + if( ExprHasProperty(pExpr, EP_SubtArg) + && sqlite3ExprCanReturnSubtype(pParse, pExpr) + ){ + continue; + } + v = pParse->pVdbe; assert( v!=0 ); if( p->bMaybeNullRow ){ @@ -114263,7 +114819,7 @@ expr_code_doover: break; } testcase( pX->op==TK_COLUMN ); - exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); + sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); testcase( regFree1==0 ); memset(&opCompare, 0, sizeof(opCompare)); opCompare.op = TK_EQ; @@ -114317,15 +114873,14 @@ expr_code_doover: } assert( !ExprHasProperty(pExpr, EP_IntValue) ); if( pExpr->affExpr==OE_Ignore ){ - sqlite3VdbeAddOp4( - v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); + sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, OE_Ignore); VdbeCoverage(v); }else{ - sqlite3HaltConstraint(pParse, + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + sqlite3VdbeAddOp3(v, OP_Halt, pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, - pExpr->affExpr, pExpr->u.zToken, 0, 0); + pExpr->affExpr, r1); } - break; } #endif @@ -114614,7 +115169,7 @@ static void exprCodeBetween( compRight.op = TK_LE; compRight.pLeft = pDel; compRight.pRight = pExpr->x.pList->a[1].pExpr; - exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); + sqlite3ExprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ @@ -117508,8 +118063,9 @@ static int renameResolveTrigger(Parse *pParse){ int i; for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ SrcItem *p = &pStep->pFrom->a[i]; - if( p->pSelect ){ - sqlite3SelectPrep(pParse, p->pSelect, 0); + if( p->fg.isSubquery ){ + assert( p->u4.pSubq!=0 ); + sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); } } } @@ -117577,8 +118133,12 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ } if( pStep->pFrom ){ int i; - for(i=0; ipFrom->nSrc; i++){ - sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].pSelect); + SrcList *pFrom = pStep->pFrom; + for(i=0; inSrc; i++){ + if( pFrom->a[i].fg.isSubquery ){ + assert( pFrom->a[i].u4.pSubq!=0 ); + sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect); + } } } } @@ -117825,7 +118385,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ } for(i=0; inSrc; i++){ SrcItem *pItem = &pSrc->a[i]; - if( pItem->pTab==p->pTab ){ + if( pItem->pSTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } } @@ -120253,12 +120813,13 @@ static int loadStatTbl( while( sqlite3_step(pStmt)==SQLITE_ROW ){ int nIdxCol = 1; /* Number of columns in stat4 records */ - char *zIndex; /* Index name */ - Index *pIdx; /* Pointer to the index object */ - int nSample; /* Number of samples */ - int nByte; /* Bytes of space required */ - int i; /* Bytes of space required */ - tRowcnt *pSpace; + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + int nSample; /* Number of samples */ + i64 nByte; /* Bytes of space required */ + i64 i; /* Bytes of space required */ + tRowcnt *pSpace; /* Available allocated memory space */ + u8 *pPtr; /* Available memory as a u8 for easier manipulation */ zIndex = (char *)sqlite3_column_text(pStmt, 0); if( zIndex==0 ) continue; @@ -120278,7 +120839,7 @@ static int loadStatTbl( } pIdx->nSampleCol = nIdxCol; pIdx->mxSample = nSample; - nByte = sizeof(IndexSample) * nSample; + nByte = ROUND8(sizeof(IndexSample) * nSample); nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ @@ -120287,7 +120848,10 @@ static int loadStatTbl( sqlite3_finalize(pStmt); return SQLITE_NOMEM_BKPT; } - pSpace = (tRowcnt*)&pIdx->aSample[nSample]; + pPtr = (u8*)pIdx->aSample; + pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0])); + pSpace = (tRowcnt*)pPtr; + assert( EIGHT_BYTE_ALIGNMENT( pSpace ) ); pIdx->aAvgEq = pSpace; pSpace += nIdxCol; pIdx->pTable->tabFlags |= TF_HasStat4; for(i=0; ia; inSrc; i++, pItem++){ - if( pFix->bTemp==0 ){ - if( pItem->zDatabase ){ - if( iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){ + if( pFix->bTemp==0 && pItem->fg.isSubquery==0 ){ + if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ + if( iDb!=sqlite3FindDbName(db, pItem->u4.zDatabase) ){ sqlite3ErrorMsg(pFix->pParse, "%s %T cannot reference objects in database %s", - pFix->zType, pFix->pName, pItem->zDatabase); + pFix->zType, pFix->pName, pItem->u4.zDatabase); return WRC_Abort; } - sqlite3DbFree(db, pItem->zDatabase); - pItem->zDatabase = 0; + sqlite3DbFree(db, pItem->u4.zDatabase); pItem->fg.notCte = 1; + pItem->fg.hadSchema = 1; } - pItem->pSchema = pFix->pSchema; + pItem->u4.pSchema = pFix->pSchema; pItem->fg.fromDDL = 1; + pItem->fg.fixedSchema = 1; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) if( pList->a[i].fg.isUsing==0 @@ -121261,7 +121826,7 @@ SQLITE_PRIVATE void sqlite3AuthRead( assert( pTabList ); for(iSrc=0; iSrcnSrc; iSrc++){ if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ - pTab = pTabList->a[iSrc].pTab; + pTab = pTabList->a[iSrc].pSTab; break; } } @@ -121864,12 +122429,12 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem( SrcItem *p ){ const char *zDb; - assert( p->pSchema==0 || p->zDatabase==0 ); - if( p->pSchema ){ - int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema); + if( p->fg.fixedSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema); zDb = pParse->db->aDb[iDb].zDbSName; }else{ - zDb = p->zDatabase; + assert( !p->fg.isSubquery ); + zDb = p->u4.zDatabase; } return sqlite3LocateTable(pParse, flags, p->zName, zDb); } @@ -124854,6 +125419,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); + assert( pName->a[0].fg.fixedSchema==0 ); + assert( pName->a[0].fg.isSubquery==0 ); if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; assert( isView==0 || isView==LOCATE_VIEW ); @@ -124862,7 +125429,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, if( pTab==0 ){ if( noErr ){ - sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); sqlite3ForceNotReadOnly(pParse); } goto exit_drop_table; @@ -125953,15 +126520,17 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists } assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); + assert( pName->a[0].fg.fixedSchema==0 ); + assert( pName->a[0].fg.isSubquery==0 ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto exit_drop_index; } - pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); + pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].u4.zDatabase); if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); }else{ - sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; @@ -126258,12 +126827,14 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( if( pDatabase && pDatabase->z==0 ){ pDatabase = 0; } + assert( pItem->fg.fixedSchema==0 ); + assert( pItem->fg.isSubquery==0 ); if( pDatabase ){ pItem->zName = sqlite3NameFromToken(db, pDatabase); - pItem->zDatabase = sqlite3NameFromToken(db, pTable); + pItem->u4.zDatabase = sqlite3NameFromToken(db, pTable); }else{ pItem->zName = sqlite3NameFromToken(db, pTable); - pItem->zDatabase = 0; + pItem->u4.zDatabase = 0; } return pList; } @@ -126279,13 +126850,40 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ for(i=0, pItem=pList->a; inSrc; i++, pItem++){ if( pItem->iCursor>=0 ) continue; pItem->iCursor = pParse->nTab++; - if( pItem->pSelect ){ - sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc); + if( pItem->fg.isSubquery ){ + assert( pItem->u4.pSubq!=0 ); + assert( pItem->u4.pSubq->pSelect!=0 ); + assert( pItem->u4.pSubq->pSelect->pSrc!=0 ); + sqlite3SrcListAssignCursors(pParse, pItem->u4.pSubq->pSelect->pSrc); } } } } +/* +** Delete a Subquery object and its substructure. +*/ +SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3 *db, Subquery *pSubq){ + assert( pSubq!=0 && pSubq->pSelect!=0 ); + sqlite3SelectDelete(db, pSubq->pSelect); + sqlite3DbFree(db, pSubq); +} + +/* +** Remove a Subquery from a SrcItem. Return the associated Select object. +** The returned Select becomes the responsibility of the caller. +*/ +SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3 *db, SrcItem *pItem){ + Select *pSel; + assert( pItem!=0 ); + assert( pItem->fg.isSubquery ); + pSel = pItem->u4.pSubq->pSelect; + sqlite3DbFree(db, pItem->u4.pSubq); + pItem->u4.pSubq = 0; + pItem->fg.isSubquery = 0; + return pSel; +} + /* ** Delete an entire SrcList including all its substructure. */ @@ -126295,13 +126893,24 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ assert( db!=0 ); if( pList==0 ) return; for(pItem=pList->a, i=0; inSrc; i++, pItem++){ - if( pItem->zDatabase ) sqlite3DbNNFreeNN(db, pItem->zDatabase); + + /* Check invariants on SrcItem */ + assert( !pItem->fg.isIndexedBy || !pItem->fg.isTabFunc ); + assert( !pItem->fg.isCte || !pItem->fg.isIndexedBy ); + assert( !pItem->fg.fixedSchema || !pItem->fg.isSubquery ); + assert( !pItem->fg.isSubquery || (pItem->u4.pSubq!=0 && + pItem->u4.pSubq->pSelect!=0) ); + if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName); if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias); + if( pItem->fg.isSubquery ){ + sqlite3SubqueryDelete(db, pItem->u4.pSubq); + }else if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ + sqlite3DbNNFreeNN(db, pItem->u4.zDatabase); + } if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); - sqlite3DeleteTable(db, pItem->pTab); - if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect); + sqlite3DeleteTable(db, pItem->pSTab); if( pItem->fg.isUsing ){ sqlite3IdListDelete(db, pItem->u3.pUsing); }else if( pItem->u3.pOn ){ @@ -126311,6 +126920,54 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ sqlite3DbNNFreeNN(db, pList); } +/* +** Attach a Subquery object to pItem->uv.pSubq. Set the +** pSelect value but leave all the other values initialized +** to zero. +** +** A copy of the Select object is made if dupSelect is true, and the +** SrcItem takes responsibility for deleting the copy. If dupSelect is +** false, ownership of the Select passes to the SrcItem. Either way, +** the SrcItem will take responsibility for deleting the Select. +** +** When dupSelect is zero, that means the Select might get deleted right +** away if there is an OOM error. Beware. +** +** Return non-zero on success. Return zero on an OOM error. +*/ +SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery( + Parse *pParse, /* Parsing context */ + SrcItem *pItem, /* Item to which the subquery is to be attached */ + Select *pSelect, /* The subquery SELECT. Must be non-NULL */ + int dupSelect /* If true, attach a copy of pSelect, not pSelect itself.*/ +){ + Subquery *p; + assert( pSelect!=0 ); + assert( pItem->fg.isSubquery==0 ); + if( pItem->fg.fixedSchema ){ + pItem->u4.pSchema = 0; + pItem->fg.fixedSchema = 0; + }else if( pItem->u4.zDatabase!=0 ){ + sqlite3DbFree(pParse->db, pItem->u4.zDatabase); + pItem->u4.zDatabase = 0; + } + if( dupSelect ){ + pSelect = sqlite3SelectDup(pParse->db, pSelect, 0); + if( pSelect==0 ) return 0; + } + p = pItem->u4.pSubq = sqlite3DbMallocRawNN(pParse->db, sizeof(Subquery)); + if( p==0 ){ + sqlite3SelectDelete(pParse->db, pSelect); + return 0; + } + pItem->fg.isSubquery = 1; + p->pSelect = pSelect; + assert( offsetof(Subquery, pSelect)==0 ); + memset(((char*)p)+sizeof(p->pSelect), 0, sizeof(*p)-sizeof(p->pSelect)); + return 1; +} + + /* ** This routine is called by the parser to add a new term to the ** end of a growing FROM clause. The "p" parameter is the part of @@ -126360,10 +127017,12 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); } + assert( pSubquery==0 || pDatabase==0 ); if( pSubquery ){ - pItem->pSelect = pSubquery; - if( pSubquery->selFlags & SF_NestedFrom ){ - pItem->fg.isNestedFrom = 1; + if( sqlite3SrcItemAttachSubquery(pParse, pItem, pSubquery, 0) ){ + if( pSubquery->selFlags & SF_NestedFrom ){ + pItem->fg.isNestedFrom = 1; + } } } assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); @@ -127641,8 +128300,8 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ ** ** The following fields are initialized appropriate in pSrc: ** -** pSrc->a[0].pTab Pointer to the Table object -** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one +** pSrc->a[0].spTab Pointer to the Table object +** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one ** */ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ @@ -127650,8 +128309,8 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ Table *pTab; assert( pItem && pSrc->nSrc>=1 ); pTab = sqlite3LocateTableItem(pParse, 0, pItem); - if( pItem->pTab ) sqlite3DeleteTable(pParse->db, pItem->pTab); - pItem->pTab = pTab; + if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab); + pItem->pSTab = pTab; pItem->fg.notCte = 1; if( pTab ){ pTab->nTabRef++; @@ -127692,6 +128351,7 @@ SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char * ** is for a top-level SQL statement. */ static int vtabIsReadOnly(Parse *pParse, Table *pTab){ + assert( IsVirtual(pTab) ); if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ return 1; } @@ -127773,7 +128433,8 @@ SQLITE_PRIVATE void sqlite3MaterializeView( if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); - pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); + assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 ); + pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); assert( pFrom->a[0].fg.isUsing==0 ); assert( pFrom->a[0].u3.pOn==0 ); } @@ -127835,7 +128496,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( ** ); */ - pTab = pSrc->a[0].pTab; + pTab = pSrc->a[0].pSTab; if( HasRowid(pTab) ){ pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0); pEList = sqlite3ExprListAppend( @@ -127868,9 +128529,9 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ - pSrc->a[0].pTab = 0; + pSrc->a[0].pSTab = 0; pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); - pSrc->a[0].pTab = pTab; + pSrc->a[0].pSTab = pTab; if( pSrc->a[0].fg.isIndexedBy ){ assert( pSrc->a[0].fg.isCte==0 ); pSrc->a[0].u2.pIBIndex = 0; @@ -130697,7 +131358,11 @@ static void minMaxFinalize(sqlite3_context *context){ ** group_concat(EXPR, ?SEPARATOR?) ** string_agg(EXPR, SEPARATOR) ** -** The SEPARATOR goes before the EXPR string. This is tragic. The +** Content is accumulated in GroupConcatCtx.str with the SEPARATOR +** coming before the EXPR value, except for the first entry which +** omits the SEPARATOR. +** +** It is tragic that the SEPARATOR goes before the EXPR string. The ** groupConcatInverse() implementation would have been easier if the ** SEPARATOR were appended after EXPR. And the order is undocumented, ** so we could change it, in theory. But the old behavior has been @@ -130801,7 +131466,7 @@ static void groupConcatInverse( /* pGCC is always non-NULL since groupConcatStep() will have always ** run first to initialize it */ if( ALWAYS(pGCC) ){ - int nVS; + int nVS; /* Number of characters to remove */ /* Must call sqlite3_value_text() to convert the argument into text prior ** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */ (void)sqlite3_value_text(argv[0]); @@ -131179,7 +131844,13 @@ static void signFunc( ** Implementation of fpdecode(x,y,z) function. ** ** x is a real number that is to be decoded. y is the precision. -** z is the maximum real precision. +** z is the maximum real precision. Return a string that shows the +** results of the sqlite3FpDecode() function. +** +** Used for testing and debugging only, specifically testing and debugging +** of the sqlite3FpDecode() function. This SQL function does not appear +** in production builds. This function is not an API and is subject to +** modification or removal in future versions of SQLite. */ static void fpdecodeFunc( sqlite3_context *context, @@ -131195,6 +131866,7 @@ static void fpdecodeFunc( x = sqlite3_value_double(argv[0]); y = sqlite3_value_int(argv[1]); z = sqlite3_value_int(argv[2]); + if( z<=0 ) z = 1; sqlite3FpDecode(&s, x, y, z); if( s.isSpecial==2 ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN"); @@ -131205,6 +131877,82 @@ static void fpdecodeFunc( } #endif /* SQLITE_DEBUG */ +#ifdef SQLITE_DEBUG +/* +** Implementation of parseuri(uri,flags) function. +** +** Required Arguments: +** "uri" The URI to parse. +** "flags" Bitmask of flags, as if to sqlite3_open_v2(). +** +** Additional arguments beyond the first two make calls to +** sqlite3_uri_key() for integers and sqlite3_uri_parameter for +** anything else. +** +** The result is a string showing the results of calling sqlite3ParseUri(). +** +** Used for testing and debugging only, specifically testing and debugging +** of the sqlite3ParseUri() function. This SQL function does not appear +** in production builds. This function is not an API and is subject to +** modification or removal in future versions of SQLite. +*/ +static void parseuriFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + sqlite3_str *pResult; + const char *zVfs; + const char *zUri; + unsigned int flgs; + int rc; + sqlite3_vfs *pVfs = 0; + char *zFile = 0; + char *zErr = 0; + + if( argc<2 ) return; + pVfs = sqlite3_vfs_find(0); + assert( pVfs ); + zVfs = pVfs->zName; + zUri = (const char*)sqlite3_value_text(argv[0]); + if( zUri==0 ) return; + flgs = (unsigned int)sqlite3_value_int(argv[1]); + rc = sqlite3ParseUri(zVfs, zUri, &flgs, &pVfs, &zFile, &zErr); + pResult = sqlite3_str_new(0); + if( pResult ){ + int i; + sqlite3_str_appendf(pResult, "rc=%d", rc); + sqlite3_str_appendf(pResult, ", flags=0x%x", flgs); + sqlite3_str_appendf(pResult, ", vfs=%Q", pVfs ? pVfs->zName: 0); + sqlite3_str_appendf(pResult, ", err=%Q", zErr); + sqlite3_str_appendf(pResult, ", file=%Q", zFile); + if( zFile ){ + const char *z = zFile; + z += sqlite3Strlen30(z)+1; + while( z[0] ){ + sqlite3_str_appendf(pResult, ", %Q", z); + z += sqlite3Strlen30(z)+1; + } + for(i=2; ia; - pItem->pTab = pFKey->pFrom; + pItem->pSTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; - pItem->pTab->nTabRef++; + pItem->pSTab->nTabRef++; pItem->iCursor = pParse->nTab++; if( regNew!=0 ){ @@ -132736,7 +133486,8 @@ static Trigger *fkActionTrigger( SrcList *pSrc; Expr *pRaise; - pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); + pRaise = sqlite3Expr(db, TK_STRING, "FOREIGN KEY constraint failed"), + pRaise = sqlite3PExpr(pParse, TK_RAISE, pRaise, 0); if( pRaise ){ pRaise->affExpr = OE_Abort; } @@ -132744,7 +133495,8 @@ static Trigger *fkActionTrigger( if( pSrc ){ assert( pSrc->nSrc==1 ); pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); - pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); + assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 ); + pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), @@ -133478,8 +134230,11 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ SrcItem *pItem = &pVal->pSrc->a[0]; - sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn); - sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1); + assert( (pItem->fg.isSubquery && pItem->u4.pSubq!=0) || pParse->nErr ); + if( pItem->fg.isSubquery ){ + sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn); + sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1); + } } } @@ -133607,6 +134362,7 @@ SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList if( pRet ){ SelectDest dest; + Subquery *pSubq; pRet->pSrc->nSrc = 1; pRet->pPrior = pLeft->pPrior; pRet->op = pLeft->op; @@ -133616,28 +134372,32 @@ SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList assert( pLeft->pNext==0 ); assert( pRet->pNext==0 ); p = &pRet->pSrc->a[0]; - p->pSelect = pLeft; p->fg.viaCoroutine = 1; - p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; - p->regReturn = ++pParse->nMem; p->iCursor = -1; + assert( !p->fg.isIndexedBy && !p->fg.isTabFunc ); p->u1.nRow = 2; - sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub); - sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn); + if( sqlite3SrcItemAttachSubquery(pParse, p, pLeft, 0) ){ + pSubq = p->u4.pSubq; + pSubq->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; + pSubq->regReturn = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, + pSubq->regReturn, 0, pSubq->addrFillSub); + sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); - /* Allocate registers for the output of the co-routine. Do so so - ** that there are two unused registers immediately before those - ** used by the co-routine. This allows the code in sqlite3Insert() - ** to use these registers directly, instead of copying the output - ** of the co-routine to a separate array for processing. */ - dest.iSdst = pParse->nMem + 3; - dest.nSdst = pLeft->pEList->nExpr; - pParse->nMem += 2 + dest.nSdst; + /* Allocate registers for the output of the co-routine. Do so so + ** that there are two unused registers immediately before those + ** used by the co-routine. This allows the code in sqlite3Insert() + ** to use these registers directly, instead of copying the output + ** of the co-routine to a separate array for processing. */ + dest.iSdst = pParse->nMem + 3; + dest.nSdst = pLeft->pEList->nExpr; + pParse->nMem += 2 + dest.nSdst; - pLeft->selFlags |= SF_MultiValue; - sqlite3Select(pParse, pLeft, &dest); - p->regResult = dest.iSdst; - assert( pParse->nErr || dest.iSdst>0 ); + pLeft->selFlags |= SF_MultiValue; + sqlite3Select(pParse, pLeft, &dest); + pSubq->regResult = dest.iSdst; + assert( pParse->nErr || dest.iSdst>0 ); + } pLeft = pRet; } }else{ @@ -133647,12 +134407,18 @@ SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList } if( pParse->nErr==0 ){ + Subquery *pSubq; assert( p!=0 ); - if( p->pSelect->pEList->nExpr!=pRow->nExpr ){ - sqlite3SelectWrongNumTermsError(pParse, p->pSelect); + assert( p->fg.isSubquery ); + pSubq = p->u4.pSubq; + assert( pSubq!=0 ); + assert( pSubq->pSelect!=0 ); + assert( pSubq->pSelect->pEList!=0 ); + if( pSubq->pSelect->pEList->nExpr!=pRow->nExpr ){ + sqlite3SelectWrongNumTermsError(pParse, pSubq->pSelect); }else{ - sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0); - sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn); + sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0); + sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn); } } sqlite3ExprListDelete(pParse->db, pRow); @@ -134003,9 +134769,14 @@ SQLITE_PRIVATE void sqlite3Insert( && pSelect->pPrior==0 ){ SrcItem *pItem = &pSelect->pSrc->a[0]; - dest.iSDParm = pItem->regReturn; - regFromSelect = pItem->regResult; - nColumn = pItem->pSelect->pEList->nExpr; + Subquery *pSubq; + assert( pItem->fg.isSubquery ); + pSubq = pItem->u4.pSubq; + dest.iSDParm = pSubq->regReturn; + regFromSelect = pSubq->regResult; + assert( pSubq->pSelect!=0 ); + assert( pSubq->pSelect->pEList!=0 ); + nColumn = pSubq->pSelect->pEList->nExpr; ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); if( bIdListInOrder && nColumn==pTab->nCol ){ regData = regFromSelect; @@ -135925,7 +136696,7 @@ static int xferOptimization( if( pSelect->pSrc->nSrc!=1 ){ return 0; /* FROM clause must have exactly one term */ } - if( pSelect->pSrc->a[0].pSelect ){ + if( pSelect->pSrc->a[0].fg.isSubquery ){ return 0; /* FROM clause cannot contain a subquery */ } if( pSelect->pWhere ){ @@ -140485,6 +141256,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Make sure sufficient number of registers have been allocated */ sqlite3TouchRegister(pParse, 8+cnt); + sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ @@ -142810,12 +143582,24 @@ static int sqlite3Prepare16( if( !sqlite3SafetyCheckOk(db)||zSql==0 ){ return SQLITE_MISUSE_BKPT; } + + /* Make sure nBytes is non-negative and correct. It should be the + ** number of bytes until the end of the input buffer or until the first + ** U+0000 character. If the input nBytes is odd, convert it into + ** an even number. If the input nBytes is negative, then the input + ** must be terminated by at least one U+0000 character */ if( nBytes>=0 ){ int sz; const char *z = (const char*)zSql; for(sz=0; szmutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE); if( zSql8 ){ @@ -142829,7 +143613,7 @@ static int sqlite3Prepare16( ** the same number of characters into the UTF-16 string. */ int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8)); - *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed); + *pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, nBytes, chars_parsed); } sqlite3DbFree(db, zSql8); rc = sqlite3ApiExit(db, rc); @@ -143223,11 +144007,13 @@ SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol){ */ SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ assert( pItem!=0 ); - assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); + assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); if( pItem->fg.isNestedFrom ){ ExprList *pResults; - assert( pItem->pSelect!=0 ); - pResults = pItem->pSelect->pEList; + assert( pItem->fg.isSubquery ); + assert( pItem->u4.pSubq!=0 ); + assert( pItem->u4.pSubq->pSelect!=0 ); + pResults = pItem->u4.pSubq->pSelect->pEList; assert( pResults!=0 ); assert( iCol>=0 && iColnExpr ); pResults->a[iCol].fg.bUsed = 1; @@ -143261,9 +144047,9 @@ static int tableAndColumnIndex( assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ for(i=iStart; i<=iEnd; i++){ - iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol); + iCol = sqlite3ColumnIndex(pSrc->a[i].pSTab, zCol); if( iCol>=0 - && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0) + && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pSTab->aCol[iCol])==0) ){ if( piTab ){ sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); @@ -143392,10 +144178,10 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){ pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; inSrc-1; i++, pRight++, pLeft++){ - Table *pRightTab = pRight->pTab; + Table *pRightTab = pRight->pSTab; u32 joinType; - if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; + if( NEVER(pLeft->pSTab==0 || pRightTab==0) ) continue; joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; /* If this is a NATURAL join, synthesize an appropriate USING clause @@ -144268,12 +145054,18 @@ static void selectInnerLoop( ** case the order does matter */ pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); + pDest->iSDParm2 = 0; /* Signal that any Bloom filter is unpopulated */ }else{ int r1 = sqlite3GetTempReg(pParse); assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol, r1, pDest->zAffSdst, nResultCol); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); + if( pDest->iSDParm2 ){ + sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, + regResult, nResultCol); + ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); + } sqlite3ReleaseTempReg(pParse, r1); } break; @@ -144815,8 +145607,12 @@ static const char *columnTypeImpl( SrcList *pTabList = pNC->pSrcList; for(j=0;jnSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); if( jnSrc ){ - pTab = pTabList->a[j].pTab; - pS = pTabList->a[j].pSelect; + pTab = pTabList->a[j].pSTab; + if( pTabList->a[j].fg.isSubquery ){ + pS = pTabList->a[j].u4.pSubq->pSelect; + }else{ + pS = 0; + } }else{ pNC = pNC->pNext; } @@ -145383,7 +146179,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ p->iLimit = iLimit = ++pParse->nMem; v = sqlite3GetVdbe(pParse); assert( v!=0 ); - if( sqlite3ExprIsInteger(pLimit->pLeft, &n) ){ + if( sqlite3ExprIsInteger(pLimit->pLeft, &n, pParse) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ @@ -145863,7 +146659,7 @@ static int multiSelect( p->pPrior = pPrior; p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); if( p->pLimit - && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit) + && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) ){ p->nSelectRow = sqlite3LogEst((u64)nLimit); @@ -146207,6 +147003,11 @@ static int generateOutputSubroutine( r1, pDest->zAffSdst, pIn->nSdst); sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1, pIn->iSdst, pIn->nSdst); + if( pDest->iSDParm2>0 ){ + sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pDest->iSDParm2, 0, + pIn->iSdst, pIn->nSdst); + ExplainQueryPlan((pParse, 0, "CREATE BLOOM FILTER")); + } sqlite3ReleaseTempReg(pParse, r1); break; } @@ -146863,7 +147664,9 @@ static void substSelect( pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - substSelect(pSubst, pItem->pSelect, 1); + if( pItem->fg.isSubquery ){ + substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); + } if( pItem->fg.isTabFunc ){ substExprList(pSubst, pItem->u1.pFuncArg); } @@ -146894,7 +147697,7 @@ static void recomputeColumnsUsed( SrcItem *pSrcItem /* Which FROM clause item to recompute */ ){ Walker w; - if( NEVER(pSrcItem->pTab==0) ) return; + if( NEVER(pSrcItem->pSTab==0) ) return; memset(&w, 0, sizeof(w)); w.xExprCallback = recomputeColumnsUsedExpr; w.xSelectCallback = sqlite3SelectWalkNoop; @@ -146934,8 +147737,10 @@ static void srclistRenumberCursors( aCsrMap[pItem->iCursor+1] = pParse->nTab++; } pItem->iCursor = aCsrMap[pItem->iCursor+1]; - for(p=pItem->pSelect; p; p=p->pPrior){ - srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); + if( pItem->fg.isSubquery ){ + for(p=pItem->u4.pSubq->pSelect; p; p=p->pPrior){ + srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); + } } } } @@ -147246,7 +148051,8 @@ static int flattenSubquery( assert( pSrc && iFrom>=0 && iFromnSrc ); pSubitem = &pSrc->a[iFrom]; iParent = pSubitem->iCursor; - pSub = pSubitem->pSelect; + assert( pSubitem->fg.isSubquery ); + pSub = pSubitem->u4.pSubq->pSelect; assert( pSub!=0 ); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -147299,7 +148105,7 @@ static int flattenSubquery( */ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ if( pSubSrc->nSrc>1 /* (3a) */ - || IsVirtual(pSubSrc->a[0].pTab) /* (3b) */ + || IsVirtual(pSubSrc->a[0].pSTab) /* (3b) */ || (p->selFlags & SF_Distinct)!=0 /* (3d) */ || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ ){ @@ -147385,14 +148191,18 @@ static int flattenSubquery( pParse->zAuthContext = zSavedAuthContext; /* Delete the transient structures associated with the subquery */ - pSub1 = pSubitem->pSelect; - sqlite3DbFree(db, pSubitem->zDatabase); + + if( ALWAYS(pSubitem->fg.isSubquery) ){ + pSub1 = sqlite3SubqueryDetach(db, pSubitem); + }else{ + pSub1 = 0; + } + assert( pSubitem->fg.isSubquery==0 ); + assert( pSubitem->fg.fixedSchema==0 ); sqlite3DbFree(db, pSubitem->zName); sqlite3DbFree(db, pSubitem->zAlias); - pSubitem->zDatabase = 0; pSubitem->zName = 0; pSubitem->zAlias = 0; - pSubitem->pSelect = 0; assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); /* If the sub-query is a compound SELECT statement, then (by restrictions @@ -147433,8 +148243,8 @@ static int flattenSubquery( ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; Select *pPrior = p->pPrior; - Table *pItemTab = pSubitem->pTab; - pSubitem->pTab = 0; + Table *pItemTab = pSubitem->pSTab; + pSubitem->pSTab = 0; p->pOrderBy = 0; p->pPrior = 0; p->pLimit = 0; @@ -147442,7 +148252,7 @@ static int flattenSubquery( p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->op = TK_ALL; - pSubitem->pTab = pItemTab; + pSubitem->pSTab = pItemTab; if( pNew==0 ){ p->pPrior = pPrior; }else{ @@ -147457,11 +148267,14 @@ static int flattenSubquery( TREETRACE(0x4,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } - assert( pSubitem->pSelect==0 ); + assert( pSubitem->fg.isSubquery==0 ); } sqlite3DbFree(db, aCsrMap); if( db->mallocFailed ){ - pSubitem->pSelect = pSub1; + assert( pSubitem->fg.fixedSchema==0 ); + assert( pSubitem->fg.isSubquery==0 ); + assert( pSubitem->u4.zDatabase==0 ); + sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0); return 1; } @@ -147472,8 +148285,8 @@ static int flattenSubquery( ** ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ - if( ALWAYS(pSubitem->pTab!=0) ){ - Table *pTabToDel = pSubitem->pTab; + if( ALWAYS(pSubitem->pSTab!=0) ){ + Table *pTabToDel = pSubitem->pSTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); @@ -147481,7 +148294,7 @@ static int flattenSubquery( }else{ pTabToDel->nTabRef--; } - pSubitem->pTab = 0; + pSubitem->pSTab = 0; } /* The following loop runs once for each term in a compound-subquery @@ -147537,8 +148350,11 @@ static int flattenSubquery( */ for(i=0; ia[i+iFrom]; - if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); assert( pItem->fg.isTabFunc==0 ); + assert( pItem->fg.isSubquery + || pItem->fg.fixedSchema + || pItem->u4.zDatabase==0 ); + if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); *pItem = pSubSrc->a[i]; pItem->fg.jointype |= ltorj; iNewParent = pSubSrc->a[i].iCursor; @@ -147957,7 +148773,8 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** ** NAME AMBIGUITY ** -** This optimization is called the "WHERE-clause push-down optimization". +** This optimization is called the "WHERE-clause push-down optimization" +** or sometimes the "predicate push-down optimization". ** ** Do not confuse this optimization with another unrelated optimization ** with a similar name: The "MySQL push-down optimization" causes WHERE @@ -148221,10 +149038,10 @@ static int disableUnusedSubqueryResultColumns(SrcItem *pItem){ if( pItem->fg.isCorrelated || pItem->fg.isCte ){ return 0; } - assert( pItem->pTab!=0 ); - pTab = pItem->pTab; - assert( pItem->pSelect!=0 ); - pSub = pItem->pSelect; + assert( pItem->pSTab!=0 ); + pTab = pItem->pSTab; + assert( pItem->fg.isSubquery ); + pSub = pItem->u4.pSubq->pSelect; assert( pSub->pEList->nExpr==pTab->nCol ); for(pX=pSub; pX; pX=pX->pPrior){ if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ @@ -148353,13 +149170,13 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ if( p->pWhere || p->pEList->nExpr!=1 || p->pSrc->nSrc!=1 - || p->pSrc->a[0].pSelect + || p->pSrc->a[0].fg.isSubquery || pAggInfo->nFunc!=1 || p->pHaving ){ return 0; } - pTab = p->pSrc->a[0].pTab; + pTab = p->pSrc->a[0].pSTab; assert( pTab!=0 ); assert( !IsView(pTab) ); if( !IsOrdinaryTable(pTab) ) return 0; @@ -148384,7 +149201,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ ** pFrom->pIndex and return SQLITE_OK. */ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ - Table *pTab = pFrom->pTab; + Table *pTab = pFrom->pSTab; char *zIndexedBy = pFrom->u1.zIndexedBy; Index *pIdx; assert( pTab!=0 ); @@ -148461,7 +149278,11 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); - if( pNewSrc==0 ) return WRC_Abort; + assert( pNewSrc!=0 || pParse->nErr ); + if( pParse->nErr ){ + sqlite3SrcListDelete(db, pNewSrc); + return WRC_Abort; + } *pNew = *p; p->pSrc = pNewSrc; p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0)); @@ -148516,7 +149337,7 @@ static struct Cte *searchWith( ){ const char *zName = pItem->zName; With *p; - assert( pItem->zDatabase==0 ); + assert( pItem->fg.fixedSchema || pItem->u4.zDatabase==0 ); assert( zName!=0 ); for(p=pWith; p; p=p->pOuter){ int i; @@ -148586,7 +149407,7 @@ static int resolveFromTermToCte( Cte *pCte; /* Matched CTE (or NULL if no match) */ With *pWith; /* The matching WITH */ - assert( pFrom->pTab==0 ); + assert( pFrom->pSTab==0 ); if( pParse->pWith==0 ){ /* There are no WITH clauses in the stack. No match is possible */ return 0; @@ -148596,7 +149417,8 @@ static int resolveFromTermToCte( ** go no further. */ return 0; } - if( pFrom->zDatabase!=0 ){ + assert( pFrom->fg.hadSchema==0 || pFrom->fg.notCte!=0 ); + if( pFrom->fg.fixedSchema==0 && pFrom->u4.zDatabase!=0 ){ /* The FROM term contains a schema qualifier (ex: main.t1) and so ** it cannot possibly be a CTE reference. */ return 0; @@ -148632,7 +149454,7 @@ static int resolveFromTermToCte( } if( cannotBeFunction(pParse, pFrom) ) return 2; - assert( pFrom->pTab==0 ); + assert( pFrom->pSTab==0 ); pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return 2; pCteUse = pCte->pUse; @@ -148646,26 +149468,29 @@ static int resolveFromTermToCte( } pCteUse->eM10d = pCte->eM10d; } - pFrom->pTab = pTab; + pFrom->pSTab = pTab; pTab->nTabRef = 1; pTab->zName = sqlite3DbStrDup(db, pCte->zName); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; - pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); + sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1); if( db->mallocFailed ) return 2; - pFrom->pSelect->selFlags |= SF_CopyCte; - assert( pFrom->pSelect ); + assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); + pSel = pFrom->u4.pSubq->pSelect; + assert( pSel!=0 ); + pSel->selFlags |= SF_CopyCte; if( pFrom->fg.isIndexedBy ){ sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); return 2; } + assert( !pFrom->fg.isIndexedBy ); pFrom->fg.isCte = 1; pFrom->u2.pCteUse = pCteUse; pCteUse->nUse++; /* Check if this is a recursive CTE. */ - pRecTerm = pSel = pFrom->pSelect; + pRecTerm = pSel; bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); while( bMayRecursive && pRecTerm->op==pSel->op ){ int i; @@ -148673,11 +149498,13 @@ static int resolveFromTermToCte( assert( pRecTerm->pPrior!=0 ); for(i=0; inSrc; i++){ SrcItem *pItem = &pSrc->a[i]; - if( pItem->zDatabase==0 - && pItem->zName!=0 + if( pItem->zName!=0 + && !pItem->fg.hadSchema + && ALWAYS( !pItem->fg.isSubquery ) + && (pItem->fg.fixedSchema || pItem->u4.zDatabase==0) && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ - pItem->pTab = pTab; + pItem->pSTab = pTab; pTab->nTabRef++; pItem->fg.isRecursive = 1; if( pRecTerm->selFlags & SF_Recursive ){ @@ -148779,11 +149606,14 @@ SQLITE_PRIVATE void sqlite3SelectPopWith(Walker *pWalker, Select *p){ ** SQLITE_NOMEM. */ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ - Select *pSel = pFrom->pSelect; + Select *pSel; Table *pTab; + assert( pFrom->fg.isSubquery ); + assert( pFrom->u4.pSubq!=0 ); + pSel = pFrom->u4.pSubq->pSelect; assert( pSel ); - pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); + pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); if( pTab==0 ) return SQLITE_NOMEM; pTab->nTabRef = 1; if( pFrom->zAlias ){ @@ -148903,33 +149733,35 @@ static int selectExpander(Walker *pWalker, Select *p){ */ for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab; - assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); - if( pFrom->pTab ) continue; + assert( pFrom->fg.isRecursive==0 || pFrom->pSTab!=0 ); + if( pFrom->pSTab ) continue; assert( pFrom->fg.isRecursive==0 ); if( pFrom->zName==0 ){ #ifndef SQLITE_OMIT_SUBQUERY - Select *pSel = pFrom->pSelect; + Select *pSel; + assert( pFrom->fg.isSubquery && pFrom->u4.pSubq!=0 ); + pSel = pFrom->u4.pSubq->pSelect; /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); - assert( pFrom->pTab==0 ); + assert( pFrom->pSTab==0 ); if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort; #endif #ifndef SQLITE_OMIT_CTE }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){ if( rc>1 ) return WRC_Abort; - pTab = pFrom->pTab; + pTab = pFrom->pSTab; assert( pTab!=0 ); #endif }else{ /* An ordinary table or view name in the FROM clause */ - assert( pFrom->pTab==0 ); - pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); + assert( pFrom->pSTab==0 ); + pFrom->pSTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); if( pTab==0 ) return WRC_Abort; if( pTab->nTabRef>=0xffff ){ sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", pTab->zName); - pFrom->pTab = 0; + pFrom->pSTab = 0; return WRC_Abort; } pTab->nTabRef++; @@ -148941,7 +149773,7 @@ static int selectExpander(Walker *pWalker, Select *p){ i16 nCol; u8 eCodeOrig = pWalker->eCode; if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; - assert( pFrom->pSelect==0 ); + assert( pFrom->fg.isSubquery==0 ); if( IsView(pTab) ){ if( (db->flags & SQLITE_EnableView)==0 && pTab->pSchema!=db->aDb[1].pSchema @@ -148949,7 +149781,7 @@ static int selectExpander(Walker *pWalker, Select *p){ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", pTab->zName); } - pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0); + sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( ALWAYS(IsVirtual(pTab)) @@ -148965,7 +149797,9 @@ static int selectExpander(Walker *pWalker, Select *p){ nCol = pTab->nCol; pTab->nCol = -1; pWalker->eCode = 1; /* Turn on Select.selId renumbering */ - sqlite3WalkSelect(pWalker, pFrom->pSelect); + if( pFrom->fg.isSubquery ){ + sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect); + } pWalker->eCode = eCodeOrig; pTab->nCol = nCol; } @@ -149052,7 +149886,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ int nAdd; /* Number of cols including rowid */ - Table *pTab = pFrom->pTab; /* Table for this data source */ + Table *pTab = pFrom->pSTab; /* Table for this data source */ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ char *zTabName; /* AS name for this data source */ const char *zSchemaName = 0; /* Schema name for this data source */ @@ -149063,10 +149897,11 @@ static int selectExpander(Walker *pWalker, Select *p){ zTabName = pTab->zName; } if( db->mallocFailed ) break; - assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) ); + assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) ); if( pFrom->fg.isNestedFrom ){ - assert( pFrom->pSelect!=0 ); - pNestedFrom = pFrom->pSelect->pEList; + assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); + assert( pFrom->u4.pSubq->pSelect!=0 ); + pNestedFrom = pFrom->u4.pSubq->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); @@ -149305,14 +150140,12 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; + Table *pTab = pFrom->pSTab; assert( pTab!=0 ); - if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ + if( (pTab->tabFlags & TF_Ephemeral)!=0 && pFrom->fg.isSubquery ){ /* A sub-query in the FROM clause of a SELECT */ - Select *pSel = pFrom->pSelect; - if( pSel ){ - sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); - } + Select *pSel = pFrom->u4.pSubq->pSelect; + sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); } } } @@ -149626,6 +150459,7 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); + if( pParse->nErr ) return; pList = pF->pFExpr->x.pList; if( pF->iOBTab>=0 ){ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs @@ -149835,6 +150669,7 @@ static void updateAccumulator( if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); } + if( pParse->nErr ) return; } if( regHit==0 && pAggInfo->nAccumulator ){ regHit = regAcc; @@ -149844,6 +150679,7 @@ static void updateAccumulator( } for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); + if( pParse->nErr ) return; } pAggInfo->directMode = 0; @@ -149959,25 +150795,28 @@ static SrcItem *isSelfJoinView( int iFirst, int iEnd /* Range of FROM-clause entries to search. */ ){ SrcItem *pItem; - assert( pThis->pSelect!=0 ); - if( pThis->pSelect->selFlags & SF_PushDown ) return 0; + Select *pSel; + assert( pThis->fg.isSubquery ); + pSel = pThis->u4.pSubq->pSelect; + assert( pSel!=0 ); + if( pSel->selFlags & SF_PushDown ) return 0; while( iFirsta[iFirst++]; - if( pItem->pSelect==0 ) continue; + if( !pItem->fg.isSubquery ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; - assert( pItem->pTab!=0 ); - assert( pThis->pTab!=0 ); - if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) continue; + assert( pItem->pSTab!=0 ); + assert( pThis->pSTab!=0 ); + if( pItem->pSTab->pSchema!=pThis->pSTab->pSchema ) continue; if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; - pS1 = pItem->pSelect; - if( pItem->pTab->pSchema==0 && pThis->pSelect->selId!=pS1->selId ){ + pS1 = pItem->u4.pSubq->pSelect; + if( pItem->pSTab->pSchema==0 && pSel->selId!=pS1->selId ){ /* The query flattener left two different CTE tables with identical ** names in the same FROM clause. */ continue; } - if( pItem->pSelect->selFlags & SF_PushDown ){ + if( pS1->selFlags & SF_PushDown ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; @@ -150021,6 +150860,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ Expr *pExpr; Expr *pCount; sqlite3 *db; + SrcItem *pFrom; if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ if( p->pWhere ) return 0; @@ -150035,8 +150875,9 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */ - pSub = p->pSrc->a[0].pSelect; - if( pSub==0 ) return 0; /* The FROM is a subquery */ + pFrom = p->pSrc->a; + if( pFrom->fg.isSubquery==0 ) return 0; /* FROM is a subquery */ + pSub = pFrom->u4.pSubq->pSelect; if( pSub->pPrior==0 ) return 0; /* Must be a compound */ if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ do{ @@ -150045,7 +150886,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ if( pSub->pLimit ) return 0; /* No LIMIT clause */ if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ assert( pSub->pHaving==0 ); /* Due to the previous */ - pSub = pSub->pPrior; /* Repeat over compound */ + pSub = pSub->pPrior; /* Repeat over compound */ }while( pSub ); /* If we reach this point then it is OK to perform the transformation */ @@ -150053,8 +150894,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ db = pParse->db; pCount = pExpr; pExpr = 0; - pSub = p->pSrc->a[0].pSelect; - p->pSrc->a[0].pSelect = 0; + pSub = sqlite3SubqueryDetach(db, pFrom); sqlite3SrcListDelete(db, p->pSrc); p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); while( pSub ){ @@ -150099,12 +150939,12 @@ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ for(i=0; inSrc; i++){ SrcItem *p1 = &pSrc->a[i]; if( p1==p0 ) continue; - if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ + if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ return 1; } - if( p1->pSelect - && (p1->pSelect->selFlags & SF_NestedFrom)!=0 - && sameSrcAlias(p0, p1->pSelect->pSrc) + if( p1->fg.isSubquery + && (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 + && sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc) ){ return 1; } @@ -150169,13 +151009,13 @@ static int fromClauseTermCanBeCoroutine( if( i==0 ) break; i--; pItem--; - if( pItem->pSelect!=0 ) return 0; /* (1c-i) */ + if( pItem->fg.isSubquery ) return 0; /* (1c-i) */ } return 1; } /* -** Generate code for the SELECT statement given in the p argument. +** Generate byte-code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. @@ -150186,6 +151026,40 @@ static int fromClauseTermCanBeCoroutine( ** ** This routine does NOT free the Select structure passed in. The ** calling function needs to do that. +** +** This is a long function. The following is an outline of the processing +** steps, with tags referencing various milestones: +** +** * Resolve names and similar preparation tag-select-0100 +** * Scan of the FROM clause tag-select-0200 +** + OUTER JOIN strength reduction tag-select-0220 +** + Sub-query ORDER BY removal tag-select-0230 +** + Query flattening tag-select-0240 +** * Separate subroutine for compound-SELECT tag-select-0300 +** * WHERE-clause constant propagation tag-select-0330 +** * Count()-of-VIEW optimization tag-select-0350 +** * Scan of the FROM clause again tag-select-0400 +** + Authorize unreferenced tables tag-select-0410 +** + Predicate push-down optimization tag-select-0420 +** + Omit unused subquery columns optimization tag-select-0440 +** + Generate code to implement subqueries tag-select-0480 +** - Co-routines tag-select-0482 +** - Reuse previously computed CTE tag-select-0484 +** - REuse previously computed VIEW tag-select-0486 +** - Materialize a VIEW or CTE tag-select-0488 +** * DISTINCT ORDER BY -> GROUP BY optimization tag-select-0500 +** * Set up for ORDER BY tag-select-0600 +** * Create output table tag-select-0630 +** * Prepare registers for LIMIT tag-select-0650 +** * Setup for DISTINCT tag-select-0680 +** * Generate code for non-aggregate and non-GROUP BY tag-select-0700 +** * Generate code for aggregate and/or GROUP BY tag-select-0800 +** + GROUP BY queries tag-select-0810 +** + non-GROUP BY queries tag-select-0820 +** - Special case of count() w/o GROUP BY tag-select-0821 +** - General case of non-GROUP BY aggregates tag-select-0822 +** * Sort results, as needed tag-select-0900 +** * Internal self-checks tag-select-1000 */ SQLITE_PRIVATE int sqlite3Select( Parse *pParse, /* The parser context */ @@ -150229,6 +151103,7 @@ SQLITE_PRIVATE int sqlite3Select( } #endif + /* tag-select-0100 */ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); @@ -150280,7 +151155,7 @@ SQLITE_PRIVATE int sqlite3Select( if( sameSrcAlias(p0, p->pSrc) ){ sqlite3ErrorMsg(pParse, "target object/alias may not appear in FROM clause: %s", - p0->zAlias ? p0->zAlias : p0->pTab->zName + p0->zAlias ? p0->zAlias : p0->pSTab->zName ); goto select_end; } @@ -150315,12 +151190,13 @@ SQLITE_PRIVATE int sqlite3Select( /* Try to do various optimizations (flattening subqueries, and strength ** reduction of join operators) in the FROM clause up into the main query + ** tag-select-0200 */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && inSrc; i++){ SrcItem *pItem = &pTabList->a[i]; - Select *pSub = pItem->pSelect; - Table *pTab = pItem->pTab; + Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0; + Table *pTab = pItem->pSTab; /* The expander should have already created transient Table objects ** even for FROM clause elements such as subqueries that do not correspond @@ -150337,6 +151213,7 @@ SQLITE_PRIVATE int sqlite3Select( ** way that the i-th table cannot be the NULL row of a join, then ** perform the appropriate simplification. This is called ** "OUTER JOIN strength reduction" in the SQLite documentation. + ** tag-select-0220 */ if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor, @@ -150407,7 +151284,8 @@ SQLITE_PRIVATE int sqlite3Select( if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); - /* If a FROM-clause subquery has an ORDER BY clause that is not + /* tag-select-0230: + ** If a FROM-clause subquery has an ORDER BY clause that is not ** really doing anything, then delete it now so that it does not ** interfere with query flattening. See the discussion at ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a @@ -150426,13 +151304,16 @@ SQLITE_PRIVATE int sqlite3Select( ** (a) The outer query has a different ORDER BY clause ** (b) The subquery is part of a join ** See forum post 062d576715d277c8 + ** (6) The subquery is not a recursive CTE. ORDER BY has a different + ** meaning for recursive CTEs and this optimization does not + ** apply. ** ** Also retain the ORDER BY if the OmitOrderBy optimization is disabled. */ if( pSub->pOrderBy!=0 && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */ && pSub->pLimit==0 /* Condition (1) */ - && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */ + && (pSub->selFlags & (SF_OrderByReqd|SF_Recursive))==0 /* (2) and (6) */ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */ && OptimizationEnabled(db, SQLITE_OmitOrderBy) ){ @@ -150470,6 +151351,7 @@ SQLITE_PRIVATE int sqlite3Select( continue; } + /* tag-select-0240 */ if( flattenSubquery(pParse, p, i, isAgg) ){ if( pParse->nErr ) goto select_end; /* This subquery can be absorbed into its parent. */ @@ -150485,7 +151367,7 @@ SQLITE_PRIVATE int sqlite3Select( #ifndef SQLITE_OMIT_COMPOUND_SELECT /* Handle compound SELECT statements using the separate multiSelect() - ** procedure. + ** procedure. tag-select-0300 */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); @@ -150501,9 +151383,9 @@ SQLITE_PRIVATE int sqlite3Select( #endif /* Do the WHERE-clause constant propagation optimization if this is - ** a join. No need to speed time on this operation for non-join queries + ** a join. No need to spend time on this operation for non-join queries ** as the equivalent optimization will be handled by query planner in - ** sqlite3WhereBegin(). + ** sqlite3WhereBegin(). tag-select-0330 */ if( p->pWhere!=0 && p->pWhere->op==TK_AND @@ -150520,6 +151402,7 @@ SQLITE_PRIVATE int sqlite3Select( TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); } + /* tag-select-0350 */ if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) && countOfViewOptimization(pParse, p) ){ @@ -150527,20 +151410,26 @@ SQLITE_PRIVATE int sqlite3Select( pTabList = p->pSrc; } - /* For each term in the FROM clause, do two things: - ** (1) Authorized unreferenced tables - ** (2) Generate code for all sub-queries + /* Loop over all terms in the FROM clause and do two things for each term: + ** + ** (1) Authorize unreferenced tables + ** (2) Generate code for all sub-queries + ** + ** tag-select-0400 */ for(i=0; inSrc; i++){ SrcItem *pItem = &pTabList->a[i]; SrcItem *pPrior; SelectDest dest; + Subquery *pSubq; Select *pSub; #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) const char *zSavedAuthContext; #endif - /* Issue SQLITE_READ authorizations with a fake column name for any + /* Authorized unreferenced tables. tag-select-0410 + ** + ** Issue SQLITE_READ authorizations with a fake column name for any ** tables that are referenced but from which no values are extracted. ** Examples of where these kinds of null SQLITE_READ authorizations ** would occur: @@ -150557,17 +151446,28 @@ SQLITE_PRIVATE int sqlite3Select( ** string for the fake column name seems safer. */ if( pItem->colUsed==0 && pItem->zName!=0 ){ - sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase); + const char *zDb; + if( pItem->fg.fixedSchema ){ + int iDb = sqlite3SchemaToIndex(pParse->db, pItem->u4.pSchema); + zDb = db->aDb[iDb].zDbSName; + }else if( pItem->fg.isSubquery ){ + zDb = 0; + }else{ + zDb = pItem->u4.zDatabase; + } + sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", zDb); } #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Generate code for all sub-queries in the FROM clause */ - pSub = pItem->pSelect; - if( pSub==0 || pItem->addrFillSub!=0 ) continue; + if( pItem->fg.isSubquery==0 ) continue; + pSubq = pItem->u4.pSubq; + assert( pSubq!=0 ); + pSub = pSubq->pSelect; /* The code for a subquery should only be generated once. */ - assert( pItem->addrFillSub==0 ); + if( pSubq->addrFillSub!=0 ) continue; /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -150580,6 +151480,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. + ** This is the "predicate push-down optimization". tag-select-0420 */ if( OptimizationEnabled(db, SQLITE_PushDown) && (pItem->fg.isCte==0 @@ -150593,13 +151494,14 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3TreeViewSelect(0, p, 0); } #endif - assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); + assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); }else{ TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); } /* Convert unused result columns of the subquery into simple NULL ** expressions, to avoid unneeded searching and computation. + ** tag-select-0440 */ if( OptimizationEnabled(db, SQLITE_NullUnusedCols) && disableUnusedSubqueryResultColumns(pItem) @@ -150617,32 +151519,33 @@ SQLITE_PRIVATE int sqlite3Select( zSavedAuthContext = pParse->zAuthContext; pParse->zAuthContext = pItem->zName; - /* Generate code to implement the subquery + /* Generate byte-code to implement the subquery tag-select-0480 */ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ /* Implement a co-routine that will return a single row of the result - ** set on each invocation. + ** set on each invocation. tag-select-0482 */ int addrTop = sqlite3VdbeCurrentAddr(v)+1; - pItem->regReturn = ++pParse->nMem; - sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); + pSubq->regReturn = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop); VdbeComment((v, "%!S", pItem)); - pItem->addrFillSub = addrTop; - sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); + pSubq->addrFillSub = addrTop; + sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowLogEst = pSub->nSelectRow; + pItem->pSTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; - pItem->regResult = dest.iSdst; - sqlite3VdbeEndCoroutine(v, pItem->regReturn); + pSubq->regResult = dest.iSdst; + sqlite3VdbeEndCoroutine(v, pSubq->regReturn); + VdbeComment((v, "end %!S", pItem)); sqlite3VdbeJumpHere(v, addrTop-1); sqlite3ClearTempRegCache(pParse); }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ /* This is a CTE for which materialization code has already been ** generated. Invoke the subroutine to compute the materialization, - ** the make the pItem->iCursor be a copy of the ephemeral table that - ** holds the result of the materialization. */ + ** then make the pItem->iCursor be a copy of the ephemeral table that + ** holds the result of the materialization. tag-select-0484 */ CteUse *pCteUse = pItem->u2.pCteUse; sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); if( pItem->iCursor!=pCteUse->iCur ){ @@ -150652,25 +151555,30 @@ SQLITE_PRIVATE int sqlite3Select( pSub->nSelectRow = pCteUse->nRowEst; }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){ /* This view has already been materialized by a prior entry in - ** this same FROM clause. Reuse it. */ - if( pPrior->addrFillSub ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); + ** this same FROM clause. Reuse it. tag-select-0486 */ + Subquery *pPriorSubq; + assert( pPrior->fg.isSubquery ); + pPriorSubq = pPrior->u4.pSubq; + assert( pPriorSubq!=0 ); + if( pPriorSubq->addrFillSub ){ + sqlite3VdbeAddOp2(v, OP_Gosub, pPriorSubq->regReturn, + pPriorSubq->addrFillSub); } sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); - pSub->nSelectRow = pPrior->pSelect->nSelectRow; + pSub->nSelectRow = pPriorSubq->pSelect->nSelectRow; }else{ /* Materialize the view. If the view is not correlated, generate a ** subroutine to do the materialization so that subsequent uses of - ** the same view can reuse the materialization. */ + ** the same view can reuse the materialization. tag-select-0488 */ int topAddr; int onceAddr = 0; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int addrExplain; #endif - pItem->regReturn = ++pParse->nMem; + pSubq->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp0(v, OP_Goto); - pItem->addrFillSub = topAddr+1; + pSubq->addrFillSub = topAddr+1; pItem->fg.isMaterialized = 1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of @@ -150685,17 +151593,17 @@ SQLITE_PRIVATE int sqlite3Select( ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowLogEst = pSub->nSelectRow; + pItem->pSTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); - sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); + sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ CteUse *pCteUse = pItem->u2.pCteUse; - pCteUse->addrM9e = pItem->addrFillSub; - pCteUse->regRtn = pItem->regReturn; + pCteUse->addrM9e = pSubq->addrFillSub; + pCteUse->regRtn = pSubq->regReturn; pCteUse->iCur = pItem->iCursor; pCteUse->nRowEst = pSub->nSelectRow; } @@ -150721,7 +151629,9 @@ SQLITE_PRIVATE int sqlite3Select( } #endif - /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and + /* tag-select-0500 + ** + ** If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query ** can be rewritten as a GROUP BY. In other words, this: ** @@ -150738,12 +151648,18 @@ SQLITE_PRIVATE int sqlite3Select( */ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 + && OptimizationEnabled(db, SQLITE_GroupByOrder) #ifndef SQLITE_OMIT_WINDOWFUNC && p->pWin==0 #endif ){ p->selFlags &= ~SF_Distinct; pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); + if( pGroupBy ){ + for(i=0; inExpr; i++){ + pGroupBy->a[i].u.x.iOrderByCol = i+1; + } + } p->selFlags |= SF_Aggregate; /* Notice that even thought SF_Distinct has been cleared from p->selFlags, ** the sDistinct.isTnct is still set. Hence, isTnct represents the @@ -150765,7 +151681,7 @@ SQLITE_PRIVATE int sqlite3Select( ** If that is the case, then the OP_OpenEphemeral instruction will be ** changed to an OP_Noop once we figure out that the sorting index is ** not needed. The sSort.addrSortIndex variable is used to facilitate - ** that change. + ** that change. tag-select-0600 */ if( sSort.pOrderBy ){ KeyInfo *pKeyInfo; @@ -150782,6 +151698,7 @@ SQLITE_PRIVATE int sqlite3Select( } /* If the output is destined for a temporary table, open that table. + ** tag-select-0630 */ if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); @@ -150799,7 +151716,7 @@ SQLITE_PRIVATE int sqlite3Select( } } - /* Set the limiter. + /* Set the limiter. tag-select-0650 */ iEnd = sqlite3VdbeMakeLabel(pParse); if( (p->selFlags & SF_FixedLimit)==0 ){ @@ -150811,7 +151728,7 @@ SQLITE_PRIVATE int sqlite3Select( sSort.sortFlags |= SORTFLAG_UseSorter; } - /* Open an ephemeral index to use for the distinct set. + /* Open an ephemeral index to use for the distinct set. tag-select-0680 */ if( p->selFlags & SF_Distinct ){ sDistinct.tabTnct = pParse->nTab++; @@ -150826,7 +151743,7 @@ SQLITE_PRIVATE int sqlite3Select( } if( !isAgg && pGroupBy==0 ){ - /* No aggregate functions and no GROUP BY clause */ + /* No aggregate functions and no GROUP BY clause. tag-select-0700 */ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0) | (p->selFlags & SF_FixedLimit); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -150899,8 +151816,8 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3WhereEnd(pWInfo); } }else{ - /* This case when there exist aggregate functions or a GROUP BY clause - ** or both */ + /* This case is for when there exist aggregate functions or a GROUP BY + ** clause or both. tag-select-0800 */ NameContext sNC; /* Name context for processing aggregate information */ int iAMem; /* First Mem address for storing current GROUP BY */ int iBMem; /* First Mem address for previous GROUP BY */ @@ -151019,7 +151936,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Processing for aggregates with GROUP BY is very different and - ** much more complex than aggregates without a GROUP BY. + ** much more complex than aggregates without a GROUP BY. tag-select-0810 */ if( pGroupBy ){ KeyInfo *pKeyInfo; /* Keying information for the group by clause */ @@ -151206,12 +152123,25 @@ SQLITE_PRIVATE int sqlite3Select( sortOut, sortPTab); } for(j=0; jnExpr; j++){ + int iOrderByCol = pGroupBy->a[j].u.x.iOrderByCol; + if( groupBySort ){ sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); }else{ pAggInfo->directMode = 1; sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); } + + if( iOrderByCol ){ + Expr *pX = p->pEList->a[iOrderByCol-1].pExpr; + Expr *pBase = sqlite3ExprSkipCollateAndLikely(pX); + if( ALWAYS(pBase!=0) + && pBase->op!=TK_AGG_COLUMN + && pBase->op!=TK_REGISTER + ){ + sqlite3ExprToRegister(pX, iAMem+j); + } + } } sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); @@ -151227,9 +152157,9 @@ SQLITE_PRIVATE int sqlite3Select( ** and resets the aggregate accumulator registers in preparation ** for the next GROUP BY batch. */ - sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); VdbeComment((v, "output one row")); + sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v); VdbeComment((v, "check abort flag")); sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); @@ -151303,9 +152233,12 @@ SQLITE_PRIVATE int sqlite3Select( } } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { + /* Aggregate functions without GROUP BY. tag-select-0820 */ Table *pTab; if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ - /* If isSimpleCount() returns a pointer to a Table structure, then + /* tag-select-0821 + ** + ** If isSimpleCount() returns a pointer to a Table structure, then ** the SQL statement is of the form: ** ** SELECT count(*) FROM @@ -151364,6 +152297,8 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else{ + /* The general case of an aggregate query without GROUP BY + ** tag-select-0822 */ int regAcc = 0; /* "populate accumulators" flag */ ExprList *pDistinct = 0; u16 distFlag = 0; @@ -151452,7 +152387,7 @@ SQLITE_PRIVATE int sqlite3Select( } /* If there is an ORDER BY clause, then we need to sort the results - ** and send them to the callback one by one. + ** and send them to the callback one by one. tag-select-0900 */ if( sSort.pOrderBy ){ assert( p->pEList==pEList ); @@ -151475,6 +152410,7 @@ select_end: assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG + /* Internal self-checks. tag-select-1000 */ if( pAggInfo && !db->mallocFailed ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x20 ){ @@ -151864,8 +152800,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( ** name on pTableName if we are reparsing out of the schema table */ if( db->init.busy && iDb!=1 ){ - sqlite3DbFree(db, pTableName->a[0].zDatabase); - pTableName->a[0].zDatabase = 0; + assert( pTableName->a[0].fg.fixedSchema==0 ); + assert( pTableName->a[0].fg.isSubquery==0 ); + sqlite3DbFree(db, pTableName->a[0].u4.zDatabase); + pTableName->a[0].u4.zDatabase = 0; } /* If the trigger name was unqualified, and the table is a temp table, @@ -152343,7 +153281,8 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) } assert( pName->nSrc==1 ); - zDb = pName->a[0].zDatabase; + assert( pName->a[0].fg.fixedSchema==0 && pName->a[0].fg.isSubquery==0 ); + zDb = pName->a[0].u4.zDatabase; zName = pName->a[0].zName; assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ @@ -152580,7 +153519,9 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( Schema *pSchema = pStep->pTrig->pSchema; pSrc->a[0].zName = zName; if( pSchema!=db->aDb[1].pSchema ){ - pSrc->a[0].pSchema = pSchema; + assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 ); + pSrc->a[0].u4.pSchema = pSchema; + pSrc->a[0].fg.fixedSchema = 1; } if( pStep->pFrom ){ SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); @@ -152693,7 +153634,7 @@ static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ pSrc = pSelect->pSrc; assert( pSrc!=0 ); for(i=0; inSrc; i++){ - if( pSrc->a[i].pTab==pWalker->u.pTab ){ + if( pSrc->a[i].pSTab==pWalker->u.pTab ){ testcase( pSelect->selFlags & SF_Correlated ); pSelect->selFlags |= SF_Correlated; pWalker->eCode = 1; @@ -152764,7 +153705,7 @@ static void codeReturningTrigger( sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; - sFrom.a[0].pTab = pTab; + sFrom.a[0].pSTab = pTab; sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); @@ -153475,7 +154416,7 @@ static void updateFromSelect( Expr *pLimit2 = 0; ExprList *pOrderBy2 = 0; sqlite3 *db = pParse->db; - Table *pTab = pTabList->a[0].pTab; + Table *pTab = pTabList->a[0].pSTab; SrcList *pSrc; Expr *pWhere2; int eDest; @@ -153499,8 +154440,8 @@ static void updateFromSelect( if( pSrc ){ assert( pSrc->a[0].fg.notCte ); pSrc->a[0].iCursor = -1; - pSrc->a[0].pTab->nTabRef--; - pSrc->a[0].pTab = 0; + pSrc->a[0].pSTab->nTabRef--; + pSrc->a[0].pSTab = 0; } if( pPk ){ for(i=0; inKeyCol; i++){ @@ -154748,7 +155689,7 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( int nClause = 0; /* Counter of ON CONFLICT clauses */ assert( pTabList->nSrc==1 ); - assert( pTabList->a[0].pTab!=0 ); + assert( pTabList->a[0].pSTab!=0 ); assert( pUpsert!=0 ); assert( pUpsert->pUpsertTarget!=0 ); @@ -154767,7 +155708,7 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( if( rc ) return rc; /* Check to see if the conflict target matches the rowid. */ - pTab = pTabList->a[0].pTab; + pTab = pTabList->a[0].pSTab; pTarget = pUpsert->pUpsertTarget; iCursor = pTabList->a[0].iCursor; if( HasRowid(pTab) @@ -155138,6 +156079,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ + u64 iRandom; /* Random value used for zDbVacuum[] */ + char zDbVacuum[42]; /* Name of the ATTACH-ed database used for vacuum */ + if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); @@ -155178,27 +156122,29 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); - /* Attach the temporary database as 'vacuum_db'. The synchronous pragma + /* Attach the temporary database as 'vacuum_XXXXXX'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimization would be to use a non-journaled pager. - ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but + ** (Later:) I tried setting "PRAGMA vacuum_XXXXXX.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially ** empty. Only the journal header is written. Apparently it takes more ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ + sqlite3_randomness(sizeof(iRandom),&iRandom); + sqlite3_snprintf(sizeof(zDbVacuum), zDbVacuum, "vacuum_%016llx", iRandom); nDb = db->nDb; - rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); + rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS %s", zOut, zDbVacuum); db->openFlags = saved_openFlags; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; - assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); + assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); pTemp = pDb->pBt; if( pOut ){ sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); @@ -155275,11 +156221,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( ** the contents to the temporary database. */ rc = execSqlF(db, pzErrMsg, - "SELECT'INSERT INTO vacuum_db.'||quote(name)" + "SELECT'INSERT INTO %s.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" - "FROM vacuum_db.sqlite_schema " + "FROM %s.sqlite_schema " "WHERE type='table'AND coalesce(rootpage,1)>0", - zDbMain + zDbVacuum, zDbMain, zDbVacuum ); assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); db->mDbFlags &= ~DBFLAG_Vacuum; @@ -155291,11 +156237,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( ** from the schema table. */ rc = execSqlF(db, pzErrMsg, - "INSERT INTO vacuum_db.sqlite_schema" + "INSERT INTO %s.sqlite_schema" " SELECT*FROM \"%w\".sqlite_schema" " WHERE type IN('view','trigger')" " OR(type='table'AND rootpage=0)", - zDbMain + zDbVacuum, zDbMain ); if( rc ) goto end_of_vacuum; @@ -156259,6 +157205,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ Table *pNew = sParse.pNewTable; Index *pIdx; pTab->aCol = pNew->aCol; + assert( IsOrdinaryTable(pNew) ); sqlite3ExprListDelete(db, pNew->u.tab.pDfltList); pTab->nNVCol = pTab->nCol = pNew->nCol; pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); @@ -156933,11 +157880,13 @@ struct WhereLoop { u16 nTop; /* Size of TOP vector */ u16 nDistinctCol; /* Index columns used to sort for DISTINCT */ Index *pIndex; /* Index used, or NULL */ + ExprList *pOrderBy; /* ORDER BY clause if this is really a subquery */ } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ u32 bOmitOffset : 1; /* True to let virtual table handle offset */ + u32 bIdxNumHex : 1; /* Show idxNum as hex in EXPLAIN QUERY PLAN */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ @@ -156950,6 +157899,8 @@ struct WhereLoop { /**** whereLoopXfer() copies fields above ***********************/ # define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) u16 nLSlot; /* Number of slots allocated for aLTerm[] */ + LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not + ** initialized unless pWInfo->nOutStarDelta>0 */ WhereTerm **aLTerm; /* WhereTerms used */ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */ @@ -157272,6 +158223,7 @@ struct WhereInfo { unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */ unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */ unsigned sorted :1; /* True if really sorted (not just grouped) */ + LogEst nOutStarDelta; /* Artifical nOut reduction for star-query */ LogEst nRowOut; /* Estimated number of output rows */ int iTop; /* The very beginning of the WHERE loop */ int iEndWhere; /* End of the WHERE clause itself */ @@ -157423,7 +158375,8 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ #define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ #define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ - /* 0x02000000 -- available for reuse */ +#define WHERE_COROUTINE 0x02000000 /* Implemented by co-routine. + ** NB: False-negatives are possible */ #define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */ #endif /* !defined(SQLITE_WHEREINT_H) */ @@ -157568,7 +158521,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( assert( pLoop->u.btree.pIndex!=0 ); pIdx = pLoop->u.btree.pIndex; assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); - if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){ + if( !HasRowid(pItem->pSTab) && IsPrimaryKeyIndex(pIdx) ){ if( isSearch ){ zFmt = "PRIMARY KEY"; } @@ -157611,7 +158564,9 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ - sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s", + sqlite3_str_appendall(&str, " VIRTUAL TABLE INDEX "); + sqlite3_str_appendf(&str, + pLoop->u.vtab.bIdxNumHex ? "0x%x:%s" : "%d:%s", pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif @@ -157629,7 +158584,8 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( zMsg = sqlite3StrAccumFinish(&str); sqlite3ExplainBreakpoint("",zMsg); ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), - pParse->addrExplain, 0, zMsg,P4_DYNAMIC); + pParse->addrExplain, pLoop->rRun, + zMsg, P4_DYNAMIC); } return ret; } @@ -157664,7 +158620,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); pLoop = pLevel->pWLoop; if( pLoop->wsFlags & WHERE_IPK ){ - const Table *pTab = pItem->pTab; + const Table *pTab = pItem->pSTab; if( pTab->iPKey>=0 ){ sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); }else{ @@ -157727,7 +158683,9 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur); } }else{ - int addr = pSrclist->a[pLvl->iFrom].addrFillSub; + int addr; + assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); + addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); @@ -157871,6 +158829,39 @@ static void updateRangeAffinityStr( } } +/* +** The pOrderBy->a[].u.x.iOrderByCol values might be incorrect because +** columns might have been rearranged in the result set. This routine +** fixes them up. +** +** pEList is the new result set. The pEList->a[].u.x.iOrderByCol values +** contain the *old* locations of each expression. This is a temporary +** use of u.x.iOrderByCol, not its intended use. The caller must reset +** u.x.iOrderByCol back to zero for all entries in pEList before the +** caller returns. +** +** This routine changes pOrderBy->a[].u.x.iOrderByCol values from +** pEList->a[N].u.x.iOrderByCol into N+1. (The "+1" is because of the 1-based +** indexing used by iOrderByCol.) Or if no match, iOrderByCol is set to zero. +*/ +static void adjustOrderByCol(ExprList *pOrderBy, ExprList *pEList){ + int i, j; + if( pOrderBy==0 ) return; + for(i=0; inExpr; i++){ + int t = pOrderBy->a[i].u.x.iOrderByCol; + if( t==0 ) continue; + for(j=0; jnExpr; j++){ + if( pEList->a[j].u.x.iOrderByCol==t ){ + pOrderBy->a[i].u.x.iOrderByCol = j+1; + break; + } + } + if( j>=pEList->nExpr ){ + pOrderBy->a[i].u.x.iOrderByCol = 0; + } + } +} + /* ** pX is an expression of the form: (vector) IN (SELECT ...) @@ -157934,6 +158925,7 @@ static Expr *removeUnindexableInClauseTerms( if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr); pOrigRhs->a[iField].pExpr = 0; + if( pRhs ) pRhs->a[pRhs->nExpr-1].u.x.iOrderByCol = iField+1; if( pOrigLhs ){ assert( pOrigLhs->a[iField].pExpr!=0 ); pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr); @@ -157956,18 +158948,16 @@ static Expr *removeUnindexableInClauseTerms( sqlite3ExprDelete(db, pNew->pLeft); pNew->pLeft = p; } - if( pSelect->pOrderBy ){ - /* If the SELECT statement has an ORDER BY clause, zero the - ** iOrderByCol variables. These are set to non-zero when an - ** ORDER BY term exactly matches one of the terms of the - ** result-set. Since the result-set of the SELECT statement may - ** have been modified or reordered, these variables are no longer - ** set correctly. Since setting them is just an optimization, - ** it's easiest just to zero them here. */ - ExprList *pOrderBy = pSelect->pOrderBy; - for(i=0; inExpr; i++){ - pOrderBy->a[i].u.x.iOrderByCol = 0; - } + + /* If either the ORDER BY clause or the GROUP BY clause contains + ** references to result-set columns, those references might now be + ** obsolete. So fix them up. + */ + assert( pRhs!=0 || db->mallocFailed ); + if( pRhs ){ + adjustOrderByCol(pSelect->pOrderBy, pRhs); + adjustOrderByCol(pSelect->pGroupBy, pRhs); + for(i=0; inExpr; i++) pRhs->a[i].u.x.iOrderByCol = 0; } #if 0 @@ -157982,6 +158972,147 @@ static Expr *removeUnindexableInClauseTerms( } +#ifndef SQLITE_OMIT_SUBQUERY +/* +** Generate code for a single X IN (....) term of the WHERE clause. +** +** This is a special-case of codeEqualityTerm() that works for IN operators +** only. It is broken out into a subroutine because this case is +** uncommon and by splitting it off into a subroutine, the common case +** runs faster. +** +** The current value for the constraint is left in register iTarget. +** This routine sets up a loop that will iterate over all values of X. +*/ +static SQLITE_NOINLINE void codeINTerm( + Parse *pParse, /* The parsing context */ + WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ + WhereLevel *pLevel, /* The level of the FROM clause we are working on */ + int iEq, /* Index of the equality term within this level */ + int bRev, /* True for reverse-order IN operations */ + int iTarget /* Attempt to leave results in this register */ +){ + Expr *pX = pTerm->pExpr; + int eType = IN_INDEX_NOOP; + int iTab; + struct InLoop *pIn; + WhereLoop *pLoop = pLevel->pWLoop; + Vdbe *v = pParse->pVdbe; + int i; + int nEq = 0; + int *aiMap = 0; + + if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 + && pLoop->u.btree.pIndex!=0 + && pLoop->u.btree.pIndex->aSortOrder[iEq] + ){ + testcase( iEq==0 ); + testcase( bRev ); + bRev = !bRev; + } + assert( pX->op==TK_IN ); + + for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ + disableTerm(pLevel, pTerm); + return; + } + } + for(i=iEq;inLTerm; i++){ + assert( pLoop->aLTerm[i]!=0 ); + if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; + } + + iTab = 0; + if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); + }else{ + Expr *pExpr = pTerm->pExpr; + if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){ + sqlite3 *db = pParse->db; + pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); + if( !db->mallocFailed ){ + aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); + pExpr->iTable = iTab; + } + sqlite3ExprDelete(db, pX); + }else{ + int n = sqlite3ExprVectorSize(pX->pLeft); + aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n)); + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); + } + pX = pExpr; + } + + if( eType==IN_INDEX_INDEX_DESC ){ + testcase( bRev ); + bRev = !bRev; + } + sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); + VdbeCoverageIf(v, bRev); + VdbeCoverageIf(v, !bRev); + + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + pLoop->wsFlags |= WHERE_IN_ABLE; + if( pLevel->u.in.nIn==0 ){ + pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); + } + if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){ + pLoop->wsFlags |= WHERE_IN_EARLYOUT; + } + + i = pLevel->u.in.nIn; + pLevel->u.in.nIn += nEq; + pLevel->u.in.aInLoop = + sqlite3WhereRealloc(pTerm->pWC->pWInfo, + pLevel->u.in.aInLoop, + sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); + pIn = pLevel->u.in.aInLoop; + if( pIn ){ + int iMap = 0; /* Index in aiMap[] */ + pIn += i; + for(i=iEq;inLTerm; i++){ + if( pLoop->aLTerm[i]->pExpr==pX ){ + int iOut = iTarget + i - iEq; + if( eType==IN_INDEX_ROWID ){ + pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); + }else{ + int iCol = aiMap ? aiMap[iMap++] : 0; + pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); + } + sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); + if( i==iEq ){ + pIn->iCur = iTab; + pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; + if( iEq>0 ){ + pIn->iBase = iTarget - i; + pIn->nPrefix = i; + }else{ + pIn->nPrefix = 0; + } + }else{ + pIn->eEndLoopOp = OP_Noop; + } + pIn++; + } + } + testcase( iEq>0 + && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 + && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ); + if( iEq>0 + && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0 + ){ + sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq); + } + }else{ + pLevel->u.in.nIn = 0; + } + sqlite3DbFree(pParse->db, aiMap); +} +#endif + + /* ** Generate code for a single equality term of the WHERE clause. An equality ** term can be either X=expr or X IN (...). pTerm is the term to be @@ -158006,7 +159137,6 @@ static int codeEqualityTerm( int iTarget /* Attempt to leave results in this register */ ){ Expr *pX = pTerm->pExpr; - Vdbe *v = pParse->pVdbe; int iReg; /* Register holding results */ assert( pLevel->pWLoop->aLTerm[iEq]==pTerm ); @@ -158015,125 +159145,12 @@ static int codeEqualityTerm( iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget); }else if( pX->op==TK_ISNULL ){ iReg = iTarget; - sqlite3VdbeAddOp2(v, OP_Null, 0, iReg); + sqlite3VdbeAddOp2(pParse->pVdbe, OP_Null, 0, iReg); #ifndef SQLITE_OMIT_SUBQUERY }else{ - int eType = IN_INDEX_NOOP; - int iTab; - struct InLoop *pIn; - WhereLoop *pLoop = pLevel->pWLoop; - int i; - int nEq = 0; - int *aiMap = 0; - - if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 - && pLoop->u.btree.pIndex!=0 - && pLoop->u.btree.pIndex->aSortOrder[iEq] - ){ - testcase( iEq==0 ); - testcase( bRev ); - bRev = !bRev; - } assert( pX->op==TK_IN ); iReg = iTarget; - - for(i=0; iaLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){ - disableTerm(pLevel, pTerm); - return iTarget; - } - } - for(i=iEq;inLTerm; i++){ - assert( pLoop->aLTerm[i]!=0 ); - if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; - } - - iTab = 0; - if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){ - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); - }else{ - Expr *pExpr = pTerm->pExpr; - if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){ - sqlite3 *db = pParse->db; - pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); - if( !db->mallocFailed ){ - aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); - pExpr->iTable = iTab; - } - sqlite3ExprDelete(db, pX); - }else{ - int n = sqlite3ExprVectorSize(pX->pLeft); - aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n)); - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); - } - pX = pExpr; - } - - if( eType==IN_INDEX_INDEX_DESC ){ - testcase( bRev ); - bRev = !bRev; - } - sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); - VdbeCoverageIf(v, bRev); - VdbeCoverageIf(v, !bRev); - - assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); - pLoop->wsFlags |= WHERE_IN_ABLE; - if( pLevel->u.in.nIn==0 ){ - pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); - } - if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){ - pLoop->wsFlags |= WHERE_IN_EARLYOUT; - } - - i = pLevel->u.in.nIn; - pLevel->u.in.nIn += nEq; - pLevel->u.in.aInLoop = - sqlite3WhereRealloc(pTerm->pWC->pWInfo, - pLevel->u.in.aInLoop, - sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn); - pIn = pLevel->u.in.aInLoop; - if( pIn ){ - int iMap = 0; /* Index in aiMap[] */ - pIn += i; - for(i=iEq;inLTerm; i++){ - if( pLoop->aLTerm[i]->pExpr==pX ){ - int iOut = iReg + i - iEq; - if( eType==IN_INDEX_ROWID ){ - pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); - }else{ - int iCol = aiMap ? aiMap[iMap++] : 0; - pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut); - } - sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v); - if( i==iEq ){ - pIn->iCur = iTab; - pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; - if( iEq>0 ){ - pIn->iBase = iReg - i; - pIn->nPrefix = i; - }else{ - pIn->nPrefix = 0; - } - }else{ - pIn->eEndLoopOp = OP_Noop; - } - pIn++; - } - } - testcase( iEq>0 - && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 - && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ); - if( iEq>0 - && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0 - ){ - sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq); - } - }else{ - pLevel->u.in.nIn = 0; - } - sqlite3DbFree(pParse->db, aiMap); + codeINTerm(pParse, pTerm, pLevel, iEq, bRev, iTarget); #endif } @@ -158805,7 +159822,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( iCur = pTabItem->iCursor; pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; - VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); + VdbeModuleComment((v, "Begin WHERE-loop%d: %s", + iLevel, pTabItem->pSTab->zName)); #if WHERETRACE_ENABLED /* 0x4001 */ if( sqlite3WhereTrace & 0x1 ){ sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n", @@ -158860,11 +159878,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ - int regYield = pTabItem->regReturn; - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); + int regYield; + Subquery *pSubq; + assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 ); + pSubq = pTabItem->u4.pSubq; + regYield = pSubq->regReturn; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); - VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); + VdbeComment((v, "next row of %s", pTabItem->pSTab->zName)); pLevel->op = OP_Goto; }else @@ -159593,7 +160615,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ - Table *pTab = pTabItem->pTab; + Table *pTab = pTabItem->pSTab; pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); @@ -160052,7 +161074,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** least once. This is accomplished by storing the PK for the row in ** both the iMatch index and the regBloom Bloom filter. */ - pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab; + pTab = pWInfo->pTabList->a[pLevel->iFrom].pSTab; if( HasRowid(pTab) ){ r = sqlite3GetTempRange(pParse, 2); sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); @@ -160159,7 +161181,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( Bitmask mAll = 0; int k; - ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName)); + ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, pRJ->regReturn); for(k=0; kpTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; if( pRight->fg.viaCoroutine ){ + Subquery *pSubq; + assert( pRight->fg.isSubquery && pRight->u4.pSubq!=0 ); + pSubq = pRight->u4.pSubq; + assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 ); sqlite3VdbeAddOp3( - v, OP_Null, 0, pRight->regResult, - pRight->regResult + pRight->pSelect->pEList->nExpr-1 + v, OP_Null, 0, pSubq->regResult, + pSubq->regResult + pSubq->pSelect->pEList->nExpr-1 ); } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); @@ -160209,7 +161235,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( int nPk; int jmp; int addrCont = sqlite3WhereContinueLabel(pSubWInfo); - Table *pTab = pTabItem->pTab; + Table *pTab = pTabItem->pSTab; if( HasRowid(pTab) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); nPk = 1; @@ -160342,7 +161368,12 @@ static int allowedOp(int op){ assert( TK_LT>TK_EQ && TK_LTTK_EQ && TK_LE=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS; + assert( TK_INTK_GE ) return 0; + if( op>=TK_EQ ) return 1; + return op==TK_IN || op==TK_ISNULL || op==TK_IS; } /* @@ -160375,15 +161406,16 @@ static u16 exprCommute(Parse *pParse, Expr *pExpr){ static u16 operatorMask(int op){ u16 c; assert( allowedOp(op) ); - if( op==TK_IN ){ + if( op>=TK_EQ ){ + assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); + c = (u16)(WO_EQ<<(op-TK_EQ)); + }else if( op==TK_IN ){ c = WO_IN; }else if( op==TK_ISNULL ){ c = WO_ISNULL; - }else if( op==TK_IS ){ - c = WO_IS; }else{ - assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff ); - c = (u16)(WO_EQ<<(op-TK_EQ)); + assert( op==TK_IS ); + c = WO_IS; } assert( op!=TK_ISNULL || c==WO_ISNULL ); assert( op!=TK_IN || c==WO_IN ); @@ -160454,12 +161486,26 @@ static int isLikeOrGlob( z = (u8*)pRight->u.zToken; } if( z ){ - - /* Count the number of prefix characters prior to the first wildcard */ + /* Count the number of prefix bytes prior to the first wildcard. + ** or U+fffd character. If the underlying database has a UTF16LE + ** encoding, then only consider ASCII characters. Note that the + ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in + ** this code, but the database engine itself might be processing + ** content using a different encoding. */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; - if( c==wc[3] && z[cnt]!=0 ) cnt++; + if( c==wc[3] && z[cnt]>0 && z[cnt]<0x80 ){ + cnt++; + }else if( c>=0x80 ){ + const u8 *z2 = z+cnt-1; + if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){ + cnt--; + break; + }else{ + cnt = (int)(z2-z); + } + } } /* The optimization is possible only if (1) the pattern does not begin @@ -160470,11 +161516,11 @@ static int isLikeOrGlob( ** range search. The third is because the caller assumes that the pattern ** consists of at least one character after all escapes have been ** removed. */ - if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){ + if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && ALWAYS(255!=(u8)z[cnt-1]) ){ Expr *pPrefix; /* A "complete" match if the pattern ends with "*" or "%" */ - *pisComplete = c==wc[0] && z[cnt+1]==0; + *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; /* Get the pattern prefix. Remove all escapes from the prefix. */ pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); @@ -160670,6 +161716,13 @@ static int isAuxiliaryVtabOperator( } } } + }else if( pExpr->op>=TK_EQ ){ + /* Comparison operators are a common case. Save a few comparisons for + ** that common case by terminating early. */ + assert( TK_NE < TK_EQ ); + assert( TK_ISNOT < TK_EQ ); + assert( TK_NOTNULL < TK_EQ ); + return 0; }else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){ int res = 0; Expr *pLeft = pExpr->pLeft; @@ -161186,7 +162239,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){ if( ALWAYS(pSrc!=0) ){ int i; for(i=0; inSrc; i++){ - mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect); + if( pSrc->a[i].fg.isSubquery ){ + mask |= exprSelectUsage(pMaskSet, pSrc->a[i].u4.pSubq->pSelect); + } if( pSrc->a[i].fg.isUsing==0 ){ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); } @@ -161224,7 +162279,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2( int iCur; do{ iCur = pFrom->a[j].iCursor; - for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(pIdx=pFrom->a[j].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr==0 ) continue; for(i=0; inKeyCol; i++){ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; @@ -161268,7 +162323,7 @@ static int exprMightBeIndexed( for(i=0; inSrc; i++){ Index *pIdx; - for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(pIdx=pFrom->a[i].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr ){ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i); } @@ -161811,7 +162866,7 @@ static void whereAddLimitExpr( Expr *pNew; int iVal = 0; - if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){ + if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); if( pVal==0 ) return; ExprSetProperty(pVal, EP_IntValue); @@ -161856,7 +162911,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ if( p->pGroupBy==0 && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ - && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pSTab)) /* 3 */ ){ ExprList *pOrderBy = p->pOrderBy; int iCsr = p->pSrc->a[0].iCursor; @@ -162077,7 +163132,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs( Expr *pColRef; Expr *pTerm; if( pItem->fg.isTabFunc==0 ) return; - pTab = pItem->pTab; + pTab = pItem->pSTab; assert( pTab!=0 ); pArgs = pItem->u1.pFuncArg; if( pArgs==0 ) return; @@ -162761,7 +163816,7 @@ static int isDistinctRedundant( ** clause is redundant. */ if( pTabList->nSrc!=1 ) return 0; iBase = pTabList->a[0].iCursor; - pTab = pTabList->a[0].pTab; + pTab = pTabList->a[0].pSTab; /* If any of the expressions is an IPK column on table iBase, then return ** true. Note: The (p->iTable==iBase) part of this test may be false if the @@ -162836,6 +163891,12 @@ static void translateColumnToCopy( VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); int iEnd = sqlite3VdbeCurrentAddr(v); if( pParse->db->mallocFailed ) return; +#ifdef SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("CHECKING for column-to-copy on cursor %d for %d..%d\n", + iTabCur, iStart, iEnd); + } +#endif for(; iStartp1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ @@ -162957,6 +164018,40 @@ static int constraintCompatibleWithOuterJoin( return 1; } +#ifndef SQLITE_OMIT_AUTOMATIC_INDEX +/* +** Return true if column iCol of table pTab seem like it might be a +** good column to use as part of a query-time index. +** +** Current algorithm (subject to improvement!): +** +** 1. If iCol is already the left-most column of some other index, +** then return false. +** +** 2. If iCol is part of an existing index that has an aiRowLogEst of +** more than 20, then return false. +** +** 3. If no disqualifying conditions above are found, return true. +*/ +static SQLITE_NOINLINE int columnIsGoodIndexCandidate( + const Table *pTab, + int iCol +){ + const Index *pIdx; + for(pIdx = pTab->pIndex; pIdx!=0; pIdx=pIdx->pNext){ + int j; + for(j=0; jnKeyCol; j++){ + if( pIdx->aiColumn[j]==iCol ){ + if( j==0 ) return 0; + if( pIdx->hasStat1 && pIdx->aiRowLogEst[j+1]>20 ) return 0; + break; + } + } + } + return 1; +} +#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ + #ifndef SQLITE_OMIT_AUTOMATIC_INDEX @@ -162971,6 +164066,8 @@ static int termCanDriveIndex( const Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; + int leftCol; + if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0; assert( (pSrc->fg.jointype & JT_RIGHT)==0 ); @@ -162981,11 +164078,12 @@ static int termCanDriveIndex( } if( (pTerm->prereqRight & notReady)!=0 ) return 0; assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); - if( pTerm->u.x.leftColumn<0 ) return 0; - aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity; + leftCol = pTerm->u.x.leftColumn; + if( leftCol<0 ) return 0; + aff = pSrc->pSTab->aCol[leftCol].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; testcase( pTerm->pExpr->op==TK_IS ); - return 1; + return columnIsGoodIndexCandidate(pSrc->pSTab, leftCol); } #endif @@ -163093,7 +164191,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( nKeyCol = 0; pTabList = pWC->pWInfo->pTabList; pSrc = &pTabList->a[pLevel->iFrom]; - pTable = pSrc->pTab; + pTable = pSrc->pSTab; pWCEnd = &pWC->a[pWC->nTerm]; pLoop = pLevel->pWLoop; idxCols = 0; @@ -163235,12 +164333,17 @@ static SQLITE_NOINLINE void constructAutomaticIndex( /* Fill the automatic index with content */ assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); if( pSrc->fg.viaCoroutine ){ - int regYield = pSrc->regReturn; + int regYield; + Subquery *pSubq; + assert( pSrc->fg.isSubquery ); + pSubq = pSrc->u4.pSubq; + assert( pSubq!=0 ); + regYield = pSubq->regReturn; addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub); + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); VdbeCoverage(v); - VdbeComment((v, "next row of %s", pSrc->pTab->zName)); + VdbeComment((v, "next row of %s", pSrc->pSTab->zName)); }else{ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } @@ -163262,11 +164365,12 @@ static SQLITE_NOINLINE void constructAutomaticIndex( sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pSrc->fg.viaCoroutine ){ + assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, - pSrc->regResult, pLevel->iIdxCur); + pSrc->u4.pSubq->regResult, pLevel->iIdxCur); sqlite3VdbeGoto(v, addrTop); pSrc->fg.viaCoroutine = 0; }else{ @@ -163357,7 +164461,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( iSrc = pLevel->iFrom; pItem = &pTabList->a[iSrc]; assert( pItem!=0 ); - pTab = pItem->pTab; + pTab = pItem->pSTab; assert( pTab!=0 ); sz = sqlite3LogEstToInt(pTab->nRowLogEst); if( sz<10000 ){ @@ -163388,7 +164492,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( int r1 = sqlite3GetTempRange(pParse, n); int jj; for(jj=0; jjpTable==pItem->pTab ); + assert( pIdx->pTable==pItem->pSTab ); sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj); } sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n); @@ -163426,6 +164530,20 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return term iTerm of the WhereClause passed as the first argument. Terms +** are numbered from 0 upwards, starting with the terms in pWC->a[], then +** those in pWC->pOuter->a[] (if any), and so on. +*/ +static WhereTerm *termFromWhereClause(WhereClause *pWC, int iTerm){ + WhereClause *p; + for(p=pWC; p; p=p->pOuter){ + if( iTermnTerm ) return &p->a[iTerm]; + iTerm -= p->nTerm; + } + return 0; +} + /* ** Allocate and populate an sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure @@ -163452,9 +164570,10 @@ static sqlite3_index_info *allocateIndexInfo( const Table *pTab; int eDistinct = 0; ExprList *pOrderBy = pWInfo->pOrderBy; + WhereClause *p; assert( pSrc!=0 ); - pTab = pSrc->pTab; + pTab = pSrc->pSTab; assert( pTab!=0 ); assert( IsVirtual(pTab) ); @@ -163462,28 +164581,30 @@ static sqlite3_index_info *allocateIndexInfo( ** Mark each term with the TERM_OK flag. Set nTerm to the number of ** terms found. */ - for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - pTerm->wtFlags &= ~TERM_OK; - if( pTerm->leftCursor != pSrc->iCursor ) continue; - if( pTerm->prereqRight & mUnusable ) continue; - assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); - testcase( pTerm->eOperator & WO_IN ); - testcase( pTerm->eOperator & WO_ISNULL ); - testcase( pTerm->eOperator & WO_IS ); - testcase( pTerm->eOperator & WO_ALL ); - if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; - if( pTerm->wtFlags & TERM_VNULL ) continue; + for(p=pWC, nTerm=0; p; p=p->pOuter){ + for(i=0, pTerm=p->a; inTerm; i++, pTerm++){ + pTerm->wtFlags &= ~TERM_OK; + if( pTerm->leftCursor != pSrc->iCursor ) continue; + if( pTerm->prereqRight & mUnusable ) continue; + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); + testcase( pTerm->eOperator & WO_IN ); + testcase( pTerm->eOperator & WO_ISNULL ); + testcase( pTerm->eOperator & WO_IS ); + testcase( pTerm->eOperator & WO_ALL ); + if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue; + if( pTerm->wtFlags & TERM_VNULL ) continue; - assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); - assert( pTerm->u.x.leftColumn>=XN_ROWID ); - assert( pTerm->u.x.leftColumnnCol ); - if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 - && !constraintCompatibleWithOuterJoin(pTerm,pSrc) - ){ - continue; + assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); + assert( pTerm->u.x.leftColumn>=XN_ROWID ); + assert( pTerm->u.x.leftColumnnCol ); + if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 + && !constraintCompatibleWithOuterJoin(pTerm,pSrc) + ){ + continue; + } + nTerm++; + pTerm->wtFlags |= TERM_OK; } - nTerm++; - pTerm->wtFlags |= TERM_OK; } /* If the ORDER BY clause contains only columns in the current @@ -163558,53 +164679,69 @@ static sqlite3_index_info *allocateIndexInfo( pIdxInfo->aConstraint = pIdxCons; pIdxInfo->aOrderBy = pIdxOrderBy; pIdxInfo->aConstraintUsage = pUsage; + pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; + if( HasRowid(pTab)==0 ){ + /* Ensure that all bits associated with PK columns are set. This is to + ** ensure they are available for cases like RIGHT joins or OR loops. */ + Index *pPk = sqlite3PrimaryKeyIndex((Table*)pTab); + assert( pPk!=0 ); + for(i=0; inKeyCol; i++){ + int iCol = pPk->aiColumn[i]; + assert( iCol>=0 ); + if( iCol>=BMS-1 ) iCol = BMS-1; + pIdxInfo->colUsed |= MASKBIT(iCol); + } + } pHidden->pWC = pWC; pHidden->pParse = pParse; pHidden->eDistinct = eDistinct; pHidden->mIn = 0; - for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - u16 op; - if( (pTerm->wtFlags & TERM_OK)==0 ) continue; - pIdxCons[j].iColumn = pTerm->u.x.leftColumn; - pIdxCons[j].iTermOffset = i; - op = pTerm->eOperator & WO_ALL; - if( op==WO_IN ){ - if( (pTerm->wtFlags & TERM_SLICE)==0 ){ - pHidden->mIn |= SMASKBIT32(j); + for(p=pWC, i=j=0; p; p=p->pOuter){ + int nLast = i+p->nTerm;; + for(pTerm=p->a; iwtFlags & TERM_OK)==0 ) continue; + pIdxCons[j].iColumn = pTerm->u.x.leftColumn; + pIdxCons[j].iTermOffset = i; + op = pTerm->eOperator & WO_ALL; + if( op==WO_IN ){ + if( (pTerm->wtFlags & TERM_SLICE)==0 ){ + pHidden->mIn |= SMASKBIT32(j); + } + op = WO_EQ; } - op = WO_EQ; - } - if( op==WO_AUX ){ - pIdxCons[j].op = pTerm->eMatchOp; - }else if( op & (WO_ISNULL|WO_IS) ){ - if( op==WO_ISNULL ){ - pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; + if( op==WO_AUX ){ + pIdxCons[j].op = pTerm->eMatchOp; + }else if( op & (WO_ISNULL|WO_IS) ){ + if( op==WO_ISNULL ){ + pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; + }else{ + pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; + } }else{ - pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; - } - }else{ - pIdxCons[j].op = (u8)op; - /* The direct assignment in the previous line is possible only because - ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The - ** following asserts verify this fact. */ - assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); - assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); - assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); - assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); - assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); - assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); + pIdxCons[j].op = (u8)op; + /* The direct assignment in the previous line is possible only because + ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The + ** following asserts verify this fact. */ + assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); + assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); + assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); + assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); + assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); + assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) ); - if( op & (WO_LT|WO_LE|WO_GT|WO_GE) - && sqlite3ExprIsVector(pTerm->pExpr->pRight) - ){ - testcase( j!=i ); - if( j<16 ) mNoOmit |= (1 << j); - if( op==WO_LT ) pIdxCons[j].op = WO_LE; - if( op==WO_GT ) pIdxCons[j].op = WO_GE; + if( op & (WO_LT|WO_LE|WO_GT|WO_GE) + && sqlite3ExprIsVector(pTerm->pExpr->pRight) + ){ + testcase( j!=i ); + if( j<16 ) mNoOmit |= (1 << j); + if( op==WO_LT ) pIdxCons[j].op = WO_LE; + if( op==WO_GT ) pIdxCons[j].op = WO_GE; + } } + + j++; } - - j++; } assert( j==nTerm ); pIdxInfo->nConstraint = j; @@ -163624,6 +164761,17 @@ static sqlite3_index_info *allocateIndexInfo( return pIdxInfo; } +/* +** Free and zero the sqlite3_index_info.idxStr value if needed. +*/ +static void freeIdxStr(sqlite3_index_info *pIdxInfo){ + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } +} + /* ** Free an sqlite3_index_info structure allocated by allocateIndexInfo() ** and possibly modified by xBestIndex methods. @@ -163639,6 +164787,7 @@ static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){ sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ pHidden->aRhs[i] = 0; } + freeIdxStr(pIdxInfo); sqlite3DbFree(db, pIdxInfo); } @@ -163659,9 +164808,11 @@ static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){ ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; + sqlite3_vtab *pVtab; + assert( IsVirtual(pTab) ); + pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; whereTraceIndexInfoInputs(p, pTab); pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); @@ -164432,7 +165583,7 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; - Table *pTab = pItem->pTab; + Table *pTab = pItem->pSTab; Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); @@ -164604,7 +165755,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ ** and Y has additional constraints that might speed the search that X lacks ** but the cost of running X is not more than the cost of running Y. ** -** In other words, return true if the cost relationwship between X and Y +** In other words, return true if the cost relationship between X and Y ** is inverted and needs to be adjusted. ** ** Case 1: @@ -164990,7 +166141,7 @@ static void whereLoopOutputAdjust( Expr *pRight = pTerm->pExpr->pRight; int k = 0; testcase( pTerm->pExpr->op==TK_IS ); - if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){ + if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ k = 10; }else{ k = 20; @@ -165287,7 +166438,7 @@ static int whereLoopAddBtreeIndex( || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ){ if( iCol==XN_ROWID || pProbe->uniqNotNull - || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) + || (pProbe->nKeyCol==1 && pProbe->onError && (eOp & WO_EQ)) ){ pNew->wsFlags |= WHERE_ONEROW; }else{ @@ -165420,7 +166571,7 @@ static int whereLoopAddBtreeIndex( ** 2. Stepping forward in the index pNew->nOut times to find all ** additional matching entries. */ - assert( pSrc->pTab->szTabRow>0 ); + assert( pSrc->pSTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior ** pages are small. Thus szIdxRow gives a good estimate of seek cost. @@ -165428,7 +166579,7 @@ static int whereLoopAddBtreeIndex( ** under-estimate the scanning cost. */ rCostIdx = pNew->nOut + 16; }else{ - rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; + rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pSTab->szTabRow; } rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); @@ -165893,9 +167044,9 @@ static int whereLoopAddBtree( pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; - pTab = pSrc->pTab; + pTab = pSrc->pSTab; pWC = pBuilder->pWC; - assert( !IsVirtual(pSrc->pTab) ); + assert( !IsVirtual(pSrc->pSTab) ); if( pSrc->fg.isIndexedBy ){ assert( pSrc->fg.isCte==0 ); @@ -165920,7 +167071,7 @@ static int whereLoopAddBtree( sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; - pFirst = pSrc->pTab->pIndex; + pFirst = pSrc->pSTab->pIndex; if( pSrc->fg.notIndexed==0 ){ /* The real indices of the table are only considered if the ** NOT INDEXED qualifier is omitted from the FROM clause */ @@ -166010,6 +167161,7 @@ static int whereLoopAddBtree( pNew->prereq = mPrereq; pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; + pNew->u.btree.pOrderBy = 0; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ @@ -166039,6 +167191,10 @@ static int whereLoopAddBtree( #endif ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); + if( pSrc->fg.isSubquery ){ + if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; + pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; + } rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if( rc ) break; @@ -166241,7 +167397,7 @@ static int whereLoopAddVirtualOne( ** arguments mUsable and mExclude. */ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; ia[pIdxCons->iTermOffset]; + WhereTerm *pTerm = termFromWhereClause(pWC, pIdxCons->iTermOffset); pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 @@ -166260,11 +167416,10 @@ static int whereLoopAddVirtualOne( pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; - pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ - rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); + rc = vtabBestIndex(pParse, pSrc->pSTab, pIdxInfo); if( rc ){ if( rc==SQLITE_CONSTRAINT ){ /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means @@ -166272,6 +167427,7 @@ static int whereLoopAddVirtualOne( ** Make no entries in the loop table. */ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n")); + freeIdxStr(pIdxInfo); return SQLITE_OK; } return rc; @@ -166289,18 +167445,17 @@ static int whereLoopAddVirtualOne( int j = pIdxCons->iTermOffset; if( iTerm>=nConstraint || j<0 - || j>=pWC->nTerm + || (pTerm = termFromWhereClause(pWC, j))==0 || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ){ - sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - testcase( pIdxInfo->needToFreeIdxStr ); + sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); + freeIdxStr(pIdxInfo); return SQLITE_ERROR; } testcase( iTerm==nConstraint-1 ); testcase( j==0 ); testcase( j==pWC->nTerm-1 ); - pTerm = &pWC->a[j]; pNew->prereq |= pTerm->prereqRight; assert( iTermnLSlot ); pNew->aLTerm[iTerm] = pTerm; @@ -166345,11 +167500,7 @@ static int whereLoopAddVirtualOne( ** the plan cannot be used. In these cases set variable *pbRetryLimit ** to true to tell the caller to retry with LIMIT and OFFSET ** disabled. */ - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - pIdxInfo->idxStr = 0; - pIdxInfo->needToFreeIdxStr = 0; - } + freeIdxStr(pIdxInfo); *pbRetryLimit = 1; return SQLITE_OK; } @@ -166361,8 +167512,8 @@ static int whereLoopAddVirtualOne( if( pNew->aLTerm[i]==0 ){ /* The non-zero argvIdx values must be contiguous. Raise an ** error if they are not */ - sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); - testcase( pIdxInfo->needToFreeIdxStr ); + sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); + freeIdxStr(pIdxInfo); return SQLITE_ERROR; } } @@ -166373,6 +167524,7 @@ static int whereLoopAddVirtualOne( pNew->u.vtab.idxStr = pIdxInfo->idxStr; pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? pIdxInfo->nOrderBy : 0); + pNew->u.vtab.bIdxNumHex = (pIdxInfo->idxFlags&SQLITE_INDEX_SCAN_HEX)!=0; pNew->rSetup = 0; pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); @@ -166417,7 +167569,7 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int if( iCons>=0 && iConsnConstraint ){ CollSeq *pC = 0; int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset; - Expr *pX = pHidden->pWC->a[iTerm].pExpr; + Expr *pX = termFromWhereClause(pHidden->pWC, iTerm)->pExpr; if( pX->pLeft ){ pC = sqlite3ExprCompareCollSeq(pHidden->pParse, pX); } @@ -166463,7 +167615,9 @@ SQLITE_API int sqlite3_vtab_rhs_value( rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */ }else{ if( pH->aRhs[iCons]==0 ){ - WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset]; + WhereTerm *pTerm = termFromWhereClause( + pH->pWC, pIdxInfo->aConstraint[iCons].iTermOffset + ); rc = sqlite3ValueFromExpr( pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), SQLITE_AFF_BLOB, &pH->aRhs[iCons] @@ -166561,7 +167715,7 @@ static int whereLoopAddVirtual( pWC = pBuilder->pWC; pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; - assert( IsVirtual(pSrc->pTab) ); + assert( IsVirtual(pSrc->pSTab) ); p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; @@ -166575,7 +167729,7 @@ static int whereLoopAddVirtual( } /* First call xBestIndex() with all constraints usable. */ - WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); + WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pSTab->zName)); WHERETRACE(0x800, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry @@ -166619,9 +167773,8 @@ static int whereLoopAddVirtual( Bitmask mNext = ALLBITS; assert( mNext>0 ); for(i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq - ); + int iTerm = p->aConstraint[i].iTermOffset; + Bitmask mThis = termFromWhereClause(pWC, iTerm)->prereqRight & ~mPrereq; if( mThis>mPrev && mThisneedToFreeIdxStr ) sqlite3_free(p->idxStr); freeIndexInfo(pParse->db, p); - WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); + WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pSTab->zName, rc)); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -166731,7 +167883,7 @@ static int whereLoopAddOr( } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pItem->pTab) ){ + if( IsVirtual(pItem->pSTab) ){ rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); }else #endif @@ -166845,7 +167997,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pItem->pTab) ){ + if( IsVirtual(pItem->pSTab) ){ SrcItem *p; for(p=&pItem[1]; pfg.jointype & (JT_OUTER|JT_CROSS)) ){ @@ -166877,6 +168029,97 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ return rc; } +/* Implementation of the order-by-subquery optimization: +** +** WhereLoop pLoop, which the iLoop-th term of the nested loop, is really +** a subquery or CTE that has an ORDER BY clause. See if any of the terms +** in the subquery ORDER BY clause will satisfy pOrderBy from the outer +** query. Mark off all satisfied terms (by setting bits in *pOBSat) and +** return TRUE if they do. If not, return false. +** +** Example: +** +** CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b)); +** CREATE TABLE t2(x,y); +** WITH t3(p,q) AS MATERIALIZED (SELECT x+y, x-y FROM t2 ORDER BY x+y) +** SELECT * FROM t3 JOIN t1 ON a=q ORDER BY p, b; +** +** The CTE named "t3" comes out in the natural order of "p", so the first +** first them of "ORDER BY p,b" is satisfied by a sequential scan of "t3" +** and sorting only needs to occur on the second term "b". +** +** Limitations: +** +** (1) The optimization is not applied if the outer ORDER BY contains +** a COLLATE clause. The optimization might be applied if the +** outer ORDER BY uses NULLS FIRST, NULLS LAST, ASC, and/or DESC as +** long as the subquery ORDER BY does the same. But if the +** outer ORDER BY uses COLLATE, even a redundant COLLATE, the +** optimization is bypassed. +** +** (2) The subquery ORDER BY terms must exactly match subquery result +** columns, including any COLLATE annotations. This routine relies +** on iOrderByCol to do matching between order by terms and result +** columns, and iOrderByCol will not be set if the result column +** and ORDER BY collations differ. +** +** (3) The subquery and outer ORDER BY can be in opposite directions as +** long as the subquery is materialized. If the subquery is +** implemented as a co-routine, the sort orders must be in the same +** direction because there is no way to run a co-routine backwards. +*/ +static SQLITE_NOINLINE int wherePathMatchSubqueryOB( + WhereInfo *pWInfo, /* The WHERE clause */ + WhereLoop *pLoop, /* The nested loop term that is a subquery */ + int iLoop, /* Which level of the nested loop. 0==outermost */ + int iCur, /* Cursor used by the this loop */ + ExprList *pOrderBy, /* The ORDER BY clause on the whole query */ + Bitmask *pRevMask, /* When loops need to go in reverse order */ + Bitmask *pOBSat /* Which terms of pOrderBy are satisfied so far */ +){ + int iOB; /* Index into pOrderBy->a[] */ + int jSub; /* Index into pSubOB->a[] */ + u8 rev = 0; /* True if iOB and jSub sort in opposite directions */ + u8 revIdx = 0; /* Sort direction for jSub */ + Expr *pOBExpr; /* Current term of outer ORDER BY */ + ExprList *pSubOB; /* Complete ORDER BY on the subquery */ + + pSubOB = pLoop->u.btree.pOrderBy; + assert( pSubOB!=0 ); + for(iOB=0; (MASKBIT(iOB) & *pOBSat)!=0; iOB++){} + for(jSub=0; jSubnExpr && iOBnExpr; jSub++, iOB++){ + if( pSubOB->a[jSub].u.x.iOrderByCol==0 ) break; + pOBExpr = pOrderBy->a[iOB].pExpr; + if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) break; + if( pOBExpr->iTable!=iCur ) break; + if( pOBExpr->iColumn!=pSubOB->a[jSub].u.x.iOrderByCol-1 ) break; + if( (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){ + u8 sfOB = pOrderBy->a[iOB].fg.sortFlags; /* sortFlags for iOB */ + u8 sfSub = pSubOB->a[jSub].fg.sortFlags; /* sortFlags for jSub */ + if( (sfSub & KEYINFO_ORDER_BIGNULL) != (sfOB & KEYINFO_ORDER_BIGNULL) ){ + break; + } + revIdx = sfSub & KEYINFO_ORDER_DESC; + if( jSub>0 ){ + if( (rev^revIdx)!=(sfOB & KEYINFO_ORDER_DESC) ){ + break; + } + }else{ + rev = revIdx ^ (sfOB & KEYINFO_ORDER_DESC); + if( rev ){ + if( (pLoop->wsFlags & WHERE_COROUTINE)!=0 ){ + /* Cannot run a co-routine in reverse order */ + break; + } + *pRevMask |= MASKBIT(iLoop); + } + } + } + *pOBSat |= MASKBIT(iOB); + } + return jSub>0; +} + /* ** Examine a WherePath (with the addition of the extra WhereLoop of the 6th ** parameters) to see if it outputs rows in the requested ORDER BY @@ -167022,9 +168265,18 @@ static i8 wherePathSatisfiesOrderBy( if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){ if( pLoop->wsFlags & WHERE_IPK ){ + if( pLoop->u.btree.pOrderBy + && OptimizationEnabled(db, SQLITE_OrderBySubq) + && wherePathMatchSubqueryOB(pWInfo,pLoop,iLoop,iCur, + pOrderBy,pRevMask, &obSat) + ){ + nColumn = 0; + isOrderDistinct = 0; + }else{ + nColumn = 1; + } pIndex = 0; nKeyCol = 0; - nColumn = 1; }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){ return 0; }else{ @@ -167034,7 +168286,7 @@ static i8 wherePathSatisfiesOrderBy( assert( pIndex->aiColumn[nColumn-1]==XN_ROWID || !HasRowid(pIndex->pTable)); /* All relevant terms of the index must also be non-NULL in order - ** for isOrderDistinct to be true. So the isOrderDistint value + ** for isOrderDistinct to be true. So the isOrderDistinct value ** computed here might be a false positive. Corrections will be ** made at tag-20210426-1 below */ isOrderDistinct = IsUniqueIndex(pIndex) @@ -167119,7 +168371,7 @@ static i8 wherePathSatisfiesOrderBy( } /* Find the ORDER BY term that corresponds to the j-th column - ** of the index and mark that ORDER BY term off + ** of the index and mark that ORDER BY term having been satisfied. */ isMatch = 0; for(i=0; bOnce && inOutStarDelta and the cost adjustment +** for each WhereLoop is stored in its rStarDelta field. +*/ +static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){ + int nLoop = pWInfo->nLevel; /* Number of terms in the join */ + if( nRowEst==0 && nLoop>=5 ){ + /* Check to see if we are dealing with a star schema and if so, reduce + ** the cost of fact tables relative to dimension tables, as a heuristic + ** to help keep the fact tables in outer loops. + */ + int iLoop; /* Counter over join terms */ + Bitmask m; /* Bitmask for current loop */ + assert( pWInfo->nOutStarDelta==0 ); + for(iLoop=0, m=1; iLooppLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( (pWLoop->prereq & m)!=0 && (pWLoop->maskSelf & mSeen)==0 ){ + nDep++; + mSeen |= pWLoop->maskSelf; + } + } + if( nDep<=3 ) continue; + rDelta = 15*(nDep-3); +#ifdef WHERETRACE_ENABLED /* 0x4 */ + if( sqlite3WhereTrace&0x4 ){ + SrcItem *pItem = pWInfo->pTabList->a + iLoop; + sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n", + pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName, + nDep, rDelta); + } +#endif + if( pWInfo->nOutStarDelta==0 ){ + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + pWLoop->rStarDelta = 0; + } + } + pWInfo->nOutStarDelta += rDelta; + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( pWLoop->maskSelf==m ){ + pWLoop->rRun -= rDelta; + pWLoop->nOut -= rDelta; + pWLoop->rStarDelta = rDelta; + } + } + } + } + return pWInfo->nOutStarDelta>0 ? 18 : 12; +} + /* ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine ** attempts to find the lowest cost path that visits each WhereLoop @@ -167361,13 +168690,25 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pParse = pWInfo->pParse; nLoop = pWInfo->nLevel; - /* TUNING: For simple queries, only the best path is tracked. - ** For 2-way joins, the 5 best paths are followed. - ** For joins of 3 or more tables, track the 10 best paths */ - mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); - assert( nLoop<=pWInfo->pTabList->nSrc ); WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d, nQueryLoop=%d)\n", nRowEst, pParse->nQueryLoop)); + /* TUNING: mxChoice is the maximum number of possible paths to preserve + ** at each step. Based on the number of loops in the FROM clause: + ** + ** nLoop mxChoice + ** ----- -------- + ** 1 1 // the most common case + ** 2 5 + ** 3+ 12 or 18 // see computeMxChoice() + */ + if( nLoop<=1 ){ + mxChoice = 1; + }else if( nLoop==2 ){ + mxChoice = 5; + }else{ + mxChoice = computeMxChoice(pWInfo, nRowEst); + } + assert( nLoop<=pWInfo->pTabList->nSrc ); /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this ** case the purpose of this call is to estimate the number of rows returned @@ -167450,7 +168791,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* At this point, pWLoop is a candidate to be the next loop. ** Compute its cost */ - rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); + rUnsorted = pWLoop->rRun + pFrom->nRow; + if( pWLoop->rSetup ){ + rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup, rUnsorted); + } rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); nOut = pFrom->nRow + pWLoop->nOut; maskNew = pFrom->maskLoop | pWLoop->maskSelf; @@ -167495,6 +168839,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range ** of legal values for isOrdered, -1..64. */ + testcase( nTo==0 ); for(jj=0, pTo=aTo; jjmaskLoop==maskNew && ((pTo->isOrdered^isOrdered)&0x80)==0 @@ -167611,16 +168956,28 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #ifdef WHERETRACE_ENABLED /* >=2 */ if( sqlite3WhereTrace & 0x02 ){ + LogEst rMin, rFloor = 0; + int nDone = 0; sqlite3DebugPrintf("---- after round %d ----\n", iLoop); - for(ii=0, pTo=aTo; iirCost, pTo->nRow, - pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); - if( pTo->isOrdered>0 ){ - sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); - }else{ - sqlite3DebugPrintf("\n"); + while( nDonerCost>rFloor && pTo->rCostrCost; } + for(ii=0, pTo=aTo; iirCost==rMin ){ + sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, + pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); + if( pTo->isOrdered>0 ){ + sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); + }else{ + sqlite3DebugPrintf("\n"); + } + nDone++; + } + } + rFloor = rMin; } } #endif @@ -167715,7 +169072,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } - pWInfo->nRowOut = pFrom->nRow; + pWInfo->nRowOut = pFrom->nRow + pWInfo->nOutStarDelta; /* Free temporary memory and return success */ sqlite3StackFreeNN(pParse->db, pSpace); @@ -167826,7 +169183,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; - pTab = pItem->pTab; + pTab = pItem->pSTab; if( IsVirtual(pTab) ) return 0; if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ testcase( pItem->fg.isIndexedBy ); @@ -168016,6 +169373,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( WhereTerm *pTerm, *pEnd; SrcItem *pItem; WhereLoop *pLoop; + Bitmask m1; pLoop = pWInfo->a[i].pWLoop; pItem = &pWInfo->pTabList->a[pLoop->iTab]; if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue; @@ -168042,7 +169400,10 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( } } if( pTerm drop loop %c not used\n", pLoop->cId)); + WHERETRACE(0xffffffff,("-> omit unused FROM-clause term %c\n",pLoop->cId)); + m1 = MASKBIT(i)-1; + testcase( ((pWInfo->revMask>>1) & ~m1)!=0 ); + pWInfo->revMask = (m1 & pWInfo->revMask) | ((pWInfo->revMask>>1) & ~m1); notReady &= ~pLoop->maskSelf; for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ @@ -168089,7 +169450,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( WhereLoop *pLoop = pWInfo->a[i].pWLoop; const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ); SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; - Table *pTab = pItem->pTab; + Table *pTab = pItem->pSTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; pTab->tabFlags |= TF_MaybeReanalyze; if( i>=1 @@ -168109,61 +169470,10 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } nSearch += pLoop->nOut; + if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; } } -/* -** Expression Node callback for sqlite3ExprCanReturnSubtype(). -** -** Only a function call is able to return a subtype. So if the node -** is not a function call, return WRC_Prune immediately. -** -** A function call is able to return a subtype if it has the -** SQLITE_RESULT_SUBTYPE property. -** -** Assume that every function is able to pass-through a subtype from -** one of its argument (using sqlite3_result_value()). Most functions -** are not this way, but we don't have a mechanism to distinguish those -** that are from those that are not, so assume they all work this way. -** That means that if one of its arguments is another function and that -** other function is able to return a subtype, then this function is -** able to return a subtype. -*/ -static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ - int n; - FuncDef *pDef; - sqlite3 *db; - if( pExpr->op!=TK_FUNCTION ){ - return WRC_Prune; - } - assert( ExprUseXList(pExpr) ); - db = pWalker->pParse->db; - n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; - pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); - if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ - pWalker->eCode = 1; - return WRC_Prune; - } - return WRC_Continue; -} - -/* -** Return TRUE if expression pExpr is able to return a subtype. -** -** A TRUE return does not guarantee that a subtype will be returned. -** It only indicates that a subtype return is possible. False positives -** are acceptable as they only disable an optimization. False negatives, -** on the other hand, can lead to incorrect answers. -*/ -static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ - Walker w; - memset(&w, 0, sizeof(w)); - w.pParse = pParse; - w.xExprCallback = exprNodeCanReturnSubtype; - sqlite3WalkExpr(&w, pExpr); - return w.eCode; -} - /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor @@ -168197,12 +169507,6 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( continue; } if( sqlite3ExprIsConstant(0,pExpr) ) continue; - if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ - /* Functions that might set a subtype should not be replaced by the - ** value taken from an expression index since the index omits the - ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ - continue; - } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; p->pIENext = pParse->pIdxEpr; @@ -168245,8 +169549,8 @@ static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ SrcItem *pItem = &pWInfo->pTabList->a[ii]; if( !pItem->fg.isCte || pItem->u2.pCteUse->eM10d!=M10d_Yes - || NEVER(pItem->pSelect==0) - || pItem->pSelect->pOrderBy==0 + || NEVER(pItem->fg.isSubquery==0) + || pItem->u4.pSubq->pSelect->pOrderBy==0 ){ pWInfo->revMask |= MASKBIT(ii); } @@ -168625,7 +169929,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( db->mallocFailed ) goto whereBeginError; if( pWInfo->pOrderBy ){ whereInterstageHeuristic(pWInfo); - wherePathSolver(pWInfo, pWInfo->nRowOut+1); + wherePathSolver(pWInfo, pWInfo->nRowOut<0 ? 1 : pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } @@ -168736,15 +170040,15 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; - assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) ); + assert( !(wsFlags&WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pSTab) ); if( bOnerow || ( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) - && !IsVirtual(pTabList->a[0].pTab) + && !IsVirtual(pTabList->a[0].pSTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) && OptimizationEnabled(db, SQLITE_OnePass) )){ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; - if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){ + if( HasRowid(pTabList->a[0].pSTab) && (wsFlags & WHERE_IDX_ONLY) ){ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ bFordelete = OPFLAG_FORDELETE; } @@ -168762,7 +170066,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( SrcItem *pTabItem; pTabItem = &pTabList->a[pLevel->iFrom]; - pTab = pTabItem->pTab; + pTab = pTabItem->pSTab; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); pLoop = pLevel->pWLoop; if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ @@ -168833,7 +170137,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( iIndexCur = pLevel->iTabCur; op = 0; }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ - Index *pJ = pTabItem->pTab->pIndex; + Index *pJ = pTabItem->pSTab->pIndex; iIndexCur = iAuxArg; assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); while( ALWAYS(pJ) && pJ!=pIx ){ @@ -168900,7 +170204,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); pRJ->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); - assert( pTab==pTabItem->pTab ); + assert( pTab==pTabItem->pSTab ); if( HasRowid(pTab) ){ KeyInfo *pInfo; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); @@ -168939,13 +170243,18 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( wsFlags = pLevel->pWLoop->wsFlags; pSrc = &pTabList->a[pLevel->iFrom]; if( pSrc->fg.isMaterialized ){ - if( pSrc->fg.isCorrelated ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); + Subquery *pSubq; + int iOnce = 0; + assert( pSrc->fg.isSubquery ); + pSubq = pSrc->u4.pSubq; + if( pSrc->fg.isCorrelated==0 ){ + iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); }else{ - int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); - sqlite3VdbeJumpHere(v, iOnce); + iOnce = 0; } + sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); + VdbeComment((v, "materialize %!S", pSrc)); + if( iOnce ) sqlite3VdbeJumpHere(v, iOnce); } assert( pTabList == pWInfo->pTabList ); if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ @@ -169158,9 +170467,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; - n = pSrc->regResult; - assert( pSrc->pTab!=0 ); - m = pSrc->pTab->nCol; + assert( pSrc->fg.isSubquery ); + n = pSrc->u4.pSubq->regResult; + assert( pSrc->pSTab!=0 ); + m = pSrc->pSTab->nCol; sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); @@ -169184,7 +170494,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, - pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); + pWInfo->pTabList->a[pLevel->iFrom].pSTab->zName)); } assert( pWInfo->nLevel<=pTabList->nSrc ); @@ -169193,7 +170503,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ VdbeOp *pOp, *pLastOp; Index *pIdx = 0; SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; - Table *pTab = pTabItem->pTab; + Table *pTab = pTabItem->pSTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; @@ -169212,9 +170522,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); - assert( pTabItem->regResult>=0 ); + assert( pTabItem->fg.isSubquery ); + assert( pTabItem->u4.pSubq->regResult>=0 ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, - pTabItem->regResult, 0); + pTabItem->u4.pSubq->regResult, 0); continue; } @@ -170256,7 +171567,7 @@ static ExprList *exprListAppendList( int iDummy; Expr *pSub; pSub = sqlite3ExprSkipCollateAndLikely(pDup); - if( sqlite3ExprIsInteger(pSub, &iDummy) ){ + if( sqlite3ExprIsInteger(pSub, &iDummy, 0) ){ pSub->op = TK_NULL; pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); pSub->u.zToken = 0; @@ -170424,9 +171735,10 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside ** of sqlite3DbMallocRawNN() called from ** sqlite3SrcListAppend() */ - if( p->pSrc ){ + if( p->pSrc==0 ){ + sqlite3SelectDelete(db, pSub); + }else if( sqlite3SrcItemAttachSubquery(pParse, &p->pSrc->a[0], pSub, 0) ){ Table *pTab2; - p->pSrc->a[0].pSelect = pSub; p->pSrc->a[0].fg.isCorrelated = 1; sqlite3SrcListAssignCursors(pParse, p->pSrc); pSub->selFlags |= SF_Expanded|SF_OrderByReqd; @@ -170440,7 +171752,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ }else{ memcpy(pTab, pTab2, sizeof(Table)); pTab->tabFlags |= TF_Ephemeral; - p->pSrc->a[0].pTab = pTab; + p->pSrc->a[0].pSTab = pTab; pTab = pTab2; memset(&w, 0, sizeof(w)); w.xExprCallback = sqlite3WindowExtraAggFuncDepth; @@ -170448,8 +171760,6 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ w.xSelectCallback2 = sqlite3WalkerDepthDecrease; sqlite3WalkSelect(&w, pSub); } - }else{ - sqlite3SelectDelete(db, pSub); } if( db->mallocFailed ) rc = SQLITE_NOMEM; @@ -170736,10 +172046,15 @@ SQLITE_PRIVATE int sqlite3WindowCompare( ** and initialize registers and cursors used by sqlite3WindowCodeStep(). */ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ - int nEphExpr = pSelect->pSrc->a[0].pSelect->pEList->nExpr; - Window *pMWin = pSelect->pWin; Window *pWin; - Vdbe *v = sqlite3GetVdbe(pParse); + int nEphExpr; + Window *pMWin; + Vdbe *v; + + assert( pSelect->pSrc->a[0].fg.isSubquery ); + nEphExpr = pSelect->pSrc->a[0].u4.pSubq->pSelect->pEList->nExpr; + pMWin = pSelect->pWin; + v = sqlite3GetVdbe(pParse); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr); sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); @@ -172136,7 +173451,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep( Vdbe *v = sqlite3GetVdbe(pParse); int csrWrite; /* Cursor used to write to eph. table */ int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ - int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ + int nInput = p->pSrc->a[0].pSTab->nCol; /* Number of cols returned by sub */ int iInput; /* To iterate through sub cols */ int addrNe; /* Address of OP_Ne */ int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ @@ -172733,132 +174048,132 @@ static void updateDeleteLimitError( #define TK_OR 43 #define TK_AND 44 #define TK_IS 45 -#define TK_MATCH 46 -#define TK_LIKE_KW 47 -#define TK_BETWEEN 48 -#define TK_IN 49 -#define TK_ISNULL 50 -#define TK_NOTNULL 51 -#define TK_NE 52 -#define TK_EQ 53 -#define TK_GT 54 -#define TK_LE 55 -#define TK_LT 56 -#define TK_GE 57 -#define TK_ESCAPE 58 -#define TK_ID 59 -#define TK_COLUMNKW 60 -#define TK_DO 61 -#define TK_FOR 62 -#define TK_IGNORE 63 -#define TK_INITIALLY 64 -#define TK_INSTEAD 65 -#define TK_NO 66 -#define TK_KEY 67 -#define TK_OF 68 -#define TK_OFFSET 69 -#define TK_PRAGMA 70 -#define TK_RAISE 71 -#define TK_RECURSIVE 72 -#define TK_REPLACE 73 -#define TK_RESTRICT 74 -#define TK_ROW 75 -#define TK_ROWS 76 -#define TK_TRIGGER 77 -#define TK_VACUUM 78 -#define TK_VIEW 79 -#define TK_VIRTUAL 80 -#define TK_WITH 81 -#define TK_NULLS 82 -#define TK_FIRST 83 -#define TK_LAST 84 -#define TK_CURRENT 85 -#define TK_FOLLOWING 86 -#define TK_PARTITION 87 -#define TK_PRECEDING 88 -#define TK_RANGE 89 -#define TK_UNBOUNDED 90 -#define TK_EXCLUDE 91 -#define TK_GROUPS 92 -#define TK_OTHERS 93 -#define TK_TIES 94 -#define TK_GENERATED 95 -#define TK_ALWAYS 96 -#define TK_MATERIALIZED 97 -#define TK_REINDEX 98 -#define TK_RENAME 99 -#define TK_CTIME_KW 100 -#define TK_ANY 101 -#define TK_BITAND 102 -#define TK_BITOR 103 -#define TK_LSHIFT 104 -#define TK_RSHIFT 105 -#define TK_PLUS 106 -#define TK_MINUS 107 -#define TK_STAR 108 -#define TK_SLASH 109 -#define TK_REM 110 -#define TK_CONCAT 111 -#define TK_PTR 112 -#define TK_COLLATE 113 -#define TK_BITNOT 114 -#define TK_ON 115 -#define TK_INDEXED 116 -#define TK_STRING 117 -#define TK_JOIN_KW 118 -#define TK_CONSTRAINT 119 -#define TK_DEFAULT 120 -#define TK_NULL 121 -#define TK_PRIMARY 122 -#define TK_UNIQUE 123 -#define TK_CHECK 124 -#define TK_REFERENCES 125 -#define TK_AUTOINCR 126 -#define TK_INSERT 127 -#define TK_DELETE 128 -#define TK_UPDATE 129 -#define TK_SET 130 -#define TK_DEFERRABLE 131 -#define TK_FOREIGN 132 -#define TK_DROP 133 -#define TK_UNION 134 -#define TK_ALL 135 -#define TK_EXCEPT 136 -#define TK_INTERSECT 137 -#define TK_SELECT 138 -#define TK_VALUES 139 -#define TK_DISTINCT 140 -#define TK_DOT 141 -#define TK_FROM 142 -#define TK_JOIN 143 -#define TK_USING 144 -#define TK_ORDER 145 -#define TK_GROUP 146 -#define TK_HAVING 147 -#define TK_LIMIT 148 -#define TK_WHERE 149 -#define TK_RETURNING 150 -#define TK_INTO 151 -#define TK_NOTHING 152 -#define TK_FLOAT 153 -#define TK_BLOB 154 -#define TK_INTEGER 155 -#define TK_VARIABLE 156 -#define TK_CASE 157 -#define TK_WHEN 158 -#define TK_THEN 159 -#define TK_ELSE 160 -#define TK_INDEX 161 -#define TK_ALTER 162 -#define TK_ADD 163 -#define TK_WINDOW 164 -#define TK_OVER 165 -#define TK_FILTER 166 -#define TK_COLUMN 167 -#define TK_AGG_FUNCTION 168 -#define TK_AGG_COLUMN 169 -#define TK_TRUEFALSE 170 -#define TK_ISNOT 171 +#define TK_ISNOT 46 +#define TK_MATCH 47 +#define TK_LIKE_KW 48 +#define TK_BETWEEN 49 +#define TK_IN 50 +#define TK_ISNULL 51 +#define TK_NOTNULL 52 +#define TK_NE 53 +#define TK_EQ 54 +#define TK_GT 55 +#define TK_LE 56 +#define TK_LT 57 +#define TK_GE 58 +#define TK_ESCAPE 59 +#define TK_ID 60 +#define TK_COLUMNKW 61 +#define TK_DO 62 +#define TK_FOR 63 +#define TK_IGNORE 64 +#define TK_INITIALLY 65 +#define TK_INSTEAD 66 +#define TK_NO 67 +#define TK_KEY 68 +#define TK_OF 69 +#define TK_OFFSET 70 +#define TK_PRAGMA 71 +#define TK_RAISE 72 +#define TK_RECURSIVE 73 +#define TK_REPLACE 74 +#define TK_RESTRICT 75 +#define TK_ROW 76 +#define TK_ROWS 77 +#define TK_TRIGGER 78 +#define TK_VACUUM 79 +#define TK_VIEW 80 +#define TK_VIRTUAL 81 +#define TK_WITH 82 +#define TK_NULLS 83 +#define TK_FIRST 84 +#define TK_LAST 85 +#define TK_CURRENT 86 +#define TK_FOLLOWING 87 +#define TK_PARTITION 88 +#define TK_PRECEDING 89 +#define TK_RANGE 90 +#define TK_UNBOUNDED 91 +#define TK_EXCLUDE 92 +#define TK_GROUPS 93 +#define TK_OTHERS 94 +#define TK_TIES 95 +#define TK_GENERATED 96 +#define TK_ALWAYS 97 +#define TK_MATERIALIZED 98 +#define TK_REINDEX 99 +#define TK_RENAME 100 +#define TK_CTIME_KW 101 +#define TK_ANY 102 +#define TK_BITAND 103 +#define TK_BITOR 104 +#define TK_LSHIFT 105 +#define TK_RSHIFT 106 +#define TK_PLUS 107 +#define TK_MINUS 108 +#define TK_STAR 109 +#define TK_SLASH 110 +#define TK_REM 111 +#define TK_CONCAT 112 +#define TK_PTR 113 +#define TK_COLLATE 114 +#define TK_BITNOT 115 +#define TK_ON 116 +#define TK_INDEXED 117 +#define TK_STRING 118 +#define TK_JOIN_KW 119 +#define TK_CONSTRAINT 120 +#define TK_DEFAULT 121 +#define TK_NULL 122 +#define TK_PRIMARY 123 +#define TK_UNIQUE 124 +#define TK_CHECK 125 +#define TK_REFERENCES 126 +#define TK_AUTOINCR 127 +#define TK_INSERT 128 +#define TK_DELETE 129 +#define TK_UPDATE 130 +#define TK_SET 131 +#define TK_DEFERRABLE 132 +#define TK_FOREIGN 133 +#define TK_DROP 134 +#define TK_UNION 135 +#define TK_ALL 136 +#define TK_EXCEPT 137 +#define TK_INTERSECT 138 +#define TK_SELECT 139 +#define TK_VALUES 140 +#define TK_DISTINCT 141 +#define TK_DOT 142 +#define TK_FROM 143 +#define TK_JOIN 144 +#define TK_USING 145 +#define TK_ORDER 146 +#define TK_GROUP 147 +#define TK_HAVING 148 +#define TK_LIMIT 149 +#define TK_WHERE 150 +#define TK_RETURNING 151 +#define TK_INTO 152 +#define TK_NOTHING 153 +#define TK_FLOAT 154 +#define TK_BLOB 155 +#define TK_INTEGER 156 +#define TK_VARIABLE 157 +#define TK_CASE 158 +#define TK_WHEN 159 +#define TK_THEN 160 +#define TK_ELSE 161 +#define TK_INDEX 162 +#define TK_ALTER 163 +#define TK_ADD 164 +#define TK_WINDOW 165 +#define TK_OVER 166 +#define TK_FILTER 167 +#define TK_COLUMN 168 +#define TK_AGG_FUNCTION 169 +#define TK_AGG_COLUMN 170 +#define TK_TRUEFALSE 171 #define TK_FUNCTION 172 #define TK_UPLUS 173 #define TK_UMINUS 174 @@ -172939,7 +174254,7 @@ static void updateDeleteLimitError( #define YYCODETYPE unsigned short int #define YYNOCODE 322 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 101 +#define YYWILDCARD 102 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; @@ -173076,446 +174391,452 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2142) +#define YY_ACTTAB_COUNT (2207) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 576, 128, 125, 232, 1622, 549, 576, 1290, 1281, 576, - /* 10 */ 328, 576, 1300, 212, 576, 128, 125, 232, 578, 412, - /* 20 */ 578, 391, 1542, 51, 51, 523, 405, 1293, 529, 51, - /* 30 */ 51, 983, 51, 51, 81, 81, 1107, 61, 61, 984, - /* 40 */ 1107, 1292, 380, 135, 136, 90, 1228, 1228, 1063, 1066, - /* 50 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 1577, 412, - /* 60 */ 287, 287, 7, 287, 287, 422, 1050, 1050, 1064, 1067, - /* 70 */ 289, 556, 492, 573, 524, 561, 573, 497, 561, 482, - /* 80 */ 530, 262, 229, 135, 136, 90, 1228, 1228, 1063, 1066, - /* 90 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 128, 125, - /* 100 */ 232, 1506, 132, 132, 132, 132, 131, 131, 130, 130, - /* 110 */ 130, 129, 126, 450, 1204, 1255, 1, 1, 582, 2, - /* 120 */ 1259, 1571, 420, 1582, 379, 320, 1174, 153, 1174, 1584, - /* 130 */ 412, 378, 1582, 543, 1341, 330, 111, 570, 570, 570, - /* 140 */ 293, 1054, 132, 132, 132, 132, 131, 131, 130, 130, - /* 150 */ 130, 129, 126, 450, 135, 136, 90, 1228, 1228, 1063, - /* 160 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 287, - /* 170 */ 287, 1204, 1205, 1204, 255, 287, 287, 510, 507, 506, - /* 180 */ 137, 455, 573, 212, 561, 447, 446, 505, 573, 1616, - /* 190 */ 561, 134, 134, 134, 134, 127, 400, 243, 132, 132, - /* 200 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, - /* 210 */ 282, 471, 345, 132, 132, 132, 132, 131, 131, 130, - /* 220 */ 130, 130, 129, 126, 450, 574, 155, 936, 936, 454, - /* 230 */ 227, 521, 1236, 412, 1236, 134, 134, 134, 134, 132, - /* 240 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, - /* 250 */ 450, 130, 130, 130, 129, 126, 450, 135, 136, 90, - /* 260 */ 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, - /* 270 */ 134, 134, 128, 125, 232, 450, 576, 412, 397, 1249, - /* 280 */ 180, 92, 93, 132, 132, 132, 132, 131, 131, 130, - /* 290 */ 130, 130, 129, 126, 450, 381, 387, 1204, 383, 81, - /* 300 */ 81, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, - /* 310 */ 133, 133, 134, 134, 134, 134, 132, 132, 132, 132, - /* 320 */ 131, 131, 130, 130, 130, 129, 126, 450, 131, 131, - /* 330 */ 130, 130, 130, 129, 126, 450, 556, 1204, 302, 319, - /* 340 */ 567, 121, 568, 480, 4, 555, 1149, 1657, 1628, 1657, - /* 350 */ 45, 128, 125, 232, 1204, 1205, 1204, 1250, 571, 1169, - /* 360 */ 132, 132, 132, 132, 131, 131, 130, 130, 130, 129, - /* 370 */ 126, 450, 1169, 287, 287, 1169, 1019, 576, 422, 1019, - /* 380 */ 412, 451, 1602, 582, 2, 1259, 573, 44, 561, 95, - /* 390 */ 320, 110, 153, 565, 1204, 1205, 1204, 522, 522, 1341, - /* 400 */ 81, 81, 7, 44, 135, 136, 90, 1228, 1228, 1063, - /* 410 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 295, - /* 420 */ 1149, 1658, 1040, 1658, 1204, 1147, 319, 567, 119, 119, - /* 430 */ 343, 466, 331, 343, 287, 287, 120, 556, 451, 577, - /* 440 */ 451, 1169, 1169, 1028, 319, 567, 438, 573, 210, 561, - /* 450 */ 1339, 1451, 546, 531, 1169, 1169, 1598, 1169, 1169, 416, - /* 460 */ 319, 567, 243, 132, 132, 132, 132, 131, 131, 130, - /* 470 */ 130, 130, 129, 126, 450, 1028, 1028, 1030, 1031, 35, - /* 480 */ 44, 1204, 1205, 1204, 472, 287, 287, 1328, 412, 1307, - /* 490 */ 372, 1595, 359, 225, 454, 1204, 195, 1328, 573, 1147, - /* 500 */ 561, 1333, 1333, 274, 576, 1188, 576, 340, 46, 196, - /* 510 */ 537, 217, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, - /* 520 */ 1053, 133, 133, 134, 134, 134, 134, 19, 19, 19, - /* 530 */ 19, 412, 581, 1204, 1259, 511, 1204, 319, 567, 320, - /* 540 */ 944, 153, 425, 491, 430, 943, 1204, 488, 1341, 1450, - /* 550 */ 532, 1277, 1204, 1205, 1204, 135, 136, 90, 1228, 1228, - /* 560 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, - /* 570 */ 575, 132, 132, 132, 132, 131, 131, 130, 130, 130, - /* 580 */ 129, 126, 450, 287, 287, 528, 287, 287, 372, 1595, - /* 590 */ 1204, 1205, 1204, 1204, 1205, 1204, 573, 486, 561, 573, - /* 600 */ 889, 561, 412, 1204, 1205, 1204, 886, 40, 22, 22, - /* 610 */ 220, 243, 525, 1449, 132, 132, 132, 132, 131, 131, - /* 620 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, - /* 630 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 640 */ 134, 412, 180, 454, 1204, 879, 255, 287, 287, 510, - /* 650 */ 507, 506, 372, 1595, 1568, 1331, 1331, 576, 889, 505, - /* 660 */ 573, 44, 561, 559, 1207, 135, 136, 90, 1228, 1228, - /* 670 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, - /* 680 */ 81, 81, 422, 576, 377, 132, 132, 132, 132, 131, - /* 690 */ 131, 130, 130, 130, 129, 126, 450, 297, 287, 287, - /* 700 */ 460, 1204, 1205, 1204, 1204, 534, 19, 19, 448, 448, - /* 710 */ 448, 573, 412, 561, 230, 436, 1187, 535, 319, 567, - /* 720 */ 363, 432, 1207, 1435, 132, 132, 132, 132, 131, 131, - /* 730 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, - /* 740 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 750 */ 134, 412, 211, 949, 1169, 1041, 1110, 1110, 494, 547, - /* 760 */ 547, 1204, 1205, 1204, 7, 539, 1570, 1169, 376, 576, - /* 770 */ 1169, 5, 1204, 486, 3, 135, 136, 90, 1228, 1228, - /* 780 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, - /* 790 */ 576, 513, 19, 19, 427, 132, 132, 132, 132, 131, - /* 800 */ 131, 130, 130, 130, 129, 126, 450, 305, 1204, 433, - /* 810 */ 225, 1204, 385, 19, 19, 273, 290, 371, 516, 366, - /* 820 */ 515, 260, 412, 538, 1568, 549, 1024, 362, 437, 1204, - /* 830 */ 1205, 1204, 902, 1552, 132, 132, 132, 132, 131, 131, - /* 840 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, - /* 850 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 860 */ 134, 412, 1435, 514, 1281, 1204, 1205, 1204, 1204, 1205, - /* 870 */ 1204, 903, 48, 342, 1568, 1568, 1279, 1627, 1568, 911, - /* 880 */ 576, 129, 126, 450, 110, 135, 136, 90, 1228, 1228, - /* 890 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, - /* 900 */ 265, 576, 459, 19, 19, 132, 132, 132, 132, 131, - /* 910 */ 131, 130, 130, 130, 129, 126, 450, 1345, 204, 576, - /* 920 */ 459, 458, 50, 47, 19, 19, 49, 434, 1105, 573, - /* 930 */ 497, 561, 412, 428, 108, 1224, 1569, 1554, 376, 205, - /* 940 */ 550, 550, 81, 81, 132, 132, 132, 132, 131, 131, - /* 950 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, - /* 960 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 970 */ 134, 480, 576, 1204, 576, 1541, 412, 1435, 969, 315, - /* 980 */ 1659, 398, 284, 497, 969, 893, 1569, 1569, 376, 376, - /* 990 */ 1569, 461, 376, 1224, 459, 80, 80, 81, 81, 497, - /* 1000 */ 374, 114, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, - /* 1010 */ 133, 134, 134, 134, 134, 132, 132, 132, 132, 131, - /* 1020 */ 131, 130, 130, 130, 129, 126, 450, 1204, 1505, 576, - /* 1030 */ 1204, 1205, 1204, 1366, 316, 486, 281, 281, 497, 431, - /* 1040 */ 557, 288, 288, 402, 1340, 471, 345, 298, 429, 573, - /* 1050 */ 576, 561, 81, 81, 573, 374, 561, 971, 386, 132, - /* 1060 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, - /* 1070 */ 450, 231, 117, 81, 81, 287, 287, 231, 287, 287, - /* 1080 */ 576, 1511, 576, 1336, 1204, 1205, 1204, 139, 573, 556, - /* 1090 */ 561, 573, 412, 561, 441, 456, 969, 213, 558, 1511, - /* 1100 */ 1513, 1550, 969, 143, 143, 145, 145, 1368, 314, 478, - /* 1110 */ 444, 970, 412, 850, 851, 852, 135, 136, 90, 1228, - /* 1120 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 1130 */ 134, 357, 412, 397, 1148, 304, 135, 136, 90, 1228, - /* 1140 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 1150 */ 134, 1575, 323, 6, 862, 7, 135, 124, 90, 1228, - /* 1160 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, - /* 1170 */ 134, 409, 408, 1511, 212, 132, 132, 132, 132, 131, - /* 1180 */ 131, 130, 130, 130, 129, 126, 450, 411, 118, 1204, - /* 1190 */ 116, 10, 352, 265, 355, 132, 132, 132, 132, 131, - /* 1200 */ 131, 130, 130, 130, 129, 126, 450, 576, 324, 306, - /* 1210 */ 576, 306, 1250, 469, 158, 132, 132, 132, 132, 131, - /* 1220 */ 131, 130, 130, 130, 129, 126, 450, 207, 1224, 1126, - /* 1230 */ 65, 65, 470, 66, 66, 412, 447, 446, 882, 531, - /* 1240 */ 335, 258, 257, 256, 1127, 1233, 1204, 1205, 1204, 327, - /* 1250 */ 1235, 874, 159, 576, 16, 480, 1085, 1040, 1234, 1128, - /* 1260 */ 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, - /* 1270 */ 134, 134, 134, 134, 1029, 576, 81, 81, 1028, 1040, - /* 1280 */ 922, 576, 463, 1236, 576, 1236, 1224, 502, 107, 1435, - /* 1290 */ 923, 6, 576, 410, 1498, 882, 1029, 480, 21, 21, - /* 1300 */ 1028, 332, 1380, 334, 53, 53, 497, 81, 81, 874, - /* 1310 */ 1028, 1028, 1030, 445, 259, 19, 19, 533, 132, 132, - /* 1320 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, - /* 1330 */ 551, 301, 1028, 1028, 1030, 107, 532, 545, 121, 568, - /* 1340 */ 1188, 4, 1126, 1576, 449, 576, 462, 7, 1282, 418, - /* 1350 */ 462, 350, 1435, 576, 518, 571, 544, 1127, 121, 568, - /* 1360 */ 442, 4, 1188, 464, 533, 1180, 1223, 9, 67, 67, - /* 1370 */ 487, 576, 1128, 303, 410, 571, 54, 54, 451, 576, - /* 1380 */ 123, 944, 576, 417, 576, 333, 943, 1379, 576, 236, - /* 1390 */ 565, 576, 1574, 564, 68, 68, 7, 576, 451, 362, - /* 1400 */ 419, 182, 69, 69, 541, 70, 70, 71, 71, 540, - /* 1410 */ 565, 72, 72, 484, 55, 55, 473, 1180, 296, 1040, - /* 1420 */ 56, 56, 296, 493, 541, 119, 119, 410, 1573, 542, - /* 1430 */ 569, 418, 7, 120, 1244, 451, 577, 451, 465, 1040, - /* 1440 */ 1028, 576, 1557, 552, 476, 119, 119, 527, 259, 121, - /* 1450 */ 568, 240, 4, 120, 576, 451, 577, 451, 576, 477, - /* 1460 */ 1028, 576, 156, 576, 57, 57, 571, 576, 286, 229, - /* 1470 */ 410, 336, 1028, 1028, 1030, 1031, 35, 59, 59, 219, - /* 1480 */ 983, 60, 60, 220, 73, 73, 74, 74, 984, 451, - /* 1490 */ 75, 75, 1028, 1028, 1030, 1031, 35, 96, 216, 291, - /* 1500 */ 552, 565, 1188, 318, 395, 395, 394, 276, 392, 576, - /* 1510 */ 485, 859, 474, 1311, 410, 541, 576, 417, 1530, 1144, - /* 1520 */ 540, 399, 1188, 292, 237, 1153, 326, 38, 23, 576, - /* 1530 */ 1040, 576, 20, 20, 325, 299, 119, 119, 164, 76, - /* 1540 */ 76, 1529, 121, 568, 120, 4, 451, 577, 451, 203, - /* 1550 */ 576, 1028, 141, 141, 142, 142, 576, 322, 39, 571, - /* 1560 */ 341, 1021, 110, 264, 239, 901, 900, 423, 242, 908, - /* 1570 */ 909, 370, 173, 77, 77, 43, 479, 1310, 264, 62, - /* 1580 */ 62, 369, 451, 1028, 1028, 1030, 1031, 35, 1601, 1192, - /* 1590 */ 453, 1092, 238, 291, 565, 163, 1309, 110, 395, 395, - /* 1600 */ 394, 276, 392, 986, 987, 859, 481, 346, 264, 110, - /* 1610 */ 1032, 489, 576, 1188, 503, 1088, 261, 261, 237, 576, - /* 1620 */ 326, 121, 568, 1040, 4, 347, 1376, 413, 325, 119, - /* 1630 */ 119, 948, 319, 567, 351, 78, 78, 120, 571, 451, - /* 1640 */ 577, 451, 79, 79, 1028, 354, 356, 576, 360, 1092, - /* 1650 */ 110, 576, 974, 942, 264, 123, 457, 358, 239, 576, - /* 1660 */ 519, 451, 939, 1104, 123, 1104, 173, 576, 1032, 43, - /* 1670 */ 63, 63, 1324, 565, 168, 168, 1028, 1028, 1030, 1031, - /* 1680 */ 35, 576, 169, 169, 1308, 872, 238, 157, 1589, 576, - /* 1690 */ 86, 86, 365, 89, 568, 375, 4, 1103, 941, 1103, - /* 1700 */ 123, 576, 1040, 1389, 64, 64, 1188, 1434, 119, 119, - /* 1710 */ 571, 576, 82, 82, 563, 576, 120, 165, 451, 577, - /* 1720 */ 451, 413, 1362, 1028, 144, 144, 319, 567, 576, 1374, - /* 1730 */ 562, 498, 279, 451, 83, 83, 1439, 576, 166, 166, - /* 1740 */ 576, 1289, 554, 576, 1280, 565, 576, 12, 576, 1268, - /* 1750 */ 457, 146, 146, 1267, 576, 1028, 1028, 1030, 1031, 35, - /* 1760 */ 140, 140, 1269, 167, 167, 1609, 160, 160, 1359, 150, - /* 1770 */ 150, 149, 149, 311, 1040, 576, 312, 147, 147, 313, - /* 1780 */ 119, 119, 222, 235, 576, 1188, 396, 576, 120, 576, - /* 1790 */ 451, 577, 451, 1192, 453, 1028, 508, 291, 148, 148, - /* 1800 */ 1421, 1612, 395, 395, 394, 276, 392, 85, 85, 859, - /* 1810 */ 87, 87, 84, 84, 553, 576, 294, 576, 1426, 338, - /* 1820 */ 339, 1425, 237, 300, 326, 1416, 1409, 1028, 1028, 1030, - /* 1830 */ 1031, 35, 325, 344, 403, 483, 226, 1307, 52, 52, - /* 1840 */ 58, 58, 368, 1371, 1502, 566, 1501, 121, 568, 221, - /* 1850 */ 4, 208, 268, 209, 390, 1244, 1549, 1188, 1372, 1370, - /* 1860 */ 1369, 1547, 239, 184, 571, 233, 421, 1241, 95, 218, - /* 1870 */ 173, 1507, 193, 43, 91, 94, 178, 186, 467, 188, - /* 1880 */ 468, 1422, 13, 189, 190, 191, 501, 451, 245, 108, - /* 1890 */ 238, 401, 1428, 1427, 1430, 475, 404, 1496, 197, 565, - /* 1900 */ 14, 490, 249, 101, 1518, 496, 349, 280, 251, 201, - /* 1910 */ 353, 499, 252, 406, 1270, 253, 517, 1327, 1326, 435, - /* 1920 */ 1325, 1318, 103, 893, 1296, 413, 227, 407, 1040, 1626, - /* 1930 */ 319, 567, 1625, 1297, 119, 119, 439, 367, 1317, 1295, - /* 1940 */ 1624, 526, 120, 440, 451, 577, 451, 1594, 309, 1028, - /* 1950 */ 310, 373, 266, 267, 457, 1580, 1579, 443, 138, 1394, - /* 1960 */ 552, 1393, 11, 1483, 384, 115, 317, 1350, 109, 536, - /* 1970 */ 42, 579, 382, 214, 1349, 388, 1198, 389, 275, 277, - /* 1980 */ 278, 1028, 1028, 1030, 1031, 35, 580, 1265, 414, 1260, - /* 1990 */ 170, 415, 183, 1534, 1535, 1533, 171, 154, 307, 1532, - /* 2000 */ 846, 223, 224, 88, 452, 215, 172, 321, 234, 1102, - /* 2010 */ 152, 1188, 1100, 329, 185, 174, 1223, 925, 187, 241, - /* 2020 */ 337, 244, 1116, 192, 175, 176, 424, 426, 97, 194, - /* 2030 */ 98, 99, 100, 177, 1119, 1115, 246, 247, 161, 24, - /* 2040 */ 248, 348, 1238, 264, 1108, 250, 495, 199, 198, 15, - /* 2050 */ 861, 500, 369, 254, 504, 509, 512, 200, 102, 25, - /* 2060 */ 179, 361, 26, 364, 104, 891, 308, 162, 105, 904, - /* 2070 */ 520, 106, 1185, 1069, 1155, 17, 228, 27, 1154, 283, - /* 2080 */ 285, 263, 978, 202, 972, 123, 28, 1175, 29, 30, - /* 2090 */ 1179, 1171, 31, 1173, 1160, 41, 32, 206, 548, 33, - /* 2100 */ 110, 1178, 1083, 8, 112, 1070, 113, 1068, 1072, 34, - /* 2110 */ 1073, 560, 1125, 269, 1124, 270, 36, 18, 1194, 1033, - /* 2120 */ 873, 151, 122, 37, 393, 271, 272, 572, 181, 1193, - /* 2130 */ 1256, 1256, 1256, 935, 1256, 1256, 1256, 1256, 1256, 1256, - /* 2140 */ 1256, 1617, + /* 0 */ 130, 127, 234, 282, 282, 1328, 576, 1307, 460, 289, + /* 10 */ 289, 576, 1622, 381, 576, 1328, 573, 576, 562, 413, + /* 20 */ 1300, 1542, 573, 481, 562, 524, 460, 459, 558, 82, + /* 30 */ 82, 983, 294, 375, 51, 51, 498, 61, 61, 984, + /* 40 */ 82, 82, 1577, 137, 138, 91, 7, 1228, 1228, 1063, + /* 50 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 413, + /* 60 */ 288, 288, 182, 288, 288, 481, 536, 288, 288, 130, + /* 70 */ 127, 234, 432, 573, 525, 562, 573, 557, 562, 1290, + /* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063, + /* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296, + /* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132, + /* 110 */ 132, 132, 131, 128, 451, 44, 1050, 1050, 1064, 1067, + /* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174, + /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 498, 1341, + /* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132, + /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 1105, 1228, + /* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, + /* 170 */ 136, 1204, 320, 567, 288, 288, 283, 288, 288, 523, + /* 180 */ 523, 1250, 139, 1541, 7, 214, 503, 573, 1169, 562, + /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 401, 547, + /* 200 */ 487, 1169, 245, 1568, 1169, 245, 133, 133, 132, 132, + /* 210 */ 132, 131, 128, 451, 261, 134, 134, 134, 134, 133, + /* 220 */ 133, 132, 132, 132, 131, 128, 451, 451, 1204, 1205, + /* 230 */ 1204, 130, 127, 234, 455, 413, 182, 455, 130, 127, + /* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132, + /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 576, 137, + /* 260 */ 138, 91, 261, 1228, 1228, 1063, 1066, 1053, 1053, 135, + /* 270 */ 135, 136, 136, 136, 136, 44, 472, 346, 1204, 472, + /* 280 */ 346, 51, 51, 418, 93, 157, 134, 134, 134, 134, + /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 166, 363, + /* 300 */ 298, 134, 134, 134, 134, 133, 133, 132, 132, 132, + /* 310 */ 131, 128, 451, 1293, 461, 1570, 423, 377, 275, 134, + /* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128, + /* 330 */ 451, 418, 320, 567, 1292, 1204, 1205, 1204, 257, 413, + /* 340 */ 483, 511, 508, 507, 94, 132, 132, 132, 131, 128, + /* 350 */ 451, 506, 1204, 548, 548, 388, 576, 384, 7, 413, + /* 360 */ 550, 229, 522, 137, 138, 91, 530, 1228, 1228, 1063, + /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 51, + /* 380 */ 51, 1582, 380, 137, 138, 91, 331, 1228, 1228, 1063, + /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 320, + /* 400 */ 567, 288, 288, 320, 567, 1602, 582, 2, 1259, 1204, + /* 410 */ 1205, 1204, 1628, 321, 573, 155, 562, 576, 1511, 264, + /* 420 */ 231, 520, 1341, 134, 134, 134, 134, 133, 133, 132, + /* 430 */ 132, 132, 131, 128, 451, 519, 1511, 1513, 1333, 1333, + /* 440 */ 82, 82, 498, 134, 134, 134, 134, 133, 133, 132, + /* 450 */ 132, 132, 131, 128, 451, 1435, 257, 288, 288, 511, + /* 460 */ 508, 507, 944, 1568, 413, 1019, 1204, 943, 360, 506, + /* 470 */ 573, 1598, 562, 44, 575, 551, 551, 557, 1107, 1582, + /* 480 */ 544, 576, 1107, 40, 417, 245, 531, 1505, 137, 138, + /* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, + /* 500 */ 136, 136, 136, 136, 81, 81, 1281, 1204, 413, 553, + /* 510 */ 1511, 48, 512, 448, 447, 493, 578, 455, 578, 344, + /* 520 */ 45, 1204, 1233, 1204, 1205, 1204, 428, 1235, 158, 882, + /* 530 */ 320, 567, 137, 138, 91, 1234, 1228, 1228, 1063, 1066, + /* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134, + /* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, + /* 560 */ 1236, 576, 1236, 329, 1204, 1205, 1204, 387, 492, 403, + /* 570 */ 1040, 382, 489, 123, 568, 1569, 4, 377, 1204, 1205, + /* 580 */ 1204, 570, 570, 570, 82, 82, 882, 1029, 1331, 1331, + /* 590 */ 571, 1028, 134, 134, 134, 134, 133, 133, 132, 132, + /* 600 */ 132, 131, 128, 451, 288, 288, 1281, 1204, 576, 423, + /* 610 */ 576, 1568, 413, 423, 452, 378, 886, 573, 1279, 562, + /* 620 */ 46, 557, 532, 1028, 1028, 1030, 565, 130, 127, 234, + /* 630 */ 556, 82, 82, 82, 82, 479, 137, 138, 91, 462, + /* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, + /* 650 */ 136, 136, 1188, 487, 1506, 1040, 413, 6, 1204, 50, + /* 660 */ 879, 121, 121, 948, 1204, 1205, 1204, 358, 557, 122, + /* 670 */ 316, 452, 577, 452, 535, 1204, 1028, 439, 303, 212, + /* 680 */ 137, 138, 91, 213, 1228, 1228, 1063, 1066, 1053, 1053, + /* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134, + /* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028, + /* 710 */ 1030, 1031, 35, 288, 288, 1204, 1205, 1204, 1040, 1339, + /* 720 */ 533, 123, 568, 1569, 4, 377, 573, 1019, 562, 353, + /* 730 */ 1277, 356, 1204, 1205, 1204, 1029, 488, 1188, 571, 1028, + /* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, + /* 750 */ 128, 451, 576, 343, 288, 288, 449, 449, 449, 971, + /* 760 */ 413, 1627, 452, 911, 1187, 288, 288, 573, 464, 562, + /* 770 */ 238, 1028, 1028, 1030, 565, 82, 82, 498, 573, 411, + /* 780 */ 562, 344, 467, 332, 137, 138, 91, 197, 1228, 1228, + /* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, + /* 800 */ 1188, 528, 1169, 1040, 413, 1110, 1110, 495, 1041, 121, + /* 810 */ 121, 1204, 317, 540, 862, 1169, 1244, 122, 1169, 452, + /* 820 */ 577, 452, 1340, 198, 1028, 1204, 481, 526, 137, 138, + /* 830 */ 91, 560, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, + /* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133, + /* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031, + /* 860 */ 35, 1204, 288, 288, 1204, 477, 288, 288, 1204, 1205, + /* 870 */ 1204, 539, 481, 437, 470, 573, 1451, 562, 364, 573, + /* 880 */ 1153, 562, 1204, 1205, 1204, 1188, 5, 576, 134, 134, + /* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, + /* 900 */ 221, 214, 302, 96, 1149, 1657, 232, 1657, 413, 392, + /* 910 */ 19, 19, 1024, 949, 406, 373, 1595, 1085, 1204, 1205, + /* 920 */ 1204, 1204, 1205, 1204, 1204, 426, 1149, 1658, 413, 1658, + /* 930 */ 1659, 399, 137, 138, 91, 3, 1228, 1228, 1063, 1066, + /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 304, 1311, + /* 950 */ 514, 1204, 137, 138, 91, 1498, 1228, 1228, 1063, 1066, + /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 131, + /* 970 */ 128, 451, 375, 1204, 274, 291, 372, 517, 367, 516, + /* 980 */ 262, 1204, 1205, 1204, 1147, 227, 363, 448, 447, 1435, + /* 990 */ 1568, 1310, 134, 134, 134, 134, 133, 133, 132, 132, + /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 487, 1204, 1205, + /* 1010 */ 1204, 442, 134, 134, 134, 134, 133, 133, 132, 132, + /* 1020 */ 132, 131, 128, 451, 386, 576, 485, 576, 19, 19, + /* 1030 */ 1204, 1205, 1204, 1345, 1236, 970, 1236, 574, 47, 936, + /* 1040 */ 936, 473, 413, 431, 1552, 573, 1125, 562, 19, 19, + /* 1050 */ 19, 19, 49, 336, 850, 851, 852, 111, 1368, 315, + /* 1060 */ 429, 576, 413, 433, 341, 306, 137, 138, 91, 115, + /* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, + /* 1080 */ 136, 136, 576, 1309, 82, 82, 137, 138, 91, 529, + /* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, + /* 1100 */ 136, 136, 1569, 222, 377, 19, 19, 305, 1126, 1169, + /* 1110 */ 398, 1148, 22, 22, 498, 333, 1569, 335, 377, 576, + /* 1120 */ 438, 445, 1169, 1127, 486, 1169, 134, 134, 134, 134, + /* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576, + /* 1140 */ 902, 576, 145, 145, 6, 576, 134, 134, 134, 134, + /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 214, 1336, + /* 1160 */ 922, 576, 19, 19, 19, 19, 1282, 419, 19, 19, + /* 1170 */ 923, 412, 515, 141, 576, 1169, 413, 206, 465, 207, + /* 1180 */ 903, 215, 1575, 552, 147, 147, 7, 227, 1169, 411, + /* 1190 */ 1250, 1169, 120, 307, 117, 307, 413, 66, 66, 334, + /* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053, + /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 285, 209, 969, + /* 1220 */ 137, 138, 91, 471, 1228, 1228, 1063, 1066, 1053, 1053, + /* 1230 */ 135, 135, 136, 136, 136, 136, 435, 10, 1450, 267, + /* 1240 */ 137, 126, 91, 1435, 1228, 1228, 1063, 1066, 1053, 1053, + /* 1250 */ 135, 135, 136, 136, 136, 136, 1435, 1435, 410, 409, + /* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, + /* 1270 */ 128, 451, 576, 969, 576, 1224, 498, 373, 1595, 1554, + /* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, + /* 1290 */ 128, 451, 532, 457, 576, 82, 82, 82, 82, 111, + /* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, + /* 1310 */ 128, 451, 109, 233, 430, 1576, 546, 67, 67, 7, + /* 1320 */ 413, 351, 550, 1550, 260, 259, 258, 494, 443, 569, + /* 1330 */ 419, 983, 446, 1224, 450, 545, 1207, 576, 969, 984, + /* 1340 */ 413, 475, 1449, 1574, 1180, 138, 91, 7, 1228, 1228, + /* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, + /* 1360 */ 21, 21, 267, 576, 300, 1126, 91, 233, 1228, 1228, + /* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, + /* 1380 */ 1127, 373, 1595, 161, 1573, 16, 53, 53, 7, 108, + /* 1390 */ 533, 38, 969, 125, 1207, 1128, 1180, 576, 1224, 123, + /* 1400 */ 568, 893, 4, 324, 134, 134, 134, 134, 133, 133, + /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 576, + /* 1420 */ 68, 68, 576, 39, 134, 134, 134, 134, 133, 133, + /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 160, 1571, 1223, + /* 1440 */ 452, 576, 54, 54, 576, 69, 69, 576, 1366, 576, + /* 1450 */ 420, 184, 565, 463, 297, 576, 1224, 463, 297, 70, + /* 1460 */ 70, 576, 44, 474, 71, 71, 576, 72, 72, 576, + /* 1470 */ 73, 73, 55, 55, 411, 874, 242, 576, 56, 56, + /* 1480 */ 576, 1040, 576, 478, 57, 57, 576, 121, 121, 59, + /* 1490 */ 59, 23, 60, 60, 411, 122, 319, 452, 577, 452, + /* 1500 */ 74, 74, 1028, 75, 75, 76, 76, 411, 290, 20, + /* 1510 */ 20, 108, 287, 231, 553, 123, 568, 325, 4, 320, + /* 1520 */ 567, 97, 218, 944, 1144, 328, 400, 576, 943, 576, + /* 1530 */ 1380, 424, 571, 874, 1028, 1028, 1030, 1031, 35, 293, + /* 1540 */ 534, 576, 1104, 576, 1104, 9, 576, 342, 576, 111, + /* 1550 */ 77, 77, 143, 143, 576, 205, 452, 222, 1379, 889, + /* 1560 */ 576, 901, 900, 1188, 144, 144, 78, 78, 565, 62, + /* 1570 */ 62, 79, 79, 323, 1021, 576, 266, 63, 63, 908, + /* 1580 */ 909, 1589, 542, 80, 80, 576, 371, 541, 123, 568, + /* 1590 */ 480, 4, 266, 482, 244, 266, 370, 1040, 64, 64, + /* 1600 */ 576, 466, 576, 121, 121, 571, 1557, 576, 170, 170, + /* 1610 */ 576, 122, 576, 452, 577, 452, 576, 889, 1028, 576, + /* 1620 */ 165, 576, 111, 171, 171, 87, 87, 337, 1616, 452, + /* 1630 */ 65, 65, 1530, 83, 83, 146, 146, 986, 987, 84, + /* 1640 */ 84, 565, 168, 168, 148, 148, 1092, 347, 1032, 111, + /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 1103, 576, 1103, 576, + /* 1660 */ 543, 123, 568, 504, 4, 263, 576, 361, 1529, 111, + /* 1670 */ 1040, 1088, 576, 263, 576, 490, 121, 121, 571, 1188, + /* 1680 */ 142, 142, 169, 169, 122, 576, 452, 577, 452, 162, + /* 1690 */ 162, 1028, 576, 563, 576, 152, 152, 151, 151, 348, + /* 1700 */ 1376, 974, 452, 266, 1092, 942, 1032, 125, 149, 149, + /* 1710 */ 939, 576, 125, 576, 565, 150, 150, 86, 86, 872, + /* 1720 */ 352, 159, 576, 1028, 1028, 1030, 1031, 35, 542, 941, + /* 1730 */ 576, 125, 355, 541, 88, 88, 85, 85, 357, 359, + /* 1740 */ 1324, 1308, 366, 1040, 376, 52, 52, 499, 1389, 121, + /* 1750 */ 121, 1434, 1188, 58, 58, 1362, 1374, 122, 1439, 452, + /* 1760 */ 577, 452, 1289, 167, 1028, 1280, 280, 1268, 1267, 1269, + /* 1770 */ 1609, 1359, 312, 313, 12, 314, 397, 1421, 224, 1416, + /* 1780 */ 295, 237, 1409, 339, 340, 1426, 301, 345, 484, 228, + /* 1790 */ 1371, 1307, 1372, 1370, 1425, 404, 1028, 1028, 1030, 1031, + /* 1800 */ 35, 1601, 1192, 454, 509, 369, 292, 1502, 210, 1501, + /* 1810 */ 1369, 396, 396, 395, 277, 393, 211, 566, 859, 1612, + /* 1820 */ 1244, 123, 568, 391, 4, 1188, 223, 270, 1549, 1547, + /* 1830 */ 1241, 239, 186, 327, 422, 96, 195, 220, 571, 235, + /* 1840 */ 180, 326, 188, 468, 190, 1507, 191, 192, 92, 193, + /* 1850 */ 469, 95, 1422, 13, 502, 247, 1430, 109, 199, 402, + /* 1860 */ 476, 405, 452, 1496, 1428, 1427, 14, 491, 251, 102, + /* 1870 */ 497, 1518, 241, 281, 565, 253, 203, 354, 500, 254, + /* 1880 */ 175, 1270, 407, 43, 350, 518, 1327, 436, 255, 1326, + /* 1890 */ 1325, 1318, 104, 893, 1626, 229, 408, 440, 1625, 441, + /* 1900 */ 240, 310, 1296, 1040, 311, 1317, 527, 1594, 1297, 121, + /* 1910 */ 121, 368, 1295, 1624, 268, 269, 1580, 122, 1579, 452, + /* 1920 */ 577, 452, 374, 444, 1028, 1394, 1393, 140, 553, 90, + /* 1930 */ 568, 11, 4, 1483, 383, 414, 385, 110, 116, 216, + /* 1940 */ 320, 567, 1350, 555, 42, 318, 571, 537, 1349, 389, + /* 1950 */ 390, 579, 1198, 276, 279, 278, 1028, 1028, 1030, 1031, + /* 1960 */ 35, 580, 415, 1265, 458, 1260, 416, 185, 1534, 172, + /* 1970 */ 452, 1535, 173, 156, 308, 846, 1533, 1532, 453, 217, + /* 1980 */ 225, 89, 565, 174, 322, 1188, 226, 236, 1102, 154, + /* 1990 */ 1100, 330, 176, 187, 1223, 189, 925, 338, 243, 1116, + /* 2000 */ 246, 194, 177, 178, 425, 427, 98, 99, 196, 100, + /* 2010 */ 101, 1040, 179, 1119, 248, 1115, 249, 121, 121, 24, + /* 2020 */ 163, 250, 349, 1108, 266, 122, 1238, 452, 577, 452, + /* 2030 */ 1192, 454, 1028, 200, 292, 496, 252, 201, 861, 396, + /* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256, + /* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239, + /* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326, + /* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309, + /* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155, + /* 2090 */ 17, 1154, 284, 1188, 286, 978, 265, 204, 125, 1171, + /* 2100 */ 241, 230, 972, 1175, 28, 1160, 29, 1179, 175, 1173, + /* 2110 */ 30, 43, 31, 1178, 241, 32, 41, 549, 8, 33, + /* 2120 */ 208, 111, 175, 1083, 1070, 43, 113, 1068, 240, 114, + /* 2130 */ 1072, 34, 1073, 561, 1124, 118, 271, 36, 18, 1194, + /* 2140 */ 1033, 873, 240, 935, 124, 37, 272, 273, 1617, 572, + /* 2150 */ 183, 153, 394, 1193, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567, + /* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256, + /* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2190 */ 1256, 1256, 458, 1256, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 194, 276, 277, 278, 216, 194, 194, 217, 194, 194, - /* 10 */ 194, 194, 224, 194, 194, 276, 277, 278, 204, 19, - /* 20 */ 206, 202, 297, 217, 218, 205, 207, 217, 205, 217, - /* 30 */ 218, 31, 217, 218, 217, 218, 29, 217, 218, 39, - /* 40 */ 33, 217, 220, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 312, 19, - /* 60 */ 240, 241, 316, 240, 241, 194, 46, 47, 48, 49, - /* 70 */ 22, 254, 65, 253, 254, 255, 253, 194, 255, 194, - /* 80 */ 263, 258, 259, 43, 44, 45, 46, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 276, 277, - /* 100 */ 278, 285, 102, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 113, 59, 186, 187, 188, 189, 190, - /* 120 */ 191, 310, 239, 317, 318, 196, 86, 198, 88, 317, - /* 130 */ 19, 319, 317, 318, 205, 264, 25, 211, 212, 213, - /* 140 */ 205, 121, 102, 103, 104, 105, 106, 107, 108, 109, - /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, - /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 240, - /* 170 */ 241, 116, 117, 118, 119, 240, 241, 122, 123, 124, - /* 180 */ 69, 298, 253, 194, 255, 106, 107, 132, 253, 141, - /* 190 */ 255, 54, 55, 56, 57, 58, 207, 268, 102, 103, - /* 200 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 210 */ 214, 128, 129, 102, 103, 104, 105, 106, 107, 108, - /* 220 */ 109, 110, 111, 112, 113, 134, 25, 136, 137, 300, - /* 230 */ 165, 166, 153, 19, 155, 54, 55, 56, 57, 102, - /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 250 */ 113, 108, 109, 110, 111, 112, 113, 43, 44, 45, - /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 270 */ 56, 57, 276, 277, 278, 113, 194, 19, 22, 23, - /* 280 */ 194, 67, 24, 102, 103, 104, 105, 106, 107, 108, - /* 290 */ 109, 110, 111, 112, 113, 220, 250, 59, 252, 217, - /* 300 */ 218, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, - /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 106, 107, - /* 330 */ 108, 109, 110, 111, 112, 113, 254, 59, 205, 138, - /* 340 */ 139, 19, 20, 194, 22, 263, 22, 23, 231, 25, - /* 350 */ 72, 276, 277, 278, 116, 117, 118, 101, 36, 76, - /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 370 */ 112, 113, 89, 240, 241, 92, 73, 194, 194, 73, - /* 380 */ 19, 59, 188, 189, 190, 191, 253, 81, 255, 151, - /* 390 */ 196, 25, 198, 71, 116, 117, 118, 311, 312, 205, - /* 400 */ 217, 218, 316, 81, 43, 44, 45, 46, 47, 48, - /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 270, - /* 420 */ 22, 23, 100, 25, 59, 101, 138, 139, 106, 107, - /* 430 */ 127, 128, 129, 127, 240, 241, 114, 254, 116, 117, - /* 440 */ 118, 76, 76, 121, 138, 139, 263, 253, 264, 255, - /* 450 */ 205, 275, 87, 19, 89, 89, 194, 92, 92, 199, - /* 460 */ 138, 139, 268, 102, 103, 104, 105, 106, 107, 108, - /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, - /* 480 */ 81, 116, 117, 118, 129, 240, 241, 224, 19, 226, - /* 490 */ 314, 315, 23, 25, 300, 59, 22, 234, 253, 101, - /* 500 */ 255, 236, 237, 26, 194, 183, 194, 152, 72, 22, - /* 510 */ 145, 150, 43, 44, 45, 46, 47, 48, 49, 50, - /* 520 */ 51, 52, 53, 54, 55, 56, 57, 217, 218, 217, - /* 530 */ 218, 19, 189, 59, 191, 23, 59, 138, 139, 196, - /* 540 */ 135, 198, 232, 283, 232, 140, 59, 287, 205, 275, - /* 550 */ 116, 205, 116, 117, 118, 43, 44, 45, 46, 47, - /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 570 */ 194, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 580 */ 111, 112, 113, 240, 241, 194, 240, 241, 314, 315, - /* 590 */ 116, 117, 118, 116, 117, 118, 253, 194, 255, 253, - /* 600 */ 59, 255, 19, 116, 117, 118, 23, 22, 217, 218, - /* 610 */ 142, 268, 205, 275, 102, 103, 104, 105, 106, 107, - /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, - /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 640 */ 57, 19, 194, 300, 59, 23, 119, 240, 241, 122, - /* 650 */ 123, 124, 314, 315, 194, 236, 237, 194, 117, 132, - /* 660 */ 253, 81, 255, 205, 59, 43, 44, 45, 46, 47, - /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 680 */ 217, 218, 194, 194, 194, 102, 103, 104, 105, 106, - /* 690 */ 107, 108, 109, 110, 111, 112, 113, 294, 240, 241, - /* 700 */ 120, 116, 117, 118, 59, 194, 217, 218, 211, 212, - /* 710 */ 213, 253, 19, 255, 194, 19, 23, 254, 138, 139, - /* 720 */ 24, 232, 117, 194, 102, 103, 104, 105, 106, 107, - /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, - /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 750 */ 57, 19, 264, 108, 76, 23, 127, 128, 129, 311, - /* 760 */ 312, 116, 117, 118, 316, 87, 306, 89, 308, 194, - /* 770 */ 92, 22, 59, 194, 22, 43, 44, 45, 46, 47, - /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 790 */ 194, 95, 217, 218, 265, 102, 103, 104, 105, 106, - /* 800 */ 107, 108, 109, 110, 111, 112, 113, 232, 59, 113, - /* 810 */ 25, 59, 194, 217, 218, 119, 120, 121, 122, 123, - /* 820 */ 124, 125, 19, 145, 194, 194, 23, 131, 232, 116, - /* 830 */ 117, 118, 35, 194, 102, 103, 104, 105, 106, 107, - /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, - /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 860 */ 57, 19, 194, 66, 194, 116, 117, 118, 116, 117, - /* 870 */ 118, 74, 242, 294, 194, 194, 206, 23, 194, 25, - /* 880 */ 194, 111, 112, 113, 25, 43, 44, 45, 46, 47, - /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 900 */ 24, 194, 194, 217, 218, 102, 103, 104, 105, 106, - /* 910 */ 107, 108, 109, 110, 111, 112, 113, 241, 232, 194, - /* 920 */ 212, 213, 242, 242, 217, 218, 242, 130, 11, 253, - /* 930 */ 194, 255, 19, 265, 149, 59, 306, 194, 308, 232, - /* 940 */ 309, 310, 217, 218, 102, 103, 104, 105, 106, 107, - /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, - /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 970 */ 57, 194, 194, 59, 194, 239, 19, 194, 25, 254, - /* 980 */ 303, 304, 23, 194, 25, 126, 306, 306, 308, 308, - /* 990 */ 306, 271, 308, 117, 286, 217, 218, 217, 218, 194, - /* 1000 */ 194, 159, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, - /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 59, 239, 194, - /* 1030 */ 116, 117, 118, 260, 254, 194, 240, 241, 194, 233, - /* 1040 */ 205, 240, 241, 205, 239, 128, 129, 270, 265, 253, - /* 1050 */ 194, 255, 217, 218, 253, 194, 255, 143, 280, 102, - /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1070 */ 113, 118, 159, 217, 218, 240, 241, 118, 240, 241, - /* 1080 */ 194, 194, 194, 239, 116, 117, 118, 22, 253, 254, - /* 1090 */ 255, 253, 19, 255, 233, 194, 143, 24, 263, 212, - /* 1100 */ 213, 194, 143, 217, 218, 217, 218, 261, 262, 271, - /* 1110 */ 254, 143, 19, 7, 8, 9, 43, 44, 45, 46, - /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1130 */ 57, 16, 19, 22, 23, 294, 43, 44, 45, 46, - /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1150 */ 57, 312, 194, 214, 21, 316, 43, 44, 45, 46, - /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1170 */ 57, 106, 107, 286, 194, 102, 103, 104, 105, 106, - /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 207, 158, 59, - /* 1190 */ 160, 22, 77, 24, 79, 102, 103, 104, 105, 106, - /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 194, 194, 229, - /* 1210 */ 194, 231, 101, 80, 22, 102, 103, 104, 105, 106, - /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 288, 59, 12, - /* 1230 */ 217, 218, 293, 217, 218, 19, 106, 107, 59, 19, - /* 1240 */ 16, 127, 128, 129, 27, 115, 116, 117, 118, 194, - /* 1250 */ 120, 59, 22, 194, 24, 194, 123, 100, 128, 42, - /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1270 */ 54, 55, 56, 57, 117, 194, 217, 218, 121, 100, - /* 1280 */ 63, 194, 245, 153, 194, 155, 117, 19, 115, 194, - /* 1290 */ 73, 214, 194, 256, 161, 116, 117, 194, 217, 218, - /* 1300 */ 121, 77, 194, 79, 217, 218, 194, 217, 218, 117, - /* 1310 */ 153, 154, 155, 254, 46, 217, 218, 144, 102, 103, - /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 1330 */ 232, 270, 153, 154, 155, 115, 116, 66, 19, 20, - /* 1340 */ 183, 22, 12, 312, 254, 194, 262, 316, 209, 210, - /* 1350 */ 266, 239, 194, 194, 108, 36, 85, 27, 19, 20, - /* 1360 */ 265, 22, 183, 245, 144, 94, 25, 48, 217, 218, - /* 1370 */ 293, 194, 42, 270, 256, 36, 217, 218, 59, 194, - /* 1380 */ 25, 135, 194, 115, 194, 161, 140, 194, 194, 15, - /* 1390 */ 71, 194, 312, 63, 217, 218, 316, 194, 59, 131, - /* 1400 */ 301, 302, 217, 218, 85, 217, 218, 217, 218, 90, - /* 1410 */ 71, 217, 218, 19, 217, 218, 245, 146, 262, 100, - /* 1420 */ 217, 218, 266, 265, 85, 106, 107, 256, 312, 90, - /* 1430 */ 209, 210, 316, 114, 60, 116, 117, 118, 194, 100, - /* 1440 */ 121, 194, 194, 145, 115, 106, 107, 19, 46, 19, - /* 1450 */ 20, 24, 22, 114, 194, 116, 117, 118, 194, 245, - /* 1460 */ 121, 194, 164, 194, 217, 218, 36, 194, 258, 259, - /* 1470 */ 256, 194, 153, 154, 155, 156, 157, 217, 218, 150, - /* 1480 */ 31, 217, 218, 142, 217, 218, 217, 218, 39, 59, - /* 1490 */ 217, 218, 153, 154, 155, 156, 157, 149, 150, 5, - /* 1500 */ 145, 71, 183, 245, 10, 11, 12, 13, 14, 194, - /* 1510 */ 116, 17, 129, 227, 256, 85, 194, 115, 194, 23, - /* 1520 */ 90, 25, 183, 99, 30, 97, 32, 22, 22, 194, - /* 1530 */ 100, 194, 217, 218, 40, 152, 106, 107, 23, 217, - /* 1540 */ 218, 194, 19, 20, 114, 22, 116, 117, 118, 257, - /* 1550 */ 194, 121, 217, 218, 217, 218, 194, 133, 53, 36, - /* 1560 */ 23, 23, 25, 25, 70, 120, 121, 61, 141, 7, - /* 1570 */ 8, 121, 78, 217, 218, 81, 23, 227, 25, 217, - /* 1580 */ 218, 131, 59, 153, 154, 155, 156, 157, 0, 1, - /* 1590 */ 2, 59, 98, 5, 71, 23, 227, 25, 10, 11, - /* 1600 */ 12, 13, 14, 83, 84, 17, 23, 23, 25, 25, - /* 1610 */ 59, 194, 194, 183, 23, 23, 25, 25, 30, 194, - /* 1620 */ 32, 19, 20, 100, 22, 194, 194, 133, 40, 106, - /* 1630 */ 107, 108, 138, 139, 194, 217, 218, 114, 36, 116, - /* 1640 */ 117, 118, 217, 218, 121, 194, 194, 194, 23, 117, - /* 1650 */ 25, 194, 23, 23, 25, 25, 162, 194, 70, 194, - /* 1660 */ 145, 59, 23, 153, 25, 155, 78, 194, 117, 81, - /* 1670 */ 217, 218, 194, 71, 217, 218, 153, 154, 155, 156, - /* 1680 */ 157, 194, 217, 218, 194, 23, 98, 25, 321, 194, - /* 1690 */ 217, 218, 194, 19, 20, 194, 22, 153, 23, 155, - /* 1700 */ 25, 194, 100, 194, 217, 218, 183, 194, 106, 107, - /* 1710 */ 36, 194, 217, 218, 237, 194, 114, 243, 116, 117, - /* 1720 */ 118, 133, 194, 121, 217, 218, 138, 139, 194, 194, - /* 1730 */ 194, 290, 289, 59, 217, 218, 194, 194, 217, 218, - /* 1740 */ 194, 194, 140, 194, 194, 71, 194, 244, 194, 194, - /* 1750 */ 162, 217, 218, 194, 194, 153, 154, 155, 156, 157, - /* 1760 */ 217, 218, 194, 217, 218, 194, 217, 218, 257, 217, - /* 1770 */ 218, 217, 218, 257, 100, 194, 257, 217, 218, 257, - /* 1780 */ 106, 107, 215, 299, 194, 183, 192, 194, 114, 194, - /* 1790 */ 116, 117, 118, 1, 2, 121, 221, 5, 217, 218, - /* 1800 */ 273, 197, 10, 11, 12, 13, 14, 217, 218, 17, - /* 1810 */ 217, 218, 217, 218, 140, 194, 246, 194, 273, 295, - /* 1820 */ 247, 273, 30, 247, 32, 269, 269, 153, 154, 155, - /* 1830 */ 156, 157, 40, 246, 273, 295, 230, 226, 217, 218, - /* 1840 */ 217, 218, 220, 261, 220, 282, 220, 19, 20, 244, - /* 1850 */ 22, 250, 141, 250, 246, 60, 201, 183, 261, 261, - /* 1860 */ 261, 201, 70, 299, 36, 299, 201, 38, 151, 150, - /* 1870 */ 78, 285, 22, 81, 296, 296, 43, 235, 18, 238, - /* 1880 */ 201, 274, 272, 238, 238, 238, 18, 59, 200, 149, - /* 1890 */ 98, 247, 274, 274, 235, 247, 247, 247, 235, 71, - /* 1900 */ 272, 201, 200, 158, 292, 62, 291, 201, 200, 22, - /* 1910 */ 201, 222, 200, 222, 201, 200, 115, 219, 219, 64, - /* 1920 */ 219, 228, 22, 126, 221, 133, 165, 222, 100, 225, - /* 1930 */ 138, 139, 225, 219, 106, 107, 24, 219, 228, 219, - /* 1940 */ 219, 307, 114, 113, 116, 117, 118, 315, 284, 121, - /* 1950 */ 284, 222, 201, 91, 162, 320, 320, 82, 148, 267, - /* 1960 */ 145, 267, 22, 279, 201, 158, 281, 251, 147, 146, - /* 1970 */ 25, 203, 250, 249, 251, 248, 13, 247, 195, 195, - /* 1980 */ 6, 153, 154, 155, 156, 157, 193, 193, 305, 193, - /* 1990 */ 208, 305, 302, 214, 214, 214, 208, 223, 223, 214, - /* 2000 */ 4, 215, 215, 214, 3, 22, 208, 163, 15, 23, - /* 2010 */ 16, 183, 23, 139, 151, 130, 25, 20, 142, 24, - /* 2020 */ 16, 144, 1, 142, 130, 130, 61, 37, 53, 151, - /* 2030 */ 53, 53, 53, 130, 116, 1, 34, 141, 5, 22, - /* 2040 */ 115, 161, 75, 25, 68, 141, 41, 115, 68, 24, - /* 2050 */ 20, 19, 131, 125, 67, 67, 96, 22, 22, 22, - /* 2060 */ 37, 23, 22, 24, 22, 59, 67, 23, 149, 28, - /* 2070 */ 22, 25, 23, 23, 23, 22, 141, 34, 97, 23, - /* 2080 */ 23, 34, 116, 22, 143, 25, 34, 75, 34, 34, - /* 2090 */ 75, 88, 34, 86, 23, 22, 34, 25, 24, 34, - /* 2100 */ 25, 93, 23, 44, 142, 23, 142, 23, 23, 22, - /* 2110 */ 11, 25, 23, 25, 23, 22, 22, 22, 1, 23, - /* 2120 */ 23, 23, 22, 22, 15, 141, 141, 25, 25, 1, - /* 2130 */ 322, 322, 322, 135, 322, 322, 322, 322, 322, 322, - /* 2140 */ 322, 141, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2150 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2160 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2180 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2190 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2200 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 0 */ 276, 277, 278, 240, 241, 224, 194, 226, 194, 240, + /* 10 */ 241, 194, 216, 220, 194, 234, 253, 194, 255, 19, + /* 20 */ 224, 297, 253, 194, 255, 205, 212, 213, 205, 217, + /* 30 */ 218, 31, 205, 194, 217, 218, 194, 217, 218, 39, + /* 40 */ 217, 218, 312, 43, 44, 45, 316, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19, + /* 60 */ 240, 241, 194, 240, 241, 194, 254, 240, 241, 276, + /* 70 */ 277, 278, 233, 253, 254, 255, 253, 254, 255, 217, + /* 80 */ 253, 239, 255, 43, 44, 45, 263, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 270, + /* 100 */ 286, 22, 23, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 114, 82, 47, 48, 49, 50, + /* 120 */ 186, 187, 188, 189, 190, 191, 189, 87, 191, 89, + /* 130 */ 196, 19, 198, 196, 317, 198, 319, 25, 194, 205, + /* 140 */ 298, 270, 205, 103, 104, 105, 106, 107, 108, 109, + /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 11, 47, + /* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 170 */ 58, 60, 139, 140, 240, 241, 214, 240, 241, 311, + /* 180 */ 312, 102, 70, 239, 316, 194, 19, 253, 77, 255, + /* 190 */ 253, 122, 255, 55, 56, 57, 58, 59, 207, 88, + /* 200 */ 194, 90, 268, 194, 93, 268, 107, 108, 109, 110, + /* 210 */ 111, 112, 113, 114, 47, 103, 104, 105, 106, 107, + /* 220 */ 108, 109, 110, 111, 112, 113, 114, 114, 117, 118, + /* 230 */ 119, 276, 277, 278, 300, 19, 194, 300, 276, 277, + /* 240 */ 278, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 194, 43, + /* 260 */ 44, 45, 47, 47, 48, 49, 50, 51, 52, 53, + /* 270 */ 54, 55, 56, 57, 58, 82, 129, 130, 60, 129, + /* 280 */ 130, 217, 218, 116, 68, 25, 103, 104, 105, 106, + /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 23, 132, + /* 300 */ 294, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 310 */ 112, 113, 114, 217, 121, 306, 194, 308, 26, 103, + /* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 330 */ 114, 116, 139, 140, 217, 117, 118, 119, 120, 19, + /* 340 */ 194, 123, 124, 125, 24, 109, 110, 111, 112, 113, + /* 350 */ 114, 133, 60, 311, 312, 250, 194, 252, 316, 19, + /* 360 */ 194, 166, 167, 43, 44, 45, 205, 47, 48, 49, + /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 217, + /* 380 */ 218, 317, 318, 43, 44, 45, 264, 47, 48, 49, + /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 139, + /* 400 */ 140, 240, 241, 139, 140, 188, 189, 190, 191, 117, + /* 410 */ 118, 119, 231, 196, 253, 198, 255, 194, 194, 258, + /* 420 */ 259, 146, 205, 103, 104, 105, 106, 107, 108, 109, + /* 430 */ 110, 111, 112, 113, 114, 109, 212, 213, 236, 237, + /* 440 */ 217, 218, 194, 103, 104, 105, 106, 107, 108, 109, + /* 450 */ 110, 111, 112, 113, 114, 194, 120, 240, 241, 123, + /* 460 */ 124, 125, 136, 194, 19, 74, 60, 141, 23, 133, + /* 470 */ 253, 194, 255, 82, 194, 309, 310, 254, 29, 317, + /* 480 */ 318, 194, 33, 22, 199, 268, 263, 239, 43, 44, + /* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54, + /* 500 */ 55, 56, 57, 58, 217, 218, 194, 60, 19, 146, + /* 510 */ 286, 242, 23, 107, 108, 66, 204, 300, 206, 128, + /* 520 */ 73, 60, 116, 117, 118, 119, 265, 121, 165, 60, + /* 530 */ 139, 140, 43, 44, 45, 129, 47, 48, 49, 50, + /* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, + /* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + /* 560 */ 154, 194, 156, 194, 117, 118, 119, 280, 283, 205, + /* 570 */ 101, 220, 287, 19, 20, 306, 22, 308, 117, 118, + /* 580 */ 119, 211, 212, 213, 217, 218, 117, 118, 236, 237, + /* 590 */ 36, 122, 103, 104, 105, 106, 107, 108, 109, 110, + /* 600 */ 111, 112, 113, 114, 240, 241, 194, 60, 194, 194, + /* 610 */ 194, 194, 19, 194, 60, 194, 23, 253, 206, 255, + /* 620 */ 73, 254, 19, 154, 155, 156, 72, 276, 277, 278, + /* 630 */ 263, 217, 218, 217, 218, 271, 43, 44, 45, 271, + /* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 650 */ 57, 58, 183, 194, 285, 101, 19, 214, 60, 242, + /* 660 */ 23, 107, 108, 109, 117, 118, 119, 16, 254, 115, + /* 670 */ 254, 117, 118, 119, 194, 60, 122, 263, 205, 264, + /* 680 */ 43, 44, 45, 264, 47, 48, 49, 50, 51, 52, + /* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, + /* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155, + /* 710 */ 156, 157, 158, 240, 241, 117, 118, 119, 101, 205, + /* 720 */ 117, 19, 20, 306, 22, 308, 253, 74, 255, 78, + /* 730 */ 205, 80, 117, 118, 119, 118, 293, 183, 36, 122, + /* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 750 */ 113, 114, 194, 294, 240, 241, 211, 212, 213, 144, + /* 760 */ 19, 23, 60, 25, 23, 240, 241, 253, 245, 255, + /* 770 */ 15, 154, 155, 156, 72, 217, 218, 194, 253, 256, + /* 780 */ 255, 128, 129, 130, 43, 44, 45, 22, 47, 48, + /* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 800 */ 183, 19, 77, 101, 19, 128, 129, 130, 23, 107, + /* 810 */ 108, 60, 254, 88, 21, 90, 61, 115, 93, 117, + /* 820 */ 118, 119, 239, 22, 122, 60, 194, 205, 43, 44, + /* 830 */ 45, 205, 47, 48, 49, 50, 51, 52, 53, 54, + /* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108, + /* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157, + /* 860 */ 158, 60, 240, 241, 60, 116, 240, 241, 117, 118, + /* 870 */ 119, 146, 194, 19, 81, 253, 275, 255, 24, 253, + /* 880 */ 98, 255, 117, 118, 119, 183, 22, 194, 103, 104, + /* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + /* 900 */ 151, 194, 270, 152, 22, 23, 194, 25, 19, 202, + /* 910 */ 217, 218, 23, 109, 207, 314, 315, 124, 117, 118, + /* 920 */ 119, 117, 118, 119, 60, 232, 22, 23, 19, 25, + /* 930 */ 303, 304, 43, 44, 45, 22, 47, 48, 49, 50, + /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 270, 227, + /* 950 */ 96, 60, 43, 44, 45, 162, 47, 48, 49, 50, + /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 112, + /* 970 */ 113, 114, 194, 60, 120, 121, 122, 123, 124, 125, + /* 980 */ 126, 117, 118, 119, 102, 25, 132, 107, 108, 194, + /* 990 */ 194, 227, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1000 */ 111, 112, 113, 114, 194, 194, 102, 194, 117, 118, + /* 1010 */ 119, 233, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1020 */ 111, 112, 113, 114, 194, 194, 19, 194, 217, 218, + /* 1030 */ 117, 118, 119, 241, 154, 144, 156, 135, 242, 137, + /* 1040 */ 138, 130, 19, 232, 194, 253, 23, 255, 217, 218, + /* 1050 */ 217, 218, 242, 16, 7, 8, 9, 25, 261, 262, + /* 1060 */ 265, 194, 19, 232, 153, 232, 43, 44, 45, 160, + /* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1080 */ 57, 58, 194, 227, 217, 218, 43, 44, 45, 194, + /* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1100 */ 57, 58, 306, 143, 308, 217, 218, 294, 12, 77, + /* 1110 */ 22, 23, 217, 218, 194, 78, 306, 80, 308, 194, + /* 1120 */ 232, 254, 90, 27, 117, 93, 103, 104, 105, 106, + /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 194, + /* 1140 */ 35, 194, 217, 218, 214, 194, 103, 104, 105, 106, + /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 194, 239, + /* 1160 */ 64, 194, 217, 218, 217, 218, 209, 210, 217, 218, + /* 1170 */ 74, 207, 67, 22, 194, 77, 19, 232, 245, 232, + /* 1180 */ 75, 24, 312, 232, 217, 218, 316, 25, 90, 256, + /* 1190 */ 102, 93, 159, 229, 161, 231, 19, 217, 218, 162, + /* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52, + /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 23, 288, 25, + /* 1220 */ 43, 44, 45, 293, 47, 48, 49, 50, 51, 52, + /* 1230 */ 53, 54, 55, 56, 57, 58, 131, 22, 275, 24, + /* 1240 */ 43, 44, 45, 194, 47, 48, 49, 50, 51, 52, + /* 1250 */ 53, 54, 55, 56, 57, 58, 194, 194, 107, 108, + /* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1270 */ 113, 114, 194, 25, 194, 60, 194, 314, 315, 194, + /* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1290 */ 113, 114, 19, 194, 194, 217, 218, 217, 218, 25, + /* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1310 */ 113, 114, 150, 119, 265, 312, 67, 217, 218, 316, + /* 1320 */ 19, 239, 194, 194, 128, 129, 130, 265, 265, 209, + /* 1330 */ 210, 31, 254, 118, 254, 86, 60, 194, 144, 39, + /* 1340 */ 19, 130, 275, 312, 95, 44, 45, 316, 47, 48, + /* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 1360 */ 217, 218, 24, 194, 153, 12, 45, 119, 47, 48, + /* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 1380 */ 27, 314, 315, 22, 312, 24, 217, 218, 316, 116, + /* 1390 */ 117, 22, 144, 25, 118, 42, 147, 194, 60, 19, + /* 1400 */ 20, 127, 22, 194, 103, 104, 105, 106, 107, 108, + /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 194, + /* 1420 */ 217, 218, 194, 54, 103, 104, 105, 106, 107, 108, + /* 1430 */ 109, 110, 111, 112, 113, 114, 194, 22, 310, 25, + /* 1440 */ 60, 194, 217, 218, 194, 217, 218, 194, 260, 194, + /* 1450 */ 301, 302, 72, 262, 262, 194, 118, 266, 266, 217, + /* 1460 */ 218, 194, 82, 245, 217, 218, 194, 217, 218, 194, + /* 1470 */ 217, 218, 217, 218, 256, 60, 24, 194, 217, 218, + /* 1480 */ 194, 101, 194, 245, 217, 218, 194, 107, 108, 217, + /* 1490 */ 218, 22, 217, 218, 256, 115, 245, 117, 118, 119, + /* 1500 */ 217, 218, 122, 217, 218, 217, 218, 256, 22, 217, + /* 1510 */ 218, 116, 258, 259, 146, 19, 20, 194, 22, 139, + /* 1520 */ 140, 150, 151, 136, 23, 194, 25, 194, 141, 194, + /* 1530 */ 194, 62, 36, 118, 154, 155, 156, 157, 158, 100, + /* 1540 */ 145, 194, 154, 194, 156, 49, 194, 23, 194, 25, + /* 1550 */ 217, 218, 217, 218, 194, 257, 60, 143, 194, 60, + /* 1560 */ 194, 121, 122, 183, 217, 218, 217, 218, 72, 217, + /* 1570 */ 218, 217, 218, 134, 23, 194, 25, 217, 218, 7, + /* 1580 */ 8, 321, 86, 217, 218, 194, 122, 91, 19, 20, + /* 1590 */ 23, 22, 25, 23, 142, 25, 132, 101, 217, 218, + /* 1600 */ 194, 194, 194, 107, 108, 36, 194, 194, 217, 218, + /* 1610 */ 194, 115, 194, 117, 118, 119, 194, 118, 122, 194, + /* 1620 */ 23, 194, 25, 217, 218, 217, 218, 194, 142, 60, + /* 1630 */ 217, 218, 194, 217, 218, 217, 218, 84, 85, 217, + /* 1640 */ 218, 72, 217, 218, 217, 218, 60, 23, 60, 25, + /* 1650 */ 154, 155, 156, 157, 158, 86, 154, 194, 156, 194, + /* 1660 */ 91, 19, 20, 23, 22, 25, 194, 23, 194, 25, + /* 1670 */ 101, 23, 194, 25, 194, 194, 107, 108, 36, 183, + /* 1680 */ 217, 218, 217, 218, 115, 194, 117, 118, 119, 217, + /* 1690 */ 218, 122, 194, 237, 194, 217, 218, 217, 218, 194, + /* 1700 */ 194, 23, 60, 25, 118, 23, 118, 25, 217, 218, + /* 1710 */ 23, 194, 25, 194, 72, 217, 218, 217, 218, 23, + /* 1720 */ 194, 25, 194, 154, 155, 156, 157, 158, 86, 23, + /* 1730 */ 194, 25, 194, 91, 217, 218, 217, 218, 194, 194, + /* 1740 */ 194, 194, 194, 101, 194, 217, 218, 290, 194, 107, + /* 1750 */ 108, 194, 183, 217, 218, 194, 194, 115, 194, 117, + /* 1760 */ 118, 119, 194, 243, 122, 194, 289, 194, 194, 194, + /* 1770 */ 194, 257, 257, 257, 244, 257, 192, 273, 215, 269, + /* 1780 */ 246, 299, 269, 295, 247, 273, 247, 246, 295, 230, + /* 1790 */ 261, 226, 261, 261, 273, 273, 154, 155, 156, 157, + /* 1800 */ 158, 0, 1, 2, 221, 220, 5, 220, 250, 220, + /* 1810 */ 261, 10, 11, 12, 13, 14, 250, 282, 17, 197, + /* 1820 */ 61, 19, 20, 246, 22, 183, 244, 142, 201, 201, + /* 1830 */ 38, 30, 299, 32, 201, 152, 22, 151, 36, 299, + /* 1840 */ 43, 40, 235, 18, 238, 285, 238, 238, 296, 238, + /* 1850 */ 201, 296, 274, 272, 18, 200, 235, 150, 235, 247, + /* 1860 */ 247, 247, 60, 247, 274, 274, 272, 201, 200, 159, + /* 1870 */ 63, 292, 71, 201, 72, 200, 22, 201, 222, 200, + /* 1880 */ 79, 201, 222, 82, 291, 116, 219, 65, 200, 219, + /* 1890 */ 219, 228, 22, 127, 225, 166, 222, 24, 225, 114, + /* 1900 */ 99, 284, 221, 101, 284, 228, 307, 315, 219, 107, + /* 1910 */ 108, 219, 219, 219, 201, 92, 320, 115, 320, 117, + /* 1920 */ 118, 119, 222, 83, 122, 267, 267, 149, 146, 19, + /* 1930 */ 20, 22, 22, 279, 250, 134, 201, 148, 159, 249, + /* 1940 */ 139, 140, 251, 141, 25, 281, 36, 147, 251, 248, + /* 1950 */ 247, 203, 13, 195, 6, 195, 154, 155, 156, 157, + /* 1960 */ 158, 193, 305, 193, 163, 193, 305, 302, 214, 208, + /* 1970 */ 60, 214, 208, 223, 223, 4, 214, 214, 3, 22, + /* 1980 */ 215, 214, 72, 208, 164, 183, 215, 15, 23, 16, + /* 1990 */ 23, 140, 131, 152, 25, 143, 20, 16, 24, 1, + /* 2000 */ 145, 143, 131, 131, 62, 37, 54, 54, 152, 54, + /* 2010 */ 54, 101, 131, 117, 34, 1, 142, 107, 108, 22, + /* 2020 */ 5, 116, 162, 69, 25, 115, 76, 117, 118, 119, + /* 2030 */ 1, 2, 122, 69, 5, 41, 142, 116, 20, 10, + /* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126, + /* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30, + /* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40, + /* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68, + /* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23, + /* 2090 */ 22, 98, 23, 183, 23, 117, 34, 22, 25, 89, + /* 2100 */ 71, 142, 144, 76, 34, 23, 34, 76, 79, 87, + /* 2110 */ 34, 82, 34, 94, 71, 34, 22, 24, 44, 34, + /* 2120 */ 25, 25, 79, 23, 23, 82, 143, 23, 99, 143, + /* 2130 */ 23, 22, 11, 25, 23, 25, 22, 22, 22, 1, + /* 2140 */ 23, 23, 99, 136, 22, 22, 142, 142, 142, 25, + /* 2150 */ 25, 23, 15, 1, 322, 322, 322, 322, 322, 322, + /* 2160 */ 322, 322, 322, 134, 322, 322, 322, 322, 139, 140, + /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 134, 322, 322, + /* 2180 */ 322, 322, 139, 140, 322, 322, 322, 322, 322, 322, + /* 2190 */ 322, 322, 163, 322, 322, 322, 322, 322, 322, 322, + /* 2200 */ 322, 322, 322, 322, 322, 322, 163, 322, 322, 322, /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, @@ -173527,118 +174848,125 @@ static const YYCODETYPE yy_lookahead[] = { /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, + /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2330 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2340 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + /* 2350 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + /* 2360 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + /* 2370 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + /* 2380 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + /* 2390 */ 186, 186, 186, }; #define YY_SHIFT_COUNT (582) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2128) +#define YY_SHIFT_MAX (2152) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1792, 1588, 1494, 322, 322, 399, 306, 1319, 1339, 1430, - /* 10 */ 1828, 1828, 1828, 580, 399, 399, 399, 399, 399, 0, - /* 20 */ 0, 214, 1093, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 30 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1130, 1130, - /* 40 */ 365, 365, 55, 278, 436, 713, 713, 201, 201, 201, - /* 50 */ 201, 40, 111, 258, 361, 469, 512, 583, 622, 693, - /* 60 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, - /* 70 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, - /* 80 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1523, 1602, - /* 90 */ 1674, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 100 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 110 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 120 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 130 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, - /* 140 */ 137, 181, 181, 181, 181, 181, 181, 181, 96, 222, - /* 150 */ 143, 477, 713, 1133, 1268, 713, 713, 79, 79, 713, - /* 160 */ 770, 83, 65, 65, 65, 288, 162, 162, 2142, 2142, - /* 170 */ 696, 696, 696, 238, 474, 474, 474, 474, 1217, 1217, - /* 180 */ 678, 477, 324, 398, 713, 713, 713, 713, 713, 713, - /* 190 */ 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, - /* 200 */ 713, 713, 713, 1220, 366, 366, 713, 917, 283, 283, - /* 210 */ 434, 434, 605, 605, 1298, 2142, 2142, 2142, 2142, 2142, - /* 220 */ 2142, 2142, 1179, 1157, 1157, 487, 527, 585, 645, 749, - /* 230 */ 914, 968, 752, 713, 713, 713, 713, 713, 713, 713, - /* 240 */ 713, 713, 713, 303, 713, 713, 713, 713, 713, 713, - /* 250 */ 713, 713, 713, 713, 713, 713, 797, 797, 797, 713, - /* 260 */ 713, 713, 959, 713, 713, 713, 1169, 1271, 713, 713, - /* 270 */ 1330, 713, 713, 713, 713, 713, 713, 713, 713, 629, - /* 280 */ 7, 91, 876, 876, 876, 876, 953, 91, 91, 1246, - /* 290 */ 1065, 1106, 1374, 1329, 1348, 468, 1348, 1394, 785, 1329, - /* 300 */ 1329, 785, 1329, 468, 1394, 859, 854, 1402, 1449, 1449, - /* 310 */ 1449, 1173, 1173, 1173, 1173, 1355, 1355, 1030, 1341, 405, - /* 320 */ 1230, 1795, 1795, 1711, 1711, 1829, 1829, 1711, 1717, 1719, - /* 330 */ 1850, 1833, 1860, 1860, 1860, 1860, 1711, 1868, 1740, 1719, - /* 340 */ 1719, 1740, 1850, 1833, 1740, 1833, 1740, 1711, 1868, 1745, - /* 350 */ 1843, 1711, 1868, 1887, 1711, 1868, 1711, 1868, 1887, 1801, - /* 360 */ 1801, 1801, 1855, 1900, 1900, 1887, 1801, 1797, 1801, 1855, - /* 370 */ 1801, 1801, 1761, 1912, 1830, 1830, 1887, 1711, 1862, 1862, - /* 380 */ 1875, 1875, 1810, 1815, 1940, 1711, 1807, 1810, 1821, 1823, - /* 390 */ 1740, 1945, 1963, 1963, 1974, 1974, 1974, 2142, 2142, 2142, - /* 400 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, - /* 410 */ 2142, 2142, 20, 1224, 256, 1111, 1115, 1114, 1192, 1496, - /* 420 */ 1424, 1505, 1427, 355, 1383, 1537, 1506, 1538, 1553, 1583, - /* 430 */ 1584, 1591, 1625, 541, 1445, 1562, 1450, 1572, 1515, 1428, - /* 440 */ 1532, 1592, 1629, 1520, 1630, 1639, 1510, 1544, 1662, 1675, - /* 450 */ 1551, 48, 1996, 2001, 1983, 1844, 1993, 1994, 1986, 1989, - /* 460 */ 1874, 1863, 1885, 1991, 1991, 1995, 1876, 1997, 1877, 2004, - /* 470 */ 2021, 1881, 1894, 1991, 1895, 1965, 1990, 1991, 1878, 1975, - /* 480 */ 1977, 1978, 1979, 1903, 1918, 2002, 1896, 2034, 2033, 2017, - /* 490 */ 1925, 1880, 1976, 2018, 1980, 1967, 2005, 1904, 1932, 2025, - /* 500 */ 2030, 2032, 1921, 1928, 2035, 1987, 2036, 2037, 2038, 2040, - /* 510 */ 1988, 2006, 2039, 1960, 2041, 2042, 1999, 2023, 2044, 2043, - /* 520 */ 1919, 2048, 2049, 2050, 2046, 2051, 2053, 1981, 1935, 2056, - /* 530 */ 2057, 1966, 2047, 2061, 1941, 2060, 2052, 2054, 2055, 2058, - /* 540 */ 2003, 2012, 2007, 2059, 2015, 2008, 2062, 2071, 2073, 2074, - /* 550 */ 2072, 2075, 2065, 1962, 1964, 2079, 2060, 2082, 2084, 2085, - /* 560 */ 2087, 2086, 2089, 2088, 2091, 2093, 2099, 2094, 2095, 2096, - /* 570 */ 2097, 2100, 2101, 2102, 1998, 1984, 1985, 2000, 2103, 2098, - /* 580 */ 2109, 2117, 2128, + /* 0 */ 2029, 1801, 2043, 1380, 1380, 33, 391, 1496, 1569, 1642, + /* 10 */ 702, 702, 702, 193, 33, 33, 33, 33, 33, 0, + /* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702, + /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 406, 406, + /* 40 */ 111, 111, 218, 447, 547, 598, 598, 260, 260, 260, + /* 50 */ 260, 40, 112, 320, 340, 445, 489, 593, 637, 741, + /* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177, + /* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, + /* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554, + /* 90 */ 1802, 1910, 702, 702, 702, 702, 702, 702, 702, 702, + /* 100 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, + /* 110 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, + /* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, + /* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, + /* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198, + /* 150 */ 183, 99, 236, 292, 598, 793, 167, 598, 598, 880, + /* 160 */ 880, 598, 857, 150, 195, 195, 195, 264, 113, 113, + /* 170 */ 2207, 2207, 854, 854, 854, 751, 765, 765, 765, 765, + /* 180 */ 1096, 1096, 725, 292, 882, 904, 598, 598, 598, 598, + /* 190 */ 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, + /* 200 */ 598, 598, 598, 598, 598, 1273, 1032, 1032, 598, 147, + /* 210 */ 1098, 1098, 603, 603, 1276, 1276, 363, 2207, 2207, 2207, + /* 220 */ 2207, 2207, 2207, 2207, 469, 617, 617, 801, 336, 461, + /* 230 */ 804, 864, 615, 891, 913, 598, 598, 598, 598, 598, + /* 240 */ 598, 598, 598, 598, 598, 653, 598, 598, 598, 598, + /* 250 */ 598, 598, 598, 598, 598, 598, 598, 598, 1105, 1105, + /* 260 */ 1105, 598, 598, 598, 1194, 598, 598, 598, 1215, 1249, + /* 270 */ 598, 1353, 598, 598, 598, 598, 598, 598, 598, 598, + /* 280 */ 677, 449, 902, 1338, 1338, 1338, 1338, 1248, 902, 902, + /* 290 */ 326, 1151, 1047, 755, 749, 1371, 960, 1371, 1007, 1162, + /* 300 */ 749, 749, 1162, 749, 960, 1007, 1274, 738, 215, 1300, + /* 310 */ 1300, 1300, 1395, 1395, 1395, 1395, 1368, 1368, 1033, 1414, + /* 320 */ 1387, 1361, 1759, 1759, 1685, 1685, 1792, 1792, 1685, 1683, + /* 330 */ 1686, 1814, 1797, 1825, 1825, 1825, 1825, 1685, 1836, 1707, + /* 340 */ 1686, 1686, 1707, 1814, 1797, 1707, 1797, 1707, 1685, 1836, + /* 350 */ 1710, 1807, 1685, 1836, 1854, 1685, 1836, 1685, 1836, 1854, + /* 360 */ 1769, 1769, 1769, 1822, 1870, 1870, 1854, 1769, 1766, 1769, + /* 370 */ 1822, 1769, 1769, 1729, 1873, 1785, 1785, 1854, 1685, 1823, + /* 380 */ 1823, 1840, 1840, 1778, 1782, 1909, 1685, 1779, 1778, 1789, + /* 390 */ 1800, 1707, 1919, 1939, 1939, 1948, 1948, 1948, 2207, 2207, + /* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, + /* 410 */ 2207, 2207, 2207, 69, 1037, 79, 1088, 651, 1196, 1415, + /* 420 */ 1501, 1439, 1369, 1452, 911, 1211, 1524, 1469, 1551, 1567, + /* 430 */ 1570, 1624, 1640, 1644, 1499, 1440, 1572, 1464, 1597, 275, + /* 440 */ 782, 1586, 1648, 1678, 1553, 1682, 1687, 1388, 1502, 1696, + /* 450 */ 1706, 1588, 1486, 1971, 1975, 1957, 1820, 1972, 1973, 1965, + /* 460 */ 1967, 1851, 1841, 1861, 1969, 1969, 1974, 1852, 1976, 1855, + /* 470 */ 1981, 1998, 1858, 1871, 1969, 1872, 1942, 1968, 1969, 1856, + /* 480 */ 1952, 1953, 1955, 1956, 1881, 1896, 1980, 1874, 2014, 2015, + /* 490 */ 1997, 1905, 1860, 1954, 1999, 1964, 1950, 1994, 1894, 1921, + /* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047, + /* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057, + /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1959, + /* 530 */ 2069, 2071, 1978, 2062, 2075, 1958, 2073, 2070, 2072, 2076, + /* 540 */ 2078, 2010, 2027, 2022, 2074, 2031, 2019, 2081, 2082, 2094, + /* 550 */ 2093, 2095, 2096, 2085, 1983, 1986, 2100, 2073, 2101, 2104, + /* 560 */ 2107, 2109, 2108, 2110, 2111, 2114, 2121, 2115, 2116, 2117, + /* 570 */ 2118, 2122, 2123, 2124, 2007, 2004, 2005, 2006, 2125, 2128, + /* 580 */ 2137, 2138, 2152, }; -#define YY_REDUCE_COUNT (411) -#define YY_REDUCE_MIN (-275) -#define YY_REDUCE_MAX (1798) +#define YY_REDUCE_COUNT (412) +#define YY_REDUCE_MIN (-276) +#define YY_REDUCE_MAX (1775) static const short yy_reduce_ofst[] = { - /* 0 */ -71, 194, 343, 835, -180, -177, 838, -194, -188, -185, - /* 10 */ -183, 82, 183, -65, 133, 245, 346, 407, 458, -178, - /* 20 */ 75, -275, -4, 310, 312, 489, 575, 596, 463, 686, - /* 30 */ 707, 725, 780, 1098, 856, 778, 1059, 1090, 708, 887, - /* 40 */ 86, 448, 980, 630, 680, 681, 684, 796, 801, 796, - /* 50 */ 801, -261, -261, -261, -261, -261, -261, -261, -261, -261, - /* 60 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - /* 70 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - /* 80 */ -261, -261, -261, -261, -261, -261, -261, -261, 391, 886, - /* 90 */ 888, 1013, 1016, 1081, 1087, 1151, 1159, 1177, 1185, 1188, - /* 100 */ 1190, 1194, 1197, 1203, 1247, 1260, 1264, 1267, 1269, 1273, - /* 110 */ 1315, 1322, 1335, 1337, 1356, 1362, 1418, 1425, 1453, 1457, - /* 120 */ 1465, 1473, 1487, 1495, 1507, 1517, 1521, 1534, 1543, 1546, - /* 130 */ 1549, 1552, 1554, 1560, 1581, 1590, 1593, 1595, 1621, 1623, - /* 140 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - /* 150 */ -261, -186, -117, 260, 263, 460, 631, -74, 497, -181, - /* 160 */ -261, 939, 176, 274, 338, 676, -261, -261, -261, -261, - /* 170 */ -212, -212, -212, -184, 149, 777, 1061, 1103, 265, 419, - /* 180 */ -254, 670, 677, 677, -11, -129, 184, 488, 736, 789, - /* 190 */ 805, 844, 403, 529, 579, 668, 783, 841, 1158, 1112, - /* 200 */ 806, 861, 1095, 846, 839, 1031, -189, 1077, 1080, 1116, - /* 210 */ 1084, 1156, 1139, 1221, 46, 1099, 1037, 1118, 1171, 1214, - /* 220 */ 1210, 1258, -210, -190, -176, -115, 117, 262, 376, 490, - /* 230 */ 511, 520, 618, 639, 743, 901, 907, 958, 1014, 1055, - /* 240 */ 1108, 1193, 1244, 720, 1248, 1277, 1324, 1347, 1417, 1431, - /* 250 */ 1432, 1440, 1451, 1452, 1463, 1478, 1286, 1350, 1369, 1490, - /* 260 */ 1498, 1501, 773, 1509, 1513, 1528, 1292, 1367, 1535, 1536, - /* 270 */ 1477, 1542, 376, 1547, 1550, 1555, 1559, 1568, 1571, 1441, - /* 280 */ 1443, 1474, 1511, 1516, 1519, 1522, 773, 1474, 1474, 1503, - /* 290 */ 1567, 1594, 1484, 1527, 1556, 1570, 1557, 1524, 1573, 1545, - /* 300 */ 1548, 1576, 1561, 1587, 1540, 1575, 1606, 1611, 1622, 1624, - /* 310 */ 1626, 1582, 1597, 1598, 1599, 1601, 1603, 1563, 1608, 1605, - /* 320 */ 1604, 1564, 1566, 1655, 1660, 1578, 1579, 1665, 1586, 1607, - /* 330 */ 1610, 1642, 1641, 1645, 1646, 1647, 1679, 1688, 1644, 1618, - /* 340 */ 1619, 1648, 1628, 1659, 1649, 1663, 1650, 1700, 1702, 1612, - /* 350 */ 1615, 1706, 1708, 1689, 1709, 1712, 1713, 1715, 1691, 1698, - /* 360 */ 1699, 1701, 1693, 1704, 1707, 1705, 1714, 1703, 1718, 1710, - /* 370 */ 1720, 1721, 1632, 1634, 1664, 1666, 1729, 1751, 1635, 1636, - /* 380 */ 1692, 1694, 1716, 1722, 1684, 1763, 1685, 1723, 1724, 1727, - /* 390 */ 1730, 1768, 1783, 1784, 1793, 1794, 1796, 1683, 1686, 1690, - /* 400 */ 1782, 1779, 1780, 1781, 1785, 1788, 1774, 1775, 1786, 1787, - /* 410 */ 1789, 1798, + /* 0 */ -66, 217, -63, -177, -180, 161, 364, 64, -183, 162, + /* 10 */ 223, 367, 414, -173, 473, 514, 525, 622, 626, -207, + /* 20 */ 351, -276, -38, 693, 811, 831, 833, 888, -188, 945, + /* 30 */ 947, 416, 558, 951, 867, 287, 1078, 1080, -186, 224, + /* 40 */ -132, 42, 964, 269, 417, 796, 810, -237, -231, -237, + /* 50 */ -231, -45, -45, -45, -45, -45, -45, -45, -45, -45, + /* 60 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + /* 70 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + /* 80 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, 895, + /* 90 */ 925, 967, 980, 1100, 1143, 1169, 1203, 1225, 1228, 1242, + /* 100 */ 1247, 1250, 1253, 1255, 1261, 1267, 1272, 1275, 1283, 1286, + /* 110 */ 1288, 1292, 1333, 1335, 1347, 1349, 1352, 1354, 1360, 1366, + /* 120 */ 1381, 1391, 1406, 1408, 1413, 1416, 1418, 1422, 1425, 1427, + /* 130 */ 1463, 1465, 1472, 1478, 1480, 1491, 1498, 1500, 1517, 1519, + /* 140 */ 1528, 1536, -45, -45, -45, -45, -45, -45, -45, -45, + /* 150 */ -45, -45, -45, 312, -158, 285, -219, 9, 166, 370, + /* 160 */ 545, 707, -45, 930, 601, 963, 1067, 792, -45, -45, + /* 170 */ -45, -45, -204, -204, -204, 369, -171, -129, 632, 678, + /* 180 */ 202, 352, -270, 412, 627, 627, -9, 122, 415, 419, + /* 190 */ -56, 248, 583, 920, 6, 261, 459, 795, 1049, 813, + /* 200 */ 1062, 1082, -161, 778, 1063, 797, 870, 1003, 1128, 443, + /* 210 */ 1031, 1072, 1191, 1192, 957, 1120, 105, 1149, 523, 933, + /* 220 */ 1218, 1238, 1254, 1251, -138, 96, 117, 146, 181, 277, + /* 230 */ 280, 421, 480, 712, 830, 850, 1085, 1099, 1129, 1209, + /* 240 */ 1323, 1331, 1336, 1364, 1407, 368, 1412, 1433, 1438, 1474, + /* 250 */ 1481, 1505, 1506, 1526, 1538, 1544, 1545, 1546, 722, 764, + /* 260 */ 856, 1547, 1548, 1550, 1188, 1554, 1557, 1561, 1298, 1260, + /* 270 */ 1562, 1456, 1564, 280, 1568, 1571, 1573, 1574, 1575, 1576, + /* 280 */ 1457, 1477, 1520, 1514, 1515, 1516, 1518, 1188, 1520, 1520, + /* 290 */ 1530, 1563, 1584, 1482, 1504, 1510, 1534, 1513, 1488, 1537, + /* 300 */ 1512, 1521, 1539, 1522, 1541, 1493, 1583, 1559, 1565, 1585, + /* 310 */ 1587, 1589, 1529, 1531, 1532, 1549, 1558, 1566, 1535, 1577, + /* 320 */ 1582, 1622, 1533, 1540, 1627, 1628, 1552, 1555, 1633, 1560, + /* 330 */ 1578, 1581, 1607, 1606, 1608, 1609, 1611, 1649, 1655, 1612, + /* 340 */ 1590, 1591, 1613, 1594, 1621, 1614, 1623, 1616, 1666, 1668, + /* 350 */ 1579, 1593, 1672, 1675, 1656, 1676, 1679, 1680, 1688, 1660, + /* 360 */ 1667, 1670, 1671, 1663, 1669, 1673, 1674, 1689, 1681, 1692, + /* 370 */ 1677, 1693, 1694, 1592, 1599, 1617, 1620, 1700, 1713, 1596, + /* 380 */ 1598, 1658, 1659, 1691, 1684, 1654, 1735, 1664, 1697, 1690, + /* 390 */ 1701, 1703, 1748, 1758, 1760, 1768, 1770, 1772, 1657, 1661, + /* 400 */ 1665, 1761, 1754, 1757, 1762, 1763, 1764, 1750, 1751, 1765, + /* 410 */ 1771, 1767, 1775, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, @@ -173647,57 +174975,57 @@ static const YYACTIONTYPE yy_default[] = { /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, - /* 60 */ 1492, 1493, 1254, 1254, 1254, 1543, 1545, 1508, 1420, 1419, - /* 70 */ 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, 1486, - /* 80 */ 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, 1254, + /* 60 */ 1492, 1493, 1254, 1254, 1254, 1254, 1543, 1545, 1508, 1420, + /* 70 */ 1419, 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, + /* 80 */ 1486, 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 140 */ 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, 1456, 1458, - /* 150 */ 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, 1254, 1254, - /* 160 */ 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, 1473, 1472, - /* 170 */ 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, 1254, 1254, - /* 180 */ 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 140 */ 1254, 1254, 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, + /* 150 */ 1456, 1458, 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, + /* 160 */ 1254, 1254, 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, + /* 170 */ 1473, 1472, 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, + /* 180 */ 1254, 1254, 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 200 */ 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, 1578, 1578, - /* 210 */ 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, 1358, 1358, - /* 220 */ 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, 1546, 1254, - /* 240 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 200 */ 1254, 1254, 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, + /* 210 */ 1578, 1578, 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, + /* 220 */ 1358, 1358, 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, + /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, + /* 240 */ 1546, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, 1254, 1254, - /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, 1254, - /* 280 */ 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, 1357, - /* 290 */ 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, 1423, - /* 300 */ 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, 1397, - /* 310 */ 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, 1357, - /* 320 */ 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, 1638, - /* 330 */ 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, 1638, - /* 340 */ 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, 1525, - /* 350 */ 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, 1330, - /* 360 */ 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, 1319, - /* 370 */ 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, 1588, - /* 380 */ 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, 1401, - /* 390 */ 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, 1558, - /* 400 */ 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, 1288, - /* 410 */ 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, 1254, - /* 420 */ 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1564, - /* 440 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 450 */ 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, 1254, - /* 460 */ 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, 1254, - /* 470 */ 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, 1254, - /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, 1254, - /* 490 */ 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, 1254, + /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, + /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, + /* 280 */ 1254, 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, + /* 290 */ 1357, 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, + /* 300 */ 1423, 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, + /* 310 */ 1397, 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, + /* 320 */ 1357, 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, + /* 330 */ 1638, 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, + /* 340 */ 1638, 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, + /* 350 */ 1525, 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, + /* 360 */ 1330, 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, + /* 370 */ 1319, 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, + /* 380 */ 1588, 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, + /* 390 */ 1401, 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, + /* 400 */ 1558, 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, + /* 410 */ 1288, 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, + /* 420 */ 1254, 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, + /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 440 */ 1564, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 450 */ 1254, 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, + /* 460 */ 1254, 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, + /* 470 */ 1254, 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, + /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, + /* 490 */ 1254, 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 510 */ 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 510 */ 1254, 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 530 */ 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, 1254, + /* 530 */ 1254, 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 550 */ 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, 1254, - /* 560 */ 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 550 */ 1254, 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, + /* 560 */ 1254, 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, /* 580 */ 1266, 1254, 1254, }; @@ -173721,52 +175049,53 @@ static const YYACTIONTYPE yy_default[] = { static const YYCODETYPE yyFallback[] = { 0, /* $ => nothing */ 0, /* SEMI => nothing */ - 59, /* EXPLAIN => ID */ - 59, /* QUERY => ID */ - 59, /* PLAN => ID */ - 59, /* BEGIN => ID */ + 60, /* EXPLAIN => ID */ + 60, /* QUERY => ID */ + 60, /* PLAN => ID */ + 60, /* BEGIN => ID */ 0, /* TRANSACTION => nothing */ - 59, /* DEFERRED => ID */ - 59, /* IMMEDIATE => ID */ - 59, /* EXCLUSIVE => ID */ + 60, /* DEFERRED => ID */ + 60, /* IMMEDIATE => ID */ + 60, /* EXCLUSIVE => ID */ 0, /* COMMIT => nothing */ - 59, /* END => ID */ - 59, /* ROLLBACK => ID */ - 59, /* SAVEPOINT => ID */ - 59, /* RELEASE => ID */ + 60, /* END => ID */ + 60, /* ROLLBACK => ID */ + 60, /* SAVEPOINT => ID */ + 60, /* RELEASE => ID */ 0, /* TO => nothing */ 0, /* TABLE => nothing */ 0, /* CREATE => nothing */ - 59, /* IF => ID */ + 60, /* IF => ID */ 0, /* NOT => nothing */ 0, /* EXISTS => nothing */ - 59, /* TEMP => ID */ + 60, /* TEMP => ID */ 0, /* LP => nothing */ 0, /* RP => nothing */ 0, /* AS => nothing */ 0, /* COMMA => nothing */ - 59, /* WITHOUT => ID */ - 59, /* ABORT => ID */ - 59, /* ACTION => ID */ - 59, /* AFTER => ID */ - 59, /* ANALYZE => ID */ - 59, /* ASC => ID */ - 59, /* ATTACH => ID */ - 59, /* BEFORE => ID */ - 59, /* BY => ID */ - 59, /* CASCADE => ID */ - 59, /* CAST => ID */ - 59, /* CONFLICT => ID */ - 59, /* DATABASE => ID */ - 59, /* DESC => ID */ - 59, /* DETACH => ID */ - 59, /* EACH => ID */ - 59, /* FAIL => ID */ + 60, /* WITHOUT => ID */ + 60, /* ABORT => ID */ + 60, /* ACTION => ID */ + 60, /* AFTER => ID */ + 60, /* ANALYZE => ID */ + 60, /* ASC => ID */ + 60, /* ATTACH => ID */ + 60, /* BEFORE => ID */ + 60, /* BY => ID */ + 60, /* CASCADE => ID */ + 60, /* CAST => ID */ + 60, /* CONFLICT => ID */ + 60, /* DATABASE => ID */ + 60, /* DESC => ID */ + 60, /* DETACH => ID */ + 60, /* EACH => ID */ + 60, /* FAIL => ID */ 0, /* OR => nothing */ 0, /* AND => nothing */ 0, /* IS => nothing */ - 59, /* MATCH => ID */ - 59, /* LIKE_KW => ID */ + 0, /* ISNOT => nothing */ + 60, /* MATCH => ID */ + 60, /* LIKE_KW => ID */ 0, /* BETWEEN => nothing */ 0, /* IN => nothing */ 0, /* ISNULL => nothing */ @@ -173779,47 +175108,47 @@ static const YYCODETYPE yyFallback[] = { 0, /* GE => nothing */ 0, /* ESCAPE => nothing */ 0, /* ID => nothing */ - 59, /* COLUMNKW => ID */ - 59, /* DO => ID */ - 59, /* FOR => ID */ - 59, /* IGNORE => ID */ - 59, /* INITIALLY => ID */ - 59, /* INSTEAD => ID */ - 59, /* NO => ID */ - 59, /* KEY => ID */ - 59, /* OF => ID */ - 59, /* OFFSET => ID */ - 59, /* PRAGMA => ID */ - 59, /* RAISE => ID */ - 59, /* RECURSIVE => ID */ - 59, /* REPLACE => ID */ - 59, /* RESTRICT => ID */ - 59, /* ROW => ID */ - 59, /* ROWS => ID */ - 59, /* TRIGGER => ID */ - 59, /* VACUUM => ID */ - 59, /* VIEW => ID */ - 59, /* VIRTUAL => ID */ - 59, /* WITH => ID */ - 59, /* NULLS => ID */ - 59, /* FIRST => ID */ - 59, /* LAST => ID */ - 59, /* CURRENT => ID */ - 59, /* FOLLOWING => ID */ - 59, /* PARTITION => ID */ - 59, /* PRECEDING => ID */ - 59, /* RANGE => ID */ - 59, /* UNBOUNDED => ID */ - 59, /* EXCLUDE => ID */ - 59, /* GROUPS => ID */ - 59, /* OTHERS => ID */ - 59, /* TIES => ID */ - 59, /* GENERATED => ID */ - 59, /* ALWAYS => ID */ - 59, /* MATERIALIZED => ID */ - 59, /* REINDEX => ID */ - 59, /* RENAME => ID */ - 59, /* CTIME_KW => ID */ + 60, /* COLUMNKW => ID */ + 60, /* DO => ID */ + 60, /* FOR => ID */ + 60, /* IGNORE => ID */ + 60, /* INITIALLY => ID */ + 60, /* INSTEAD => ID */ + 60, /* NO => ID */ + 60, /* KEY => ID */ + 60, /* OF => ID */ + 60, /* OFFSET => ID */ + 60, /* PRAGMA => ID */ + 60, /* RAISE => ID */ + 60, /* RECURSIVE => ID */ + 60, /* REPLACE => ID */ + 60, /* RESTRICT => ID */ + 60, /* ROW => ID */ + 60, /* ROWS => ID */ + 60, /* TRIGGER => ID */ + 60, /* VACUUM => ID */ + 60, /* VIEW => ID */ + 60, /* VIRTUAL => ID */ + 60, /* WITH => ID */ + 60, /* NULLS => ID */ + 60, /* FIRST => ID */ + 60, /* LAST => ID */ + 60, /* CURRENT => ID */ + 60, /* FOLLOWING => ID */ + 60, /* PARTITION => ID */ + 60, /* PRECEDING => ID */ + 60, /* RANGE => ID */ + 60, /* UNBOUNDED => ID */ + 60, /* EXCLUDE => ID */ + 60, /* GROUPS => ID */ + 60, /* OTHERS => ID */ + 60, /* TIES => ID */ + 60, /* GENERATED => ID */ + 60, /* ALWAYS => ID */ + 60, /* MATERIALIZED => ID */ + 60, /* REINDEX => ID */ + 60, /* RENAME => ID */ + 60, /* CTIME_KW => ID */ 0, /* ANY => nothing */ 0, /* BITAND => nothing */ 0, /* BITOR => nothing */ @@ -173890,7 +175219,6 @@ static const YYCODETYPE yyFallback[] = { 0, /* AGG_FUNCTION => nothing */ 0, /* AGG_COLUMN => nothing */ 0, /* TRUEFALSE => nothing */ - 0, /* ISNOT => nothing */ 0, /* FUNCTION => nothing */ 0, /* UPLUS => nothing */ 0, /* UMINUS => nothing */ @@ -174034,132 +175362,132 @@ static const char *const yyTokenName[] = { /* 43 */ "OR", /* 44 */ "AND", /* 45 */ "IS", - /* 46 */ "MATCH", - /* 47 */ "LIKE_KW", - /* 48 */ "BETWEEN", - /* 49 */ "IN", - /* 50 */ "ISNULL", - /* 51 */ "NOTNULL", - /* 52 */ "NE", - /* 53 */ "EQ", - /* 54 */ "GT", - /* 55 */ "LE", - /* 56 */ "LT", - /* 57 */ "GE", - /* 58 */ "ESCAPE", - /* 59 */ "ID", - /* 60 */ "COLUMNKW", - /* 61 */ "DO", - /* 62 */ "FOR", - /* 63 */ "IGNORE", - /* 64 */ "INITIALLY", - /* 65 */ "INSTEAD", - /* 66 */ "NO", - /* 67 */ "KEY", - /* 68 */ "OF", - /* 69 */ "OFFSET", - /* 70 */ "PRAGMA", - /* 71 */ "RAISE", - /* 72 */ "RECURSIVE", - /* 73 */ "REPLACE", - /* 74 */ "RESTRICT", - /* 75 */ "ROW", - /* 76 */ "ROWS", - /* 77 */ "TRIGGER", - /* 78 */ "VACUUM", - /* 79 */ "VIEW", - /* 80 */ "VIRTUAL", - /* 81 */ "WITH", - /* 82 */ "NULLS", - /* 83 */ "FIRST", - /* 84 */ "LAST", - /* 85 */ "CURRENT", - /* 86 */ "FOLLOWING", - /* 87 */ "PARTITION", - /* 88 */ "PRECEDING", - /* 89 */ "RANGE", - /* 90 */ "UNBOUNDED", - /* 91 */ "EXCLUDE", - /* 92 */ "GROUPS", - /* 93 */ "OTHERS", - /* 94 */ "TIES", - /* 95 */ "GENERATED", - /* 96 */ "ALWAYS", - /* 97 */ "MATERIALIZED", - /* 98 */ "REINDEX", - /* 99 */ "RENAME", - /* 100 */ "CTIME_KW", - /* 101 */ "ANY", - /* 102 */ "BITAND", - /* 103 */ "BITOR", - /* 104 */ "LSHIFT", - /* 105 */ "RSHIFT", - /* 106 */ "PLUS", - /* 107 */ "MINUS", - /* 108 */ "STAR", - /* 109 */ "SLASH", - /* 110 */ "REM", - /* 111 */ "CONCAT", - /* 112 */ "PTR", - /* 113 */ "COLLATE", - /* 114 */ "BITNOT", - /* 115 */ "ON", - /* 116 */ "INDEXED", - /* 117 */ "STRING", - /* 118 */ "JOIN_KW", - /* 119 */ "CONSTRAINT", - /* 120 */ "DEFAULT", - /* 121 */ "NULL", - /* 122 */ "PRIMARY", - /* 123 */ "UNIQUE", - /* 124 */ "CHECK", - /* 125 */ "REFERENCES", - /* 126 */ "AUTOINCR", - /* 127 */ "INSERT", - /* 128 */ "DELETE", - /* 129 */ "UPDATE", - /* 130 */ "SET", - /* 131 */ "DEFERRABLE", - /* 132 */ "FOREIGN", - /* 133 */ "DROP", - /* 134 */ "UNION", - /* 135 */ "ALL", - /* 136 */ "EXCEPT", - /* 137 */ "INTERSECT", - /* 138 */ "SELECT", - /* 139 */ "VALUES", - /* 140 */ "DISTINCT", - /* 141 */ "DOT", - /* 142 */ "FROM", - /* 143 */ "JOIN", - /* 144 */ "USING", - /* 145 */ "ORDER", - /* 146 */ "GROUP", - /* 147 */ "HAVING", - /* 148 */ "LIMIT", - /* 149 */ "WHERE", - /* 150 */ "RETURNING", - /* 151 */ "INTO", - /* 152 */ "NOTHING", - /* 153 */ "FLOAT", - /* 154 */ "BLOB", - /* 155 */ "INTEGER", - /* 156 */ "VARIABLE", - /* 157 */ "CASE", - /* 158 */ "WHEN", - /* 159 */ "THEN", - /* 160 */ "ELSE", - /* 161 */ "INDEX", - /* 162 */ "ALTER", - /* 163 */ "ADD", - /* 164 */ "WINDOW", - /* 165 */ "OVER", - /* 166 */ "FILTER", - /* 167 */ "COLUMN", - /* 168 */ "AGG_FUNCTION", - /* 169 */ "AGG_COLUMN", - /* 170 */ "TRUEFALSE", - /* 171 */ "ISNOT", + /* 46 */ "ISNOT", + /* 47 */ "MATCH", + /* 48 */ "LIKE_KW", + /* 49 */ "BETWEEN", + /* 50 */ "IN", + /* 51 */ "ISNULL", + /* 52 */ "NOTNULL", + /* 53 */ "NE", + /* 54 */ "EQ", + /* 55 */ "GT", + /* 56 */ "LE", + /* 57 */ "LT", + /* 58 */ "GE", + /* 59 */ "ESCAPE", + /* 60 */ "ID", + /* 61 */ "COLUMNKW", + /* 62 */ "DO", + /* 63 */ "FOR", + /* 64 */ "IGNORE", + /* 65 */ "INITIALLY", + /* 66 */ "INSTEAD", + /* 67 */ "NO", + /* 68 */ "KEY", + /* 69 */ "OF", + /* 70 */ "OFFSET", + /* 71 */ "PRAGMA", + /* 72 */ "RAISE", + /* 73 */ "RECURSIVE", + /* 74 */ "REPLACE", + /* 75 */ "RESTRICT", + /* 76 */ "ROW", + /* 77 */ "ROWS", + /* 78 */ "TRIGGER", + /* 79 */ "VACUUM", + /* 80 */ "VIEW", + /* 81 */ "VIRTUAL", + /* 82 */ "WITH", + /* 83 */ "NULLS", + /* 84 */ "FIRST", + /* 85 */ "LAST", + /* 86 */ "CURRENT", + /* 87 */ "FOLLOWING", + /* 88 */ "PARTITION", + /* 89 */ "PRECEDING", + /* 90 */ "RANGE", + /* 91 */ "UNBOUNDED", + /* 92 */ "EXCLUDE", + /* 93 */ "GROUPS", + /* 94 */ "OTHERS", + /* 95 */ "TIES", + /* 96 */ "GENERATED", + /* 97 */ "ALWAYS", + /* 98 */ "MATERIALIZED", + /* 99 */ "REINDEX", + /* 100 */ "RENAME", + /* 101 */ "CTIME_KW", + /* 102 */ "ANY", + /* 103 */ "BITAND", + /* 104 */ "BITOR", + /* 105 */ "LSHIFT", + /* 106 */ "RSHIFT", + /* 107 */ "PLUS", + /* 108 */ "MINUS", + /* 109 */ "STAR", + /* 110 */ "SLASH", + /* 111 */ "REM", + /* 112 */ "CONCAT", + /* 113 */ "PTR", + /* 114 */ "COLLATE", + /* 115 */ "BITNOT", + /* 116 */ "ON", + /* 117 */ "INDEXED", + /* 118 */ "STRING", + /* 119 */ "JOIN_KW", + /* 120 */ "CONSTRAINT", + /* 121 */ "DEFAULT", + /* 122 */ "NULL", + /* 123 */ "PRIMARY", + /* 124 */ "UNIQUE", + /* 125 */ "CHECK", + /* 126 */ "REFERENCES", + /* 127 */ "AUTOINCR", + /* 128 */ "INSERT", + /* 129 */ "DELETE", + /* 130 */ "UPDATE", + /* 131 */ "SET", + /* 132 */ "DEFERRABLE", + /* 133 */ "FOREIGN", + /* 134 */ "DROP", + /* 135 */ "UNION", + /* 136 */ "ALL", + /* 137 */ "EXCEPT", + /* 138 */ "INTERSECT", + /* 139 */ "SELECT", + /* 140 */ "VALUES", + /* 141 */ "DISTINCT", + /* 142 */ "DOT", + /* 143 */ "FROM", + /* 144 */ "JOIN", + /* 145 */ "USING", + /* 146 */ "ORDER", + /* 147 */ "GROUP", + /* 148 */ "HAVING", + /* 149 */ "LIMIT", + /* 150 */ "WHERE", + /* 151 */ "RETURNING", + /* 152 */ "INTO", + /* 153 */ "NOTHING", + /* 154 */ "FLOAT", + /* 155 */ "BLOB", + /* 156 */ "INTEGER", + /* 157 */ "VARIABLE", + /* 158 */ "CASE", + /* 159 */ "WHEN", + /* 160 */ "THEN", + /* 161 */ "ELSE", + /* 162 */ "INDEX", + /* 163 */ "ALTER", + /* 164 */ "ADD", + /* 165 */ "WINDOW", + /* 166 */ "OVER", + /* 167 */ "FILTER", + /* 168 */ "COLUMN", + /* 169 */ "AGG_FUNCTION", + /* 170 */ "AGG_COLUMN", + /* 171 */ "TRUEFALSE", /* 172 */ "FUNCTION", /* 173 */ "UPLUS", /* 174 */ "UMINUS", @@ -174597,7 +175925,7 @@ static const char *const yyRuleName[] = { /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", /* 278 */ "trigger_cmd ::= scanpt select scanpt", /* 279 */ "expr ::= RAISE LP IGNORE RP", - /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 280 */ "expr ::= RAISE LP raisetype COMMA expr RP", /* 281 */ "raisetype ::= ROLLBACK", /* 282 */ "raisetype ::= ABORT", /* 283 */ "raisetype ::= FAIL", @@ -175522,7 +176850,7 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ 293, /* (278) trigger_cmd ::= scanpt select scanpt */ 218, /* (279) expr ::= RAISE LP IGNORE RP */ - 218, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + 218, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ 237, /* (281) raisetype ::= ROLLBACK */ 237, /* (282) raisetype ::= ABORT */ 237, /* (283) raisetype ::= FAIL */ @@ -175936,7 +177264,7 @@ static const signed char yyRuleInfoNRhs[] = { -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -3, /* (278) trigger_cmd ::= scanpt select scanpt */ -4, /* (279) expr ::= RAISE LP IGNORE RP */ - -6, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + -6, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ -1, /* (281) raisetype ::= ROLLBACK */ -1, /* (282) raisetype ::= ABORT */ -1, /* (283) raisetype ::= FAIL */ @@ -176574,11 +177902,21 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-5].minor.yy203 ){ SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; SrcItem *pOld = yymsp[-3].minor.yy203->a; + assert( pOld->fg.fixedSchema==0 ); pNew->zName = pOld->zName; - pNew->zDatabase = pOld->zDatabase; - pNew->pSelect = pOld->pSelect; - if( pNew->pSelect && (pNew->pSelect->selFlags & SF_NestedFrom)!=0 ){ - pNew->fg.isNestedFrom = 1; + assert( pOld->fg.fixedSchema==0 ); + if( pOld->fg.isSubquery ){ + pNew->fg.isSubquery = 1; + pNew->u4.pSubq = pOld->u4.pSubq; + pOld->u4.pSubq = 0; + pOld->fg.isSubquery = 0; + assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 ); + if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){ + pNew->fg.isNestedFrom = 1; + } + }else{ + pNew->u4.zDatabase = pOld->u4.zDatabase; + pOld->u4.zDatabase = 0; } if( pOld->fg.isTabFunc ){ pNew->u1.pFuncArg = pOld->u1.pFuncArg; @@ -176586,8 +177924,7 @@ static YYACTIONTYPE yy_reduce( pOld->fg.isTabFunc = 0; pNew->fg.isTabFunc = 1; } - pOld->zName = pOld->zDatabase = 0; - pOld->pSelect = 0; + pOld->zName = 0; } sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); }else{ @@ -177321,9 +178658,9 @@ static YYACTIONTYPE yy_reduce( } } break; - case 280: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */ { - yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + yymsp[-5].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy454, 0); if( yymsp[-5].minor.yy454 ) { yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144; } @@ -179914,32 +181251,6 @@ SQLITE_API char *sqlite3_temp_directory = 0; */ SQLITE_API char *sqlite3_data_directory = 0; -/* -** Determine whether or not high-precision (long double) floating point -** math works correctly on CPU currently running. -*/ -static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){ - if( sizeof(LONGDOUBLE_TYPE)<=8 ){ - /* If the size of "long double" is not more than 8, then - ** high-precision math is not possible. */ - return 0; - }else{ - /* Just because sizeof(long double)>8 does not mean that the underlying - ** hardware actually supports high-precision floating point. For example, - ** clearing the 0x100 bit in the floating-point control word on Intel - ** processors will make long double work like double, even though long - ** double takes up more space. The only way to determine if long double - ** actually works is to run an experiment. */ - LONGDOUBLE_TYPE a, b, c; - rc++; - a = 1.0+rc*0.1; - b = 1.0e+18+rc*25.0; - c = a+b; - return b!=c; - } -} - - /* ** Initialize SQLite. ** @@ -180134,13 +181445,6 @@ SQLITE_API int sqlite3_initialize(void){ rc = SQLITE_EXTRA_INIT(0); } #endif - - /* Experimentally determine if high-precision floating point is - ** available. */ -#ifndef SQLITE_OMIT_WSD - sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc); -#endif - return rc; } @@ -181681,7 +182985,8 @@ SQLITE_PRIVATE int sqlite3CreateFunc( assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY ); extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY| - SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE); + SQLITE_SUBTYPE|SQLITE_INNOCUOUS| + SQLITE_RESULT_SUBTYPE|SQLITE_SELFORDER1); enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY); /* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But @@ -183236,6 +184541,7 @@ static int openDatabase( if( ((1<<(flags&7)) & 0x46)==0 ){ rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ }else{ + if( zFilename==0 ) zFilename = ":memory:"; rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); } if( rc!=SQLITE_OK ){ @@ -184148,6 +185454,18 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } + /* sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, sqlite3 *db, int *N) + ** + ** Write the current optimization settings into *N. A zero bit means that + ** the optimization is on, and a 1 bit means that the optimization is off. + */ + case SQLITE_TESTCTRL_GETOPT: { + sqlite3 *db = va_arg(ap, sqlite3*); + int *pN = va_arg(ap, int*); + *pN = db->dbOptFlags; + break; + } + /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); ** ** If parameter onoff is 1, subsequent calls to localtime() fail. @@ -184379,24 +185697,6 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } -#if !defined(SQLITE_OMIT_WSD) - /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X); - ** - ** X<0 Make no changes to the bUseLongDouble. Just report value. - ** X==0 Disable bUseLongDouble - ** X==1 Enable bUseLongDouble - ** X>=2 Set bUseLongDouble to its default value for this platform - */ - case SQLITE_TESTCTRL_USELONGDOUBLE: { - int b = va_arg(ap, int); - if( b>=2 ) b = hasHighPrecisionDouble(b); - if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; - rc = sqlite3Config.bUseLongDouble!=0; - break; - } -#endif - - #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) ** @@ -184704,7 +186004,11 @@ SQLITE_API int sqlite3_snapshot_get( if( iDb==0 || iDb>1 ){ Btree *pBt = db->aDb[iDb].pBt; if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){ + Pager *pPager = sqlite3BtreePager(pBt); + i64 dummy = 0; + sqlite3PagerSnapshotOpen(pPager, (sqlite3_snapshot*)&dummy); rc = sqlite3BtreeBeginTrans(pBt, 0, 0); + sqlite3PagerSnapshotOpen(pPager, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); } @@ -195475,11 +196779,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( #ifdef SQLITE_TEST -#if defined(INCLUDE_SQLITE_TCL_H) -# include "sqlite_tcl.h" -#else -# include "tcl.h" -#endif +#include "tclsqlite.h" /* #include */ /* @@ -202706,6 +204006,7 @@ static int fts3SnippetNextCandidate(SnippetIter *pIter){ return 1; } + assert( pIter->nSnippet>=0 ); pIter->iCurrent = iStart = iEnd - pIter->nSnippet + 1; for(i=0; inPhrase; i++){ SnippetPhrase *pPhrase = &pIter->aPhrase[i]; @@ -207699,7 +209000,9 @@ static u32 jsonLookupStep( zPath++; if( zPath[0]=='"' ){ zKey = zPath + 1; - for(i=1; zPath[i] && zPath[i]!='"'; i++){} + for(i=1; zPath[i] && zPath[i]!='"'; i++){ + if( zPath[i]=='\\' && zPath[i+1]!=0 ) i++; + } nKey = i-1; if( zPath[i] ){ i++; @@ -208709,10 +210012,16 @@ static void jsonExtractFunc( ** NUMBER ==> $[NUMBER] // PG compatible ** LABEL ==> $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + ** + ** Updated 2024-05-27: If the NUMBER is negative, then PG counts from + ** the right of the array. Hence for negative NUMBER: + ** + ** NUMBER ==> $[#NUMBER] // PG compatible */ jsonStringInit(&jx, ctx); if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ jsonAppendRawNZ(&jx, "[", 1); + if( zPath[0]=='-' ) jsonAppendRawNZ(&jx,"#",1); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "]", 2); }else if( jsonAllAlphanum(zPath, nPath) ){ @@ -218454,6 +219763,27 @@ struct RbuFrame { u32 iWalFrame; }; +#ifndef UNUSED_PARAMETER +/* +** The following macros are used to suppress compiler warnings and to +** make it clear to human readers when a function parameter is deliberately +** left unused within the body of a function. This usually happens when +** a function is called via a function pointer. For example the +** implementation of an SQL aggregate step callback may not use the +** parameter indicating the number of arguments passed to the aggregate, +** if it knows that this is enforced elsewhere. +** +** When a function parameter is not used at all within the body of a function, +** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. +** However, these macros may also be used to suppress warnings related to +** parameters that may or may not be used depending on compilation options. +** For example those parameters only used in assert() statements. In these +** cases the parameters are named as per the usual conventions. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) +#endif + /* ** RBU handle. ** @@ -218505,7 +219835,7 @@ struct sqlite3rbu { int rc; /* Value returned by last rbu_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ int nStep; /* Rows processed for current object */ - int nProgress; /* Rows processed for all objects */ + sqlite3_int64 nProgress; /* Rows processed for all objects */ RbuObjIter objiter; /* Iterator for skipping through tbl/idx */ const char *zVfsName; /* Name of automatically created rbu vfs */ rbu_file *pTargetFd; /* File handle open on target db */ @@ -218622,7 +219952,7 @@ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){ v = (v<<6) + c; } z--; - *pLen -= z - zStart; + *pLen -= (int)(z - zStart); *pz = (char*)z; return v; } @@ -218807,6 +220137,7 @@ static void rbuFossilDeltaFunc( char *aOut; assert( argc==2 ); + UNUSED_PARAMETER(argc); nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); @@ -220386,13 +221717,13 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ else if( c==')' ){ nParen--; if( nParen==0 ){ - int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; + int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); pIter->aIdxCol[iIdxCol++].nSpan = nSpan; i++; break; } }else if( c==',' && nParen==1 ){ - int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan; + int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan); pIter->aIdxCol[iIdxCol++].nSpan = nSpan; pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1]; }else if( c=='"' || c=='\'' || c=='`' ){ @@ -221082,6 +222413,8 @@ static void rbuFileSuffix3(const char *zBase, char *z){ for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); } +#else + UNUSED_PARAMETER2(zBase,z); #endif } @@ -221666,7 +222999,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){ "(%d, %Q), " "(%d, %Q), " "(%d, %d), " - "(%d, %d), " + "(%d, %lld), " "(%d, %lld), " "(%d, %lld), " "(%d, %lld), " @@ -222024,6 +223357,7 @@ static void rbuIndexCntFunc( sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain); assert( nVal==1 ); + UNUSED_PARAMETER(nVal); rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, sqlite3_mprintf("SELECT count(*) FROM sqlite_schema " @@ -222299,7 +223633,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( ){ if( zTarget==0 ){ return rbuMisuseError(); } if( zState ){ - int n = strlen(zState); + size_t n = strlen(zState); if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ return rbuMisuseError(); } @@ -222516,6 +223850,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ */ static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){ int rc = SQLITE_OK; + UNUSED_PARAMETER(pArg); #if defined(_WIN32_WCE) { LPWSTR zWideOld; @@ -223420,6 +224755,9 @@ static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ ** No-op. */ static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){ + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(a); + UNUSED_PARAMETER(b); return 0; } @@ -223818,6 +225156,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ pIdxInfo->orderByConsumed = 1; pIdxInfo->idxNum |= 0x08; } + pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_HEX; return SQLITE_OK; } @@ -224475,7 +225814,13 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } ** ** The data field of sqlite_dbpage table can be updated. The new ** value must be a BLOB which is the correct page size, otherwise the -** update fails. Rows may not be deleted or inserted. +** update fails. INSERT operations also work, and operate as if they +** where REPLACE. The size of the database can be extended by INSERT-ing +** new pages on the end. +** +** Rows may not be deleted. However, doing an INSERT to page number N +** with NULL page data causes the N-th page and all subsequent pages to be +** deleted and the database to be truncated. */ /* #include "sqliteInt.h" ** Requires access to internal data structures ** */ @@ -224498,6 +225843,8 @@ struct DbpageCursor { struct DbpageTable { sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* The database */ + int iDbTrunc; /* Database to truncate */ + Pgno pgnoTrunc; /* Size to truncate to */ }; /* Columns */ @@ -224506,7 +225853,6 @@ struct DbpageTable { #define DBPAGE_COLUMN_SCHEMA 2 - /* ** Connect to or create a dbpagevfs virtual table. */ @@ -224768,11 +226114,11 @@ static int dbpageUpdate( DbPage *pDbPage = 0; int rc = SQLITE_OK; char *zErr = 0; - const char *zSchema; int iDb; Btree *pBt; Pager *pPager; int szPage; + int isInsert; (void)pRowid; if( pTab->db->flags & SQLITE_Defensive ){ @@ -224783,21 +226129,29 @@ static int dbpageUpdate( zErr = "cannot delete"; goto update_fail; } - pgno = sqlite3_value_int(argv[0]); - if( sqlite3_value_type(argv[0])==SQLITE_NULL - || (Pgno)sqlite3_value_int(argv[1])!=pgno - ){ - zErr = "cannot insert"; - goto update_fail; + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ + pgno = (Pgno)sqlite3_value_int(argv[2]); + isInsert = 1; + }else{ + pgno = sqlite3_value_int(argv[0]); + if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ + zErr = "cannot insert"; + goto update_fail; + } + isInsert = 0; } - zSchema = (const char*)sqlite3_value_text(argv[4]); - iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1; - if( NEVER(iDb<0) ){ - zErr = "no such schema"; - goto update_fail; + if( sqlite3_value_type(argv[4])==SQLITE_NULL ){ + iDb = 0; + }else{ + const char *zSchema = (const char*)sqlite3_value_text(argv[4]); + iDb = sqlite3FindDbName(pTab->db, zSchema); + if( iDb<0 ){ + zErr = "no such schema"; + goto update_fail; + } } pBt = pTab->db->aDb[iDb].pBt; - if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){ + if( pgno<1 || NEVER(pBt==0) ){ zErr = "bad page number"; goto update_fail; } @@ -224805,18 +226159,25 @@ static int dbpageUpdate( if( sqlite3_value_type(argv[3])!=SQLITE_BLOB || sqlite3_value_bytes(argv[3])!=szPage ){ - zErr = "bad page value"; - goto update_fail; + if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){ + /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and + ** all subsequent pages to be deleted. */ + pTab->iDbTrunc = iDb; + pgno--; + pTab->pgnoTrunc = pgno; + }else{ + zErr = "bad page value"; + goto update_fail; + } } pPager = sqlite3BtreePager(pBt); rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); if( rc==SQLITE_OK ){ const void *pData = sqlite3_value_blob(argv[3]); - assert( pData!=0 || pTab->db->mallocFailed ); - if( pData - && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK - ){ - memcpy(sqlite3PagerGetData(pDbPage), pData, szPage); + if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){ + unsigned char *aPage = sqlite3PagerGetData(pDbPage); + memcpy(aPage, pData, szPage); + pTab->pgnoTrunc = 0; } } sqlite3PagerUnref(pDbPage); @@ -224840,9 +226201,31 @@ static int dbpageBegin(sqlite3_vtab *pVtab){ Btree *pBt = db->aDb[i].pBt; if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0); } + pTab->pgnoTrunc = 0; return SQLITE_OK; } +/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT +*/ +static int dbpageSync(sqlite3_vtab *pVtab){ + DbpageTable *pTab = (DbpageTable *)pVtab; + if( pTab->pgnoTrunc>0 ){ + Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; + Pager *pPager = sqlite3BtreePager(pBt); + sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); + } + pTab->pgnoTrunc = 0; + return SQLITE_OK; +} + +/* Cancel any pending truncate. +*/ +static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ + DbpageTable *pTab = (DbpageTable *)pVtab; + pTab->pgnoTrunc = 0; + (void)notUsed1; + return SQLITE_OK; +} /* ** Invoke this routine to register the "dbpage" virtual table module @@ -224864,14 +226247,14 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ dbpageRowid, /* xRowid - read data */ dbpageUpdate, /* xUpdate */ dbpageBegin, /* xBegin */ - 0, /* xSync */ + dbpageSync, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ - 0, /* xRollbackTo */ + dbpageRollbackTo, /* xRollbackTo */ 0, /* xShadowName */ 0 /* xIntegrity */ }; @@ -224959,6 +226342,10 @@ struct SessionBuffer { ** input data. Input data may be supplied either as a single large buffer ** (e.g. sqlite3changeset_start()) or using a stream function (e.g. ** sqlite3changeset_start_strm()). +** +** bNoDiscard: +** If true, then the only time data is discarded is as a result of explicit +** sessionDiscardData() calls. Not within every sessionInputBuffer() call. */ struct SessionInput { int bNoDiscard; /* If true, do not discard in InputBuffer() */ @@ -226642,16 +228029,19 @@ static void sessionPreupdateOneChange( for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ - TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); - assert( trc==SQLITE_OK ); + /* This may fail if the column has a non-NULL default and was added + ** using ALTER TABLE ADD COLUMN after this record was created. */ + rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p); }else if( pTab->abPK[i] ){ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); assert( trc==SQLITE_OK ); } - /* This may fail if SQLite value p contains a utf-16 string that must - ** be converted to utf-8 and an OOM error occurs while doing so. */ - rc = sessionSerializeValue(0, p, &nByte); + if( rc==SQLITE_OK ){ + /* This may fail if SQLite value p contains a utf-16 string that must + ** be converted to utf-8 and an OOM error occurs while doing so. */ + rc = sessionSerializeValue(0, p, &nByte); + } if( rc!=SQLITE_OK ) goto error_out; } if( pTab->bRowid ){ @@ -230009,15 +231399,21 @@ static int sessionChangesetApply( int nTab = 0; /* Result of sqlite3Strlen30(zTab) */ SessionApplyCtx sApply; /* changeset_apply() context object */ int bPatchset; + u64 savedFlag = db->flags & SQLITE_FkNoAction; assert( xConflict!=0 ); + sqlite3_mutex_enter(sqlite3_db_mutex(db)); + if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ + db->flags |= ((u64)SQLITE_FkNoAction); + db->aDb[0].pSchema->schema_cookie -= 32; + } + pIter->in.bNoDiscard = 1; memset(&sApply, 0, sizeof(sApply)); sApply.bRebase = (ppRebase && pnRebase); sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); - sqlite3_mutex_enter(sqlite3_db_mutex(db)); if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); } @@ -230179,6 +231575,12 @@ static int sessionChangesetApply( sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); sqlite3_free((char*)sApply.rebase.aBuf); + + if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ + assert( db->flags & SQLITE_FkNoAction ); + db->flags &= ~((u64)SQLITE_FkNoAction); + db->aDb[0].pSchema->schema_cookie -= 32; + } sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } @@ -230207,12 +231609,6 @@ SQLITE_API int sqlite3changeset_apply_v2( sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1); - u64 savedFlag = db->flags & SQLITE_FkNoAction; - - if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ - db->flags |= ((u64)SQLITE_FkNoAction); - db->aDb[0].pSchema->schema_cookie -= 32; - } if( rc==SQLITE_OK ){ rc = sessionChangesetApply( @@ -230220,11 +231616,6 @@ SQLITE_API int sqlite3changeset_apply_v2( ); } - if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ - assert( db->flags & SQLITE_FkNoAction ); - db->flags &= ~((u64)SQLITE_FkNoAction); - db->aDb[0].pSchema->schema_cookie -= 32; - } return rc; } @@ -230545,6 +231936,9 @@ static int sessionChangesetExtendRecord( sessionAppendBlob(pOut, aRec, nRec, &rc); if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); + if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){ + rc = sqlite3_errcode(pGrp->db); + } } for(ii=nCol; rc==SQLITE_OK && iinCol; ii++){ int eType = sqlite3_column_type(pTab->pDfltStmt, ii); @@ -230561,6 +231955,7 @@ static int sessionChangesetExtendRecord( } if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); + pOut->nBuf += 8; } break; } @@ -230700,6 +232095,8 @@ static int sessionOneChangeToHash( u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; + assert( nRec>0 ); + /* Ensure that only changesets, or only patchsets, but not a mixture ** of both, are being combined. It is an error to try to combine a ** changeset and a patchset. */ @@ -230777,6 +232174,7 @@ static int sessionChangesetToHash( int nRec; int rc = SQLITE_OK; + pIter->in.bNoDiscard = 1; while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ rc = sessionOneChangeToHash(pGrp, pIter, bRebase); if( rc!=SQLITE_OK ) break; @@ -231658,6 +233056,10 @@ struct Fts5PhraseIter { ** (i.e. if it is a contentless table), then this API always iterates ** through an empty set (all calls to xPhraseFirst() set iCol to -1). ** +** In all cases, matches are visited in (column ASC, offset ASC) order. +** i.e. all those in column 0, sorted by offset, followed by those in +** column 1, etc. +** ** xPhraseNext() ** See xPhraseFirst above. ** @@ -231724,9 +233126,32 @@ struct Fts5PhraseIter { ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. +** +** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the locale associated +** with column iCol of the current row. Usually, there is no associated +** locale, and output parameters (*pzLocale) and (*pnLocale) are set +** to NULL and 0, respectively. However, if the fts5_locale() function +** was used to associate a locale with the value when it was inserted +** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated +** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) +** is set to the size in bytes of the buffer, not including the +** nul-terminator. +** +** If successful, SQLITE_OK is returned. Or, if an error occurs, an +** SQLite error code is returned. The final value of the output parameters +** is undefined in this case. +** +** xTokenize_v2: +** Tokenize text using the tokenizer belonging to the FTS5 table. This +** API is the same as the xTokenize() API, except that it allows a tokenizer +** locale to be specified. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); @@ -231768,6 +233193,15 @@ struct Fts5ExtensionApi { const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); + + /* Below this point are iVersion>=4 only */ + int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); + int (*xTokenize_v2)(Fts5Context*, + const char *pText, int nText, /* Text to tokenize */ + const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ + void *pCtx, /* Context passed to xToken() */ + int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ + ); }; /* @@ -231788,7 +233222,7 @@ struct Fts5ExtensionApi { ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) -** pointer provided by the application when the fts5_tokenizer object +** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the @@ -231812,7 +233246,7 @@ struct Fts5ExtensionApi { ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** -** The second argument indicates the reason that FTS5 is requesting +** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** @@ -231836,6 +233270,13 @@ struct Fts5ExtensionApi { ** on a columnsize=0 database. ** ** +** The sixth and seventh arguments passed to xTokenize() - pLocale and +** nLocale - are a pointer to a buffer containing the locale to use for +** tokenization (e.g. "en_US") and its size in bytes, respectively. The +** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in +** which case nLocale is always 0) to indicate that the tokenizer should +** use its default locale. +** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth @@ -231859,6 +233300,30 @@ struct Fts5ExtensionApi { ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** +** If the tokenizer is registered using an fts5_tokenizer_v2 object, +** then the xTokenize() method has two additional arguments - pLocale +** and nLocale. These specify the locale that the tokenizer should use +** for the current request. If pLocale and nLocale are both 0, then the +** tokenizer should use its default locale. Otherwise, pLocale points to +** an nLocale byte buffer containing the name of the locale to use as utf-8 +** text. pLocale is not nul-terminated. +** +** FTS5_TOKENIZER +** +** There is also an fts5_tokenizer object. This is an older, deprecated, +** version of fts5_tokenizer_v2. It is similar except that: +** +**
    +**
  • There is no "iVersion" field, and +**
  • The xTokenize() method does not take a locale argument. +**
+** +** Legacy fts5_tokenizer tokenizers must be registered using the +** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). +** +** Tokenizer implementations registered using either API may be retrieved +** using both xFindTokenizer() and xFindTokenizer_v2(). +** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a @@ -231967,6 +233432,33 @@ struct Fts5ExtensionApi { ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; +typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; +struct fts5_tokenizer_v2 { + int iVersion; /* Currently always 2 */ + + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); + void (*xDelete)(Fts5Tokenizer*); + int (*xTokenize)(Fts5Tokenizer*, + void *pCtx, + int flags, /* Mask of FTS5_TOKENIZE_* flags */ + const char *pText, int nText, + const char *pLocale, int nLocale, + int (*xToken)( + void *pCtx, /* Copy of 2nd argument to xTokenize() */ + int tflags, /* Mask of FTS5_TOKEN_* flags */ + const char *pToken, /* Pointer to buffer containing token */ + int nToken, /* Size of token in bytes */ + int iStart, /* Byte offset of token within input text */ + int iEnd /* Byte offset of end of token within input text */ + ) + ); +}; + +/* +** New code should use the fts5_tokenizer_v2 type to define tokenizer +** implementations. The following type is included for legacy applications +** that still use it. +*/ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); @@ -231986,6 +233478,7 @@ struct fts5_tokenizer { ); }; + /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 @@ -232005,7 +233498,7 @@ struct fts5_tokenizer { */ typedef struct fts5_api fts5_api; struct fts5_api { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( @@ -232032,6 +233525,25 @@ struct fts5_api { fts5_extension_function xFunction, void (*xDestroy)(void*) ); + + /* APIs below this point are only available if iVersion>=3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void *pUserData, + fts5_tokenizer_v2 *pTokenizer, + void (*xDestroy)(void*) + ); + + /* Find an existing tokenizer */ + int (*xFindTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void **ppUserData, + fts5_tokenizer_v2 **ppTokenizer + ); }; /* @@ -232105,6 +233617,22 @@ typedef sqlite3_uint64 u64; # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) +/* The uptr type is an unsigned integer large enough to hold a pointer +*/ +#if defined(HAVE_STDINT_H) + typedef uintptr_t uptr; +#elif SQLITE_PTRSIZE==4 + typedef u32 uptr; +#else + typedef u64 uptr; +#endif + +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) +#else +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) +#endif + #endif /* Truncate very long tokens to this many bytes. Hard limit is @@ -232188,6 +233716,18 @@ struct Fts5Colset { */ typedef struct Fts5Config Fts5Config; +typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; + +struct Fts5TokenizerConfig { + Fts5Tokenizer *pTok; + fts5_tokenizer_v2 *pApi2; + fts5_tokenizer *pApi1; + const char **azArg; + int nArg; + int ePattern; /* FTS_PATTERN_XXX constant */ + const char *pLocale; /* Current locale to use */ + int nLocale; /* Size of pLocale in bytes */ +}; /* ** An instance of the following structure encodes all information that can @@ -232227,9 +233767,12 @@ typedef struct Fts5Config Fts5Config; ** ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); ** +** bLocale: +** Set to true if locale=1 was specified when the table was created. */ struct Fts5Config { sqlite3 *db; /* Database handle */ + Fts5Global *pGlobal; /* Global fts5 object for handle db */ char *zDb; /* Database holding FTS index (e.g. "main") */ char *zName; /* Name of FTS index */ int nCol; /* Number of columns */ @@ -232239,16 +233782,17 @@ struct Fts5Config { int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ + int bContentlessUnindexed; /* "contentless_unindexed=" option (dflt=0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ + int bLocale; /* "locale=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; - Fts5Tokenizer *pTok; - fts5_tokenizer *pTokApi; + Fts5TokenizerConfig t; int bLock; /* True when table is preparing statement */ - int ePattern; /* FTS_PATTERN_XXX constant */ + /* Values loaded from the %_config table */ int iVersion; /* fts5 file format 'version' */ @@ -232277,9 +233821,10 @@ struct Fts5Config { #define FTS5_CURRENT_VERSION 4 #define FTS5_CURRENT_VERSION_SECUREDELETE 5 -#define FTS5_CONTENT_NORMAL 0 -#define FTS5_CONTENT_NONE 1 -#define FTS5_CONTENT_EXTERNAL 2 +#define FTS5_CONTENT_NORMAL 0 +#define FTS5_CONTENT_NONE 1 +#define FTS5_CONTENT_EXTERNAL 2 +#define FTS5_CONTENT_UNINDEXED 3 #define FTS5_DETAIL_FULL 0 #define FTS5_DETAIL_NONE 1 @@ -232314,6 +233859,8 @@ static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, i static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); +static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...); + /* ** End of interface to code in fts5_config.c. **************************************************************************/ @@ -232358,7 +233905,7 @@ static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...); static void sqlite3Fts5Put32(u8*, int); static int sqlite3Fts5Get32(const u8*); -#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) +#define FTS5_POS2COLUMN(iPos) (int)((iPos >> 32) & 0x7FFFFFFF) #define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) typedef struct Fts5PoslistReader Fts5PoslistReader; @@ -232643,18 +234190,20 @@ struct Fts5Table { Fts5Index *pIndex; /* Full-text index */ }; -static int sqlite3Fts5GetTokenizer( - Fts5Global*, - const char **azArg, - int nArg, - Fts5Config*, - char **pzErr -); +static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); static int sqlite3Fts5FlushToDisk(Fts5Table*); +static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); +static void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); + +static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); +static int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, + const char **ppText, int *pnText, const char **ppLoc, int *pnLoc +); + /* ** End of interface to code in fts5.c. **************************************************************************/ @@ -232734,8 +234283,8 @@ static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); static int sqlite3Fts5DropAll(Fts5Config*); static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); -static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**); -static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); +static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); +static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, int, sqlite3_value**, i64*); static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); @@ -232760,6 +234309,9 @@ static int sqlite3Fts5StorageOptimize(Fts5Storage *p); static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); static int sqlite3Fts5StorageReset(Fts5Storage *p); +static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*); +static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel); + /* ** End of interface to code in fts5_storage.c. **************************************************************************/ @@ -232912,6 +234464,7 @@ static int sqlite3Fts5TokenizerPattern( int (*xCreate)(void*, const char**, int, Fts5Tokenizer**), Fts5Tokenizer *pTok ); +static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig*); /* ** End of interface to code in fts5_tokenizer.c. **************************************************************************/ @@ -234689,6 +236242,7 @@ static int fts5HighlightCb( return rc; } + /* ** Implementation of highlight() function. */ @@ -234719,12 +236273,19 @@ static void fts5HighlightFunction( sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = SQLITE_OK; }else if( ctx.zIn ){ + const char *pLoc = 0; /* Locale of column iCol */ + int nLoc = 0; /* Size of pLoc in bytes */ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); } if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); + rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc); + } + if( rc==SQLITE_OK ){ + rc = pApi->xTokenize_v2( + pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb + ); } if( ctx.bOpen ){ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); @@ -234921,6 +236482,8 @@ static void fts5SnippetFunction( memset(&sFinder, 0, sizeof(Fts5SFinder)); for(i=0; ixColumnText(pFts, i, &sFinder.zDoc, &nDoc); if( rc!=SQLITE_OK ) break; - rc = pApi->xTokenize(pFts, - sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb + rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc); + if( rc!=SQLITE_OK ) break; + rc = pApi->xTokenize_v2(pFts, + sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb ); if( rc!=SQLITE_OK ) break; rc = pApi->xColumnSize(pFts, i, &nDocsize); @@ -234987,6 +236552,9 @@ static void fts5SnippetFunction( rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); } if( ctx.zIn ){ + const char *pLoc = 0; /* Locale of column iBestCol */ + int nLoc = 0; /* Bytes in pLoc */ + if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); } @@ -235005,7 +236573,12 @@ static void fts5SnippetFunction( } if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); + rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc); + } + if( rc==SQLITE_OK ){ + rc = pApi->xTokenize_v2( + pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb + ); } if( ctx.bOpen ){ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); @@ -235189,6 +236762,53 @@ static void fts5Bm25Function( } } +/* +** Implementation of fts5_get_locale() function. +*/ +static void fts5GetLocaleFunction( + const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ + Fts5Context *pFts, /* First arg to pass to pApi functions */ + sqlite3_context *pCtx, /* Context for returning result/error */ + int nVal, /* Number of values in apVal[] array */ + sqlite3_value **apVal /* Array of trailing arguments */ +){ + int iCol = 0; + int eType = 0; + int rc = SQLITE_OK; + const char *zLocale = 0; + int nLocale = 0; + + /* xColumnLocale() must be available */ + assert( pApi->iVersion>=4 ); + + if( nVal!=1 ){ + const char *z = "wrong number of arguments to function fts5_get_locale()"; + sqlite3_result_error(pCtx, z, -1); + return; + } + + eType = sqlite3_value_numeric_type(apVal[0]); + if( eType!=SQLITE_INTEGER ){ + const char *z = "non-integer argument passed to function fts5_get_locale()"; + sqlite3_result_error(pCtx, z, -1); + return; + } + + iCol = sqlite3_value_int(apVal[0]); + if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){ + sqlite3_result_error_code(pCtx, SQLITE_RANGE); + return; + } + + rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale); + if( rc!=SQLITE_OK ){ + sqlite3_result_error_code(pCtx, rc); + return; + } + + sqlite3_result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT); +} + static int sqlite3Fts5AuxInit(fts5_api *pApi){ struct Builtin { const char *zFunc; /* Function name (nul-terminated) */ @@ -235196,9 +236816,10 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ fts5_extension_function xFunc;/* Callback function */ void (*xDestroy)(void*); /* Destructor function */ } aBuiltin [] = { - { "snippet", 0, fts5SnippetFunction, 0 }, - { "highlight", 0, fts5HighlightFunction, 0 }, - { "bm25", 0, fts5Bm25Function, 0 }, + { "snippet", 0, fts5SnippetFunction, 0 }, + { "highlight", 0, fts5HighlightFunction, 0 }, + { "bm25", 0, fts5Bm25Function, 0 }, + { "fts5_get_locale", 0, fts5GetLocaleFunction, 0 }, }; int rc = SQLITE_OK; /* Return code */ int i; /* To iterate through builtin functions */ @@ -235863,7 +237484,6 @@ static int fts5ConfigSetEnum( ** eventually free any such error message using sqlite3_free(). */ static int fts5ConfigParseSpecial( - Fts5Global *pGlobal, Fts5Config *pConfig, /* Configuration object to update */ const char *zCmd, /* Special command to parse */ const char *zArg, /* Argument to parse */ @@ -235871,6 +237491,7 @@ static int fts5ConfigParseSpecial( ){ int rc = SQLITE_OK; int nCmd = (int)strlen(zCmd); + if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; const char *p; @@ -235927,12 +237548,11 @@ static int fts5ConfigParseSpecial( if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ const char *p = (const char*)zArg; sqlite3_int64 nArg = strlen(zArg) + 1; - char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); - char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); - char *pSpace = pDel; + char **azArg = sqlite3Fts5MallocZero(&rc, (sizeof(char*) + 2) * nArg); - if( azArg && pSpace ){ - if( pConfig->pTok ){ + if( azArg ){ + char *pSpace = (char*)&azArg[nArg]; + if( pConfig->t.azArg ){ *pzErr = sqlite3_mprintf("multiple tokenize=... directives"); rc = SQLITE_ERROR; }else{ @@ -235955,16 +237575,14 @@ static int fts5ConfigParseSpecial( *pzErr = sqlite3_mprintf("parse error in tokenize directive"); rc = SQLITE_ERROR; }else{ - rc = sqlite3Fts5GetTokenizer(pGlobal, - (const char**)azArg, (int)nArg, pConfig, - pzErr - ); + pConfig->t.azArg = (const char**)azArg; + pConfig->t.nArg = nArg; + azArg = 0; } } } - sqlite3_free(azArg); - sqlite3_free(pDel); + return rc; } @@ -235993,6 +237611,16 @@ static int fts5ConfigParseSpecial( return rc; } + if( sqlite3_strnicmp("contentless_unindexed", zCmd, nCmd)==0 ){ + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ + *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive"); + rc = SQLITE_ERROR; + }else{ + pConfig->bContentlessUnindexed = (zArg[0]=='1'); + } + return rc; + } + if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ if( pConfig->zContentRowid ){ *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); @@ -236013,6 +237641,16 @@ static int fts5ConfigParseSpecial( return rc; } + if( sqlite3_strnicmp("locale", zCmd, nCmd)==0 ){ + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ + *pzErr = sqlite3_mprintf("malformed locale=... directive"); + rc = SQLITE_ERROR; + }else{ + pConfig->bLocale = (zArg[0]=='1'); + } + return rc; + } + if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ const Fts5Enum aDetail[] = { { "none", FTS5_DETAIL_NONE }, @@ -236041,16 +237679,6 @@ static int fts5ConfigParseSpecial( return SQLITE_ERROR; } -/* -** Allocate an instance of the default tokenizer ("simple") at -** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error -** code if an error occurs. -*/ -static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ - assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); - return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0); -} - /* ** Gobble up the first bareword or quoted word from the input buffer zIn. ** Return a pointer to the character immediately following the last in @@ -236110,7 +237738,8 @@ static int fts5ConfigParseColumn( Fts5Config *p, char *zCol, char *zArg, - char **pzErr + char **pzErr, + int *pbUnindexed ){ int rc = SQLITE_OK; if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) @@ -236121,6 +237750,7 @@ static int fts5ConfigParseColumn( }else if( zArg ){ if( 0==sqlite3_stricmp(zArg, "unindexed") ){ p->abUnindexed[p->nCol] = 1; + *pbUnindexed = 1; }else{ *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg); rc = SQLITE_ERROR; @@ -236141,11 +237771,26 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){ sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid); if( p->eContent!=FTS5_CONTENT_NONE ){ + assert( p->eContent==FTS5_CONTENT_EXTERNAL + || p->eContent==FTS5_CONTENT_NORMAL + || p->eContent==FTS5_CONTENT_UNINDEXED + ); for(i=0; inCol; i++){ if( p->eContent==FTS5_CONTENT_EXTERNAL ){ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]); - }else{ + }else if( p->eContent==FTS5_CONTENT_NORMAL || p->abUnindexed[i] ){ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i); + }else{ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); + } + } + } + if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ + for(i=0; inCol; i++){ + if( p->abUnindexed[i]==0 ){ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); + }else{ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); } } } @@ -236179,10 +237824,12 @@ static int sqlite3Fts5ConfigParse( Fts5Config *pRet; /* New object to return */ int i; sqlite3_int64 nByte; + int bUnindexed = 0; /* True if there are one or more UNINDEXED */ *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); if( pRet==0 ) return SQLITE_NOMEM; memset(pRet, 0, sizeof(Fts5Config)); + pRet->pGlobal = pGlobal; pRet->db = db; pRet->iCookie = -1; @@ -236231,13 +237878,13 @@ static int sqlite3Fts5ConfigParse( rc = SQLITE_ERROR; }else{ if( bOption ){ - rc = fts5ConfigParseSpecial(pGlobal, pRet, + rc = fts5ConfigParseSpecial(pRet, ALWAYS(zOne)?zOne:"", zTwo?zTwo:"", pzErr ); }else{ - rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr); + rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr, &bUnindexed); zOne = 0; } } @@ -236269,11 +237916,17 @@ static int sqlite3Fts5ConfigParse( rc = SQLITE_ERROR; } - /* If a tokenizer= option was successfully parsed, the tokenizer has - ** already been allocated. Otherwise, allocate an instance of the default - ** tokenizer (unicode61) now. */ - if( rc==SQLITE_OK && pRet->pTok==0 ){ - rc = fts5ConfigDefaultTokenizer(pGlobal, pRet); + /* We only allow contentless_unindexed=1 if the table is actually a + ** contentless one. + */ + if( rc==SQLITE_OK + && pRet->bContentlessUnindexed + && pRet->eContent!=FTS5_CONTENT_NONE + ){ + *pzErr = sqlite3_mprintf( + "contentless_unindexed=1 requires a contentless table" + ); + rc = SQLITE_ERROR; } /* If no zContent option was specified, fill in the default values. */ @@ -236284,6 +237937,9 @@ static int sqlite3Fts5ConfigParse( ); if( pRet->eContent==FTS5_CONTENT_NORMAL ){ zTail = "content"; + }else if( bUnindexed && pRet->bContentlessUnindexed ){ + pRet->eContent = FTS5_CONTENT_UNINDEXED; + zTail = "content"; }else if( pRet->bColumnsize ){ zTail = "docsize"; } @@ -236317,9 +237973,14 @@ static int sqlite3Fts5ConfigParse( static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ if( pConfig ){ int i; - if( pConfig->pTok ){ - pConfig->pTokApi->xDelete(pConfig->pTok); + if( pConfig->t.pTok ){ + if( pConfig->t.pApi1 ){ + pConfig->t.pApi1->xDelete(pConfig->t.pTok); + }else{ + pConfig->t.pApi2->xDelete(pConfig->t.pTok); + } } + sqlite3_free((char*)pConfig->t.azArg); sqlite3_free(pConfig->zDb); sqlite3_free(pConfig->zName); for(i=0; inCol; i++){ @@ -236394,10 +238055,24 @@ static int sqlite3Fts5Tokenize( void *pCtx, /* Context passed to xToken() */ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ){ - if( pText==0 ) return SQLITE_OK; - return pConfig->pTokApi->xTokenize( - pConfig->pTok, pCtx, flags, pText, nText, xToken - ); + int rc = SQLITE_OK; + if( pText ){ + if( pConfig->t.pTok==0 ){ + rc = sqlite3Fts5LoadTokenizer(pConfig); + } + if( rc==SQLITE_OK ){ + if( pConfig->t.pApi1 ){ + rc = pConfig->t.pApi1->xTokenize( + pConfig->t.pTok, pCtx, flags, pText, nText, xToken + ); + }else{ + rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags, + pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken + ); + } + } + } + return rc; } /* @@ -236651,13 +238326,10 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ rc = SQLITE_ERROR; - if( pConfig->pzErrmsg ){ - assert( 0==*pConfig->pzErrmsg ); - *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format " - "(found %d, expected %d or %d) - run 'rebuild'", - iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE - ); - } + sqlite3Fts5ConfigErrmsg(pConfig, "invalid fts5 file format " + "(found %d, expected %d or %d) - run 'rebuild'", + iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE + ); }else{ pConfig->iVersion = iVersion; } @@ -236668,6 +238340,29 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ return rc; } +/* +** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer +** containing the error message created using printf() style formatting +** string zFmt and its trailing arguments. +*/ +static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ + va_list ap; /* ... printf arguments */ + char *zMsg = 0; + + va_start(ap, zFmt); + zMsg = sqlite3_vmprintf(zFmt, ap); + if( pConfig->pzErrmsg ){ + assert( *pConfig->pzErrmsg==0 ); + *pConfig->pzErrmsg = zMsg; + }else{ + sqlite3_free(zMsg); + } + + va_end(ap); +} + + + /* ** 2014 May 31 ** @@ -236724,7 +238419,7 @@ struct Fts5Expr { /* ** eType: -** Expression node type. Always one of: +** Expression node type. Usually one of: ** ** FTS5_AND (nChild, apChild valid) ** FTS5_OR (nChild, apChild valid) @@ -236732,6 +238427,10 @@ struct Fts5Expr { ** FTS5_STRING (pNear valid) ** FTS5_TERM (pNear valid) ** +** An expression node with eType==0 may also exist. It always matches zero +** rows. This is created when a phrase containing no tokens is parsed. +** e.g. "". +** ** iHeight: ** Distance from this node to furthest leaf. This is always 0 for nodes ** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one @@ -236952,11 +238651,12 @@ static int sqlite3Fts5ExprNew( }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); sqlite3Fts5ParserFree(pEngine, fts5ParseFree); + assert( sParse.pExpr || sParse.rc!=SQLITE_OK ); assert_expr_depth_ok(sParse.rc, sParse.pExpr); /* If the LHS of the MATCH expression was a user column, apply the ** implicit column-filter. */ - if( iColnCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ + if( sParse.rc==SQLITE_OK && iColnCol ){ int n = sizeof(Fts5Colset); Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); if( pColset ){ @@ -236973,15 +238673,7 @@ static int sqlite3Fts5ExprNew( sParse.rc = SQLITE_NOMEM; sqlite3Fts5ParseNodeFree(sParse.pExpr); }else{ - if( !sParse.pExpr ){ - const int nByte = sizeof(Fts5ExprNode); - pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte); - if( pNew->pRoot ){ - pNew->pRoot->bEof = 1; - } - }else{ - pNew->pRoot = sParse.pExpr; - } + pNew->pRoot = sParse.pExpr; pNew->pIndex = 0; pNew->pConfig = pConfig; pNew->apExprPhrase = sParse.apPhrase; @@ -237799,7 +239491,7 @@ static int fts5ExprNodeTest_STRING( } }else{ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; - if( pIter->iRowid==iLast || pIter->bEof ) continue; + if( pIter->iRowid==iLast ) continue; bMatch = 0; if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ return rc; @@ -238321,9 +240013,6 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( Fts5ExprNearset *pRet = 0; if( pParse->rc==SQLITE_OK ){ - if( pPhrase==0 ){ - return pNear; - } if( pNear==0 ){ sqlite3_int64 nByte; nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); @@ -238545,6 +240234,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( }else if( sCtx.pPhrase->nTerm ){ sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; } + assert( pParse->apPhrase!=0 ); pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; } @@ -238564,7 +240254,7 @@ static int sqlite3Fts5ExprClonePhrase( Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ - if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ + if( !pExpr || iPhrase<0 || iPhrase>=pExpr->nPhrase ){ rc = SQLITE_RANGE; }else{ pOrig = pExpr->apExprPhrase[iPhrase]; @@ -238932,6 +240622,9 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){ } } +/* +** Add pSub as a child of p. +*/ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ int ii = p->nChild; if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ @@ -239076,19 +240769,23 @@ static Fts5ExprNode *sqlite3Fts5ParseNode( "fts5: %s queries are not supported (detail!=full)", pNear->nPhrase==1 ? "phrase": "NEAR" ); - sqlite3_free(pRet); + sqlite3Fts5ParseNodeFree(pRet); pRet = 0; + pNear = 0; + assert( pLeft==0 && pRight==0 ); } } }else{ + assert( pNear==0 ); fts5ExprAddChildren(pRet, pLeft); fts5ExprAddChildren(pRet, pRight); + pLeft = pRight = 0; if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ sqlite3Fts5ParseError(pParse, "fts5 expression tree is too large (maximum depth %d)", SQLITE_FTS5_MAX_EXPR_DEPTH ); - sqlite3_free(pRet); + sqlite3Fts5ParseNodeFree(pRet); pRet = 0; } } @@ -239140,6 +240837,8 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( ); if( pRight->eType==FTS5_EOF ){ + assert( pParse->apPhrase!=0 ); + assert( pParse->nPhrase>0 ); assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] ); sqlite3Fts5ParseNodeFree(pRight); pRet = pLeft; @@ -239772,6 +241471,7 @@ static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ pNode->iRowid = iRowid; pNode->bEof = 0; switch( pNode->eType ){ + case 0: case FTS5_TERM: case FTS5_STRING: return (pNode->pNear->apPhrase[0]->poslist.n>0); @@ -241355,11 +243055,12 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ if( rc==SQLITE_OK ){ u8 *aOut = 0; /* Read blob data into this buffer */ int nByte = sqlite3_blob_bytes(p->pReader); - sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; + int szData = (sizeof(Fts5Data) + 7) & ~7; + sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING; pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); if( pRet ){ pRet->nn = nByte; - aOut = pRet->p = (u8*)&pRet[1]; + aOut = pRet->p = (u8*)pRet + szData; }else{ rc = SQLITE_NOMEM; } @@ -241382,6 +243083,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ } assert( (pRet==0)==(p->rc!=SQLITE_OK) ); + assert( pRet==0 || EIGHT_BYTE_ALIGNMENT( pRet->p ) ); return pRet; } @@ -242707,7 +244409,7 @@ static void fts5SegIterNext_None( if( iOffiEndofDoclist ){ /* Next entry is on the current page */ - i64 iDelta; + u64 iDelta; iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); pIter->iLeafOffset = iOff; pIter->iRowid += iDelta; @@ -245411,6 +247113,11 @@ static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){ nBest = nPercent; } } + + /* If pLvl is already the input level to an ongoing merge, look no + ** further for a merge candidate. The caller should be allowed to + ** continue merging from pLvl first. */ + if( pLvl->nMerge ) break; } } return iRet; @@ -249335,7 +251042,7 @@ static int fts5structConnectMethod( /* ** We must have a single struct=? constraint that will be passed through -** into the xFilter method. If there is no valid stmt=? constraint, +** into the xFilter method. If there is no valid struct=? constraint, ** then return an SQLITE_CONSTRAINT error. */ static int fts5structBestIndexMethod( @@ -249677,8 +251384,17 @@ struct Fts5Global { Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ Fts5Cursor *pCsr; /* First in list of all open cursors */ + u32 aLocaleHdr[4]; }; +/* +** Size of header on fts5_locale() values. And macro to access a buffer +** containing a copy of the header from an Fts5Config pointer. +*/ +#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr )) +#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr)) + + /* ** Each auxiliary function registered with the FTS5 module is represented ** by an object of the following type. All such objects are stored as part @@ -249697,11 +251413,28 @@ struct Fts5Auxiliary { ** Each tokenizer module registered with the FTS5 module is represented ** by an object of the following type. All such objects are stored as part ** of the Fts5Global.pTok list. +** +** bV2Native: +** True if the tokenizer was registered using xCreateTokenizer_v2(), false +** for xCreateTokenizer(). If this variable is true, then x2 is populated +** with the routines as supplied by the caller and x1 contains synthesized +** wrapper routines. In this case the user-data pointer passed to +** x1.xCreate should be a pointer to the Fts5TokenizerModule structure, +** not a copy of pUserData. +** +** Of course, if bV2Native is false, then x1 contains the real routines and +** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule +** object should be passed to x2.xCreate. +** +** The synthesized wrapper routines are necessary for xFindTokenizer(_v2) +** calls. */ struct Fts5TokenizerModule { char *zName; /* Name of tokenizer */ void *pUserData; /* User pointer passed to xCreate() */ - fts5_tokenizer x; /* Tokenizer functions */ + int bV2Native; /* True if v2 native tokenizer */ + fts5_tokenizer x1; /* Tokenizer functions */ + fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ void (*xDestroy)(void*); /* Destructor function */ Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ }; @@ -249789,7 +251522,7 @@ struct Fts5Cursor { Fts5Auxiliary *pAux; /* Currently executing extension function */ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ - /* Cache used by auxiliary functions xInst() and xInstCount() */ + /* Cache used by auxiliary API functions xInst() and xInstCount() */ Fts5PoslistReader *aInstIter; /* One for each phrase */ int nInstAlloc; /* Size of aInst[] array (entries / 3) */ int nInstCount; /* Number of phrase instances */ @@ -249900,10 +251633,16 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ #endif /* -** Return true if pTab is a contentless table. +** Return true if pTab is a contentless table. If parameter bIncludeUnindexed +** is true, this includes contentless tables that store UNINDEXED columns +** only. */ -static int fts5IsContentless(Fts5FullTable *pTab){ - return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; +static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){ + int eContent = pTab->p.pConfig->eContent; + return ( + eContent==FTS5_CONTENT_NONE + || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED) + ); } /* @@ -249971,8 +251710,12 @@ static int fts5InitVtab( assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); } if( rc==SQLITE_OK ){ + pConfig->pzErrmsg = pzErr; pTab->p.pConfig = pConfig; pTab->pGlobal = pGlobal; + if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){ + rc = sqlite3Fts5LoadTokenizer(pConfig); + } } /* Open the index sub-system */ @@ -249994,11 +251737,7 @@ static int fts5InitVtab( /* Load the initial configuration */ if( rc==SQLITE_OK ){ - assert( pConfig->pzErrmsg==0 ); - pConfig->pzErrmsg = pzErr; - rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); - sqlite3Fts5IndexRollback(pTab->p.pIndex); - pConfig->pzErrmsg = 0; + rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1); } if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ @@ -250008,6 +251747,7 @@ static int fts5InitVtab( rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); } + if( pConfig ) pConfig->pzErrmsg = 0; if( rc!=SQLITE_OK ){ fts5FreeVtab(pTab); pTab = 0; @@ -250075,10 +251815,10 @@ static int fts5UsePatternMatch( ){ assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB ); assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE ); - if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ + if( pConfig->t.ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){ return 1; } - if( pConfig->ePattern==FTS5_PATTERN_LIKE + if( pConfig->t.ePattern==FTS5_PATTERN_LIKE && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB) ){ return 1; @@ -250125,10 +251865,10 @@ static int fts5UsePatternMatch( ** This function ensures that there is at most one "r" or "=". And that if ** there exists an "=" then there is no "<" or ">". ** -** Costs are assigned as follows: +** If an unusable MATCH operator is present in the WHERE clause, then +** SQLITE_CONSTRAINT is returned. ** -** a) If an unusable MATCH operator is present in the WHERE clause, the -** cost is unconditionally set to 1e50 (a really big number). +** Costs are assigned as follows: ** ** a) If a MATCH operator is present, the cost depends on the other ** constraints also present. As follows: @@ -250161,7 +251901,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ int bSeenEq = 0; int bSeenGt = 0; int bSeenLt = 0; - int bSeenMatch = 0; + int nSeenMatch = 0; int bSeenRank = 0; @@ -250192,18 +251932,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ /* A MATCH operator or equivalent */ if( p->usable==0 || iCol<0 ){ /* As there exists an unusable MATCH constraint this is an - ** unusable plan. Set a prohibitively high cost. */ - pInfo->estimatedCost = 1e50; - assert( iIdxStr < pInfo->nConstraint*6 + 1 ); - idxStr[iIdxStr] = 0; - return SQLITE_OK; + ** unusable plan. Return SQLITE_CONSTRAINT. */ + return SQLITE_CONSTRAINT; }else{ if( iCol==nCol+1 ){ if( bSeenRank ) continue; idxStr[iIdxStr++] = 'r'; bSeenRank = 1; - }else if( iCol>=0 ){ - bSeenMatch = 1; + }else{ + nSeenMatch++; idxStr[iIdxStr++] = 'M'; sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); idxStr += strlen(&idxStr[iIdxStr]); @@ -250220,6 +251957,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ idxStr += strlen(&idxStr[iIdxStr]); pInfo->aConstraintUsage[i].argvIndex = ++iCons; assert( idxStr[iIdxStr]=='\0' ); + nSeenMatch++; }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){ idxStr[iIdxStr++] = '='; bSeenEq = 1; @@ -250256,7 +251994,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ */ if( pInfo->nOrderBy==1 ){ int iSort = pInfo->aOrderBy[0].iColumn; - if( iSort==(pConfig->nCol+1) && bSeenMatch ){ + if( iSort==(pConfig->nCol+1) && nSeenMatch>0 ){ idxFlags |= FTS5_BI_ORDER_RANK; }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ idxFlags |= FTS5_BI_ORDER_ROWID; @@ -250271,14 +252009,17 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ /* Calculate the estimated cost based on the flags set in idxFlags. */ if( bSeenEq ){ - pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0; - if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo); + pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0; + if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo); }else if( bSeenLt && bSeenGt ){ - pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0; + pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0; }else if( bSeenLt || bSeenGt ){ - pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0; + pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0; }else{ - pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0; + pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0; + } + for(i=1; iestimatedCost *= 0.4; } pInfo->idxNum = idxFlags; @@ -250554,6 +252295,7 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ } }else{ rc = SQLITE_OK; + CsrFlagSet(pCsr, FTS5CSR_REQUIRE_DOCSIZE); } break; } @@ -250583,7 +252325,7 @@ static int fts5PrepareStatement( rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pRet, 0); if( rc!=SQLITE_OK ){ - *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); + sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsg(pConfig->db)); } sqlite3_free(zSql); } @@ -250807,6 +252549,145 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ return iDefault; } +/* +** Set the error message on the virtual table passed as the first argument. +*/ +static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ + va_list ap; /* ... printf arguments */ + va_start(ap, zFormat); + sqlite3_free(p->p.base.zErrMsg); + p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); + va_end(ap); +} + +/* +** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale +** specified by pLocale/nLocale. The buffer indicated by pLocale must remain +** valid until after the final call to sqlite3Fts5Tokenize() that will use +** the locale. +*/ +static void sqlite3Fts5SetLocale( + Fts5Config *pConfig, + const char *zLocale, + int nLocale +){ + Fts5TokenizerConfig *pT = &pConfig->t; + pT->pLocale = zLocale; + pT->nLocale = nLocale; +} + +/* +** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). +*/ +static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ + sqlite3Fts5SetLocale(pConfig, 0, 0); +} + +/* +** Return true if the value passed as the only argument is an +** fts5_locale() value. +*/ +static int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ + int ret = 0; + if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ + /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case. + ** If the blob was created using zeroblob(), then sqlite3_value_blob() + ** may call malloc(). If this malloc() fails, then the values returned + ** by both value_blob() and value_bytes() will be 0. If value_bytes() were + ** called first, then the NULL pointer returned by value_blob() might + ** be dereferenced. */ + const u8 *pBlob = sqlite3_value_blob(pVal); + int nBlob = sqlite3_value_bytes(pVal); + if( nBlob>FTS5_LOCALE_HDR_SIZE + && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE) + ){ + ret = 1; + } + } + return ret; +} + +/* +** Value pVal is guaranteed to be an fts5_locale() value, according to +** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale +** from the value and returns them separately. +** +** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set +** to point to buffers containing the text and locale, as utf-8, +** respectively. In this case output parameters (*pnText) and (*pnLoc) are +** set to the sizes in bytes of these two buffers. +** +** Or, if an error occurs, then an SQLite error code is returned. The final +** value of the four output parameters is undefined in this case. +*/ +static int sqlite3Fts5DecodeLocaleValue( + sqlite3_value *pVal, + const char **ppText, + int *pnText, + const char **ppLoc, + int *pnLoc +){ + const char *p = sqlite3_value_blob(pVal); + int n = sqlite3_value_bytes(pVal); + int nLoc = 0; + + assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); + assert( n>FTS5_LOCALE_HDR_SIZE ); + + for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ + if( nLoc==(n-1) ){ + return SQLITE_MISMATCH; + } + } + *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; + *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; + + *ppText = &p[nLoc+1]; + *pnText = n - nLoc - 1; + return SQLITE_OK; +} + +/* +** Argument pVal is the text of a full-text search expression. It may or +** may not have been wrapped by fts5_locale(). This function extracts +** the text of the expression, and sets output variable (*pzText) to +** point to a nul-terminated buffer containing the expression. +** +** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called +** to set the tokenizer to use the specified locale. +** +** If output variable (*pbFreeAndReset) is set to true, then the caller +** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer +** locale, and (b) call sqlite3_free() to free (*pzText). +*/ +static int fts5ExtractExprText( + Fts5Config *pConfig, /* Fts5 configuration */ + sqlite3_value *pVal, /* Value to extract expression text from */ + char **pzText, /* OUT: nul-terminated buffer of text */ + int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ +){ + int rc = SQLITE_OK; + + if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + const char *pText = 0; + int nText = 0; + const char *pLoc = 0; + int nLoc = 0; + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText); + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + } + *pbFreeAndReset = 1; + }else{ + *pzText = (char*)sqlite3_value_text(pVal); + *pbFreeAndReset = 0; + } + + return rc; +} + + /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional @@ -250841,13 +252722,7 @@ static int fts5FilterMethod( int iIdxStr = 0; Fts5Expr *pExpr = 0; - if( pConfig->bLock ){ - pTab->p.base.zErrMsg = sqlite3_mprintf( - "recursively defined fts5 content table" - ); - return SQLITE_ERROR; - } - + assert( pConfig->bLock==0 ); if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); @@ -250871,8 +252746,14 @@ static int fts5FilterMethod( pRank = apVal[i]; break; case 'M': { - const char *zText = (const char*)sqlite3_value_text(apVal[i]); + char *zText = 0; + int bFreeAndReset = 0; + int bInternal = 0; + + rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); + if( rc!=SQLITE_OK ) goto filter_out; if( zText==0 ) zText = ""; + iCol = 0; do{ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); @@ -250884,7 +252765,7 @@ static int fts5FilterMethod( ** indicates that the MATCH expression is not a full text query, ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); - goto filter_out; + bInternal = 1; }else{ char **pzErr = &pTab->p.base.zErrMsg; rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); @@ -250892,9 +252773,15 @@ static int fts5FilterMethod( rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); pExpr = 0; } - if( rc!=SQLITE_OK ) goto filter_out; } + if( bFreeAndReset ){ + sqlite3_free(zText); + sqlite3Fts5ClearLocale(pConfig); + } + + if( bInternal || rc!=SQLITE_OK ) goto filter_out; + break; } case 'L': @@ -250982,9 +252869,7 @@ static int fts5FilterMethod( } } }else if( pConfig->zContent==0 ){ - *pConfig->pzErrmsg = sqlite3_mprintf( - "%s: table does not support scanning", pConfig->zName - ); + fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName); rc = SQLITE_ERROR; }else{ /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup @@ -251027,9 +252912,13 @@ static i64 fts5CursorRowid(Fts5Cursor *pCsr){ assert( pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE + || pCsr->ePlan==FTS5_PLAN_SCAN + || pCsr->ePlan==FTS5_PLAN_ROWID ); if( pCsr->pSorter ){ return pCsr->pSorter->iRowid; + }else if( pCsr->ePlan>=FTS5_PLAN_SCAN ){ + return sqlite3_column_int64(pCsr->pStmt, 0); }else{ return sqlite3Fts5ExprRowid(pCsr->pExpr); } @@ -251046,25 +252935,16 @@ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ int ePlan = pCsr->ePlan; assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); - switch( ePlan ){ - case FTS5_PLAN_SPECIAL: - *pRowid = 0; - break; - - case FTS5_PLAN_SOURCE: - case FTS5_PLAN_MATCH: - case FTS5_PLAN_SORTED_MATCH: - *pRowid = fts5CursorRowid(pCsr); - break; - - default: - *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); - break; + if( ePlan==FTS5_PLAN_SPECIAL ){ + *pRowid = 0; + }else{ + *pRowid = fts5CursorRowid(pCsr); } return SQLITE_OK; } + /* ** If the cursor requires seeking (bSeekRequired flag is set), seek it. ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. @@ -251101,8 +252981,13 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ rc = sqlite3_reset(pCsr->pStmt); if( rc==SQLITE_OK ){ rc = FTS5_CORRUPT; + fts5SetVtabError((Fts5FullTable*)pTab, + "fts5: missing row %lld from content table %s", + fts5CursorRowid(pCsr), + pTab->pConfig->zContent + ); }else if( pTab->pConfig->pzErrmsg ){ - *pTab->pConfig->pzErrmsg = sqlite3_mprintf( + fts5SetVtabError((Fts5FullTable*)pTab, "%s", sqlite3_errmsg(pTab->pConfig->db) ); } @@ -251111,14 +252996,6 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ return rc; } -static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ - va_list ap; /* ... printf arguments */ - va_start(ap, zFormat); - assert( p->p.base.zErrMsg==0 ); - p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); - va_end(ap); -} - /* ** This function is called to handle an FTS INSERT command. In other words, ** an INSERT statement of the form: @@ -251156,7 +253033,7 @@ static int fts5SpecialInsert( } bLoadConfig = 1; }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ - if( pConfig->eContent==FTS5_CONTENT_NONE ){ + if( fts5IsContentless(pTab, 1) ){ fts5SetVtabError(pTab, "'rebuild' may not be used with a contentless fts5 table" ); @@ -251212,7 +253089,7 @@ static int fts5SpecialDelete( int eType1 = sqlite3_value_type(apVal[1]); if( eType1==SQLITE_INTEGER ){ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]); + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0); } return rc; } @@ -251225,7 +253102,7 @@ static void fts5StorageInsert( ){ int rc = *pRc; if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid); } if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); @@ -251233,6 +253110,67 @@ static void fts5StorageInsert( *pRc = rc; } +/* +** +** This function is called when the user attempts an UPDATE on a contentless +** table. Parameter bRowidModified is true if the UPDATE statement modifies +** the rowid value. Parameter apVal[] contains the new values for each user +** defined column of the fts5 table. pConfig is the configuration object of the +** table being updated (guaranteed to be contentless). The contentless_delete=1 +** and contentless_unindexed=1 options may or may not be set. +** +** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite +** error code if it cannot. In this case an error message is also loaded into +** pConfig. Output parameter (*pbContent) is set to true if the caller should +** update the %_content table only - not the FTS index or any other shadow +** table. This occurs when an UPDATE modifies only UNINDEXED columns of the +** table. +** +** An UPDATE may proceed if: +** +** * The only columns modified are UNINDEXED columns, or +** +** * The contentless_delete=1 option was specified and all of the indexed +** columns (not a subset) have been modified. +*/ +static int fts5ContentlessUpdate( + Fts5Config *pConfig, + sqlite3_value **apVal, + int bRowidModified, + int *pbContent +){ + int ii; + int bSeenIndex = 0; /* Have seen modified indexed column */ + int bSeenIndexNC = 0; /* Have seen unmodified indexed column */ + int rc = SQLITE_OK; + + for(ii=0; iinCol; ii++){ + if( pConfig->abUnindexed[ii]==0 ){ + if( sqlite3_value_nochange(apVal[ii]) ){ + bSeenIndexNC++; + }else{ + bSeenIndex++; + } + } + } + + if( bSeenIndex==0 && bRowidModified==0 ){ + *pbContent = 1; + }else{ + if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){ + rc = SQLITE_ERROR; + sqlite3Fts5ConfigErrmsg(pConfig, + (pConfig->bContentlessDelete ? + "%s a subset of columns on fts5 contentless-delete table: %s" : + "%s contentless fts5 table: %s") + , "cannot UPDATE", pConfig->zName + ); + } + } + + return rc; +} + /* ** This function is the implementation of the xUpdate callback used by ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be @@ -251319,41 +253257,46 @@ static int fts5UpdateMethod( assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); assert( nArg!=1 || eType0==SQLITE_INTEGER ); - /* Filter out attempts to run UPDATE or DELETE on contentless tables. - ** This is not suported. Except - they are both supported if the CREATE - ** VIRTUAL TABLE statement contained "contentless_delete=1". */ - if( eType0==SQLITE_INTEGER - && pConfig->eContent==FTS5_CONTENT_NONE - && pConfig->bContentlessDelete==0 - ){ - pTab->p.base.zErrMsg = sqlite3_mprintf( - "cannot %s contentless fts5 table: %s", - (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName - ); - rc = SQLITE_ERROR; - } - /* DELETE */ - else if( nArg==1 ){ - i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); - bUpdateOrDelete = 1; + if( nArg==1 ){ + /* It is only possible to DELETE from a contentless table if the + ** contentless_delete=1 flag is set. */ + if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){ + fts5SetVtabError(pTab, + "cannot DELETE from contentless fts5 table: %s", pConfig->zName + ); + rc = SQLITE_ERROR; + }else{ + i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); + bUpdateOrDelete = 1; + } } /* INSERT or UPDATE */ else{ int eType1 = sqlite3_value_numeric_type(apVal[1]); - if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){ - rc = SQLITE_MISMATCH; + /* It is an error to write an fts5_locale() value to a table without + ** the locale=1 option. */ + if( pConfig->bLocale==0 ){ + int ii; + for(ii=0; iinCol; ii++){ + sqlite3_value *pVal = apVal[ii+2]; + if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); + rc = SQLITE_MISMATCH; + goto update_out; + } + } } - else if( eType0!=SQLITE_INTEGER ){ + if( eType0!=SQLITE_INTEGER ){ /* An INSERT statement. If the conflict-mode is REPLACE, first remove ** the current entry (if any). */ if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); bUpdateOrDelete = 1; } fts5StorageInsert(&rc, pTab, apVal, pRowid); @@ -251361,30 +253304,57 @@ static int fts5UpdateMethod( /* UPDATE */ else{ + Fts5Storage *pStorage = pTab->pStorage; i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ - if( eType1==SQLITE_INTEGER && iOld!=iNew ){ + int bContent = 0; /* Content only update */ + + /* If this is a contentless table (including contentless_unindexed=1 + ** tables), check if the UPDATE may proceed. */ + if( fts5IsContentless(pTab, 1) ){ + rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent); + if( rc!=SQLITE_OK ) goto update_out; + } + + if( eType1!=SQLITE_INTEGER ){ + rc = SQLITE_MISMATCH; + }else if( iOld!=iNew ){ + assert( bContent==0 ); if( eConflict==SQLITE_REPLACE ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0); } fts5StorageInsert(&rc, pTab, apVal, pRowid); }else{ - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); + rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid); } if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid); } } + }else if( bContent ){ + /* This occurs when an UPDATE on a contentless table affects *only* + ** UNINDEXED columns. This is a no-op for contentless_unindexed=0 + ** tables, or a write to the %_content table only for =1 tables. */ + assert( fts5IsContentless(pTab, 1) ); + rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid); + } }else{ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); fts5StorageInsert(&rc, pTab, apVal, pRowid); } bUpdateOrDelete = 1; + sqlite3Fts5StorageReleaseDeleteRow(pStorage); } + } } @@ -251401,6 +253371,7 @@ static int fts5UpdateMethod( } } + update_out: pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -251422,9 +253393,11 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ - fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); - fts5NewTransaction((Fts5FullTable*)pVtab); - return SQLITE_OK; + int rc = fts5NewTransaction((Fts5FullTable*)pVtab); + if( rc==SQLITE_OK ){ + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); + } + return rc; } /* @@ -251478,17 +253451,40 @@ static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); } +/* +** Implementation of xTokenize_v2() API. +*/ +static int fts5ApiTokenize_v2( + Fts5Context *pCtx, + const char *pText, int nText, + const char *pLoc, int nLoc, + void *pUserData, + int (*xToken)(void*, int, const char*, int, int, int) +){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + int rc = SQLITE_OK; + + sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); + rc = sqlite3Fts5Tokenize(pTab->pConfig, + FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken + ); + sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); + + return rc; +} + +/* +** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0 +** passed as the locale. +*/ static int fts5ApiTokenize( Fts5Context *pCtx, const char *pText, int nText, void *pUserData, int (*xToken)(void*, int, const char*, int, int, int) ){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - return sqlite3Fts5Tokenize( - pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken - ); + return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken); } static int fts5ApiPhraseCount(Fts5Context *pCtx){ @@ -251501,6 +253497,49 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); } +/* +** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This +** function extracts the text value of column iCol of the current row. +** Additionally, if there is an associated locale, it invokes +** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller +** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point +** after this function returns. +** +** If successful, (*ppText) is set to point to a buffer containing the text +** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that +** buffer in bytes. It is not guaranteed to be nul-terminated. If an error +** occurs, an SQLite error code is returned. The final values of the two +** output parameters are undefined in this case. +*/ +static int fts5TextFromStmt( + Fts5Config *pConfig, + sqlite3_stmt *pStmt, + int iCol, + const char **ppText, + int *pnText +){ + sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); + const char *pLoc = 0; + int nLoc = 0; + int rc = SQLITE_OK; + + if( pConfig->bLocale + && pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); + }else{ + *ppText = (const char*)sqlite3_value_text(pVal); + *pnText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ + pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); + nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); + } + } + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + return rc; +} + static int fts5ApiColumnText( Fts5Context *pCtx, int iCol, @@ -251510,28 +253549,35 @@ static int fts5ApiColumnText( int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + + assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); if( iCol<0 || iCol>=pTab->pConfig->nCol ){ rc = SQLITE_RANGE; - }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) - || pCsr->ePlan==FTS5_PLAN_SPECIAL - ){ + }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){ *pz = 0; *pn = 0; }else{ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ - *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); - *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); + rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); + sqlite3Fts5ClearLocale(pTab->pConfig); } } return rc; } +/* +** This is called by various API functions - xInst, xPhraseFirst, +** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase +** of the current row. This function works for both detail=full tables (in +** which case the position-list was read from the fts index) or for other +** detail= modes if the row content is available. +*/ static int fts5CsrPoslist( - Fts5Cursor *pCsr, - int iPhrase, - const u8 **pa, - int *pn + Fts5Cursor *pCsr, /* Fts5 cursor object */ + int iPhrase, /* Phrase to find position list for */ + const u8 **pa, /* OUT: Pointer to position list buffer */ + int *pn /* OUT: Size of (*pa) in bytes */ ){ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; int rc = SQLITE_OK; @@ -251539,20 +253585,32 @@ static int fts5CsrPoslist( if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ rc = SQLITE_RANGE; + }else if( pConfig->eDetail!=FTS5_DETAIL_FULL + && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) + ){ + *pa = 0; + *pn = 0; + return SQLITE_OK; }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ Fts5PoslistPopulator *aPopulator; int i; + aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); if( aPopulator==0 ) rc = SQLITE_NOMEM; + if( rc==SQLITE_OK ){ + rc = fts5SeekCursor(pCsr, 0); + } for(i=0; inCol && rc==SQLITE_OK; i++){ - int n; const char *z; - rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n); + const char *z = 0; + int n = 0; + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ExprPopulatePoslists( pConfig, pCsr->pExpr, aPopulator, i, z, n ); } + sqlite3Fts5ClearLocale(pConfig); } sqlite3_free(aPopulator); @@ -251577,7 +253635,6 @@ static int fts5CsrPoslist( *pn = 0; } - return rc; } @@ -251646,7 +253703,8 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); - if( aInst[1]<0 || aInst[1]>=nCol ){ + assert( aInst[1]>=0 ); + if( aInst[1]>=nCol ){ rc = FTS5_CORRUPT; break; } @@ -251724,7 +253782,7 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ if( pConfig->bColumnsize ){ i64 iRowid = fts5CursorRowid(pCsr); rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); - }else if( pConfig->zContent==0 ){ + }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ int i; for(i=0; inCol; i++){ if( pConfig->abUnindexed[i]==0 ){ @@ -251733,17 +253791,19 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ } }else{ int i; + rc = fts5SeekCursor(pCsr, 0); for(i=0; rc==SQLITE_OK && inCol; i++){ if( pConfig->abUnindexed[i]==0 ){ - const char *z; int n; - void *p = (void*)(&pCsr->aColumnSize[i]); + const char *z = 0; + int n = 0; pCsr->aColumnSize[i] = 0; - rc = fts5ApiColumnText(pCtx, i, &z, &n); + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5Tokenize( - pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb + rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, + z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb ); } + sqlite3Fts5ClearLocale(pConfig); } } } @@ -251823,11 +253883,10 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ } static void fts5ApiPhraseNext( - Fts5Context *pUnused, + Fts5Context *pCtx, Fts5PhraseIter *pIter, int *piCol, int *piOff ){ - UNUSED_PARAM(pUnused); if( pIter->a>=pIter->b ){ *piCol = -1; *piOff = -1; @@ -251835,8 +253894,12 @@ static void fts5ApiPhraseNext( int iVal; pIter->a += fts5GetVarint32(pIter->a, iVal); if( iVal==1 ){ + /* Avoid returning a (*piCol) value that is too large for the table, + ** even if the position-list is corrupt. The caller might not be + ** expecting it. */ + int nCol = ((Fts5Table*)(((Fts5Cursor*)pCtx)->base.pVtab))->pConfig->nCol; pIter->a += fts5GetVarint32(pIter->a, iVal); - *piCol = iVal; + *piCol = (iVal>=nCol ? nCol-1 : iVal); *piOff = 0; pIter->a += fts5GetVarint32(pIter->a, iVal); } @@ -251986,8 +254049,48 @@ static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); +/* +** The xColumnLocale() API. +*/ +static int fts5ApiColumnLocale( + Fts5Context *pCtx, + int iCol, + const char **pzLocale, + int *pnLocale +){ + int rc = SQLITE_OK; + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; + + *pzLocale = 0; + *pnLocale = 0; + + assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); + if( iCol<0 || iCol>=pConfig->nCol ){ + rc = SQLITE_RANGE; + }else if( + pConfig->abUnindexed[iCol]==0 + && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1) + && pConfig->bLocale + ){ + rc = fts5SeekCursor(pCsr, 0); + if( rc==SQLITE_OK ){ + const char *zDummy = 0; + int nDummy = 0; + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy); + if( rc==SQLITE_OK ){ + *pzLocale = pConfig->t.pLocale; + *pnLocale = pConfig->t.nLocale; + } + sqlite3Fts5ClearLocale(pConfig); + } + } + + return rc; +} + static const Fts5ExtensionApi sFts5Api = { - 3, /* iVersion */ + 4, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, @@ -252008,7 +254111,9 @@ static const Fts5ExtensionApi sFts5Api = { fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, fts5ApiQueryToken, - fts5ApiInstToken + fts5ApiInstToken, + fts5ApiColumnLocale, + fts5ApiTokenize_v2 }; /* @@ -252059,6 +254164,7 @@ static void fts5ApiInvoke( sqlite3_value **argv ){ assert( pCsr->pAux==0 ); + assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); pCsr->pAux = pAux; pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); pCsr->pAux = 0; @@ -252072,6 +254178,21 @@ static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ return pCsr; } +/* +** Parameter zFmt is a printf() style formatting string. This function +** formats it using the trailing arguments and returns the result as +** an error message to the context passed as the first argument. +*/ +static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){ + char *zErr = 0; + va_list ap; + va_start(ap, zFmt); + zErr = sqlite3_vmprintf(zFmt, ap); + sqlite3_result_error(pCtx, zErr, -1); + sqlite3_free(zErr); + va_end(ap); +} + static void fts5ApiCallback( sqlite3_context *context, int argc, @@ -252087,12 +254208,13 @@ static void fts5ApiCallback( iCsrId = sqlite3_value_int64(argv[0]); pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); - if( pCsr==0 || pCsr->ePlan==0 ){ - char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); + if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL) ){ + fts5ResultError(context, "no such cursor: %lld", iCsrId); }else{ + sqlite3_vtab *pTab = pCsr->base.pVtab; fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); + sqlite3_free(pTab->zErrMsg); + pTab->zErrMsg = 0; } } @@ -252210,8 +254332,8 @@ static int fts5ColumnMethod( ** auxiliary function. */ sqlite3_result_int64(pCtx, pCsr->iCsrId); }else if( iCol==pConfig->nCol+1 ){ - /* The value of the "rank" column. */ + if( pCsr->ePlan==FTS5_PLAN_SOURCE ){ fts5PoslistBlob(pCtx, pCsr); }else if( @@ -252222,20 +254344,32 @@ static int fts5ColumnMethod( fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); } } - }else if( !fts5IsContentless(pTab) ){ - pConfig->pzErrmsg = &pTab->p.base.zErrMsg; - rc = fts5SeekCursor(pCsr, 1); - if( rc==SQLITE_OK ){ - sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); + }else{ + if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){ + pConfig->pzErrmsg = &pTab->p.base.zErrMsg; + rc = fts5SeekCursor(pCsr, 1); + if( rc==SQLITE_OK ){ + sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); + if( pConfig->bLocale + && pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + const char *z = 0; + int n = 0; + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); + if( rc==SQLITE_OK ){ + sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); + } + sqlite3Fts5ClearLocale(pConfig); + }else{ + sqlite3_result_value(pCtx, pVal); + } + } + + pConfig->pzErrmsg = 0; } - pConfig->pzErrmsg = 0; - }else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){ - char *zErr = sqlite3_mprintf("cannot UPDATE a subset of " - "columns on fts5 contentless-delete table: %s", pConfig->zName - ); - sqlite3_result_error(pCtx, zErr, -1); - sqlite3_free(zErr); } + return rc; } @@ -252374,9 +254508,180 @@ static int fts5CreateAux( return rc; } +/* +** This function is used by xCreateTokenizer_v2() and xCreateTokenizer(). +** It allocates and partially populates a new Fts5TokenizerModule object. +** The new object is already linked into the Fts5Global context before +** returning. +** +** If successful, SQLITE_OK is returned and a pointer to the new +** Fts5TokenizerModule object returned via output parameter (*ppNew). All +** that is required is for the caller to fill in the methods in +** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native +** as appropriate. +** +** If an error occurs, an SQLite error code is returned and the final value +** of (*ppNew) undefined. +*/ +static int fts5NewTokenizerModule( + Fts5Global *pGlobal, /* Global context (one per db handle) */ + const char *zName, /* Name of new function */ + void *pUserData, /* User data for aux. function */ + void(*xDestroy)(void*), /* Destructor for pUserData */ + Fts5TokenizerModule **ppNew +){ + int rc = SQLITE_OK; + Fts5TokenizerModule *pNew; + sqlite3_int64 nName; /* Size of zName and its \0 terminator */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ + + nName = strlen(zName) + 1; + nByte = sizeof(Fts5TokenizerModule) + nName; + *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte); + if( pNew ){ + pNew->zName = (char*)&pNew[1]; + memcpy(pNew->zName, zName, nName); + pNew->pUserData = pUserData; + pNew->xDestroy = xDestroy; + pNew->pNext = pGlobal->pTok; + pGlobal->pTok = pNew; + if( pNew->pNext==0 ){ + pGlobal->pDfltTok = pNew; + } + } + + return rc; +} + +/* +** An instance of this type is used as the Fts5Tokenizer object for +** wrapper tokenizers - those that provide access to a v1 tokenizer via +** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer +** via the fts5_tokenizer API. +*/ +typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer; +struct Fts5VtoVTokenizer { + int bV2Native; /* True if v2 native tokenizer */ + fts5_tokenizer x1; /* Tokenizer functions */ + fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ + Fts5Tokenizer *pReal; +}; + +/* +** Create a wrapper tokenizer. The context argument pCtx points to the +** Fts5TokenizerModule object. +*/ +static int fts5VtoVCreate( + void *pCtx, + const char **azArg, + int nArg, + Fts5Tokenizer **ppOut +){ + Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx; + Fts5VtoVTokenizer *pNew = 0; + int rc = SQLITE_OK; + + pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); + if( rc==SQLITE_OK ){ + pNew->x1 = pMod->x1; + pNew->x2 = pMod->x2; + pNew->bV2Native = pMod->bV2Native; + if( pMod->bV2Native ){ + rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); + }else{ + rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); + } + if( rc!=SQLITE_OK ){ + sqlite3_free(pNew); + pNew = 0; + } + } + + *ppOut = (Fts5Tokenizer*)pNew; + return rc; +} + +/* +** Delete an Fts5VtoVTokenizer wrapper tokenizer. +*/ +static void fts5VtoVDelete(Fts5Tokenizer *pTok){ + Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; + if( p ){ + if( p->bV2Native ){ + p->x2.xDelete(p->pReal); + }else{ + p->x1.xDelete(p->pReal); + } + sqlite3_free(p); + } +} + + +/* +** xTokenizer method for a wrapper tokenizer that offers the v1 interface +** (no support for locales). +*/ +static int fts5V1toV2Tokenize( + Fts5Tokenizer *pTok, + void *pCtx, int flags, + const char *pText, int nText, + int (*xToken)(void*, int, const char*, int, int, int) +){ + Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; + assert( p->bV2Native ); + return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken); +} + +/* +** xTokenizer method for a wrapper tokenizer that offers the v2 interface +** (with locale support). +*/ +static int fts5V2toV1Tokenize( + Fts5Tokenizer *pTok, + void *pCtx, int flags, + const char *pText, int nText, + const char *pLocale, int nLocale, + int (*xToken)(void*, int, const char*, int, int, int) +){ + Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; + assert( p->bV2Native==0 ); + UNUSED_PARAM2(pLocale,nLocale); + return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken); +} + /* ** Register a new tokenizer. This is the implementation of the -** fts5_api.xCreateTokenizer() method. +** fts5_api.xCreateTokenizer_v2() method. +*/ +static int fts5CreateTokenizer_v2( + fts5_api *pApi, /* Global context (one per db handle) */ + const char *zName, /* Name of new function */ + void *pUserData, /* User data for aux. function */ + fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */ + void(*xDestroy)(void*) /* Destructor for pUserData */ +){ + Fts5Global *pGlobal = (Fts5Global*)pApi; + int rc = SQLITE_OK; + + if( pTokenizer->iVersion>2 ){ + rc = SQLITE_ERROR; + }else{ + Fts5TokenizerModule *pNew = 0; + rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew); + if( pNew ){ + pNew->x2 = *pTokenizer; + pNew->bV2Native = 1; + pNew->x1.xCreate = fts5VtoVCreate; + pNew->x1.xTokenize = fts5V1toV2Tokenize; + pNew->x1.xDelete = fts5VtoVDelete; + } + } + + return rc; +} + +/* +** The fts5_api.xCreateTokenizer() method. */ static int fts5CreateTokenizer( fts5_api *pApi, /* Global context (one per db handle) */ @@ -252385,37 +254690,29 @@ static int fts5CreateTokenizer( fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ void(*xDestroy)(void*) /* Destructor for pUserData */ ){ - Fts5Global *pGlobal = (Fts5Global*)pApi; - Fts5TokenizerModule *pNew; - sqlite3_int64 nName; /* Size of zName and its \0 terminator */ - sqlite3_int64 nByte; /* Bytes of space to allocate */ + Fts5TokenizerModule *pNew = 0; int rc = SQLITE_OK; - nName = strlen(zName) + 1; - nByte = sizeof(Fts5TokenizerModule) + nName; - pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); + rc = fts5NewTokenizerModule( + (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew + ); if( pNew ){ - memset(pNew, 0, (size_t)nByte); - pNew->zName = (char*)&pNew[1]; - memcpy(pNew->zName, zName, nName); - pNew->pUserData = pUserData; - pNew->x = *pTokenizer; - pNew->xDestroy = xDestroy; - pNew->pNext = pGlobal->pTok; - pGlobal->pTok = pNew; - if( pNew->pNext==0 ){ - pGlobal->pDfltTok = pNew; - } - }else{ - rc = SQLITE_NOMEM; + pNew->x1 = *pTokenizer; + pNew->x2.xCreate = fts5VtoVCreate; + pNew->x2.xTokenize = fts5V2toV1Tokenize; + pNew->x2.xDelete = fts5VtoVDelete; } - return rc; } +/* +** Search the global context passed as the first argument for a tokenizer +** module named zName. If found, return a pointer to the Fts5TokenizerModule +** object. Otherwise, return NULL. +*/ static Fts5TokenizerModule *fts5LocateTokenizer( - Fts5Global *pGlobal, - const char *zName + Fts5Global *pGlobal, /* Global (one per db handle) object */ + const char *zName /* Name of tokenizer module to find */ ){ Fts5TokenizerModule *pMod = 0; @@ -252430,6 +254727,36 @@ static Fts5TokenizerModule *fts5LocateTokenizer( return pMod; } +/* +** Find a tokenizer. This is the implementation of the +** fts5_api.xFindTokenizer_v2() method. +*/ +static int fts5FindTokenizer_v2( + fts5_api *pApi, /* Global context (one per db handle) */ + const char *zName, /* Name of tokenizer */ + void **ppUserData, + fts5_tokenizer_v2 **ppTokenizer /* Populate this object */ +){ + int rc = SQLITE_OK; + Fts5TokenizerModule *pMod; + + pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); + if( pMod ){ + if( pMod->bV2Native ){ + *ppUserData = pMod->pUserData; + }else{ + *ppUserData = (void*)pMod; + } + *ppTokenizer = &pMod->x2; + }else{ + *ppTokenizer = 0; + *ppUserData = 0; + rc = SQLITE_ERROR; + } + + return rc; +} + /* ** Find a tokenizer. This is the implementation of the ** fts5_api.xFindTokenizer() method. @@ -252445,55 +254772,75 @@ static int fts5FindTokenizer( pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); if( pMod ){ - *pTokenizer = pMod->x; - *ppUserData = pMod->pUserData; + if( pMod->bV2Native==0 ){ + *ppUserData = pMod->pUserData; + }else{ + *ppUserData = (void*)pMod; + } + *pTokenizer = pMod->x1; }else{ - memset(pTokenizer, 0, sizeof(fts5_tokenizer)); + memset(pTokenizer, 0, sizeof(*pTokenizer)); + *ppUserData = 0; rc = SQLITE_ERROR; } return rc; } -static int sqlite3Fts5GetTokenizer( - Fts5Global *pGlobal, - const char **azArg, - int nArg, - Fts5Config *pConfig, - char **pzErr -){ - Fts5TokenizerModule *pMod; +/* +** Attempt to instantiate the tokenizer. +*/ +static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){ + const char **azArg = pConfig->t.azArg; + const int nArg = pConfig->t.nArg; + Fts5TokenizerModule *pMod = 0; int rc = SQLITE_OK; - pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]); + pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]); if( pMod==0 ){ assert( nArg>0 ); rc = SQLITE_ERROR; - if( pzErr ) *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); + sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]); }else{ - rc = pMod->x.xCreate( - pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok - ); - pConfig->pTokApi = &pMod->x; - if( rc!=SQLITE_OK ){ - if( pzErr && rc!=SQLITE_NOMEM ){ - *pzErr = sqlite3_mprintf("error in tokenizer constructor"); - } + int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0; + if( pMod->bV2Native ){ + xCreate = pMod->x2.xCreate; + pConfig->t.pApi2 = &pMod->x2; }else{ - pConfig->ePattern = sqlite3Fts5TokenizerPattern( - pMod->x.xCreate, pConfig->pTok + pConfig->t.pApi1 = &pMod->x1; + xCreate = pMod->x1.xCreate; + } + + rc = xCreate(pMod->pUserData, + (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok + ); + + if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_NOMEM ){ + sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor"); + } + }else if( pMod->bV2Native==0 ){ + pConfig->t.ePattern = sqlite3Fts5TokenizerPattern( + pMod->x1.xCreate, pConfig->t.pTok ); } } if( rc!=SQLITE_OK ){ - pConfig->pTokApi = 0; - pConfig->pTok = 0; + pConfig->t.pApi1 = 0; + pConfig->t.pApi2 = 0; + pConfig->t.pTok = 0; } return rc; } + +/* +** xDestroy callback passed to sqlite3_create_module(). This is invoked +** when the db handle is being closed. Free memory associated with +** tokenizers and aux functions registered with this db handle. +*/ static void fts5ModuleDestroy(void *pCtx){ Fts5TokenizerModule *pTok, *pNextTok; Fts5Auxiliary *pAux, *pNextAux; @@ -252514,6 +254861,10 @@ static void fts5ModuleDestroy(void *pCtx){ sqlite3_free(pGlobal); } +/* +** Implementation of the fts5() function used by clients to obtain the +** API pointer. +*/ static void fts5Fts5Func( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ @@ -252537,7 +254888,68 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e", -1, SQLITE_TRANSIENT); +} + +/* +** Implementation of fts5_locale(LOCALE, TEXT) function. +** +** If parameter LOCALE is NULL, or a zero-length string, then a copy of +** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as +** text, and the value returned is a blob consisting of: +** +** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER). +** * The LOCALE, as utf-8 text, followed by +** * 0x00, followed by +** * The TEXT, as utf-8 text. +** +** There is no final nul-terminator following the TEXT value. +*/ +static void fts5LocaleFunc( + sqlite3_context *pCtx, /* Function call context */ + int nArg, /* Number of args */ + sqlite3_value **apArg /* Function arguments */ +){ + const char *zLocale = 0; + int nLocale = 0; + const char *zText = 0; + int nText = 0; + + assert( nArg==2 ); + UNUSED_PARAM(nArg); + + zLocale = (const char*)sqlite3_value_text(apArg[0]); + nLocale = sqlite3_value_bytes(apArg[0]); + + zText = (const char*)sqlite3_value_text(apArg[1]); + nText = sqlite3_value_bytes(apArg[1]); + + if( zLocale==0 || zLocale[0]=='\0' ){ + sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); + }else{ + Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx); + u8 *pBlob = 0; + u8 *pCsr = 0; + int nBlob = 0; + + nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText; + pBlob = (u8*)sqlite3_malloc(nBlob); + if( pBlob==0 ){ + sqlite3_result_error_nomem(pCtx); + return; + } + + pCsr = pBlob; + memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE); + pCsr += FTS5_LOCALE_HDR_SIZE; + memcpy(pCsr, zLocale, nLocale); + pCsr += nLocale; + (*pCsr++) = 0x00; + if( zText ) memcpy(pCsr, zText, nText); + assert( &pCsr[nText]==&pBlob[nBlob] ); + + sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); + } } /* @@ -252632,10 +255044,22 @@ static int fts5Init(sqlite3 *db){ void *p = (void*)pGlobal; memset(pGlobal, 0, sizeof(Fts5Global)); pGlobal->db = db; - pGlobal->api.iVersion = 2; + pGlobal->api.iVersion = 3; pGlobal->api.xCreateFunction = fts5CreateAux; pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; pGlobal->api.xFindTokenizer = fts5FindTokenizer; + pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; + pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; + + /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector. + ** The constants below were generated randomly. */ + sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr); + pGlobal->aLocaleHdr[0] ^= 0xF924976D; + pGlobal->aLocaleHdr[1] ^= 0x16596E13; + pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA; + pGlobal->aLocaleHdr[3] ^= 0x9B03A67F; + assert( sizeof(pGlobal->aLocaleHdr)==16 ); + rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); @@ -252654,6 +255078,13 @@ static int fts5Init(sqlite3 *db){ p, fts5SourceIdFunc, 0, 0 ); } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function( + db, "fts5_locale", 2, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, + p, fts5LocaleFunc, 0, 0 + ); + } } /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file @@ -252728,13 +255159,40 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3 *db){ /* #include "fts5Int.h" */ +/* +** pSavedRow: +** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it +** does a by-rowid lookup to retrieve a single row from the %_content +** table or equivalent external-content table/view. +** +** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original +** values for a row being UPDATEd. In that case, the SQL statement is +** not reset and pSavedRow is set to point at it. This is so that the +** insert operation that follows the delete may access the original +** row values for any new values for which sqlite3_value_nochange() returns +** true. i.e. if the user executes: +** +** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); +** ... +** UPDATE fts SET a=?, b=? WHERE rowid=?; +** +** then the value passed to the xUpdate() method of this table as the +** new.c value is an sqlite3_value_nochange() value. So in this case it +** must be read from the saved row stored in Fts5Storage.pSavedRow. +** +** This is necessary - using sqlite3_value_nochange() instead of just having +** SQLite pass the original value back via xUpdate() - so as not to discard +** any locale information associated with such values. +** +*/ struct Fts5Storage { Fts5Config *pConfig; Fts5Index *pIndex; int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ i64 nTotalRow; /* Total number of rows in FTS table */ i64 *aTotalSize; /* Total sizes of each column */ - sqlite3_stmt *aStmt[11]; + sqlite3_stmt *pSavedRow; + sqlite3_stmt *aStmt[12]; }; @@ -252748,14 +255206,15 @@ struct Fts5Storage { # error "FTS5_STMT_LOOKUP mismatch" #endif -#define FTS5_STMT_INSERT_CONTENT 3 -#define FTS5_STMT_REPLACE_CONTENT 4 -#define FTS5_STMT_DELETE_CONTENT 5 -#define FTS5_STMT_REPLACE_DOCSIZE 6 -#define FTS5_STMT_DELETE_DOCSIZE 7 -#define FTS5_STMT_LOOKUP_DOCSIZE 8 -#define FTS5_STMT_REPLACE_CONFIG 9 -#define FTS5_STMT_SCAN 10 +#define FTS5_STMT_LOOKUP2 3 +#define FTS5_STMT_INSERT_CONTENT 4 +#define FTS5_STMT_REPLACE_CONTENT 5 +#define FTS5_STMT_DELETE_CONTENT 6 +#define FTS5_STMT_REPLACE_DOCSIZE 7 +#define FTS5_STMT_DELETE_DOCSIZE 8 +#define FTS5_STMT_LOOKUP_DOCSIZE 9 +#define FTS5_STMT_REPLACE_CONFIG 10 +#define FTS5_STMT_SCAN 11 /* ** Prepare the two insert statements - Fts5Storage.pInsertContent and @@ -252785,6 +255244,7 @@ static int fts5StorageGetStmt( "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ + "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ @@ -252800,6 +255260,8 @@ static int fts5StorageGetStmt( Fts5Config *pC = p->pConfig; char *zSql = 0; + assert( ArraySize(azStmt)==ArraySize(p->aStmt) ); + switch( eStmt ){ case FTS5_STMT_SCAN: zSql = sqlite3_mprintf(azStmt[eStmt], @@ -252816,6 +255278,7 @@ static int fts5StorageGetStmt( break; case FTS5_STMT_LOOKUP: + case FTS5_STMT_LOOKUP2: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent, pC->zContentRowid ); @@ -252823,20 +255286,35 @@ static int fts5StorageGetStmt( case FTS5_STMT_INSERT_CONTENT: case FTS5_STMT_REPLACE_CONTENT: { - int nCol = pC->nCol + 1; - char *zBind; + char *zBind = 0; int i; - zBind = sqlite3_malloc64(1 + nCol*2); - if( zBind ){ - for(i=0; ieContent==FTS5_CONTENT_NORMAL + || pC->eContent==FTS5_CONTENT_UNINDEXED + ); + + /* Add bindings for the "c*" columns - those that store the actual + ** table content. If eContent==NORMAL, then there is one binding + ** for each column. Or, if eContent==UNINDEXED, then there are only + ** bindings for the UNINDEXED columns. */ + for(i=0; rc==SQLITE_OK && i<(pC->nCol+1); i++){ + if( !i || pC->eContent==FTS5_CONTENT_NORMAL || pC->abUnindexed[i-1] ){ + zBind = sqlite3Fts5Mprintf(&rc, "%z%s?%d", zBind, zBind?",":"",i+1); } - zBind[i*2-1] = '\0'; - zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); - sqlite3_free(zBind); } + + /* Add bindings for any "l*" columns. Only non-UNINDEXED columns + ** require these. */ + if( pC->bLocale && pC->eContent==FTS5_CONTENT_NORMAL ){ + for(i=0; rc==SQLITE_OK && inCol; i++){ + if( pC->abUnindexed[i]==0 ){ + zBind = sqlite3Fts5Mprintf(&rc, "%z,?%d", zBind, pC->nCol+i+2); + } + } + } + + zSql = sqlite3Fts5Mprintf(&rc, azStmt[eStmt], pC->zDb, pC->zName,zBind); + sqlite3_free(zBind); break; } @@ -252862,7 +255340,7 @@ static int fts5StorageGetStmt( rc = SQLITE_NOMEM; }else{ int f = SQLITE_PREPARE_PERSISTENT; - if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB; + if( eStmt>FTS5_STMT_LOOKUP2 ) f |= SQLITE_PREPARE_NO_VTAB; p->pConfig->bLock++; rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); p->pConfig->bLock--; @@ -253022,9 +255500,11 @@ static int sqlite3Fts5StorageOpen( p->pIndex = pIndex; if( bCreate ){ - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL + || pConfig->eContent==FTS5_CONTENT_UNINDEXED + ){ int nDefn = 32 + pConfig->nCol*10; - char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); + char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); if( zDefn==0 ){ rc = SQLITE_NOMEM; }else{ @@ -253033,8 +255513,20 @@ static int sqlite3Fts5StorageOpen( sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); iOff = (int)strlen(zDefn); for(i=0; inCol; i++){ - sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); - iOff += (int)strlen(&zDefn[iOff]); + if( pConfig->eContent==FTS5_CONTENT_NORMAL + || pConfig->abUnindexed[i] + ){ + sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); + iOff += (int)strlen(&zDefn[iOff]); + } + } + if( pConfig->bLocale ){ + for(i=0; inCol; i++){ + if( pConfig->abUnindexed[i]==0 ){ + sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); + iOff += (int)strlen(&zDefn[iOff]); + } + } } rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); } @@ -253111,15 +255603,49 @@ static int fts5StorageInsertCallback( return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); } +/* +** This function is used as part of an UPDATE statement that modifies the +** rowid of a row. In that case, this function is called first to set +** Fts5Storage.pSavedRow to point to a statement that may be used to +** access the original values of the row being deleted - iDel. +** +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. +** It is not considered an error if row iDel does not exist. In this case +** pSavedRow is not set and SQLITE_OK returned. +*/ +static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ + int rc = SQLITE_OK; + sqlite3_stmt *pSeek = 0; + + assert( p->pSavedRow==0 ); + rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pSeek, 1, iDel); + if( sqlite3_step(pSeek)!=SQLITE_ROW ){ + rc = sqlite3_reset(pSeek); + }else{ + p->pSavedRow = pSeek; + } + } + + return rc; +} + /* ** If a row with rowid iDel is present in the %_content table, add the ** delete-markers to the FTS index necessary to delete it. Do not actually ** remove the %_content row at this time though. +** +** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left +** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access +** the original values of the row being deleted. This is used by UPDATE +** statements. */ static int fts5StorageDeleteFromIndex( Fts5Storage *p, i64 iDel, - sqlite3_value **apVal + sqlite3_value **apVal, + int bSaveRow /* True to set pSavedRow */ ){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ @@ -253128,12 +255654,21 @@ static int fts5StorageDeleteFromIndex( int iCol; Fts5InsertCtx ctx; + assert( bSaveRow==0 || apVal==0 ); + assert( bSaveRow==0 || bSaveRow==1 ); + assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 ); + if( apVal==0 ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); - if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int64(pSeek, 1, iDel); - if( sqlite3_step(pSeek)!=SQLITE_ROW ){ - return sqlite3_reset(pSeek); + if( p->pSavedRow && bSaveRow ){ + pSeek = p->pSavedRow; + p->pSavedRow = 0; + }else{ + rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+bSaveRow, &pSeek, 0); + if( rc!=SQLITE_OK ) return rc; + sqlite3_bind_int64(pSeek, 1, iDel); + if( sqlite3_step(pSeek)!=SQLITE_ROW ){ + return sqlite3_reset(pSeek); + } } } @@ -253141,26 +255676,42 @@ static int fts5StorageDeleteFromIndex( ctx.iCol = -1; for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ if( pConfig->abUnindexed[iCol-1]==0 ){ - const char *zText; - int nText; + sqlite3_value *pVal = 0; + const char *pText = 0; + int nText = 0; + const char *pLoc = 0; + int nLoc = 0; + assert( pSeek==0 || apVal==0 ); assert( pSeek!=0 || apVal!=0 ); if( pSeek ){ - zText = (const char*)sqlite3_column_text(pSeek, iCol); - nText = sqlite3_column_bytes(pSeek, iCol); - }else if( ALWAYS(apVal) ){ - zText = (const char*)sqlite3_value_text(apVal[iCol-1]); - nText = sqlite3_value_bytes(apVal[iCol-1]); + pVal = sqlite3_column_value(pSeek, iCol); }else{ - continue; + pVal = apVal[iCol-1]; } - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, - zText, nText, (void*)&ctx, fts5StorageInsertCallback - ); - p->aTotalSize[iCol-1] -= (i64)ctx.szCol; - if( p->aTotalSize[iCol-1]<0 ){ - rc = FTS5_CORRUPT; + + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale && pSeek ){ + pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol); + nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); + } + } + + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + ctx.szCol = 0; + rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, + pText, nText, (void*)&ctx, fts5StorageInsertCallback + ); + p->aTotalSize[iCol-1] -= (i64)ctx.szCol; + if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ + rc = FTS5_CORRUPT; + } + sqlite3Fts5ClearLocale(pConfig); } } } @@ -253170,11 +255721,29 @@ static int fts5StorageDeleteFromIndex( p->nTotalRow--; } - rc2 = sqlite3_reset(pSeek); - if( rc==SQLITE_OK ) rc = rc2; + if( rc==SQLITE_OK && bSaveRow ){ + assert( p->pSavedRow==0 ); + p->pSavedRow = pSeek; + }else{ + rc2 = sqlite3_reset(pSeek); + if( rc==SQLITE_OK ) rc = rc2; + } return rc; } +/* +** Reset any saved statement pSavedRow. Zero pSavedRow as well. This +** should be called by the xUpdate() method of the fts5 table before +** returning from any operation that may have set Fts5Storage.pSavedRow. +*/ +static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ + assert( pStorage->pSavedRow==0 + || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2] + ); + sqlite3_reset(pStorage->pSavedRow); + pStorage->pSavedRow = 0; +} + /* ** This function is called to process a DELETE on a contentless_delete=1 ** table. It adds the tombstone required to delete the entry with rowid @@ -253187,7 +255756,9 @@ static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){ int rc = SQLITE_OK; assert( p->pConfig->bContentlessDelete ); - assert( p->pConfig->eContent==FTS5_CONTENT_NONE ); + assert( p->pConfig->eContent==FTS5_CONTENT_NONE + || p->pConfig->eContent==FTS5_CONTENT_UNINDEXED + ); /* Look up the origin of the document in the %_docsize table. Store ** this in stack variable iOrigin. */ @@ -253231,12 +255802,12 @@ static int fts5StorageInsertDocsize( rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); sqlite3_bind_int64(pReplace, 3, iOrigin); } - if( rc==SQLITE_OK ){ - sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - sqlite3_bind_null(pReplace, 2); - } + } + if( rc==SQLITE_OK ){ + sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); + sqlite3_step(pReplace); + rc = sqlite3_reset(pReplace); + sqlite3_bind_null(pReplace, 2); } } return rc; @@ -253290,7 +255861,12 @@ static int fts5StorageSaveTotals(Fts5Storage *p){ /* ** Remove a row from the FTS table. */ -static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){ +static int sqlite3Fts5StorageDelete( + Fts5Storage *p, /* Storage object */ + i64 iDel, /* Rowid to delete from table */ + sqlite3_value **apVal, /* Optional - values to remove from index */ + int bSaveRow /* If true, set pSavedRow for deleted row */ +){ Fts5Config *pConfig = p->pConfig; int rc; sqlite3_stmt *pDel = 0; @@ -253306,8 +255882,14 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap if( rc==SQLITE_OK ){ if( p->pConfig->bContentlessDelete ){ rc = fts5StorageContentlessDelete(p, iDel); + if( rc==SQLITE_OK + && bSaveRow + && p->pConfig->eContent==FTS5_CONTENT_UNINDEXED + ){ + rc = sqlite3Fts5StorageFindDeleteRow(p, iDel); + } }else{ - rc = fts5StorageDeleteFromIndex(p, iDel, apVal); + rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); } } @@ -253322,7 +255904,9 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap } /* Delete the %_content record */ - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL + || pConfig->eContent==FTS5_CONTENT_UNINDEXED + ){ if( rc==SQLITE_OK ){ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); } @@ -253354,8 +255938,13 @@ static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ ); if( rc==SQLITE_OK && pConfig->bColumnsize ){ rc = fts5ExecPrintf(pConfig->db, 0, - "DELETE FROM %Q.'%q_docsize';", - pConfig->zDb, pConfig->zName + "DELETE FROM %Q.'%q_docsize';", pConfig->zDb, pConfig->zName + ); + } + + if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ + rc = fts5ExecPrintf(pConfig->db, 0, + "DELETE FROM %Q.'%q_content';", pConfig->zDb, pConfig->zName ); } @@ -253396,14 +255985,36 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ - const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1); - int nText = sqlite3_column_bytes(pScan, ctx.iCol+1); - rc = sqlite3Fts5Tokenize(pConfig, - FTS5_TOKENIZE_DOCUMENT, - zText, nText, - (void*)&ctx, - fts5StorageInsertCallback - ); + int nText = 0; /* Size of pText in bytes */ + const char *pText = 0; /* Pointer to buffer containing text value */ + int nLoc = 0; /* Size of pLoc in bytes */ + const char *pLoc = 0; /* Pointer to buffer containing text value */ + + sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale ){ + int iCol = ctx.iCol + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(pScan, iCol); + nLoc = sqlite3_column_bytes(pScan, iCol); + } + } + + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + rc = sqlite3Fts5Tokenize(pConfig, + FTS5_TOKENIZE_DOCUMENT, + pText, nText, + (void*)&ctx, + fts5StorageInsertCallback + ); + sqlite3Fts5ClearLocale(pConfig); + } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; @@ -253469,6 +256080,7 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ */ static int sqlite3Fts5StorageContentInsert( Fts5Storage *p, + int bReplace, /* True to use REPLACE instead of INSERT */ sqlite3_value **apVal, i64 *piRowid ){ @@ -253476,7 +256088,9 @@ static int sqlite3Fts5StorageContentInsert( int rc = SQLITE_OK; /* Insert the new row into the %_content table. */ - if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ + if( pConfig->eContent!=FTS5_CONTENT_NORMAL + && pConfig->eContent!=FTS5_CONTENT_UNINDEXED + ){ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ *piRowid = sqlite3_value_int64(apVal[1]); }else{ @@ -253485,9 +256099,52 @@ static int sqlite3Fts5StorageContentInsert( }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ - rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); - for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ - rc = sqlite3_bind_value(pInsert, i, apVal[i]); + + assert( FTS5_STMT_INSERT_CONTENT+1==FTS5_STMT_REPLACE_CONTENT ); + assert( bReplace==0 || bReplace==1 ); + rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT+bReplace, &pInsert, 0); + if( pInsert ) sqlite3_clear_bindings(pInsert); + + /* Bind the rowid value */ + sqlite3_bind_value(pInsert, 1, apVal[1]); + + /* Loop through values for user-defined columns. i=2 is the leftmost + ** user-defined column. As is column 1 of pSavedRow. */ + for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ + int bUnindexed = pConfig->abUnindexed[i-2]; + if( pConfig->eContent==FTS5_CONTENT_NORMAL || bUnindexed ){ + sqlite3_value *pVal = apVal[i]; + + if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ + /* This is an UPDATE statement, and user-defined column (i-2) was not + ** modified. Retrieve the value from Fts5Storage.pSavedRow. */ + pVal = sqlite3_column_value(p->pSavedRow, i-1); + if( pConfig->bLocale && bUnindexed==0 ){ + sqlite3_bind_value(pInsert, pConfig->nCol + i, + sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) + ); + } + }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + const char *pText = 0; + const char *pLoc = 0; + int nText = 0; + int nLoc = 0; + assert( pConfig->bLocale ); + + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + if( rc==SQLITE_OK ){ + sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); + if( bUnindexed==0 ){ + int iLoc = pConfig->nCol + i; + sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); + } + } + + continue; + } + + rc = sqlite3_bind_value(pInsert, i, pVal); + } } if( rc==SQLITE_OK ){ sqlite3_step(pInsert); @@ -253522,14 +256179,38 @@ static int sqlite3Fts5StorageIndexInsert( for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ - const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]); - int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]); - rc = sqlite3Fts5Tokenize(pConfig, - FTS5_TOKENIZE_DOCUMENT, - zText, nText, - (void*)&ctx, - fts5StorageInsertCallback - ); + int nText = 0; /* Size of pText in bytes */ + const char *pText = 0; /* Pointer to buffer containing text value */ + int nLoc = 0; /* Size of pText in bytes */ + const char *pLoc = 0; /* Pointer to buffer containing text value */ + + sqlite3_value *pVal = apVal[ctx.iCol+2]; + if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ + pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ + int iCol = ctx.iCol + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); + nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); + } + }else{ + pVal = apVal[ctx.iCol+2]; + } + + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + } + + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + rc = sqlite3Fts5Tokenize(pConfig, + FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, + fts5StorageInsertCallback + ); + sqlite3Fts5ClearLocale(pConfig); + } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; @@ -253693,29 +256374,61 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } for(i=0; rc==SQLITE_OK && inCol; i++){ - if( pConfig->abUnindexed[i] ) continue; - ctx.iCol = i; - ctx.szCol = 0; - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ - rc = sqlite3Fts5TermsetNew(&ctx.pTermset); - } - if( rc==SQLITE_OK ){ - const char *zText = (const char*)sqlite3_column_text(pScan, i+1); - int nText = sqlite3_column_bytes(pScan, i+1); - rc = sqlite3Fts5Tokenize(pConfig, - FTS5_TOKENIZE_DOCUMENT, - zText, nText, - (void*)&ctx, - fts5StorageIntegrityCallback - ); - } - if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ - rc = FTS5_CORRUPT; - } - aTotalSize[i] += ctx.szCol; - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ - sqlite3Fts5TermsetFree(ctx.pTermset); - ctx.pTermset = 0; + if( pConfig->abUnindexed[i]==0 ){ + const char *pText = 0; + int nText = 0; + const char *pLoc = 0; + int nLoc = 0; + sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); + + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue( + pVal, &pText, &nText, &pLoc, &nLoc + ); + }else{ + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ + int iCol = i + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(pScan, iCol); + nLoc = sqlite3_column_bytes(pScan, iCol); + } + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + } + + ctx.iCol = i; + ctx.szCol = 0; + + if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ + rc = sqlite3Fts5TermsetNew(&ctx.pTermset); + } + + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + rc = sqlite3Fts5Tokenize(pConfig, + FTS5_TOKENIZE_DOCUMENT, + pText, nText, + (void*)&ctx, + fts5StorageIntegrityCallback + ); + sqlite3Fts5ClearLocale(pConfig); + } + + /* If this is not a columnsize=0 database, check that the number + ** of tokens in the value matches the aColSize[] value read from + ** the %_docsize table. */ + if( rc==SQLITE_OK + && pConfig->bColumnsize + && ctx.szCol!=aColSize[i] + ){ + rc = FTS5_CORRUPT; + } + aTotalSize[i] += ctx.szCol; + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ + sqlite3Fts5TermsetFree(ctx.pTermset); + ctx.pTermset = 0; + } } } sqlite3Fts5TermsetFree(ctx.pTermset); @@ -254022,7 +256735,7 @@ static int fts5AsciiCreate( int i; memset(p, 0, sizeof(AsciiTokenizer)); memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); - for(i=0; rc==SQLITE_OK && i=0xc0 ){ \ c = sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ + while( zInpTokenizer ){ - p->tokenizer.xDelete(p->pTokenizer); + p->tokenizer_v2.xDelete(p->pTokenizer); } sqlite3_free(p); } @@ -254530,6 +257240,7 @@ static int fts5PorterCreate( PorterTokenizer *pRet; void *pUserdata = 0; const char *zBase = "unicode61"; + fts5_tokenizer_v2 *pV2 = 0; if( nArg>0 ){ zBase = azArg[0]; @@ -254538,14 +257249,15 @@ static int fts5PorterCreate( pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); if( pRet ){ memset(pRet, 0, sizeof(PorterTokenizer)); - rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer); + rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); }else{ rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ int nArg2 = (nArg>0 ? nArg-1 : 0); - const char **azArg2 = (nArg2 ? &azArg[1] : 0); - rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer); + const char **az2 = (nArg2 ? &azArg[1] : 0); + memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2)); + rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer); } if( rc!=SQLITE_OK ){ @@ -255196,6 +257908,7 @@ static int fts5PorterTokenize( void *pCtx, int flags, const char *pText, int nText, + const char *pLoc, int nLoc, int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) ){ PorterTokenizer *p = (PorterTokenizer*)pTokenizer; @@ -255203,8 +257916,8 @@ static int fts5PorterTokenize( sCtx.xToken = xToken; sCtx.pCtx = pCtx; sCtx.aBuf = p->aBuf; - return p->tokenizer.xTokenize( - p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb + return p->tokenizer_v2.xTokenize( + p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb ); } @@ -255234,41 +257947,46 @@ static int fts5TriCreate( Fts5Tokenizer **ppOut ){ int rc = SQLITE_OK; - TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); + TrigramTokenizer *pNew = 0; UNUSED_PARAM(pUnused); - if( pNew==0 ){ - rc = SQLITE_NOMEM; + if( nArg%2 ){ + rc = SQLITE_ERROR; }else{ int i; - pNew->bFold = 1; - pNew->iFoldParam = 0; - for(i=0; rc==SQLITE_OK && ibFold = 1; + pNew->iFoldParam = 0; + + for(i=0; rc==SQLITE_OK && ibFold = (zArg[0]=='0'); + } + }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ + if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ + rc = SQLITE_ERROR; + }else{ + pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; + } }else{ - pNew->bFold = (zArg[0]=='0'); - } - }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ - if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ rc = SQLITE_ERROR; - }else{ - pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; } - }else{ + } + + if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ rc = SQLITE_ERROR; } - } - if( iiFoldParam!=0 && pNew->bFold==0 ){ - rc = SQLITE_ERROR; - } - - if( rc!=SQLITE_OK ){ - fts5TriDelete((Fts5Tokenizer*)pNew); - pNew = 0; + if( rc!=SQLITE_OK ){ + fts5TriDelete((Fts5Tokenizer*)pNew); + pNew = 0; + } } } *ppOut = (Fts5Tokenizer*)pNew; @@ -255373,6 +258091,16 @@ static int sqlite3Fts5TokenizerPattern( return FTS5_PATTERN_NONE; } +/* +** Return true if the tokenizer described by p->azArg[] is the trigram +** tokenizer. This tokenizer needs to be loaded before xBestIndex is +** called for the first time in order to correctly handle LIKE/GLOB. +*/ +static int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig *p){ + return (p->nArg>=1 && 0==sqlite3_stricmp(p->azArg[0], "trigram")); +} + + /* ** Register all built-in tokenizers with FTS5. */ @@ -255383,7 +258111,6 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ } aBuiltin[] = { { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, - { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, }; @@ -255398,7 +258125,20 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ 0 ); } - + if( rc==SQLITE_OK ){ + fts5_tokenizer_v2 sPorter = { + 2, + fts5PorterCreate, + fts5PorterDelete, + fts5PorterTokenize + }; + rc = pApi->xCreateTokenizer_v2(pApi, + "porter", + (void*)pApi, + &sPorter, + 0 + ); + } return rc; } @@ -255768,6 +258508,9 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ default: return 1; } break; + + default: + return 1; } return 0; } @@ -256592,6 +259335,7 @@ struct Fts5VocabCursor { int nLeTerm; /* Size of zLeTerm in bytes */ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ + int colUsed; /* Copy of sqlite3_index_info.colUsed */ /* These are used by 'col' tables only */ int iCol; @@ -256618,9 +259362,11 @@ struct Fts5VocabCursor { /* ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. */ -#define FTS5_VOCAB_TERM_EQ 0x01 -#define FTS5_VOCAB_TERM_GE 0x02 -#define FTS5_VOCAB_TERM_LE 0x04 +#define FTS5_VOCAB_TERM_EQ 0x0100 +#define FTS5_VOCAB_TERM_GE 0x0200 +#define FTS5_VOCAB_TERM_LE 0x0400 + +#define FTS5_VOCAB_COLUSED_MASK 0xFF /* @@ -256797,11 +259543,13 @@ static int fts5VocabBestIndexMethod( int iTermEq = -1; int iTermGe = -1; int iTermLe = -1; - int idxNum = 0; + int idxNum = (int)pInfo->colUsed; int nArg = 0; UNUSED_PARAM(pUnused); + assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed ); + for(i=0; inConstraint; i++){ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; if( p->usable==0 ) continue; @@ -256893,7 +259641,7 @@ static int fts5VocabOpenMethod( if( rc==SQLITE_OK ){ pVTab->zErrMsg = sqlite3_mprintf( "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl - ); + ); rc = SQLITE_ERROR; } }else{ @@ -257053,9 +259801,19 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ switch( pTab->eType ){ case FTS5_VOCAB_ROW: - if( eDetail==FTS5_DETAIL_FULL ){ - while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ - pCsr->aCnt[0]++; + /* Do not bother counting the number of instances if the "cnt" + ** column is not being read (according to colUsed). */ + if( eDetail==FTS5_DETAIL_FULL && (pCsr->colUsed & 0x04) ){ + while( iPosaCnt[] */ + pCsr->aCnt[0]++; + } } } pCsr->aDoc[0]++; @@ -257153,6 +259911,7 @@ static int fts5VocabFilterMethod( if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; + pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK); if( pEq ){ zTerm = (const char *)sqlite3_value_text(pEq); diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index f64ca017278..eaffd1ec167 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.h +++ b/src/libs/3rdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.46.1" -#define SQLITE_VERSION_NUMBER 3046001 -#define SQLITE_SOURCE_ID "2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33" +#define SQLITE_VERSION "3.47.0" +#define SQLITE_VERSION_NUMBER 3047000 +#define SQLITE_SOURCE_ID "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -772,8 +772,8 @@ struct sqlite3_file { ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, -** PENDING, or EXCLUSIVE lock on the file. It returns true -** if such a lock exists and false otherwise. +** PENDING, or EXCLUSIVE lock on the file. It returns, via its output +** pointer parameter, true if such a lock exists and false otherwise. ** ** The xFileControl() method is a generic interface that allows custom ** VFS implementations to directly control an open file using the @@ -3570,8 +3570,8 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** [[OPEN_EXRESCODE]] ^(
[SQLITE_OPEN_EXRESCODE]
**
The database connection comes up in "extended result code mode". -** In other words, the database behaves has if -** [sqlite3_extended_result_codes(db,1)] where called on the database +** In other words, the database behaves as if +** [sqlite3_extended_result_codes(db,1)] were called on the database ** connection as soon as the connection is created. In addition to setting ** the extended result code mode, this flag also causes [sqlite3_open_v2()] ** to return an extended result code.
@@ -4222,13 +4222,17 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** and sqlite3_prepare16_v3() use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the -** first zero terminator. ^If nByte is positive, then it is the -** number of bytes read from zSql. ^If nByte is zero, then no prepared +** first zero terminator. ^If nByte is positive, then it is the maximum +** number of bytes read from zSql. When nByte is positive, zSql is read +** up to the first zero terminator or until the nByte bytes have been read, +** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string including ** the nul-terminator. +** Note that nByte measure the length of the input in bytes, not +** characters, even for the UTF-16 interfaces. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -5599,7 +5603,7 @@ SQLITE_API int sqlite3_create_window_function( ** This flag instructs SQLite to omit some corner-case optimizations that ** might disrupt the operation of the [sqlite3_value_subtype()] function, ** causing it to return zero rather than the correct subtype(). -** SQL functions that invokes [sqlite3_value_subtype()] should have this +** All SQL functions that invoke [sqlite3_value_subtype()] should have this ** property. If the SQLITE_SUBTYPE property is omitted, then the return ** value from [sqlite3_value_subtype()] might sometimes be zero even though ** a non-zero subtype was specified by the function argument expression. @@ -5615,6 +5619,15 @@ SQLITE_API int sqlite3_create_window_function( ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. +** +** [[SQLITE_SELFORDER1]]
SQLITE_SELFORDER1
+** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate +** that internally orders the values provided to the first argument. The +** ordered-set aggregate SQL notation with a single ORDER BY term can be +** used to invoke this function. If the ordered-set aggregate notation is +** used on a function that lacks this flag, then an error is raised. Note +** that the ordered-set aggregate syntax is only available if SQLite is +** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. **
** */ @@ -5623,6 +5636,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 +#define SQLITE_SELFORDER1 0x002000000 /* ** CAPI3REF: Deprecated Functions @@ -5820,7 +5834,7 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. ** -** Every [application-defined SQL function] that invoke this interface +** Every [application-defined SQL function] that invokes this interface ** should include the [SQLITE_SUBTYPE] property in the text ** encoding argument when the function is [sqlite3_create_function|registered]. ** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() @@ -7427,9 +7441,11 @@ struct sqlite3_module { ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a -** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag - -** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite -** assumes that the strategy may visit at most one row. +** mask of SQLITE_INDEX_SCAN_* flags. One such flag is +** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN] +** output to show the idxNum has hex instead of as decimal. Another flag is +** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will +** return at most one row. ** ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then ** SQLite also assumes that if a call to the xUpdate() method is made as @@ -7493,7 +7509,9 @@ struct sqlite3_index_info { ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ -#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */ +#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */ +#define SQLITE_INDEX_SCAN_HEX 0x00000002 /* Display idxNum as hex */ + /* in EXPLAIN QUERY PLAN */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes @@ -8330,6 +8348,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ +#define SQLITE_TESTCTRL_GETOPT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 @@ -8349,7 +8368,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* @@ -9325,6 +9344,16 @@ typedef struct sqlite3_backup sqlite3_backup; ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. +** +** Alternatives To Using The Backup API +** +** Other techniques for safely creating a consistent backup of an SQLite +** database include: +** +**
    +**
  • The [VACUUM INTO] command. +**
  • The [sqlite3_rsync] utility program. +**
*/ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ @@ -10524,6 +10553,14 @@ typedef struct sqlite3_snapshot { ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** +** If a read-transaction is opened by this function, then it is guaranteed +** that the returned snapshot object may not be invalidated by a database +** writer or checkpointer until after the read-transaction is closed. This +** is not guaranteed if a read-transaction is already open when this +** function is called. In that case, any subsequent write or checkpoint +** operation on the database may invalidate the returned snapshot handle, +** even while the read-transaction remains open. +** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined @@ -10832,8 +10869,6 @@ SQLITE_API int sqlite3_deserialize( #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 -# undef SQLITE_OMIT_WAL -# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ # ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION # endif @@ -13036,6 +13071,10 @@ struct Fts5PhraseIter { ** (i.e. if it is a contentless table), then this API always iterates ** through an empty set (all calls to xPhraseFirst() set iCol to -1). ** +** In all cases, matches are visited in (column ASC, offset ASC) order. +** i.e. all those in column 0, sorted by offset, followed by those in +** column 1, etc. +** ** xPhraseNext() ** See xPhraseFirst above. ** @@ -13102,9 +13141,32 @@ struct Fts5PhraseIter { ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. +** +** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the locale associated +** with column iCol of the current row. Usually, there is no associated +** locale, and output parameters (*pzLocale) and (*pnLocale) are set +** to NULL and 0, respectively. However, if the fts5_locale() function +** was used to associate a locale with the value when it was inserted +** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated +** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) +** is set to the size in bytes of the buffer, not including the +** nul-terminator. +** +** If successful, SQLITE_OK is returned. Or, if an error occurs, an +** SQLite error code is returned. The final value of the output parameters +** is undefined in this case. +** +** xTokenize_v2: +** Tokenize text using the tokenizer belonging to the FTS5 table. This +** API is the same as the xTokenize() API, except that it allows a tokenizer +** locale to be specified. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); @@ -13146,6 +13208,15 @@ struct Fts5ExtensionApi { const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); + + /* Below this point are iVersion>=4 only */ + int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); + int (*xTokenize_v2)(Fts5Context*, + const char *pText, int nText, /* Text to tokenize */ + const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ + void *pCtx, /* Context passed to xToken() */ + int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ + ); }; /* @@ -13166,7 +13237,7 @@ struct Fts5ExtensionApi { ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) -** pointer provided by the application when the fts5_tokenizer object +** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the @@ -13190,7 +13261,7 @@ struct Fts5ExtensionApi { ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** -** The second argument indicates the reason that FTS5 is requesting +** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** @@ -13214,6 +13285,13 @@ struct Fts5ExtensionApi { ** on a columnsize=0 database. ** ** +** The sixth and seventh arguments passed to xTokenize() - pLocale and +** nLocale - are a pointer to a buffer containing the locale to use for +** tokenization (e.g. "en_US") and its size in bytes, respectively. The +** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in +** which case nLocale is always 0) to indicate that the tokenizer should +** use its default locale. +** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth @@ -13237,6 +13315,30 @@ struct Fts5ExtensionApi { ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** +** If the tokenizer is registered using an fts5_tokenizer_v2 object, +** then the xTokenize() method has two additional arguments - pLocale +** and nLocale. These specify the locale that the tokenizer should use +** for the current request. If pLocale and nLocale are both 0, then the +** tokenizer should use its default locale. Otherwise, pLocale points to +** an nLocale byte buffer containing the name of the locale to use as utf-8 +** text. pLocale is not nul-terminated. +** +** FTS5_TOKENIZER +** +** There is also an fts5_tokenizer object. This is an older, deprecated, +** version of fts5_tokenizer_v2. It is similar except that: +** +**
    +**
  • There is no "iVersion" field, and +**
  • The xTokenize() method does not take a locale argument. +**
+** +** Legacy fts5_tokenizer tokenizers must be registered using the +** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). +** +** Tokenizer implementations registered using either API may be retrieved +** using both xFindTokenizer() and xFindTokenizer_v2(). +** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a @@ -13345,6 +13447,33 @@ struct Fts5ExtensionApi { ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; +typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; +struct fts5_tokenizer_v2 { + int iVersion; /* Currently always 2 */ + + int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); + void (*xDelete)(Fts5Tokenizer*); + int (*xTokenize)(Fts5Tokenizer*, + void *pCtx, + int flags, /* Mask of FTS5_TOKENIZE_* flags */ + const char *pText, int nText, + const char *pLocale, int nLocale, + int (*xToken)( + void *pCtx, /* Copy of 2nd argument to xTokenize() */ + int tflags, /* Mask of FTS5_TOKEN_* flags */ + const char *pToken, /* Pointer to buffer containing token */ + int nToken, /* Size of token in bytes */ + int iStart, /* Byte offset of token within input text */ + int iEnd /* Byte offset of end of token within input text */ + ) + ); +}; + +/* +** New code should use the fts5_tokenizer_v2 type to define tokenizer +** implementations. The following type is included for legacy applications +** that still use it. +*/ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); @@ -13364,6 +13493,7 @@ struct fts5_tokenizer { ); }; + /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 @@ -13383,7 +13513,7 @@ struct fts5_tokenizer { */ typedef struct fts5_api fts5_api; struct fts5_api { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( @@ -13410,6 +13540,25 @@ struct fts5_api { fts5_extension_function xFunction, void (*xDestroy)(void*) ); + + /* APIs below this point are only available if iVersion>=3 */ + + /* Create a new tokenizer */ + int (*xCreateTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void *pUserData, + fts5_tokenizer_v2 *pTokenizer, + void (*xDestroy)(void*) + ); + + /* Find an existing tokenizer */ + int (*xFindTokenizer_v2)( + fts5_api *pApi, + const char *zName, + void **ppUserData, + fts5_tokenizer_v2 **ppTokenizer + ); }; /* From da9cbc7b4655e8b1a861c9a5beb55a06e6ef5c4d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 30 Oct 2024 10:57:13 +0100 Subject: [PATCH 053/322] QmlDesigner: Notifier about missing qml document Change-Id: Ib04d915aa7c277149bf3dcef2051640f2ce9562a Reviewed-by: Thomas Hartmann --- .../projectstorageerrornotifier.cpp | 10 +++++++++ .../projectstorageerrornotifier.h | 4 ++++ .../projectstorageerrornotifierinterface.h | 6 +++++ .../projectstorage/projectstorageupdater.cpp | 17 ++++++++++---- .../projectstorage/projectstorageupdater.h | 10 +++++++-- .../qmldesigner/qmldesignerprojectmanager.cpp | 1 + .../mocks/projectstorageerrornotifiermock.h | 8 +++++++ .../projectstorageupdater-test.cpp | 22 +++++++++++++++++++ 8 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.cpp index a5646d39895..b722e90dd4f 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.cpp @@ -30,4 +30,14 @@ void ProjectStorageErrorNotifier::propertyNameDoesNotExists(Utils::SmallStringVi << " in file: " << m_pathCache.sourcePath(sourceId).toStringView(); } +void ProjectStorageErrorNotifier::qmlDocumentDoesNotExistsForQmldirEntry(Utils::SmallStringView typeName, + Storage::Version, + SourceId qmlDocumentSourceId, + SourceId qmldirSourceId) +{ + qDebug() << "Not existing Qml Document " + << m_pathCache.sourcePath(qmlDocumentSourceId).toStringView() << " for type " + << typeName << " in file: " << m_pathCache.sourcePath(qmldirSourceId).toStringView(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.h index f3afcd0c459..bea20ce394c 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifier.h @@ -23,6 +23,10 @@ public: Utils::SmallStringView propertyName, SourceId sourceId) override; void propertyNameDoesNotExists(Utils::SmallStringView propertyName, SourceId sourceId) override; + void qmlDocumentDoesNotExistsForQmldirEntry(Utils::SmallStringView typeName, + Storage::Version version, + SourceId qmlDocumentSourceId, + SourceId qmldirSourceId) override; private: PathCacheType &m_pathCache; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifierinterface.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifierinterface.h index 5d370519037..34cb1638d66 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifierinterface.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageerrornotifierinterface.h @@ -4,6 +4,7 @@ #pragma once #include "projectstorageids.h" +#include "projectstorageinfotypes.h" #include @@ -24,6 +25,11 @@ public: SourceId sourceId) = 0; virtual void propertyNameDoesNotExists(Utils::SmallStringView propertyName, SourceId sourceId) = 0; + virtual void qmlDocumentDoesNotExistsForQmldirEntry(Utils::SmallStringView typeName, + Storage::Version version, + SourceId qmlDocumentSourceId, + SourceId qmldirSourceId) + = 0; protected: ~ProjectStorageErrorNotifierInterface() = default; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp index 1a1083641a2..c9b7860f142 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.cpp @@ -415,7 +415,8 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat package, notUpdatedSourceIds, watchedSourceIdsIds, - qmldirState); + qmldirState, + qmldirSourceId); tracer.tick("append updated project source id", keyValue("module id", moduleId)); package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); } @@ -1058,7 +1059,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds, - FileState qmldirState) + FileState qmldirState, + SourceId qmldirSourceId) { NanotraceHR::Tracer tracer{"parse qml component", category(), @@ -1096,6 +1098,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; case FileState::NotExists: tracer.tick("file does not exits", keyValue("source id", sourceId)); + for (const auto &exportedType : exportedTypes) + m_errorNotifier.qmlDocumentDoesNotExistsForQmldirEntry(exportedType.name, + exportedType.version, + sourceId, + qmldirSourceId); break; case FileState::Changed: tracer.tick("update from qml document", keyValue("source id", sourceId)); @@ -1211,7 +1218,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds, - FileState qmldirState) + FileState qmldirState, + SourceId qmldirSourceId) { NanotraceHR::Tracer tracer{"parse qml components", category(), @@ -1234,7 +1242,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, package, notUpdatedSourceIds, watchedSourceIdsIds, - qmldirState); + qmldirState, + qmldirSourceId); }; rangeForTheSameFileName(components, callback); diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h index 6c3353ff68a..1a21e7d0322 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageupdater.h @@ -4,6 +4,7 @@ #pragma once #include "filestatus.h" +#include "projectstorageerrornotifier.h" #include "projectstorageids.h" #include "projectstoragepathwatchernotifierinterface.h" #include "projectstoragepathwatchertypes.h" @@ -48,6 +49,7 @@ public: QmlDocumentParserInterface &qmlDocumentParser, QmlTypesParserInterface &qmlTypesParser, class ProjectStoragePathWatcherInterface &pathWatcher, + ProjectStorageErrorNotifierInterface &errorNotifier, ProjectPartId projectPartId) : m_fileSystem{fileSystem} , m_projectStorage{projectStorage} @@ -56,6 +58,7 @@ public: , m_qmlDocumentParser{qmlDocumentParser} , m_qmlTypesParser{qmlTypesParser} , m_pathWatcher{pathWatcher} + , m_errorNotifier{errorNotifier} , m_projectPartId{projectPartId} {} @@ -219,7 +222,8 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds, - FileState qmldirState); + FileState qmldirState, + SourceId qmldirSourceId); void parseQmlComponent(Utils::SmallStringView fileName, Utils::SmallStringView directory, Storage::Synchronization::ExportedTypes exportedTypes, @@ -227,7 +231,8 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds, - FileState qmldirState); + FileState qmldirState, + SourceId qmldirSourceId); void parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); @@ -245,6 +250,7 @@ private: QmlDocumentParserInterface &m_qmlDocumentParser; QmlTypesParserInterface &m_qmlTypesParser; ProjectStoragePathWatcherInterface &m_pathWatcher; + ProjectStorageErrorNotifierInterface &m_errorNotifier; ProjectPartId m_projectPartId; }; diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 3ca5e479a05..faf59f62c77 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -193,6 +193,7 @@ public: qmlDocumentParser, qmlTypesParser, pathWatcher, + errorNotifier, projectPartId} {} Sqlite::Database database; diff --git a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h index 1142342f582..17281682974 100644 --- a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h +++ b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h @@ -24,4 +24,12 @@ public: propertyNameDoesNotExists, (Utils::SmallStringView propertyName, QmlDesigner::SourceId sourceId), (override)); + + MOCK_METHOD(void, + qmlDocumentDoesNotExistsForQmldirEntry, + (Utils::SmallStringView typeName, + QmlDesigner::Storage::Version version, + QmlDesigner::SourceId qmlDocumentSourceId, + QmlDesigner::SourceId qmldirSourceId), + (override)); }; diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index fae632adde0..478f811378c 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -337,6 +338,7 @@ protected: QmlDesigner::SourcePathCache sourcePathCache{ staticData->sourcePathStorage}; NiceMock patchWatcherMock; + NiceMock errorNotifierMock; QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1); QmlDesigner::ProjectPartId otherProjectPartId = QmlDesigner::ProjectPartId::create(0); QmlDesigner::ProjectStorageUpdater updater{fileSystemMock, @@ -346,6 +348,7 @@ protected: qmlDocumentParserMock, qmlTypesParserMock, patchWatcherMock, + errorNotifierMock, projectPartId}; SourceId qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes"); SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/example2.qmltypes"); @@ -2191,6 +2194,25 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) updater.update({.directories = directories}); } +TEST_F(ProjectStorageUpdater, warn_about_non_existing_qml_document) +{ + setFilesDontExists({qmlDocumentSourceId1}); + setFilesAdded({directoryPathSourceId}); + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml)"}; + setQmlFileNames(u"/path", {"First2.qml"}); + setContent(u"/path/qmldir", qmldir); + + EXPECT_CALL(errorNotifierMock, + qmlDocumentDoesNotExistsForQmldirEntry(Eq("FirstType"), + IsVersion(1, 0), + qmlDocumentSourceId1, + qmlDirPathSourceId)); + + updater.update({.directories = directories}); +} + TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_parsed_type_if_qml_document_does_not_exists) { From 246bfc0725084659298da269be1835f99933c14d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 30 Oct 2024 14:53:54 +0100 Subject: [PATCH 054/322] NanotraceHR: Add concept To provide early checks, and not some deep template errors. Change-Id: I53e7acc9337661f719868325ea4b979aaa2d741c Reviewed-by: Thomas Hartmann --- src/libs/nanotrace/nanotracehr.h | 179 +++++++++++++------------------ 1 file changed, 77 insertions(+), 102 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index b6a30ec43cd..8f19f4d6780 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -87,6 +87,24 @@ struct IsDictonary inline constexpr IsDictonary isDictonary; +template +concept HasTupleSize = requires { std::tuple_size_v == size; }; + +template +concept CanConvert = requires(Entry entry, StaticString<1000> string) { + convertToString(string, entry); +}; + +namespace Internal { +template +concept KeyValueBase = CanConvert> + && CanConvert> && HasTupleSize<2, Type>; +} + +template +concept KeyValue = Internal::KeyValueBase + && !Internal::KeyValueBase>; + template void convertToString(String &string, std::string_view text) { @@ -215,7 +233,7 @@ void convertDictonaryToString(String &string, const IsDictonary &, Entries &...e string.append('}'); } -template +template void convertToString(String &string, const std::tuple &dictonary) { std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary); @@ -348,7 +366,7 @@ auto keyValue(const Key &key, Value &&value) return std::tuple>(key, value); } -template +template auto dictonary(Entries &&...entries) { return std::make_tuple(isDictonary, std::forward(entries)...); @@ -356,6 +374,7 @@ auto dictonary(Entries &&...entries) template auto array(Entries &&...entries) + requires(!KeyValue && ...) { return std::make_tuple(isArray, std::forward(entries)...); } @@ -656,35 +675,26 @@ public: ~Token() {} - template - [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, KeyValue auto &&...) { return {}; } - template [[nodiscard]] std::pair beginAsynchronousWithFlow( - ArgumentType, Arguments &&...) + ArgumentType, KeyValue auto &&...) { return {}; } - template - [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) - { - return {}; - } + [[nodiscard]] TracerType beginDuration(ArgumentType, KeyValue auto &&...) { return {}; } - template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, - Arguments &&...) + KeyValue auto &&...) { return {}; } - template - void tick(ArgumentType, Arguments &&...) - {} + void tick(ArgumentType, KeyValue auto &&...) {} }; template @@ -715,7 +725,7 @@ public: ~Token() {} - template + template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) { if (m_id) @@ -724,7 +734,7 @@ public: return {std::move(name), m_id, m_category}; } - template + template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType name, Arguments &&...arguments) { @@ -741,13 +751,13 @@ public: std::forward_as_tuple(std::move(name), bindId, m_category)}; } - template + template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { return {traceName, m_category, std::forward(arguments)...}; } - template + template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, Arguments &&...arguments) { @@ -763,7 +773,7 @@ public: std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_category)}; } - template + template void tick(ArgumentType name, Arguments &&...arguments) { m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); @@ -804,42 +814,26 @@ public: [[nodiscard]] TokenType createToken() { return {}; } - template - [[nodiscard]] AsynchronousToken begin(ArgumentType, Arguments &&...) + [[nodiscard]] AsynchronousToken begin(ArgumentType, KeyValue auto &&...) { return {}; } + + [[nodiscard]] AsynchronousToken begin(const FlowTokenType &, ArgumentType, KeyValue auto &&...) { return {}; } - template - [[nodiscard]] AsynchronousToken begin(const FlowTokenType &, ArgumentType, Arguments &&...) - { - return {}; - } - - template [[nodiscard]] std::pair beginWithFlow(ArgumentType, - Arguments &&...) + KeyValue auto &&...) { return {}; } - template - void tick(ArgumentType, Arguments &&...) - {} + void tick(ArgumentType, KeyValue auto &&...) {} - template - void tick(const FlowTokenType &, ArgumentType, Arguments &&...) - {} + void tick(const FlowTokenType &, ArgumentType, KeyValue auto &&...) {} - template - FlowTokenType tickWithFlow(ArgumentType, Arguments &&...) - { - return {}; - } + FlowTokenType tickWithFlow(ArgumentType, KeyValue auto &&...) { return {}; } - template - void end(Arguments &&...) - {} + void end(KeyValue auto &&...) {} static constexpr bool categoryIsActive() { return Category::isActive(); } }; @@ -903,7 +897,7 @@ public: [[nodiscard]] TokenType createToken() { return {m_id, m_category}; } - template + template [[nodiscard]] AsynchronousToken begin(ArgumentType name, Arguments &&...arguments) { if (m_id) @@ -912,7 +906,7 @@ public: return AsynchronousToken{std::move(name), m_id, m_category}; } - template + template [[nodiscard]] AsynchronousToken begin(const FlowTokenType &flowToken, ArgumentType name, Arguments &&...arguments) @@ -928,7 +922,7 @@ public: return AsynchronousToken{std::move(name), m_id, m_category}; } - template + template [[nodiscard]] std::pair beginWithFlow(ArgumentType name, Arguments &&...arguments) { @@ -945,7 +939,7 @@ public: std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } - template + template void tick(ArgumentType name, Arguments &&...arguments) { if (m_id) { @@ -954,7 +948,7 @@ public: } } - template + template void tick(const FlowTokenType &flowToken, ArgumentType name, Arguments &&...arguments) { if (m_id) { @@ -967,7 +961,7 @@ public: } } - template + template FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments) { std::size_t bindId = 0; @@ -981,7 +975,7 @@ public: return {PrivateTag{}, std::move(name), bindId, m_category}; } - template + template void end(Arguments &&...arguments) { if (m_id && m_name.size()) @@ -1020,38 +1014,28 @@ public: ~FlowToken() {} - template - [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, Arguments &&...) + [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType, KeyValue auto &&...) { return {}; } - template - [[nodiscard]] std::pair beginAsynchronousWithFlow(ArgumentType, - Arguments &&...) + [[nodiscard]] std::pair beginAsynchronousWithFlow( + ArgumentType, KeyValue auto &&...) { return {}; } - template - void connectTo(const AsynchronousTokenType &, Arguments &&...) - {} + void connectTo(const AsynchronousTokenType &, KeyValue auto &&...) {} - template - [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) - { - return {}; - } + [[nodiscard]] TracerType beginDuration(ArgumentType, KeyValue auto &&...) { return {}; } - template - [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, Arguments &&...) + [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType, + KeyValue auto &&...) { return std::pair(); } - template - void tick(ArgumentType, Arguments &&...) - {} + void tick(ArgumentType, KeyValue auto &&...) {} }; template @@ -1087,7 +1071,7 @@ public: ~FlowToken() {} - template + template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType name, Arguments &&...arguments) { std::size_t id = 0; @@ -1101,7 +1085,7 @@ public: return {std::move(name), id, m_category}; } - template + template [[nodiscard]] std::pair beginAsynchronousWithFlow( ArgumentType name, Arguments &&...arguments) { @@ -1120,7 +1104,7 @@ public: std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } - template + template void connectTo(const AsynchronousTokenType &token, Arguments &&...arguments) { if (m_bindId && token.m_id) { @@ -1133,13 +1117,13 @@ public: } } - template + template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { return {m_bindId, IsFlow::In, traceName, m_category, std::forward(arguments)...}; } - template + template [[nodiscard]] std::pair beginDurationWithFlow(ArgumentType traceName, Arguments &&...arguments) { @@ -1153,7 +1137,7 @@ public: std::forward_as_tuple(PrivateTag{}, traceName, m_bindId, m_category)}; } - template + template void tick(ArgumentType name, Arguments &&...arguments) { m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward(arguments)...); @@ -1432,9 +1416,7 @@ public: friend FlowTokenType; friend Category; - template - [[nodiscard]] Tracer(ArgumentType, Category &, Arguments &&...) - {} + [[nodiscard]] Tracer(ArgumentType, Category &, KeyValue auto &&...) {} Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; @@ -1443,19 +1425,11 @@ public: TokenType createToken() { return {}; } - template - Tracer beginDuration(ArgumentType, Arguments &&...) - { - return {}; - } + Tracer beginDuration(ArgumentType, KeyValue auto &&...) { return {}; } - template - void tick(ArgumentType, Arguments &&...) - {} + void tick(ArgumentType, KeyValue auto &&...) {} - template - void end(Arguments &&...) - {} + void end(KeyValue auto &&...) {} ~Tracer() {} }; @@ -1475,7 +1449,7 @@ class Tracer friend TokenType; friend Category; - template + template [[nodiscard]] Tracer(std::size_t bindId, IsFlow flow, ArgumentType name, @@ -1491,7 +1465,7 @@ class Tracer } public: - template + template [[nodiscard]] Tracer(PrivateTag, std::size_t bindId, IsFlow flow, @@ -1501,7 +1475,7 @@ public: : Tracer{bindId, flow, std::move(name), category, std::forward(arguments)...} {} - template + template [[nodiscard]] Tracer(ArgumentType name, CategoryFunctionPointer category, Arguments &&...arguments) : m_name{name} , m_category{category} @@ -1510,7 +1484,7 @@ public: sendBeginTrace(std::forward(arguments)...); } - template + template [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments) : Tracer(std::move(name), category.self(), std::forward(arguments)...) {} @@ -1524,19 +1498,19 @@ public: ~Tracer() { sendEndTrace(); } - template + template Tracer beginDuration(ArgumentType name, Arguments &&...arguments) { return {std::move(name), m_category, std::forward(arguments)...}; } - template + template void tick(ArgumentType name, Arguments &&...arguments) { m_category().begin('i', 0, name, 0, IsFlow::No, std::forward(arguments)...); } - template + template void end(Arguments &&...arguments) { sendEndTrace(std::forward(arguments)...); @@ -1544,7 +1518,7 @@ public: } private: - template + template void sendBeginTrace(Arguments &&...arguments) { auto &category = m_category(); @@ -1561,7 +1535,7 @@ private: } } - template + template void sendEndTrace(Arguments &&...arguments) { if (m_name.size()) { @@ -1588,8 +1562,9 @@ private: CategoryFunctionPointer m_category; }; -template -Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...) - -> Tracer; +template +Tracer(typename Category::ArgumentType name, + Category &category, + Arguments &&...) -> Tracer; } // namespace NanotraceHR From da95acc43dce0be80feb529e6bee482222cdde21 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 30 Oct 2024 18:16:52 +0100 Subject: [PATCH 055/322] QmlDesigner: Use more emplace_back Instead of copying a temporary, emplace is creating the instance in place. It can lead to less cluttered code too. Change-Id: I9b136579110079f5746da9514689dd2a1a2a3058 Reviewed-by: Thomas Hartmann --- .../designercoreutils/stylesheetmerger.cpp | 3 +-- .../libs/designercore/include/forwardview.h | 2 +- .../designercore/model/bindingproperty.cpp | 2 +- .../libs/designercore/model/model.cpp | 4 ++-- .../designercore/model/nodelistproperty.cpp | 3 ++- .../qmldesigner/qmltools/qmlitemnode.cpp | 16 ++++++++-------- src/plugins/qmldesigner/qmltools/qmlstate.cpp | 6 +++--- .../qmldesigner/qmltools/qmltimeline.cpp | 2 +- .../qmldesigner/qmltools/qmlvisualnode.cpp | 18 +++++++++--------- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/designercoreutils/stylesheetmerger.cpp b/src/plugins/qmldesigner/libs/designercore/designercoreutils/stylesheetmerger.cpp index 50059428841..d1dbca9e8e2 100644 --- a/src/plugins/qmldesigner/libs/designercore/designercoreutils/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/designercoreutils/stylesheetmerger.cpp @@ -169,8 +169,7 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo continue; if (isTextAlignmentProperty(variantProperty) && !m_options.preserveTextAlignment && !styleNode.hasProperty(variantProperty.name())) continue; - propertyList.append(QPair(variantProperty.name().toByteArray(), - variantProperty.value())); + propertyList.emplace_back(variantProperty.name().toByteArray(), variantProperty.value()); } #ifdef QDS_USE_PROJECTSTORAGE diff --git a/src/plugins/qmldesigner/libs/designercore/include/forwardview.h b/src/plugins/qmldesigner/libs/designercore/include/forwardview.h index 386ac83141c..9cfd19fafd3 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/forwardview.h +++ b/src/plugins/qmldesigner/libs/designercore/include/forwardview.h @@ -125,7 +125,7 @@ static QList adjustedList(const QList& oldList, AbstractView *view) for (const T &item : oldList) { - newList.append(T(item, view)); + newList.emplace_back(item, view); } return newList; diff --git a/src/plugins/qmldesigner/libs/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/bindingproperty.cpp index 3086dbcd8f2..6b53deadf48 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/bindingproperty.cpp @@ -175,7 +175,7 @@ QList BindingProperty::resolveToModelNodeList() const const QStringList simplifiedList = commaSeparatedSimplifiedStringList(binding); for (const QString &nodeId : simplifiedList) { if (auto internalNode = privateModel()->nodeForId(nodeId)) - returnList.append(ModelNode{internalNode, model(), view()}); + returnList.emplace_back(internalNode, model(), view()); } } return returnList; diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index 4742c1a8a81..bc36e8948d7 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -1228,7 +1228,7 @@ QList ModelPrivate::toModelNodeList(Utils::span modelNodeList; modelNodeList.reserve(nodeList.size()); for (const InternalNodePointer &node : nodeList) - modelNodeList.append(ModelNode(node, m_model, view)); + modelNodeList.emplace_back(node, m_model, view); return modelNodeList; } @@ -1342,7 +1342,7 @@ static QList toPropertyPairList(const QList &p propertyPairList.reserve(propertyList.size()); for (const InternalProperty *property : propertyList) - propertyPairList.append({property->propertyOwner(), property->name()}); + propertyPairList.emplace_back(property->propertyOwner(), property->name()); return propertyPairList; } diff --git a/src/plugins/qmldesigner/libs/designercore/model/nodelistproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/nodelistproperty.cpp index c1d307fab44..c77771a52da 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/nodelistproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/nodelistproperty.cpp @@ -43,8 +43,9 @@ Internal::InternalNodeListPropertyPointer &NodeListProperty::internalNodeListPro static QList internalNodesToModelNodes(const auto &inputList, Model *model, AbstractView *view) { QList modelNodeList; + modelNodeList.reserve(inputList.size()); for (const Internal::InternalNode::Pointer &internalNode : inputList) - modelNodeList.append(ModelNode(internalNode, model, view)); + modelNodeList.emplace_back(internalNode, model, view); return modelNodeList; } diff --git a/src/plugins/qmldesigner/qmltools/qmlitemnode.cpp b/src/plugins/qmldesigner/qmltools/qmlitemnode.cpp index d12998aae14..c6506db4bb7 100644 --- a/src/plugins/qmldesigner/qmltools/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/qmltools/qmlitemnode.cpp @@ -71,9 +71,9 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS NodeMetaInfo metaInfo = view->model()->metaInfo("QtQuick.Image"); QList > propertyPairList; if (const int intX = qRound(position.x())) - propertyPairList.append({PropertyName("x"), QVariant(intX)}); + propertyPairList.emplace_back("x", intX); if (const int intY = qRound(position.y())) - propertyPairList.append({PropertyName("y"), QVariant(intY)}); + propertyPairList.emplace_back("y", intY); QString relativeImageName = imageName; @@ -81,7 +81,7 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS if (QFileInfo::exists(view->model()->fileUrl().toLocalFile())) { QDir fileDir(QFileInfo(view->model()->fileUrl().toLocalFile()).absolutePath()); relativeImageName = fileDir.relativeFilePath(imageName); - propertyPairList.append({PropertyName("source"), QVariant(relativeImageName)}); + propertyPairList.emplace_back("source", relativeImageName); } #ifdef QDS_USE_PROJECTSTORAGE @@ -147,12 +147,12 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view, auto doCreateQmlItemNodeFromFont = [=, &newQmlItemNode, &parentproperty]() { QList> propertyPairList; if (const int intX = qRound(position.x())) - propertyPairList.append({PropertyName("x"), QVariant(intX)}); + propertyPairList.emplace_back("x", intX); if (const int intY = qRound(position.y())) - propertyPairList.append({PropertyName("y"), QVariant(intY)}); - propertyPairList.append({PropertyName("font.family"), QVariant(fontFamily)}); - propertyPairList.append({PropertyName("font.pointSize"), 20}); - propertyPairList.append({PropertyName("text"), QVariant(fontFamily)}); + propertyPairList.emplace_back("y", intY); + propertyPairList.emplace_back("font.family", fontFamily); + propertyPairList.emplace_back("font.pointSize", 20); + propertyPairList.emplace_back("text", fontFamily); #ifdef QDS_USE_PROJECTSTORAGE newQmlItemNode = QmlItemNode(view->createModelNode("Text", propertyPairList)); #else diff --git a/src/plugins/qmldesigner/qmltools/qmlstate.cpp b/src/plugins/qmldesigner/qmltools/qmlstate.cpp index 60ac06f2881..e76e4787d92 100644 --- a/src/plugins/qmldesigner/qmltools/qmlstate.cpp +++ b/src/plugins/qmldesigner/qmltools/qmlstate.cpp @@ -71,7 +71,7 @@ QList QmlModelState::propertyChanges() const for (const ModelNode &childNode : nodes) { //### exception if not valid QmlModelStateOperation if (QmlPropertyChanges::isValidQmlPropertyChanges(childNode)) - returnList.append(QmlPropertyChanges(childNode)); + returnList.emplace_back(childNode); } } @@ -114,7 +114,7 @@ QList QmlModelState::stateOperations() const for (const ModelNode &childNode : nodes) { //### exception if not valid QmlModelStateOperation if (QmlModelStateOperation::isValidQmlModelStateOperation(childNode)) - returnList.append(QmlModelStateOperation(childNode)); + returnList.emplace_back(childNode); } } @@ -198,7 +198,7 @@ QList QmlModelState::allAffectedNodes() const for (const ModelNode &childNode : nodes) { if (QmlModelStateOperation::isValidQmlModelStateOperation(childNode) && !returnList.contains(QmlModelStateOperation(childNode).target())) - returnList.append(QmlModelStateOperation(childNode).target()); + returnList.emplace_back(QmlModelStateOperation(childNode).target()); } return returnList; diff --git a/src/plugins/qmldesigner/qmltools/qmltimeline.cpp b/src/plugins/qmldesigner/qmltools/qmltimeline.cpp index 676d712be63..961aee6876f 100644 --- a/src/plugins/qmldesigner/qmltools/qmltimeline.cpp +++ b/src/plugins/qmldesigner/qmltools/qmltimeline.cpp @@ -298,7 +298,7 @@ QList QmlTimeline::allKeyframeGroups() const for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(childNode)) - returnList.append(QmlTimelineKeyframeGroup(childNode)); + returnList.emplace_back(childNode); } return returnList; diff --git a/src/plugins/qmldesigner/qmltools/qmlvisualnode.cpp b/src/plugins/qmldesigner/qmltools/qmlvisualnode.cpp index 5e919a40c9b..745db5b28ae 100644 --- a/src/plugins/qmldesigner/qmltools/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/qmltools/qmlvisualnode.cpp @@ -352,13 +352,13 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, if (property.type() == "binding") { const QString value = QmlObjectNode::convertToCorrectTranslatableFunction( property.value().toString(), view->externalDependencies().designerSettings()); - propertyBindingList.append(PropertyBindingEntry(property.name(), value)); + propertyBindingList.emplace_back(property.name(), value); } else if (property.type() == "enum") { - propertyEnumList.append(PropertyBindingEntry(property.name(), property.value().toString())); + propertyEnumList.emplace_back(property.name(), property.value().toString()); } else if (property.value().toString() == QString::fromLatin1(imagePlaceHolder)) { - propertyPairList.append({property.name(), imagePlaceHolderPath(view)}); + propertyPairList.emplace_back(property.name(), imagePlaceHolderPath(view)); } else { - propertyPairList.append({property.name(), property.value()}); + propertyPairList.emplace_back(property.name(), property.value()); } } // Add position last so it'll override any default position specified in the entry @@ -611,16 +611,16 @@ QList > QmlVisualNode::Position::propertyPairList( if (m_is3D) { if (!qFuzzyIsNull(x())) - propertyPairList.append({"x", QVariant{x()}}); + propertyPairList.emplace_back("x", x()); if (!qFuzzyIsNull(y())) - propertyPairList.append({"y", QVariant{y()}}); + propertyPairList.emplace_back("y", y()); if (!qFuzzyIsNull(z())) - propertyPairList.append({"z", QVariant{z()}}); + propertyPairList.emplace_back("z", z()); } else { if (const int intX = qRound(x())) - propertyPairList.append({"x", QVariant(intX)}); + propertyPairList.emplace_back("x", intX); if (const int intY = qRound(y())) - propertyPairList.append({"y", QVariant(intY)}); + propertyPairList.emplace_back("y", intY); } return propertyPairList; From fa9305263c8d86e59ccf4ecca21c4bae871251d9 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 28 Oct 2024 16:49:21 +0100 Subject: [PATCH 056/322] Remove using namespace Remove using namespace to avoid name clashes with upcoming code. Change-Id: I83954b7377e5ea1da179e0a2059c5c6a3950c3ec Reviewed-by: Marco Bubke --- .../components/integration/designdocument.cpp | 31 ++++++++++++------- .../qmlpreviewplugin/qmlpreviewactions.cpp | 13 ++++---- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index df792810a48..c7508fbe8f9 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -50,8 +50,6 @@ #include #include -using namespace ProjectExplorer; - enum { debug = false }; @@ -752,29 +750,38 @@ void DesignDocument::redo() viewManager().resetPropertyEditorView(); } -static Target *getActiveTarget(DesignDocument *designDocument) +static ProjectExplorer::Target *getActiveTarget(DesignDocument *designDocument) { - Project *currentProject = ProjectManager::projectForFile(designDocument->fileName()); + auto currentProject = ProjectExplorer::ProjectManager::projectForFile(designDocument->fileName()); if (!currentProject) - currentProject = ProjectTree::currentProject(); + currentProject = ProjectExplorer::ProjectTree::currentProject(); if (!currentProject) return nullptr; - QObject::connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, - designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection); + QObject::connect(ProjectExplorer::ProjectTree::instance(), + &ProjectExplorer::ProjectTree::currentProjectChanged, + designDocument, + &DesignDocument::updateActiveTarget, + Qt::UniqueConnection); - QObject::connect(currentProject, &Project::activeTargetChanged, - designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection); + QObject::connect(currentProject, + &ProjectExplorer::Project::activeTargetChanged, + designDocument, + &DesignDocument::updateActiveTarget, + Qt::UniqueConnection); - Target *target = currentProject->activeTarget(); + auto target = currentProject->activeTarget(); if (!target || !target->kit()->isValid()) return nullptr; - QObject::connect(target, &Target::kitChanged, - designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection); + QObject::connect(target, + &ProjectExplorer::Target::kitChanged, + designDocument, + &DesignDocument::updateActiveTarget, + Qt::UniqueConnection); return target; } diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp index f44cb0ab24d..8352c558c1b 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp @@ -25,8 +25,6 @@ namespace QmlDesigner { -using namespace ProjectExplorer; - const Utils::Icon previewIcon({ {":/qmlpreviewplugin/images/live_preview.png", Utils::Theme::IconsBaseColor}}); const QByteArray livePreviewId = "LivePreview"; @@ -36,11 +34,11 @@ static void handleAction(const SelectionContext &context) if (context.isValid()) { if (context.toggled()) { bool skipDeploy = false; - if (const Target *startupTarget = ProjectManager::startupTarget()) { - const Kit *kit = startupTarget->kit(); + if (const auto startupTarget = ProjectExplorer::ProjectManager::startupTarget()) { + const auto kit = startupTarget->kit(); if (kit && (kit->supportedPlatforms().contains(Android::Constants::ANDROID_DEVICE_TYPE) - || DeviceTypeKitAspect::deviceTypeId(kit) + || ProjectExplorer::DeviceTypeKitAspect::deviceTypeId(kit) == Android::Constants::ANDROID_DEVICE_TYPE)) { skipDeploy = true; // In case of an android kit we don't want the live preview button to be toggled @@ -52,7 +50,8 @@ static void handleAction(const SelectionContext &context) interface->action()->setChecked(false); } } - ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE, skipDeploy); + ProjectExplorer::ProjectExplorerPlugin::runStartupProject( + ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE, skipDeploy); } else { QmlPreviewWidgetPlugin::stopAllRunControls(); } @@ -244,7 +243,7 @@ QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent) connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, comboBox, refreshComboBoxFunction); - if (auto project = ProjectManager::startupProject()) + if (auto project = ProjectExplorer::ProjectManager::startupProject()) refreshComboBoxFunction(project); // do this after refreshComboBoxFunction so we do not get currentLocaleChanged signals at initialization From 5bd13d609c8862b71d9d293388012e2685db8cc7 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 25 Oct 2024 13:38:00 +0300 Subject: [PATCH 057/322] EffectComposer: Open the shader code which is not empty on opening * A non-empty shader tab is selected by opening the code editor * Code editor stays on top * Code editor closes when another window is activated Fixes: QDS-13914 Fixes: QDS-13845 Change-Id: I3b6709312bb05f51d07c112f118dd3ef9a36b6c6 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../effectshaderscodeeditor.cpp | 34 ++++++++++++++++++- .../effectcomposer/effectshaderscodeeditor.h | 1 + 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 5b0627a22ef..d1491454442 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -43,6 +43,7 @@ EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget * , m_settings(new QSettings(qApp->organizationName(), qApp->applicationName(), this)) { setWindowFlag(Qt::Tool, true); + setWindowFlag(Qt::WindowStaysOnTopHint); setWindowTitle(title); m_fragmentEditor = createJSEditor(); @@ -75,7 +76,6 @@ void EffectShadersCodeEditor::showWidget() readAndApplyLiveUpdateSettings(); show(); raise(); - m_vertexEditor->setFocus(); setOpened(true); } @@ -158,6 +158,8 @@ EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() editorWidget->setParent(this); editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + editorWidget->installEventFilter(this); + return editorWidget; } @@ -173,6 +175,19 @@ void EffectShadersCodeEditor::setupUIComponents() verticalLayout->addWidget(createToolbar()); verticalLayout->addWidget(tabWidget); + connect(this, &EffectShadersCodeEditor::openedChanged, tabWidget, [this, tabWidget](bool opened) { + if (!opened) + return; + + QWidget *widgetToSelect = (m_vertexEditor->document()->isEmpty() + && !m_fragmentEditor->document()->isEmpty()) + ? m_fragmentEditor.get() + : m_vertexEditor.get(); + tabWidget->setCurrentWidget(widgetToSelect); + widgetToSelect->setFocus(); + activateWindow(); + }); + this->resize(660, 240); } @@ -195,6 +210,23 @@ void EffectShadersCodeEditor::closeEvent(QCloseEvent *event) setOpened(false); } +bool EffectShadersCodeEditor::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == event->FocusOut) { + QFocusEvent *focusEvent = dynamic_cast(event); + if (focusEvent && focusEvent->reason() == Qt::ActiveWindowFocusReason) { + connect( + qApp, + &QApplication::focusWindowChanged, + this, + &EffectShadersCodeEditor::close, + Qt::SingleShotConnection); + } + } + + return QWidget::eventFilter(watched, event); +} + void EffectShadersCodeEditor::writeLiveUpdateSettings() { m_settings->setValue(EFFECTCOMPOSER_LIVE_UPDATE_KEY, m_liveUpdate); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index a20f5e71fc2..374524c33e9 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -49,6 +49,7 @@ protected: void setOpened(bool value); void closeEvent(QCloseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; private: void writeLiveUpdateSettings(); From 3d72c68e6b58e882143e87934294e03cbf1e43a7 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Sun, 27 Oct 2024 15:29:27 +0100 Subject: [PATCH 058/322] QmlDesigner: Update the UI project and CMake info This patch updates several docs removing old instructions set about UI project conversion, CMake Generation. It also adds answers to some common questions regarding CMake Generator. Fixes: QDS-13825 Fixes: QDS-13761 Change-Id: I0a9f9055ebd75dd7a7f9da445a60f7529df578f1 Reviewed-by: Thomas Hartmann Reviewed-by: Knud Dollereder --- .../src/howto/creator-external-tools.qdoc | 2 +- .../qtquick-from-qmlproject-to-pro.qdoc | 17 ++ ...erting-ui-projects-to-applications.qdocinc | 178 ++++++++++++++++ .../qtquick-from-qmlproject-to-pro.qdoc | 194 ------------------ doc/qtcreator/src/vcs/creator-vcs-git.qdoc | 2 +- .../config/style/qt5-sidebar.html | 8 - doc/qtdesignstudio/src/best-practices.qdoc | 3 +- .../studio-designer-developer-workflow.qdoc | 62 +++++- .../src/qtdesignstudio-developer-topics.qdoc | 5 - .../src/qtdesignstudio-projects-overview.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 2 - doc/qtdesignstudio/src/qtdesignstudio.qdoc | 2 - .../src/qtquick-from-qt5-to-qt6.qdoc | 119 ----------- 13 files changed, 254 insertions(+), 342 deletions(-) create mode 100644 doc/qtcreator/src/qtquick/creator-only/qtquick-from-qmlproject-to-pro.qdoc create mode 100644 doc/qtcreator/src/qtquick/qtquick-converting-ui-projects-to-applications.qdocinc delete mode 100644 doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc delete mode 100644 doc/qtdesignstudio/src/qtquick-from-qt5-to-qt6.qdoc diff --git a/doc/qtcreator/src/howto/creator-external-tools.qdoc b/doc/qtcreator/src/howto/creator-external-tools.qdoc index fa58a1e80a2..61de9134d5b 100644 --- a/doc/qtcreator/src/howto/creator-external-tools.qdoc +++ b/doc/qtcreator/src/howto/creator-external-tools.qdoc @@ -10,7 +10,7 @@ /*! \page creator-editor-external.html \if defined(qtdesignstudio) - \previouspage quick-converting-ui-projects.html + \previouspage creator-vcs-git.html \nextpage studio-on-mcus.html \else \previouspage creator-how-tos.html diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-from-qmlproject-to-pro.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-from-qmlproject-to-pro.qdoc new file mode 100644 index 00000000000..80e532610de --- /dev/null +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-from-qmlproject-to-pro.qdoc @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page quick-converting-ui-projects.html + \nextpage creator-editor-external.html + \previouspage creator-reference.html + + \ingroup creator-reference-ui-design + + \title Converting UI Projects to Applications + + \brief Converting a project that has a .qmlproject file to one that has a + .pro file. + + \include qtquick-converting-ui-projects-to-applications.qdocinc {converting-ui-projects-to-applications} {Qt Quick UI Prototype} +*/ diff --git a/doc/qtcreator/src/qtquick/qtquick-converting-ui-projects-to-applications.qdocinc b/doc/qtcreator/src/qtquick/qtquick-converting-ui-projects-to-applications.qdocinc new file mode 100644 index 00000000000..fe7dea40785 --- /dev/null +++ b/doc/qtcreator/src/qtquick/qtquick-converting-ui-projects-to-applications.qdocinc @@ -0,0 +1,178 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +//! [converting-ui-projects-to-applications] + + \1 projects are useful for creating user interfaces. To + use them for application development in Qt Creator you have to add: + + \list + \li A project configuration file (CMakeLists.txt or a .pro file) + \li C++ code (.cpp) + \li Resource files + \li Code needed for deploying applications to \l{glossary-device} + {devices} + \endlist + + For more information about integrating QML and C++, see + \l{Overview - QML and C++ Integration}. + + \note Since \QDS 2.3.0, \QDS project wizard templates generate projects that + can be built with CMake. You can open the \e CMakeLists.txt project file in + Qt Creator to continue developing the project. Also, you can use Qt Creator + to create a Qt Quick Application project that you can open in \QDS. + + \target wizard-template-note + \note Since \QDS 3.9.0, \QDS project wizard templates generate projects that + automatically check out and build the Qt Quick Studio Components from + \l{https://code.qt.io/cgit/qt-labs/qtquickdesigner-components.git/} {Qt Code Review}, + using CMake. To turn off this feature, use the option \e BUILD_QDS_COMPONENTS + in the CMake configuration. + + \if defined(qtcreator) + For more information about using \QDS to create projects, see + \l{Qt Design Studio Manual}. + \endif + + To use qmake as the build system, use a Qt Creator + wizard template to create a Qt Quick application that is built using the + qmake build system and then copy the source files from the Qt UI Quick + project to the application project. + + You can use the \c RESOURCES option in the project configuration file to + automatically add all the QML files and related assets to a + \l{The Qt Resource System}{Qt resource collection file (.qrc)}. However, + large files should be included as external binary resources instead of + compiling them into the binary. + + The wizard automatically adds the \c QML_IMPORT_PATH option to the project + file for specifying the required \l{QML Import Path}{QML import path}. The + path is only needed if more than one subdirectory has QML files. + + Then you can use the \l QQuickView class in the main C++ source file to + show the main QML file when the application starts. + + \if defined(qtcreator) + The \e {Qt Quick Designer Components} module is installed when you + install \QDS. If you use Qt Quick Studio Components or Effects + from the module in a project that you want to edit in Qt Creator, + you have to build the module and install it to your Qt to be able to + build your project. For more information, see + \l{Adding Qt Quick Designer Components to Qt Installations}. + + The \l{Qt Quick Timeline} module is installed when you install \QDS. + If you only install Qt Creator and Qt, remember to also select the + Qt Quick Timeline module for installation. If your Qt is older than + 5.14, you must build the Qt Quick Timeline module and install it to + your Qt to be able to build your project. + + \section1 Converting into qmake Projects + + To convert a project that has a .qmlproject file to one that has a .pro + file: + + \list 1 + \li Select \uicontrol File > \uicontrol {New Project} > + \uicontrol {Application (Qt)} > \uicontrol {Qt Quick Application} > + \uicontrol Choose. + \li In the \uicontrol {Build system} field, select \l qmake as the build + system to use for building and running the project, and then select + \uicontrol Next (or \uicontrol Continue on \macos). + \li Follow the instructions of the wizard to create the project. + \li In the file explorer, copy the source files from the Qt Quick UI + project directory to a subdirectory in the application + project directory. For the purpose of these instructions, the + directory is called \c qml. + \li Open the application project file, and edit the value of the + \c RESOURCES option to add the following line: + \badcode + RESOURCES += \ + $$files(qml/*) + \endcode + \li Also edit the value of the \c QML_IMPORT_PATH option to specify the + QML import path: + \badcode + QML_IMPORT_PATH = qml/imports + \endcode + Where \c {qml/imports} is the import path. + \li Select \uicontrol Build > \uicontrol {Run qmake} to apply the + \c RESOURCES option to the build configuration. + \li Open the \e {main.cpp} file and replace the QQmlApplicationEngine + object with a QQuickView object: + \quotefromfile progressbar/main.cpp + \skipto QQuickView view; + \printuntil view.show() + Where \c {qrc:/qml/imports} is the import path and + \c {qrc:/qml/ProgressBar.ui.qml} is the path to and the + 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 + example from your \QDS installation (located in the + \c{\share\qtcreator\examples\ProgressBar} directory) to an empty + Qt Quick application project and make the necessary changes, the + \e {main.cpp} file should look as follows: + + \quotefile progressbar/main.cpp + \endif + + \if defined(qtdesignstudio) + If you only install Qt Creator and Qt, remember to also select the + Qt Quick Timeline module for installation. + \endif + + \section1 Handling Large Data Files + + Graphical assets used in the UI, such as images, effects, or 3D scenes + are a typical cause for performance problems in UIs. Even building the + application requires huge amounts of memory if you try to include large + asset files, such as 100-MB 3D models or 64-MB textures, into the \c .qrc + file for compiling them into the binary. + + First try to optimize your assets, as described in \l{Optimizing Designs} + and \l {Creating Optimized 3D Scenes}. + + Large assets should either be loaded directly from the file system or by + using the Qt resource system dynamically. For more information, see + \l{The Qt Resource System}. + + \section1 Adding Qt Quick Designer Components to Qt Installations + + Since \QDS 3.9, the Qt Quick Studio Components module is installed by default + as part of the application created with \QDS. You can also install the module manually. + + For example: + \list 1 + \li Clone the module repository. + \badcode + git clone https://code.qt.io/qt-labs/qtquickdesigner-components.git + \endcode + + \li Install the Qt Quick Designer Components module. + + \badcode + mkdir build + cd build + cmake -GNinja -DCMAKE_INSTALL_PREFIX= + cmake --build . + cmake --install . + \endcode + \note Here, \e and \e + needs to be replaced with the real location on your local drive. For example, + \e can be something like \e /Qt/6.3.0/msvc2019_64 + and \e like this \e ../qtquickdesigner-components/ + \endlist + + \if defined(qtcreator) + \sa {Create Qt Quick UI Prototypes} + \endif + + +//! [converting-ui-projects-to-applications] +*/ diff --git a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc b/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc deleted file mode 100644 index ec520333197..00000000000 --- a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \page quick-converting-ui-projects.html - \if defined(qtdesignstudio) - \previouspage studio-porting-projects.html - \nextpage creator-editor-external.html - \else - \previouspage creator-reference.html - \endif - - \ingroup creator-reference-ui-design - - \title Converting UI Projects to Applications - - \brief Converting a project that has a .qmlproject file to one that has a - .pro file. - - Qt Quick UI Prototype projects are useful for creating user interfaces. To - use them for application development in Qt Creator you have to add: - - \list - \li Project configuration file (CMakeLists.txt or .pro) - \li C++ code (.cpp) - \li Resource files - \li Code needed for deploying applications to \l{glossary-device} - {devices} - \endlist - - For more information about integrating QML and C++, see - \l{Overview - QML and C++ Integration}. - - \note Since \QDS 2.3.0, \QDS project wizard templates generate projects that - can be built with CMake. You can open the \e CMakeLists.txt project file in - Qt Creator to continue developing the project. Also, you can use Qt Creator - to create a Qt Quick Application project that you can open in \QDS. - - \target wizard-template-note - \note Since \QDS 3.9.0, \QDS project wizard templates generate projects that - automatically checkout and build the Qt Quick Studio Components from - \l{https://code.qt.io/cgit/qt-labs/qtquickdesigner-components.git/} {Qt Code Review}, - using CMake. To turn off this feature, use the option \e BUILD_QDS_COMPONENTS - in the CMake configuration. - - \if defined(qtdesignstudio) - For more information, see \l{Designer-Developer Workflow}. - \else - For more information about using \QDS to create projects, see - \l{Qt Design Studio Manual}. - \endif - - - If you want to use qmake as the build system, you can use a Qt Creator - wizard template to create a Qt Quick application that is built using the - qmake build system and then copy the source files from the Qt UI Quick - project to the application project. - - You can use the \c RESOURCES option in the project configuration file to - automatically add all the QML files and related assets to a - \l{The Qt Resource System}{Qt resource collection file (.qrc)}. However, - large files should be included as external binary resources instead of - compiling them into the binary. - - The wizard automatically adds the \c QML_IMPORT_PATH option to the project - file for specifying the required \l{QML Import Path}{QML import path}. The - path is only needed if more than one subdirectory has QML files. - - Then you can use the \l QQuickView class in the main C++ source file to - show the main QML file when the application starts. - - The \e {Qt Quick Designer Components} module is installed when you - install \QDS. If you use Qt Quick Studio Components or Effects - from the module in a project that you want to edit in Qt Creator, - you have to build the module and install it to your Qt to be able to - build your project. For more information, see - \l{Adding Qt Quick Designer Components to Qt Installations}. - - The \l{Qt Quick Timeline} module is installed when you install \QDS. - If you only install Qt Creator and Qt, remember to also select the - Qt Quick Timeline module for installation. If your Qt is older than - 5.14, you must build the Qt Quick Timeline module and install it to - your Qt to be able to build your project. - - \section1 Converting into qmake Projects - - To convert a project that has a .qmlproject file to one that has a .pro - file: - - \list 1 - \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol {Application (Qt)} > \uicontrol {Qt Quick Application} > - \uicontrol Choose. - \li In the \uicontrol {Build system} field, select \l qmake as the build - system to use for building and running the project, and then select - \uicontrol Next (or \uicontrol Continue on \macos). - \li Follow the instructions of the wizard to create the project. - \li In the file explorer, copy the source files from the Qt Quick UI - project directory to a subdirectory in the application - project directory. For the purpose of these instructions, the - directory is called \c qml. - \li Open the application project file, and edit the value of the - \c RESOURCES option to add the following line: - \badcode - RESOURCES += \ - $$files(qml/*) - \endcode - \li Also edit the value of the \c QML_IMPORT_PATH option to specify the - QML import path: - \badcode - QML_IMPORT_PATH = qml/imports - \endcode - Where \c {qml/imports} is the import path. - \li Select \uicontrol Build > \uicontrol {Run qmake} to apply the - \c RESOURCES option to the build configuration. - \li Open the \e {main.cpp} file and replace the QQmlApplicationEngine - object with a QQuickView object: - \quotefromfile progressbar/main.cpp - \skipto QQuickView view; - \printuntil view.show() - Where \c {qrc:/qml/imports} is the import path and - \c {qrc:/qml/ProgressBar.ui.qml} is the path to and the - 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 - example from your \QDS installation (located in the - \c{\share\qtcreator\examples\ProgressBar} directory) to an empty - Qt Quick application project and make the necessary changes, the - \e {main.cpp} file should look as follows: - - \quotefile progressbar/main.cpp - - \section1 Handling Large Data Files - - Graphical assets used in the UI, such as images, effects, or 3D scenes - are a typical cause for performance problems in UIs. Even building the - application requires huge amounts of memory if you try to include large - asset files, such as 100-MB 3D models or 64-MB textures, into the \c .qrc - file for compiling them into the binary. - - First try to optimize your assets, as described in \l{Optimizing Designs} - and \l {Creating Optimized 3D Scenes}. - - If you really need large assets, they should either be loaded directly from - the file system or use the Qt resource system in the dynamic way. For more - information, see \l{The Qt Resource System} in the Qt documentation. - - \section1 Adding Custom Fonts - - \if defined(qtdesignstudio) - To \l{Using Custom Fonts}{use custom fonts} - \else - To use custom fonts - \endif - from the Qt Quick UI project, call the QFontDatabase::addApplicationFont() - function from the \e {main.cpp} file. - - \section1 Adding Qt Quick Designer Components to Qt Installations - - Since \QDS 3.9, the Qt Quick Studio Components module is installed by default - as part of the application. You can also install the module manually. - - For example: - \list 1 - \li Clone the module repository. - \badcode - git clone https://code.qt.io/qt-labs/qtquickdesigner-components.git - \endcode - - \li Install the Qt Quick Designer Components module. - Enter the following commands: - \badcode - mkdir build - cd build - cmake -GNinja -DCMAKE_INSTALL_PREFIX= - cmake --build . - cmake --install . - \endcode - \note Here, \e and \e - needs to be replaced with the real location on your local drive. For example, - \e can be something like \e /Qt/6.3.0/msvc2019_64 - and \e like this \e ../qtquickdesigner-components/ - \endlist - - \if defined(qtcreator) - \sa {Create Qt Quick UI Prototypes} - \endif -*/ diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index e853a868122..ab58429efe5 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -11,7 +11,7 @@ \page creator-vcs-git.html \if defined(qtdesignstudio) \previouspage studio-finding-the-qt-runtime-version.html - \nextpage studio-porting-projects.html + \nextpage creator-editor-external.html \title Using Git \else diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index f1e2fc4af57..872828b8f0d 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -302,14 +302,6 @@ -
diff --git a/doc/qtdesignstudio/src/best-practices.qdoc b/doc/qtdesignstudio/src/best-practices.qdoc index 4abcc80f6af..e494ac74e92 100644 --- a/doc/qtdesignstudio/src/best-practices.qdoc +++ b/doc/qtdesignstudio/src/best-practices.qdoc @@ -26,8 +26,7 @@ \section1 Projects \list - \li \l {Converting Qt 5 Projects into Qt 6 Projects} - \li \l {Converting UI Projects to Applications} + \li \l {Converting Qt Design Studio Projects to Applications} \endlist \section1 Workflow diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 78e90404542..03f3b840b7a 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -34,16 +34,14 @@ \section1 Exporting a \QDS Project for C++ Development - To export a \QDS project for Qt Creator, you need: - + Before you export a \QDS project for Qt Creator, install the following: \list - \li Qt Creator 13.0 or above. - \li \QDS 4.5 or above. - \li Git. - - \note Learn more about getting Git \l {https://wiki.qt.io/Git_Installation} {here}. + \li Qt Creator 13.0 or above + \li \QDS 4.5 or above + \li Git. Learn more about getting Git \l {https://wiki.qt.io/Git_Installation} {here} \endlist + To export a \QDS project for Qt Creator: \list 1 \li \l {Creating a Project} {Create} or open your \QDS project with \QDS 4.5 or above. @@ -94,6 +92,51 @@ All the import assets are now bundled in the same folder, so the CMake generation works properly. + \section1 CMake Generator + \details {FAQ} + + Learn how the CMake generator works. + + \section2 What does the CMake Generator convert? + The CMake Generator converts the contents of the \e .qmlproject file + to CMake. + \note A \QDS project can run with a missing path or file filter. + However, CMake generation can fail due to missing elements in the + \e .qmlproject file. + + \section2 Which changes are tracked by the CMake Generator? + All the files that are listed in \e .qmlproject are tracked by the + CMake Generator. Editing the \e .qmlproject file triggers a CMake + generation. Adding, removing, and renaming files are tracked. + + \section2 Why do you need Stubb or Mock QML data for \QDS? + \QDS doesn't understand C++ code, so the CMake Generator ignores any + components or properties made with C++. If you want a similar + representation in \QDS for the components or properties created with + C++, add a QML mock file for \QDS. Keep these mock files in a dedicated + folder within your project folder. You can later add a \c mockImports + variable within the \e .qmlproject file and include the folder path + with mock files in the \c mockImports variable. CMake ignores all folders + and files included in the \c mockImports variable. So they only stay in the + \QDS space and are not converted with CMake Generator. + + Example: + \code + mockImports: [ "MockDatas" ] + \endcode + Here, MockDatas is a folder in your project folder with all the files + with QML mock data. If you include this code in the \e .qmlproject file, + the CMake Generator ignores everything inside the MockDatas folder. + + \section2 Is it possible to manually change the contents of the \e CMakeLists.txt files? + Yes, you can make changes to the \e CMakeLists.txt files existing in the + App and the Root folders of your project. The \e CMakeLists.txt files + in other folders of your project are overwritten as soon as the + CMake generator runs next time. Only the \e CMakeLists.txt files in + the App and the Root folders of your project are not overwritten. + + \enddetails + \section1 Exporting a \QDS Project for Python Development \list 1 @@ -161,4 +204,9 @@ Your \QDS project now runs in Python. Use Python to add more functionalities to the project. Go to \l {Qt for Python} to learn more about developing Qt projects using Python. + + \section1 Converting Qt Design Studio Projects to Applications + + \include qtquick-converting-ui-projects-to-applications.qdocinc {converting-ui-projects-to-applications} {\QDS} + */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc index bc99a9c2c0d..3b2f27d1ab4 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc @@ -28,12 +28,7 @@ the developers can keep up with your changes. Store and edit only project source files and configuration files. Do not store generated files. - \li \l{Converting UI Projects to Applications} - \QDS projects are useful for creating UIs. To use them for - application development in Qt Creator, you have to convert - them to Qt Quick Application projects that contain .pro, - .cpp, and .qrc files. \li \l{Use external tools} You can use external tools directly from \QDS. Qt Linguist, diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc index 86b2bcdbf37..7f6a8b67ca2 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc @@ -21,7 +21,7 @@ project source files and configuration files. Do not store generated files. - \li \l {Converting UI Projects to Applications} + \li \l {Converting Qt Design Studio Projects to Applications} \QDS projects are useful for creating UIs. To use them for application development in Qt Creator, you have to convert them diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 05615085770..3146ce633ec 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -229,8 +229,6 @@ \list \li \l{Finding the Qt Runtime Version} \li \l{Using Git} - \li \l{Converting Qt 5 Projects into Qt 6 Projects} - \li \l{Converting UI Projects to Applications} \li \l{Use external tools} \li \l{\QDS on MCUs} \list diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index cedc8b80460..dee3622540b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -78,8 +78,6 @@ \endlist \li Best Practices \list - \li \l{Converting Qt 5 Projects into Qt 6 Projects} - \li \l{Converting UI Projects to Applications} \li \l{Creating Glow and Bloom Effects} \li \l{Creating Optimized 3D Scenes} \li \l{Optimizing Designs} diff --git a/doc/qtdesignstudio/src/qtquick-from-qt5-to-qt6.qdoc b/doc/qtdesignstudio/src/qtquick-from-qt5-to-qt6.qdoc deleted file mode 100644 index e2f442628c7..00000000000 --- a/doc/qtdesignstudio/src/qtquick-from-qt5-to-qt6.qdoc +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \page studio-porting-projects.html - \previouspage creator-vcs-git.html - \nextpage quick-converting-ui-projects.html - - \title Converting Qt 5 Projects into Qt 6 Projects - - \QDS supports creating UIs with Qt 6 in addition to Qt 5. However, to - make a project that uses Qt 5 use Qt 6, you have to be aware of a few - differences and issues that are discussed in this topic. - - \section1 Font Loader - - Projects that were \l{Creating Projects}{created} with \QDS 2.1 use - \c FontLoader in a way that is not supported in Qt 6. Specifically, the - \c name property is read-only in Qt 6. Therefore, you must modify the - \c Constants.qml file to have fonts loaded correctly. You can either - remove the \c FontLoader or switch to using the \c source property - instead of the \c name property. - - To remove the \c FontLoader, delete the following line from the - \c Constants.qml file: - - \code - readonly property FontLoader mySystemFont: FontLoader { name: "Arial" } - \endcode - - Then, remove the following lines that contain references to mySystemFont: - - \code - readonly property font font: Qt.font({ - family: mySystemFont.name, - pixelSize: Qt.application.font.pixelSize - }) - - readonly property font largeFont: Qt.font({ - family: mySystemFont.name, - pixelSize: Qt.application.font.pixelSize * 1.6 - }) - \endcode - - Alternatively, you can keep the \c FontLoader and use the \c source property - instead of the \c name property. If you are unsure about how to do this, you - can replace the \c Constants.qml file with a new one that you create by - using \QDS 2.2. - - \section1 Qt Quick Studio Components - - \l{Summary of Shapes}{Qt Quick Studio Components} are available in Qt 6, - except for the \l {Iso Icon} component. It specifies an icon from an - ISO 7000 icon library as a \l [QtQuickExtras] {Picture} component, which - is not supported in Qt 6. Therefore, Iso Icon is also not supported in Qt 6. - - \section1 Qt Quick Studio Effects - - \l{2D Effects} are only partially supported. The following 2D effects are - not available in Qt 6: - - \list - \li Blend - \li Inner Shadow - \li Blur effects except: - \list - \li DirectionalBlur - \li FastBlur - \li GaussianBlur - \li MaskedBlur - \li RecursiveBlur - \li RadialBlur - \li ZoomBlur - \endlist - \endlist - - Substitutes are provided for the obsolete effects to keep Qt 5 based - applications working, but the effects will not be rendered as expected. - - \section1 Qt Quick 3D - - In Qt 6, you cannot use the import \c {import QtQuick3D 1.15}, which - imports a Qt 5 based Qt Quick 3D module. Qt 6 does not require a version - for imports, and therefore it is not used by default. To turn a Qt 5 based - project into a Qt 6 based project, you have to adjust the imports in all - \c .qml files that use Qt Quick 3D by removing the version numbers. - - For more information about changes in Qt Quick 3D, see the - \l{https://doc-snapshots.qt.io/qt6-dev/qtquick3d-changes-qt6.html} - {changes file}. - - \section1 QML - - For general information about changes in QML between Qt 5 and Qt 6, see: - - \list - \li \l{https://doc.qt.io/qt-6/obsoleteqmltypes.html}{Obsolete types} - \li \l{https://doc.qt.io/qt-6/quick-changes-qt6.html}{Changes in Qt Quick} - \endlist - - The most notable change is that Qt 6 does not require a version for - imports anymore. - - \section1 \QDS - - Projects that support only Qt 6 are marked with \c {qt6Project: true} in - the \c .qmlproject file. This line is added if you choose \uicontrol {Qt 6} - in the wizard when creating the project. If the project file does not - contain this line, the project will use Qt 5 and a Qt 5 kit by default. - You can change this in the project \uicontrol {Run Settings}, where you - can select \uicontrol {Qt 6} instead. - - Projects that use Qt 6 specific features will not work with Qt 5. This - means that projects that are supposed to work with both Qt 5 and Qt 6 - require versions for their imports. - - Therefore, if you want to use Qt Quick 3D, using the same project with Qt 5 - and Qt 6 is not possible. -*/ From 18829569c7c932d5dd3db990da4bc1bf18dc2420 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 31 Oct 2024 17:39:01 +0200 Subject: [PATCH 059/322] EffectComposer: Skip the last empty line from the code Fixes: QDS-14016 Change-Id: I88e6777ac25a1a1c4ae3cba71414e99e0373b1b1 Reviewed-by: Miikka Heikkinen --- src/plugins/effectcomposer/effectcomposermodel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index cbc0889b9cd..8b97582b236 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -1281,6 +1281,9 @@ void EffectComposerModel::openComposition(const QString &path) code += lineValue.toString() + '\n'; } + if (!code.isEmpty()) + code.chop(1); + return code; }; From 51e81fac7b028c5321417a8429284d8679e6b76f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 31 Oct 2024 18:54:17 +0100 Subject: [PATCH 060/322] QmlDesigner: Fix build Change-Id: I42b5b95ad4316d546aa32839d87c1b63df5679a3 Reviewed-by: Tim Jenssen --- src/plugins/effectcomposer/effectshaderscodeeditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index d1491454442..fdfc10d5036 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include From 17a7905ac2539642c000967cb7c4776d70f2f4b0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 30 Oct 2024 17:17:57 +0100 Subject: [PATCH 061/322] QmlDesigner: Performance improvment for states editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the property changes are not visible we do not have to create the items. Change-Id: Iecde333cc5c99fff22cdaa6ac978ff1d1dba85ff Reviewed-by: Henning Gründl --- share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml b/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml index ccbb5d3d0e6..d2e63a6f4d0 100644 --- a/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml +++ b/share/qtcreator/qmldesigner/stateseditor/StateThumbnail.qml @@ -341,7 +341,7 @@ Item { spacing: stateBackground.thumbSpacing Repeater { - model: propertyChangesModel + model: root.propertyChangesVisible ? propertyChangesModel : 0 delegate: Rectangle { id: propertyChanges From 2ee07eb348e788afad0dae18622c6a5e4f208d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Thu, 31 Oct 2024 19:00:18 +0100 Subject: [PATCH 062/322] QmlDesigner: fix possible crash - found in sentry https://the-qt-company-00.sentry.io/issues/6021640314 Change-Id: I3517117384e5fab2394010271a92cfd1301f4e96 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/formeditor/rotationcontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/formeditor/rotationcontroller.cpp b/src/plugins/qmldesigner/components/formeditor/rotationcontroller.cpp index 00717c1c620..19297bc258c 100644 --- a/src/plugins/qmldesigner/components/formeditor/rotationcontroller.cpp +++ b/src/plugins/qmldesigner/components/formeditor/rotationcontroller.cpp @@ -108,7 +108,7 @@ RotationController &RotationController::operator =(const RotationController &oth bool RotationController::isValid() const { - return m_data->formEditorItem && m_data->formEditorItem->qmlItemNode().isValid(); + return m_data && m_data->formEditorItem && m_data->formEditorItem->qmlItemNode().isValid(); } void RotationController::show() From 089862aa394d83dbc4e809974a61bbd8328732eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Thu, 17 Oct 2024 11:47:47 +0300 Subject: [PATCH 063/322] Doc: Update the Qt Design Studio on MCUs doc subset Updated the the Qt Design Studio on MCUs doc subset as follows: - Updated the version compatibility table to include the latest and next upcoming releases (QDS 4.6 & 4.7/Qt for MCUs 2.9.0) - Updated a few icons and screenshots to match QDS 4.6 and the new QDS doc set look & feel - Rephrased some sections to match the contents of QDS 4.6 and Qt for MCUs 2.9.0 - Added links to relevant sections in the Qt Creator and Qt for MCUs documentation - Added a link to the Qt Design Studio/MCU FAQ wiki page. Task-number: QDS-13834 Change-Id: Id9c1c50573f35e7082724c9b879b66545cbfde0d Reviewed-by: Leena Miettinen --- .../external-resources-qds.qdoc | 32 ++++++++++++++++++ .../studio-mcu-components-and-properties.webp | Bin 40202 -> 46096 bytes ...ignstudio-compatibility-with-mcu-sdks.qdoc | 7 ++-- ...gnstudio-connecting-mcus-with-creator.qdoc | 26 ++++++++------ .../qtdesignstudio-creating-uis-for-mcus.qdoc | 2 +- ...udio-developing-applications-for-mcus.qdoc | 19 ++++++----- .../mcus/qtdesignstudio-mcu-framework.qdoc | 10 +++--- .../src/mcus/qtdesignstudio-on-mcus.qdoc | 2 +- 8 files changed, 71 insertions(+), 27 deletions(-) diff --git a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc index 1cd90ae903f..c4a467a9496 100644 --- a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc @@ -105,6 +105,38 @@ \externalpage https://doc.qt.io/qt/linguist-id-based-i18n.html \title Text ID based translations */ +/*! + \externalpage https://doc.qt.io/qtcreator/creator-developing-mcu.html + \title Developing for MCUs +*/ +/*! + \externalpage https://doc.qt.io/qtcreator/creator-how-tos.html#mcus + \title How To: Develop for MCUs +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-setup-development-host.html#prerequisites + \title MCU prerequisites for desktop +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-setup-development-host.html#qt-creator-setup + \title MCU kit for desktop +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-prerequisites.html + \title MCU target device-specific prerequisites +*/ +/*! + \externalpage https://doc.qt.io/qtcreator/creator-how-to-create-mcu-kits.html + \title MCU kit for a target device +*/ +/*! + \externalpage https://doc.qt.io/QtForMCUs/qtul-supported-platforms.html + \title Supported MCU target devices +*/ +/*! + \externalpage https://wiki.qt.io/QtDesignStudio/MCU_FAQ + \title Qt Design Studio/MCU FAQ +*/ /*! \externalpage https://www.qt.io/blog/qt-design-studio-4.6-released \title Qt Design Studio 4.6 released diff --git a/doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp b/doc/qtdesignstudio/images/studio-mcu-components-and-properties.webp index 1bd070f8213c6bf92b707db492490f75834bd95e..e5067a7f5dfdb669218609cd52d8d290f57fac6a 100644 GIT binary patch literal 46096 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 VKR4XrHpvUwDXV^1^X;?8SpaH}q)7k( literal 40202 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 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 0e833cead10..e86eb5b19da 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -16,8 +16,11 @@ \li \QDS Version \li \QMCU SDK Version \row - \li 4.5 or later - \li 2.8 or later + \li 4.7 or later + \li 2.9 or later + \row + \li 4.5 up to 4.6 + \li 2.8 \row \li 4.4 \li 2.7 diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc index 0195a6e62af..98fefdb49b8 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-connecting-mcus-with-creator.qdoc @@ -8,18 +8,24 @@ \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. + With \QMCU you can develop applications for desktop and for specific MCU + target devices using either Linux or Windows as the development host + environment. - 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. + When developing for desktop, you first need to install the + \l {MCU prerequisites for desktop}. After installing the prerequisites, you + need to create a \l {MCU kit for desktop} to set up the necessary environment + for Qt Creator to build the \QMCU application for the desktop target. - For more information on how to manage the complete cycle of developing \QMCU - applications using Qt tools, see: + If you want to start developing for a specific MCU target device, you first + need to install both the common and \l {MCU target device-specific prerequisites}. + After installing all the required prerequisites, you can launch Qt Creator + and create a \l {MCU kit for a target device} to set up the necessary + environment for Qt Creator to build and run the \QMCU application on the MCU + target device. + + For examples on how to use Qt tools to manage the complete \QMCU application + development cycle for a specific target device, see: \list \li \l {Infineon Traveo II quick start guide} 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 2d11a07de8d..9b580802a00 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc @@ -48,5 +48,5 @@ \li \l {Designing a UI for Renesas RH850-D1M1A} \endlist - \sa {Specifying Component Properties} + \sa {Specifying Component Properties}, {Qt Design Studio/MCU FAQ} */ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc index 187de26c31e..edcfd5fd172 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc @@ -13,16 +13,17 @@ {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}. + 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). For more information, see + \l {Implementing Applications}. \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}. + With Qt Creator, you can test, preview, and fine-tune your designs directly + on the desktop or on an actual MCU target device. 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 on how to use Qt Creator to develop applications for + MCUs, see \l {Developing for MCUs} and \l {How To: Develop for MCUs}. */ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc index 0df424505f7..82a7b20c852 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc @@ -17,11 +17,13 @@ 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). + on microcontrollers (MCU). Also, depending on the target device, it lets you + run your applications on Bare Metal, Linux, RTOS, or Zephyr® operating + systems. For more information on the supported target devices, compilers, + and operating systems, see \l {supported MCU target devices}. - \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. + \note You can also use the desktop kit to run and test your 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 2db39fb06dd..a0bb883bccf 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc @@ -10,7 +10,7 @@ \table \row - \li \image qds-front-gs.png + \li \image icons/platform-and-toolchain.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 From 63ac6213a40e6b6d3a9995fc12ab82cef728dedb Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 31 Oct 2024 17:46:16 +0200 Subject: [PATCH 064/322] EffectComposer: Improve custom preview image handling Now custom preview images are stored in centralized location under OS documents folder. More than one custom image can be added and they are available in all projects. Images can be deleted with x button. Fixes: QDS-13958 Fixes: QDS-13959 Fixes: QDS-14011 Change-Id: Ib3208729e7c35d5b0ec9cce31674c63e2d43a696 Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri --- .../PreviewImagesComboBox.qml | 48 +++--- .../effectcomposer/effectcomposermodel.cpp | 162 ++++++++++-------- .../effectcomposer/effectcomposermodel.h | 14 +- 3 files changed, 128 insertions(+), 96 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml index 0fc5f26056b..334180db35f 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml @@ -22,17 +22,8 @@ StudioControls.ComboBox { required property Item mainRoot - property var images: [Qt.url(""), - Qt.url("images/preview0.png"), - Qt.url("images/preview1.png"), - Qt.url("images/preview2.png"), - Qt.url("images/preview3.png"), - Qt.url("images/preview4.png")] - property url selectedImage: EffectComposerBackend.effectComposerModel.currentPreviewImage != Qt.url("") - ? EffectComposerBackend.effectComposerModel.currentPreviewImage - : images[1] - - Component.onCompleted: EffectComposerBackend.effectComposerModel.currentPreviewImage = images[1] + property var images: EffectComposerBackend.effectComposerModel.previewImages + property url selectedImage: EffectComposerBackend.effectComposerModel.currentPreviewImage readonly property int popupHeight: Math.min(800, col.height + 2) @@ -142,7 +133,7 @@ StudioControls.ComboBox { anchors.topMargin: col.padding anchors.leftMargin: col.padding anchors.rightMargin: col.padding - text: qsTr("Set Custom Image") + text: qsTr("Add Custom Image") onClicked: { EffectComposerBackend.effectComposerModel.chooseCustomPreviewImage() root.popup.close() @@ -172,19 +163,14 @@ StudioControls.ComboBox { color: "transparent" border.color: root.selectedImage === modelData - || index == 0 && root.selectedImage == EffectComposerBackend.effectComposerModel.customPreviewImage ? StudioTheme.Values.themeInteraction : "transparent" width: 200 height: 200 - visible: index > 0 - || EffectComposerBackend.effectComposerModel.customPreviewImage !== Qt.url("") Image { - source: index > 0 - ? parent.modelData - : EffectComposerBackend.effectComposerModel.customPreviewImage + source: parent.modelData anchors.fill: parent fillMode: Image.PreserveAspectFit smooth: true @@ -192,19 +178,31 @@ StudioControls.ComboBox { } MouseArea { + id: imageMouseArea anchors.fill: parent + hoverEnabled: true onClicked: { - if (parent.index > 0) { - EffectComposerBackend.effectComposerModel.currentPreviewImage - = root.images[index] - } else { - EffectComposerBackend.effectComposerModel.currentPreviewImage - = EffectComposerBackend.effectComposerModel.customPreviewImage - } + EffectComposerBackend.effectComposerModel.currentPreviewImage + = root.images[index] root.popup.close() } } + + HelperWidgets.IconButton { + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.top: parent.top + anchors.topMargin: 5 + + tooltip: qsTr("Remove custom image.") + visible: index < EffectComposerBackend.effectComposerModel.customPreviewImageCount + && (containsMouse || imageMouseArea.containsMouse) + icon: StudioTheme.Constants.closeCross + buttonSize: 21 + + onClicked: EffectComposerBackend.effectComposerModel.removeCustomPreviewImage(root.images[index]) + } } } } diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 8b97582b236..26a62d8c7a9 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,7 @@ EffectComposerModel::EffectComposerModel(QObject *parent) { m_rebakeTimer.setSingleShot(true); connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders); + m_currentPreviewImage = defaultPreviewImages().first(); } QHash EffectComposerModel::roleNames() const @@ -280,24 +282,24 @@ void EffectComposerModel::chooseCustomPreviewImage() FilePath imageFile = FilePath::fromString(fileNames.first()); lastDir = imageFile.absolutePath(); if (imageFile.exists()) { - FilePath projDir = QmlDesigner::QmlDesignerPlugin::instance()->documentManager() - .currentProjectDirPath(); - if (!imageFile.isChildOf(projDir)) { - FilePath imagesDir = QmlDesigner::ModelNodeOperations::getImagesDefaultDirectory(); - FilePath targetFile = imagesDir.pathAppended(imageFile.fileName()); - if (!targetFile.exists()) - imageFile.copyFile(targetFile); - if (targetFile.exists()) - imageFile = targetFile; + FilePath imagesDir = customPreviewImagesPath(); + if (!imagesDir.exists()) + imagesDir.createDir(); + FilePath targetFile = imagesDir.pathAppended(imageFile.fileName()); + if (!targetFile.exists()) + imageFile.copyFile(targetFile); + if (targetFile.exists()) { + QUrl imgUrl = QUrl::fromLocalFile(targetFile.toFSPathString()); + if (!m_customPreviewImages.contains(imgUrl)) + m_customPreviewImages.append(imgUrl); + m_currentPreviewImage = imgUrl; + + setHasUnsavedChanges(true); + + emit currentPreviewImageChanged(); + emit previewImagesChanged(); + emit customPreviewImageCountChanged(); } - - m_customPreviewImage = QUrl::fromLocalFile(imageFile.toFSPathString()); - m_currentPreviewImage = m_customPreviewImage; - - setHasUnsavedChanges(true); - - emit currentPreviewImageChanged(); - emit customPreviewImageChanged(); } } }); @@ -305,11 +307,36 @@ void EffectComposerModel::chooseCustomPreviewImage() void EffectComposerModel::previewComboAboutToOpen() { - if (!m_customPreviewImage.isEmpty() && !Utils::FilePath::fromUrl(m_customPreviewImage).exists()) { - if (m_currentPreviewImage == m_customPreviewImage) { - m_customPreviewImage.clear(); - emit customPreviewImageChanged(); - setCurrentPreviewImage({}); + m_customPreviewImages.clear(); + const Utils::FilePaths imagePaths = customPreviewImagesPath().dirEntries(QDir::Files); + for (const auto &imagePath : imagePaths) { + QmlDesigner::Asset asset(imagePath.toFSPathString()); + if (asset.isImage()) + m_customPreviewImages.append(imagePath.toUrl()); + } + + emit previewImagesChanged(); + emit customPreviewImageCountChanged(); + + if (!previewImages().contains(m_currentPreviewImage)) + setCurrentPreviewImage({}); +} + +void EffectComposerModel::removeCustomPreviewImage(const QUrl &url) +{ + int count = m_customPreviewImages.size(); + m_customPreviewImages.removeOne(url); + if (url.isLocalFile()) { + Utils::FilePath img = Utils::FilePath::fromUrl(url); + if (img.exists()) + img.removeFile(); + } + if (m_customPreviewImages.size() != count) { + emit previewImagesChanged(); + emit customPreviewImageCountChanged(); + if (url == m_currentPreviewImage) { + m_currentPreviewImage = defaultPreviewImages().first(); + emit currentPreviewImageChanged(); } } } @@ -1086,20 +1113,13 @@ void EffectComposerModel::saveComposition(const QString &name) json.insert("version", 1); json.insert("tool", "EffectComposer"); - Utils::FilePath customPreviewPath = Utils::FilePath::fromUrl(m_customPreviewImage); - if (m_customPreviewImage.isLocalFile()) { - if (customPreviewPath.exists()) - customPreviewPath = customPreviewPath.relativePathFrom(compositionDir); - else - customPreviewPath = {}; - } - json.insert("customPreviewImage", customPreviewPath.toUrl().toString()); - - QUrl previewUrl = m_currentPreviewImage; - if (m_currentPreviewImage == m_customPreviewImage) - previewUrl = customPreviewPath.toUrl(); - - json.insert("previewImage", previewUrl.toString()); + QString previewStr; + Utils::FilePath previewPath = Utils::FilePath::fromUrl(m_currentPreviewImage); + if (m_currentPreviewImage.isLocalFile()) + previewStr = previewPath.fileName(); + else + previewStr = m_currentPreviewImage.toString(); + json.insert("previewImage", previewStr); // Add nodes QJsonArray nodesArray; @@ -1297,37 +1317,16 @@ void EffectComposerModel::openComposition(const QString &path) else resetRootFragmentShader(); - m_currentPreviewImage.clear(); + m_currentPreviewImage = defaultPreviewImages().first(); if (json.contains("previewImage")) { const QString imageStr = json["previewImage"].toString(); if (!imageStr.isEmpty()) { - const QUrl imageUrl{imageStr}; - Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl); - if (imageStr.startsWith("images/preview")) { // built-in preview image - m_currentPreviewImage = imageUrl; - } else if (imagePath.isAbsolutePath()) { - if (imagePath.exists()) - m_currentPreviewImage = imageUrl; + if (imageStr.startsWith("images/preview")) {// built-in preview image + m_currentPreviewImage = QUrl(imageStr); } else { - imagePath = effectPath.absolutePath().resolvePath(imagePath); - if (imagePath.exists()) - m_currentPreviewImage = imagePath.toUrl(); - } - } - } - - m_customPreviewImage.clear(); - if (json.contains("customPreviewImage")) { - QUrl imageUrl{json["customPreviewImage"].toString()}; - if (!imageUrl.isEmpty()) { - Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl); - if (imagePath.isAbsolutePath()) { - if (imagePath.exists()) - m_customPreviewImage = imageUrl; - } else { - imagePath = effectPath.absolutePath().resolvePath(imagePath); - if (imagePath.exists()) - m_customPreviewImage = imagePath.toUrl(); + Utils::FilePath prevPath = customPreviewImagesPath().pathAppended(imageStr); + if (prevPath.exists()) + m_currentPreviewImage = prevPath.toUrl(); } } } @@ -1361,7 +1360,6 @@ void EffectComposerModel::openComposition(const QString &path) setHasUnsavedChanges(false); emit nodesChanged(); emit currentPreviewImageChanged(); - emit customPreviewImageChanged(); } void EffectComposerModel::saveResources(const QString &name) @@ -2351,6 +2349,14 @@ void EffectComposerModel::setCodeEditorIndex(int index) emit codeEditorIndexChanged(m_codeEditorIndex); } +Utils::FilePath EffectComposerModel::customPreviewImagesPath() const +{ + QStandardPaths::StandardLocation location = QStandardPaths::DocumentsLocation; + + return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) + .pathAppended("QtDesignStudio/effect_composer/preview_images"); +} + QString EffectComposerModel::currentComposition() const { return m_currentComposition; @@ -2367,9 +2373,23 @@ void EffectComposerModel::setCurrentComposition(const QString &newCurrentComposi m_shadersCodeEditor.reset(); } -QUrl EffectComposerModel::customPreviewImage() const +QList EffectComposerModel::defaultPreviewImages() const { - return m_customPreviewImage; + static QList defaultImages = { + QUrl("images/preview0.png"), + QUrl("images/preview1.png"), + QUrl("images/preview2.png"), + QUrl("images/preview3.png"), + QUrl("images/preview4.png") + }; + + return defaultImages; +} + +QList EffectComposerModel::previewImages() const +{ + + return m_customPreviewImages + defaultPreviewImages(); } QUrl EffectComposerModel::currentPreviewImage() const @@ -2385,10 +2405,18 @@ void EffectComposerModel::setCurrentPreviewImage(const QUrl &path) if (!m_nodes.isEmpty()) setHasUnsavedChanges(true); - m_currentPreviewImage = path; + if (previewImages().contains(path)) + m_currentPreviewImage = path; + else + m_currentPreviewImage = defaultPreviewImages().first(); emit currentPreviewImageChanged(); } +int EffectComposerModel::customPreviewImageCount() const +{ + return m_customPreviewImages.size(); +} + int EffectComposerModel::mainCodeEditorIndex() const { return MAIN_CODE_EDITOR_INDEX; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index b60e07f2530..44480640d27 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -56,7 +56,8 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged) Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) Q_PROPERTY(QUrl currentPreviewImage READ currentPreviewImage WRITE setCurrentPreviewImage NOTIFY currentPreviewImageChanged) - Q_PROPERTY(QUrl customPreviewImage READ customPreviewImage NOTIFY customPreviewImageChanged) + Q_PROPERTY(QList previewImages READ previewImages NOTIFY previewImagesChanged) + Q_PROPERTY(int customPreviewImageCount READ customPreviewImageCount NOTIFY customPreviewImageCountChanged) Q_PROPERTY(int mainCodeEditorIndex READ mainCodeEditorIndex CONSTANT) public: @@ -84,6 +85,7 @@ public: Q_INVOKABLE bool nameExists(const QString &name) const; Q_INVOKABLE void chooseCustomPreviewImage(); Q_INVOKABLE void previewComboAboutToOpen(); + Q_INVOKABLE void removeCustomPreviewImage(const QUrl &url); bool shadersUpToDate() const; void setShadersUpToDate(bool newShadersUpToDate); @@ -123,9 +125,10 @@ public: QString currentComposition() const; void setCurrentComposition(const QString &newCurrentComposition); - QUrl customPreviewImage() const; + QList previewImages() const; QUrl currentPreviewImage() const; void setCurrentPreviewImage(const QUrl &path); + int customPreviewImageCount() const; int mainCodeEditorIndex() const; Utils::FilePath compositionPath() const; @@ -154,7 +157,8 @@ signals: void assignToSelectedTriggered(const QString &effectPath); void removePropertiesFromScene(QSet props, const QString &typeName); void currentPreviewImageChanged(); - void customPreviewImageChanged(); + void previewImagesChanged(); + void customPreviewImageCountChanged(); private: enum Roles { @@ -219,6 +223,8 @@ private: QSet getExposedProperties(const QByteArray &qmlContent); void setCodeEditorIndex(int index); + Utils::FilePath customPreviewImagesPath() const; + QList defaultPreviewImages() const; QList m_nodes; @@ -261,7 +267,7 @@ private: Utils::FilePath m_compositionPath; Utils::UniqueObjectLatePtr m_shadersCodeEditor; QUrl m_currentPreviewImage; - QUrl m_customPreviewImage; + QList m_customPreviewImages; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; From 570be467048de1fd28fd5876e8980c63c65dfba8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 29 Oct 2024 16:36:20 +0200 Subject: [PATCH 065/322] EffectComposer: Bake shaders always if shader files are missing Fixes: QDS-13957 Change-Id: Ib302a326392c02a9c9054294d5b939510de0aacb Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposermodel.cpp | 5 ++++- src/plugins/effectcomposer/effectshaderscodeeditor.cpp | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 26a62d8c7a9..98247d389ee 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -2014,7 +2014,10 @@ void EffectComposerModel::bakeShaders() initShaderDir(); resetEffectError(ErrorPreprocessor); - if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { + if (Utils::FilePath::fromString(m_vertexSourceFilename).exists() + && Utils::FilePath::fromString(m_fragmentSourceFilename).exists() + && m_vertexShader == generateVertexShader() + && m_fragmentShader == generateFragmentShader()) { setShadersUpToDate(true); return; } diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index fdfc10d5036..b8b9c459fdd 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -205,8 +205,7 @@ void EffectShadersCodeEditor::closeEvent(QCloseEvent *event) { QWidget::closeEvent(event); - if (!liveUpdate()) - emit rebakeRequested(); + emit rebakeRequested(); setOpened(false); } From b7ccbb2d8f9cd08eafc7fec2bd4865baca3dfb7c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Sun, 3 Nov 2024 15:55:39 +0200 Subject: [PATCH 066/322] QmlDesigner: Add EnableCMakeGeneration placeholder for all wizards Fixes: QDS-13948 Change-Id: I3dd8d80fcf760c0861ad580de5d42487e6359f4f Reviewed-by: Miikka Heikkinen --- .../studio_templates/projects/desktop-launcher/wizard.json | 3 ++- .../studio_templates/projects/mobile-scroll/wizard.json | 3 ++- .../studio_templates/projects/mobile-stack/wizard.json | 3 ++- .../studio_templates/projects/mobile-swipe/wizard.json | 3 ++- .../templates/wizards/projects/qtquickapplication/wizard.json | 1 + .../projects/qtquickapplication_compat/empty/wizard.json | 3 ++- .../templates/wizards/projects/qtquickuiprototype/wizard.json | 3 ++- 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index 2fc56890b39..0115ddb920c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -30,7 +30,8 @@ { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, - { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" } + { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index 96a35076775..158df87b14f 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -30,7 +30,8 @@ { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, - { "key": "UseVirtualKeyboard", "value": "%{JS: false}" } + { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index 6a0d9455aa1..c7d6ab9ec0a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -28,7 +28,8 @@ { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, - { "key": "UseVirtualKeyboard", "value": "%{JS: false}" } + { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index f7be95e0e37..74ae3610e1f 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -28,7 +28,8 @@ { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, - { "key": "UseVirtualKeyboard", "value": "%{JS: false}" } + { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json index 14e1c60b8fc..83a6cd534c0 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json @@ -25,6 +25,7 @@ { "key": "QdsWizardPath", "value": "%{IDE:ResourcePath}/qmldesigner/studio_templates/projects" }, { "key": "NoQdsProjectStyle", "value": "%{JS: !%{QdsProjectStyle} }" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "ImportModuleVersion", "value": "" }, { "key": "IsQt6Project", "value": true }, diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication_compat/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication_compat/empty/wizard.json index c2365108daa..3ecbaa0be93 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication_compat/empty/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication_compat/empty/wizard.json @@ -28,7 +28,8 @@ { "key": "SetQPAPhysicalSize", "value": "%{UseVirtualKeyboardByDefault}" }, { "key": "AdditionalQmlFiles", "value": "" }, { "key": "AdditionalQmlFilesQbs", "value": "" }, - { "key": "TargetName", "value": "%{JS: 'app' + value('ProjectName')}" } + { "key": "TargetName", "value": "%{JS: 'app' + value('ProjectName')}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json index 251500b4949..dd64ccca659 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json @@ -15,7 +15,8 @@ [ { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qmlproject')}" }, { "key": "MainQmlFileName", "value": "%{JS: Util.fileName(value('ProjectName'), 'qml')}" }, - { "key": "UseVirtualKeyboardByDefault", "value": "%{JS: value('Plugins').indexOf('Boot2Qt') >= 0 || value('Plugins').indexOf('Boot2QtQdb') >= 0}" } + { "key": "UseVirtualKeyboardByDefault", "value": "%{JS: value('Plugins').indexOf('Boot2Qt') >= 0 || value('Plugins').indexOf('Boot2QtQdb') >= 0}" }, + { "key": "EnableCMakeGeneration", "value": "%{JS: false}" } ], "pages": From 914af71ac0f0d9883c0b232fbb9b7f808374de24 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 4 Nov 2024 11:41:00 +0200 Subject: [PATCH 067/322] QmlDesigner: Support expanding/collapsing directories via keyboard Fixes: QDS-13820 Change-Id: Icb28348d72b36818f020fa8a069200815fd810dd Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../assetsLibraryQmlSources/AssetDelegate.qml | 8 ++-- .../assetsLibraryQmlSources/AssetsView.qml | 38 +++++++++++++------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index 53caf73cbfe..f59374e2cc9 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -184,8 +184,6 @@ TreeViewDelegate { root.assetsView.selectedAssets = {} root.assetsView.selectedAssets[root.__itemPath] = root.currFileSelected root.assetsView.selectedAssetsChanged() - - root.assetsView.currentFilePath = root.__itemPath } } @@ -244,10 +242,12 @@ TreeViewDelegate { } onClicked: (mouse) => { - if (mouse.button === Qt.LeftButton) + if (mouse.button === Qt.LeftButton) { root.__toggleExpandCurrentRow() - else + root.assetsView.currentFilePath = root.__itemPath + } else { root.__openContextMenuForCurrentRow() + } } } diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index df45bc32487..1228c67d21d 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -316,9 +316,6 @@ TreeView { function __selectRow(row: int) { let index = root.__modelIndex(row) - if (assetsModel.isDirectory(index)) - return - let filePath = assetsModel.filePath(index) root.clearSelectedAssets() @@ -334,15 +331,10 @@ TreeView { let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) : root.__modelIndex(root.firstRow) let row = root.rowAtIndex(index) - let nextRow = row - let nextIndex = index + let nextRow = row + amount - do { - nextRow = nextRow + amount - if ((amount < 0 && nextRow < root.firstRow) || (amount > 0 && nextRow > root.lastRow)) - return - nextIndex = root.__modelIndex(nextRow) - } while (assetsModel.isDirectory(nextIndex)) + if ((amount < 0 && nextRow < root.firstRow) || (amount > 0 && nextRow > root.lastRow)) + return root.__selectRow(nextRow) root.positionViewAtRow(nextRow, TableView.Contain) @@ -358,6 +350,30 @@ TreeView { moveSelection(1) } + Keys.onRightPressed: { + root.toggleDirectoryState("expand") + } + + Keys.onLeftPressed: { + root.toggleDirectoryState("collapse") + } + + function toggleDirectoryState(action) { + + let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) + : root.__modelIndex(root.firstRow) + + if (!assetsModel.isDirectory(index)) + return + + let row = root.rowAtIndex(index) + + if (action === "expand") + root.expand(row) + else if (action === "collapse") + root.collapse(row) + } + ConfirmDeleteFilesDialog { id: confirmDeleteFiles parent: root From a9e92e4d883fa3d1e5342f99995e268f42df6c46 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 27 Sep 2024 16:16:32 +0200 Subject: [PATCH 068/322] Fix developer documentation build Amends 6ec3c8f9c9fddfd0b44574a638be06ed592ef8d5 We may not remove the generator expressions, they are actually expanded and used, and important, e.g. $ The original issue was that the target that was referred to for some of these (Qt6::QmlDomPrivate) was not known at the point where the documentation target needed them. Just filter out non-existent targets instead. Change-Id: If75b2f06fdcefdfad1b2ab0ece3d994e3000edfd Reviewed-by: Cristian Adam (cherry picked from commit 91d0bf1993a1c72558b9f1e62e5c32ad88823967) Reviewed-by: Tim Jenssen --- doc/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 8d53d759d81..7d34b4365b3 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -16,8 +16,9 @@ function(_find_all_includes _ret_includes _ret_framework_paths) string(FIND "${_include}" "/src/plugins/" _in_plugins) string(FIND "${_include}" "/src/libs/" _in_libs) string(FIND "${_include}" "${CMAKE_BINARY_DIR}" _in_build) - if(_in_plugins LESS 0 AND _in_libs LESS 0 AND _in_build LESS 0) - remove_generator_expressions(_include ${_include}) + string(REGEX MATCH "\\$" _property_match "${_include}") + set(_property_target "${CMAKE_MATCH_1}") + if(_in_plugins LESS 0 AND _in_libs LESS 0 AND _in_build LESS 0 AND (NOT _property_target OR TARGET ${_property_target})) list(APPEND _all_includes ${_include}) endif() endforeach() From b0e5c217d8810da6173c2a538f7d2006a5584e4a Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 4 Nov 2024 23:33:42 +0200 Subject: [PATCH 069/322] QmlDesigner: Fix created folder name in Assets view Fixes: QDS-13901 Change-Id: I9a29062ff671760a0ffe2564acf253f97c9d170d Reviewed-by: Mahmoud Badri --- .../qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml index 3426f7965a6..6c974685744 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml @@ -87,7 +87,7 @@ StudioControls.Dialog { enabled: folderName.text !== "" && root.createdDirPath.length <= root.__maxPath onClicked: { let dirPathToCreate = root.dirPath + '/' + folderName.text - root.createdDirPath = AssetsLibraryBackend.assetsModel.addNewFolder(root.createdDirPath) + root.createdDirPath = AssetsLibraryBackend.assetsModel.addNewFolder(dirPathToCreate) if (root.createdDirPath) root.accept() From 33bd38242a62f4262ba24e32d7e87a92c02f15e4 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 4 Nov 2024 11:38:25 +0100 Subject: [PATCH 070/322] DeviceShare: Update device when adding a new device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch updates the old device information instead of adding a new device when a new device is added to the system and they have the same ID. Change-Id: Id2addc61589267bdd5d6d74c5c6b3fdf9d8a2da1 Reviewed-by: Henning Gründl --- .../components/devicesharing/device.cpp | 2 +- .../components/devicesharing/device.h | 2 +- .../devicesharing/devicemanager.cpp | 27 ++++++++++++++++++- .../components/devicesharing/devicemanager.h | 4 ++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/device.cpp b/src/plugins/qmldesigner/components/devicesharing/device.cpp index d0303128e49..b7f429bf082 100644 --- a/src/plugins/qmldesigner/components/devicesharing/device.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/device.cpp @@ -189,7 +189,7 @@ void Device::processTextMessage(const QString &data) if (dataType == PackageFromDevice::deviceInfo) { QJsonObject deviceInfo = jsonObj.value("data").toObject(); m_deviceInfo.setJsonObject(deviceInfo); - emit deviceInfoReady(m_deviceInfo.deviceId(), m_deviceInfo); + emit deviceInfoReady(m_deviceSettings.ipAddress(), m_deviceInfo.deviceId(), m_deviceInfo); } else if (dataType == PackageFromDevice::projectRunning) { emit projectStarted(m_deviceInfo.deviceId()); } else if (dataType == PackageFromDevice::projectStopped) { diff --git a/src/plugins/qmldesigner/components/devicesharing/device.h b/src/plugins/qmldesigner/components/devicesharing/device.h index 08f76d04dc8..1419c12ec78 100644 --- a/src/plugins/qmldesigner/components/devicesharing/device.h +++ b/src/plugins/qmldesigner/components/devicesharing/device.h @@ -56,7 +56,7 @@ private: signals: void connected(const QString &deviceId); void disconnected(const QString &deviceId); - void deviceInfoReady(const QString &deviceId, const DeviceInfo &deviceInfo); + void deviceInfoReady(const QString &deviceIp, const QString &deviceId, const DeviceInfo &deviceInfo); void projectStarted(const QString &deviceId); void projectStopped(const QString &deviceId); void projectLogsReceived(const QString &deviceId, const QString &logs); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index ff80f14c8e0..5a08e09791a 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -255,8 +255,33 @@ QSharedPointer DeviceManager::initDevice(const DeviceInfo &deviceInfo, return device; } -void DeviceManager::deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo) +void DeviceManager::deviceInfoReceived(const QString &deviceIp, + const QString &deviceId, + const DeviceInfo &deviceInfo) { + auto newDevIt = std::find_if(m_devices.begin(), + m_devices.end(), + [deviceId, deviceIp](const auto &device) { + return device->deviceInfo().deviceId() == deviceId + && device->deviceSettings().ipAddress() == deviceIp; + }); + auto oldDevIt = std::find_if(m_devices.begin(), + m_devices.end(), + [deviceId, deviceIp](const auto &device) { + return device->deviceInfo().deviceId() == deviceId + && device->deviceSettings().ipAddress() != deviceIp; + }); + + if (oldDevIt != m_devices.end()) { + QSharedPointer oldDevice; + QSharedPointer newDevice; + std::tie(oldDevice, newDevice) = std::make_tuple(*oldDevIt, *newDevIt); + DeviceSettings deviceSettings = oldDevice->deviceSettings(); + deviceSettings.setIpAddress(newDevice->deviceSettings().ipAddress()); + newDevice->setDeviceSettings(deviceSettings); + m_devices.removeOne(oldDevice); + } + writeSettings(); qCDebug(deviceSharePluginLog) << "Device" << deviceId << "is online"; emit deviceOnline(deviceInfo); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 066b766b2f9..012ce5fb8d2 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -53,7 +53,9 @@ private: const DeviceSettings &deviceSettings = DeviceSettings()); // device signals - void deviceInfoReceived(const QString &deviceId, const DeviceInfo &deviceInfo); + void deviceInfoReceived(const QString &deviceIp, + const QString &deviceId, + const DeviceInfo &deviceInfo); void deviceDisconnected(const QString &deviceId); QSharedPointer findDevice(const QString &deviceId) const; From 548138a8375c2f730eac91ae2dac0eee78152a2c Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 4 Nov 2024 12:26:54 +0100 Subject: [PATCH 071/322] DeviceShare: Generate alias on device creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic42150b622c4bd1ef982c2843b9a0b52baa6f8f2 Reviewed-by: Henning Gründl --- .../devicesharing/devicemanager.cpp | 19 +++++++++++++++++++ .../components/devicesharing/devicemanager.h | 5 ++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 5a08e09791a..d984025826f 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -197,6 +197,24 @@ void DeviceManager::setDeviceIP(const QString &deviceId, const QString &ip) writeSettings(); } +QString DeviceManager::generateDeviceAlias() const +{ + QStringList m_currentAliases; + + for (const auto &device : m_devices) { + m_currentAliases.append(device->deviceSettings().alias()); + } + + for (int i = 0; i < m_devices.size(); ++i) { + QString alias = QString("Device %1").arg(i); + if (!m_currentAliases.contains(alias)) { + return alias; + } + } + + return QString("Device %1").arg(m_devices.size() + 1); +} + void DeviceManager::addDevice(const QString &ip) { if (ip.isEmpty()) @@ -220,6 +238,7 @@ void DeviceManager::addDevice(const QString &ip) DeviceSettings deviceSettings; deviceSettings.setIpAddress(trimmedIp); + deviceSettings.setAlias(generateDeviceAlias()); auto device = initDevice({}, deviceSettings); m_devices.append(device); writeSettings(); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 012ce5fb8d2..ad7c5ad0572 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -16,9 +16,6 @@ class DeviceManager : public QObject public: explicit DeviceManager(QObject *parent = nullptr, const QString &settingsPath = "settings.json"); - // internal init functions - void addDevice(const QString &ip); - // Getters QList> devices() const; std::optional deviceInfo(const QString &deviceId) const; @@ -28,6 +25,7 @@ public: void setDeviceActive(const QString &deviceId, const bool active); void setDeviceIP(const QString &deviceId, const QString &ip); + void addDevice(const QString &ip); void removeDevice(const QString &deviceId); void removeDeviceAt(int index); bool sendProjectFile(const QString &deviceId, const QString &projectFile); @@ -59,6 +57,7 @@ private: void deviceDisconnected(const QString &deviceId); QSharedPointer findDevice(const QString &deviceId) const; + QString generateDeviceAlias() const; signals: void deviceAdded(const DeviceInfo &deviceInfo); From 052c8efe11f9d080827a5dde11ef4b15dedaaf84 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 5 Nov 2024 09:36:29 +0200 Subject: [PATCH 072/322] QmlDesigner: Support dragging textures from ContentLibrary to EffectComposer Fixes: QDS-13797 Change-Id: I1e3e1adbc7953145aecbf54ce34bf790b2ef32bf Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposerview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index 7d24fc66f8b..c0478790715 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -246,7 +246,8 @@ void EffectComposerView::highlightSupportedProperties(bool highlight, const QStr void EffectComposerView::dragStarted(QMimeData *mimeData) { - if (mimeData->hasFormat(QmlDesigner::Constants::MIME_TYPE_ASSETS)) { + if (mimeData->hasFormat(QmlDesigner::Constants::MIME_TYPE_ASSETS) + || mimeData->hasFormat(QmlDesigner::Constants::MIME_TYPE_BUNDLE_TEXTURE)) { QString format = mimeData->formats()[0]; const QString assetPath = QString::fromUtf8(mimeData->data(format)).split(',')[0]; const QString suffix = "*." + assetPath.split('.').last().toLower(); From a18e16aba4ab517a2f7d90200fa81dc8fc8b224f Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 5 Nov 2024 14:26:47 +0100 Subject: [PATCH 073/322] DeviceShare: Return bool on device add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9ee9ad48f7ed613cdc9433c650d7b5aab558db5f Reviewed-by: Henning Gründl --- .../components/devicesharing/devicemanager.cpp | 16 +++++++--------- .../components/devicesharing/devicemanager.h | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index d984025826f..4e41df28d43 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -215,10 +215,10 @@ QString DeviceManager::generateDeviceAlias() const return QString("Device %1").arg(m_devices.size() + 1); } -void DeviceManager::addDevice(const QString &ip) +bool DeviceManager::addDevice(const QString &ip) { if (ip.isEmpty()) - return; + return false; const auto trimmedIp = ip.trimmed(); @@ -226,13 +226,13 @@ void DeviceManager::addDevice(const QString &ip) QRegularExpression ipRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)"); if (!ipRegex.match(trimmedIp).hasMatch()) { qCWarning(deviceSharePluginLog) << "Invalid IP address" << ip; - return; + return false; } for (const auto &device : m_devices) { if (device->deviceSettings().ipAddress() == trimmedIp) { qCWarning(deviceSharePluginLog) << "Device" << trimmedIp << "already exists"; - return; + return false; } } @@ -243,6 +243,8 @@ void DeviceManager::addDevice(const QString &ip) m_devices.append(device); writeSettings(); emit deviceAdded(device->deviceInfo()); + + return true; } QSharedPointer DeviceManager::initDevice(const DeviceInfo &deviceInfo, @@ -354,11 +356,7 @@ bool DeviceManager::sendProjectFile(const QString &deviceId, const QString &proj } qCDebug(deviceSharePluginLog) << "Sending project file to device" << deviceId; - - QByteArray projectData = file.readAll(); - return device->sendProjectData(projectData); - - qCDebug(deviceSharePluginLog) << "Project file sent to device" << deviceId; + return device->sendProjectData(file.readAll()); } bool DeviceManager::stopRunningProject(const QString &deviceId) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index ad7c5ad0572..5120e407f10 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -25,7 +25,7 @@ public: void setDeviceActive(const QString &deviceId, const bool active); void setDeviceIP(const QString &deviceId, const QString &ip); - void addDevice(const QString &ip); + bool addDevice(const QString &ip); void removeDevice(const QString &deviceId); void removeDeviceAt(int index); bool sendProjectFile(const QString &deviceId, const QString &projectFile); From d7a1ba6662f9d5a3c5790ce40fce7942fe51382d Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 4 Nov 2024 14:22:46 +0100 Subject: [PATCH 074/322] QmlDesigner: Remove copyright header from templates These templates are intended for user projects, so they should not contain our copyright notice. Task-number: QDS-14043 Change-Id: I4df87f67db791852307b0f55429670fbbc9eb957 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/application-3d/detailsPage.qml | 3 --- .../projects/application-extended-3d/detailsPage.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/Button.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/CheckBox.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/DefaultStyle.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/Dial.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/ProgressBar.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/RadioButton.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/Slider.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/SwipeView.qml | 3 --- .../projects/application-mcu/MCUDefaultStyle/Switch.qml | 3 --- .../studio_templates/projects/application-mcu/detailsPage.qml | 3 --- .../studio_templates/projects/application/detailsPage.qml | 3 --- .../qmldesigner/studio_templates/projects/common/App.qml.tpl | 3 --- .../studio_templates/projects/desktop-launcher/detailsPage.qml | 3 --- .../studio_templates/projects/mobile-scroll/detailsPage.qml | 3 --- .../studio_templates/projects/mobile-stack/detailsPage.qml | 3 --- .../studio_templates/projects/mobile-swipe/detailsPage.qml | 3 --- .../projects/shared-plugin/name/EventListModel.qml.tpl | 3 --- .../projects/shared-plugin/name/EventListSimulator.qml.tpl | 3 --- 20 files changed, 60 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/detailsPage.qml index d78081ddd87..e3a8cf6ed12 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml index 224b9b1736f..e3a8cf6ed12 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml @@ -1,6 +1,3 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - import ProjectType DefaultProject { 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 e8be964890e..3581d6241af 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 e86da1c485d..9d91806b52a 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 08437b53fad..79e36200bd3 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - pragma Singleton import QtQuick 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 bcb2af25134..9b17df3783d 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 5908f115842..197b97d1b67 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 f182a82421a..1a5a5927b27 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 db7a8bc8e97..591a15b92c0 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 90065ac0ce5..1a63afab182 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T 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 e614c746df5..098dc6c14b3 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,6 +1,3 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - import QtQuick import QtQuick.Templates as T diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/detailsPage.qml index aeac4f2c040..8c63bbc2a87 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 import NewProjectDialog diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application/detailsPage.qml index d78081ddd87..e3a8cf6ed12 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl index 5a440eddccc..d7cf65e19bb 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl @@ -1,6 +1,3 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - import QtQuick import %{ImportModuleName} @if %{UseVirtualKeyboard} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/detailsPage.qml index d78081ddd87..e3a8cf6ed12 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/detailsPage.qml index 08e08bb3e72..9790cd61362 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/detailsPage.qml index d78081ddd87..e3a8cf6ed12 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/detailsPage.qml index 08e08bb3e72..9790cd61362 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/detailsPage.qml +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/detailsPage.qml @@ -1,6 +1,3 @@ -// 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 ProjectType DefaultProject { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListModel.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListModel.qml.tpl index c5cf4391360..00c70659823 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListModel.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListModel.qml.tpl @@ -1,6 +1,3 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - import QtQuick ListModel { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListSimulator.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListSimulator.qml.tpl index a2250ad5aa5..d26ae6d5071 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListSimulator.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/EventListSimulator.qml.tpl @@ -1,6 +1,3 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - import QtQuick import QtQuick.Studio.EventSimulator import QtQuick.Studio.EventSystem From b64aa8cc9782b6c72957fc4137d796a76740e021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Wed, 18 Sep 2024 12:09:20 +0200 Subject: [PATCH 075/322] QmlDesigner: fix deprecate warnings against Qt 6.8 Change-Id: Ib1316c9cc0bf0781a9f63e2c4f6c9808b43bc1ef Reviewed-by: Thomas Hartmann --- .../components/materialbrowser/materialbrowserwidget.cpp | 2 +- .../components/timelineeditor/timelinetoolbar.cpp | 2 +- .../libs/designercore/rewriter/texttomodelmerger.cpp | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 54aff337002..31f4fe1dcc0 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -193,7 +193,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, setStyleSheet(Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F8), this); + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F8), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &MaterialBrowserWidget::reloadQmlSource); connect(m_materialBrowserModel, &MaterialBrowserModel::isEmptyChanged, this, [&] { diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp index 7c0e69d9ce7..d4cc1d9b87f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp @@ -329,7 +329,7 @@ void TimelineToolBar::createCenterControls() Theme::iconFromName(Theme::Icon::loopPlayback_medium), tr("Loop Playback"), QKeySequence((Qt::ControlModifier | Qt::ShiftModifier) - + Qt::Key_Space)); // TODO: Toggles looping. Select shortcut for this QDS-4941 + | Qt::Key_Space)); // TODO: Toggles looping. Select shortcut for this QDS-4941 loopAnimation->setCheckable(true); connect(loopAnimation, &QAction::toggled, [&](bool value) { emit loopPlaybackToggled(value);} ); diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp index c4bfd576291..3cbf33e391c 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp @@ -531,14 +531,11 @@ public: QVariant value(cleanedValue); if (propertyTypeMetaInfo.isBool()) { - value.convert(QVariant::Bool); - return value; + return value.toBool(); } else if (propertyTypeMetaInfo.isInteger()) { - value.convert(QVariant::Int); - return value; + return value.toInt(); } else if (propertyTypeMetaInfo.isFloat()) { - value.convert(QVariant::Double); - return value; + return value.toDouble(); } else if (propertyTypeMetaInfo.isString()) { // nothing to do } else { //property alias et al From 6748b8c7a55bcc1698a6a8750b6e4bd113663f0e Mon Sep 17 00:00:00 2001 From: Przemyslaw Lewandowski Date: Fri, 25 Oct 2024 14:58:49 +0200 Subject: [PATCH 076/322] QmlDesigner: fix empty theme bindings for MCU Task-number: QDS-13938 Change-Id: I299687313e4b3bc8c92009eef91e357a75787234 Reviewed-by: Vikas Pachdha --- .../qmldesigner/libs/designsystem/dsthemegroup.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp index aa867a13e19..35b4e886656 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsthemegroup.cpp @@ -30,6 +30,17 @@ std::optional groupTypeName(GroupType type) return {}; } +QVariant getDefaultValueForGroupType(GroupType type) +{ + switch (type) { + case QmlDesigner::GroupType::Colors: return QVariant("#000000"); + case QmlDesigner::GroupType::Flags: return QVariant(false); + case QmlDesigner::GroupType::Numbers: return QVariant(0); + case QmlDesigner::GroupType::Strings: return QVariant(""); + } + return {}; +} + QDebug &operator<<(QDebug &s, const ThemeProperty &p) { s << "{Name:" << p.name << ", Value:" << p.value << ", isBinding:" << p.isBinding << "}"; @@ -187,7 +198,7 @@ void DSThemeGroup::decorateComponent(ModelNode node) // Add properties with type to the node for (auto &[propName, values] : m_values) { auto nodeProp = node.variantProperty(propName); - nodeProp.setDynamicTypeNameAndValue(*typeName, nodeProp.value()); + nodeProp.setDynamicTypeNameAndValue(*typeName, getDefaultValueForGroupType(m_type)); } } From cb84f92f78b7ec7de5a2d55684b65ec15af3e88d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 1 Nov 2024 19:14:55 +0100 Subject: [PATCH 077/322] QmlDesigner: Export CompoundPropertyMetaInfos Change-Id: I47af5f0e4f7b38692a4f1a9e8ba6e5e601538bde Reviewed-by: Marco Bubke --- .../libs/designercore/include/propertymetainfo.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/include/propertymetainfo.h b/src/plugins/qmldesigner/libs/designercore/include/propertymetainfo.h index 8784329854e..7594103893c 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/propertymetainfo.h +++ b/src/plugins/qmldesigner/libs/designercore/include/propertymetainfo.h @@ -107,7 +107,7 @@ private: using PropertyMetaInfos = std::vector; -struct CompoundPropertyMetaInfo +struct QMLDESIGNERCORE_EXPORT CompoundPropertyMetaInfo { CompoundPropertyMetaInfo(PropertyMetaInfo &&property) : property(std::move(property)) @@ -134,8 +134,8 @@ using CompoundPropertyMetaInfos = std::vector; namespace MetaInfoUtils { -CompoundPropertyMetaInfos inflateValueProperties(PropertyMetaInfos properties); -CompoundPropertyMetaInfos inflateValueAndReadOnlyProperties(PropertyMetaInfos properties); +QMLDESIGNERCORE_EXPORT CompoundPropertyMetaInfos inflateValueProperties(PropertyMetaInfos properties); +QMLDESIGNERCORE_EXPORT CompoundPropertyMetaInfos inflateValueAndReadOnlyProperties(PropertyMetaInfos properties); } // namespace MetaInfoUtils } // namespace QmlDesigner From 8042092f9987b009308ff7e0b1336d7d37e8e56d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:32:26 +0100 Subject: [PATCH 078/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: I21214688f1360f579287ac814ea3377f506e1133 Reviewed-by: Miikka Heikkinen --- .../components/import3d/import3dconnectionmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/import3d/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/import3d/import3dconnectionmanager.cpp index efa5c27bca3..e22d52c831c 100644 --- a/src/plugins/qmldesigner/components/import3d/import3dconnectionmanager.cpp +++ b/src/plugins/qmldesigner/components/import3d/import3dconnectionmanager.cpp @@ -29,7 +29,7 @@ void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback) void Import3dConnectionManager::dispatchCommand(const QVariant &command, ConnectionManagerInterface::Connection &connection) { - static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + static const int commandType = QMetaType::fromName("PuppetToCreatorCommand").id();; if (command.typeId() == commandType) { auto cmd = command.value(); From 8c7ab900273d1b39742328534d8e9ef04d06d4d0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 5 Sep 2024 16:09:43 +0200 Subject: [PATCH 079/322] QmlDesigner: Be more restrictive regarding imports in lite mode Change-Id: I7d415ca5db3a21a66c52ff0e94351488d23f9faa Reviewed-by: Thomas Hartmann --- .../components/itemlibrary/itemlibraryaddimportmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp index fe6c9058a73..0d7694d0f53 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp @@ -90,7 +90,7 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports) } else if (isLiteDesigner) { const QStringList liteAllowedList = {"QtQuick", "QtQuick.Layouts", "QtQuick.Controls"}; filteredImports = Utils::filtered(possibleImports, [&](const Import &import) { - return (liteAllowedList.contains(import.url()) || !import.url().startsWith("Qt")); + return (liteAllowedList.contains(import.url())); }); } else { filteredImports = possibleImports; From 04e004ffa4851432f5b631866ffe6eeccf0eb0bc Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 5 Nov 2024 16:21:40 +0100 Subject: [PATCH 080/322] QmlProjectExporter: Allow special characters in image file names Fixes: QDS-12635 Change-Id: I916a02f3a0ebed05e87a7b2645c3a9adec124fb4 Reviewed-by: Thomas Hartmann --- .../qmlprojectexporter/cmakegenerator.cpp | 6 ++++-- .../qmlprojectmanager/qmlprojectexporter/filetypes.cpp | 9 ++++++--- .../qmlprojectmanager/qmlprojectexporter/filetypes.h | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp index c529f012eef..3cc560a40b8 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp @@ -355,8 +355,10 @@ bool CMakeGenerator::findFile(const NodePtr &node, const Utils::FilePath &file) void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const { QString error; - if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error)) - logIssue(ProjectExplorer::Task::Error, error, path); + if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error)) { + if (!isImageFile(path)) + logIssue(ProjectExplorer::Task::Error, error, path); + } if (path.fileName() == "qmldir") { readQmlDir(path, node); diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.cpp index c69afd9a70c..04fd001816f 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.cpp @@ -29,15 +29,18 @@ bool isQmlFile(const Utils::FilePath &path) return suffix == "qml" || suffix == "ui.qml"; } +bool isImageFile(const Utils::FilePath &path) +{ + return imageFiles().contains(path.suffix(), Qt::CaseInsensitive); +} + bool isAssetFile(const Utils::FilePath &path) { static const QStringList suffixes = { "js", "ts", "json", "hints", "mesh", "qad", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "mng", "wav" }; - return - suffixes.contains(path.suffix(), Qt::CaseInsensitive) || - imageFiles().contains(path.suffix(), Qt::CaseInsensitive); + return suffixes.contains(path.suffix(), Qt::CaseInsensitive) || isImageFile(path); } bool isResource(const Utils::FilePath &path) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.h b/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.h index efe07df118c..25a2714aa44 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.h +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/filetypes.h @@ -11,6 +11,7 @@ QMLPROJECTMANAGER_EXPORT QStringList imageFiles( const std::function &transformer = nullptr); QMLPROJECTMANAGER_EXPORT bool isQmlFile(const Utils::FilePath &path); +QMLPROJECTMANAGER_EXPORT bool isImageFile(const Utils::FilePath &path); QMLPROJECTMANAGER_EXPORT bool isAssetFile(const Utils::FilePath &path); QMLPROJECTMANAGER_EXPORT bool isResource(const Utils::FilePath &path); From 244c1d582c4478fb62171aead6d91d03c7b2b966 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 7 Nov 2024 16:13:22 +0100 Subject: [PATCH 081/322] QmlDesigner: Fix resource generation on linux Fix resource generation on linux by disabling zstd Change-Id: I68cf6fd65ad25ea19f51467783c68948552c508a Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/componentcore/resourcegenerator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index e9874246ddd..93f3d458e94 100644 --- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -199,6 +199,7 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath) rccProcess.setWorkingDirectory(project->projectDirectory()); const QStringList arguments = {"--binary", + "--no-zstd", "--compress", "9", "--threshold", From d4a03e7b39da2480e0ed2e5aa50a2fcdef61085e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:33:12 +0100 Subject: [PATCH 082/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: I4bed4690db1a458a992ecc1c2435493a5bcc16ef Reviewed-by: Tim Jenssen --- .../qmldesigner/components/navigator/navigatortreeview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp index 8de0fb1b01c..c512122a56d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp @@ -249,7 +249,7 @@ bool NavigatorTreeView::viewportEvent(QEvent *event) m_previewToolTip->hide(); m_previewToolTipNodeId = -1; } else { - m_previewToolTip->move(mapToGlobal(he->pos()) + offset); + m_previewToolTip->move(mapToGlobal(he->position().toPoint()) + offset); } } } From 87ce52b2e7fb8b0326eceb96d122e50a679c39f0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:33:54 +0100 Subject: [PATCH 083/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: I2ae9452dcaa242a1c10255c5becc770c7fcbee5a Reviewed-by: Tim Jenssen --- .../qml2puppet/instances/qt5informationnodeinstanceserver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 7b845d2db07..aa3b4386c61 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -248,7 +248,8 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() continue; } } - QMouseEvent me(command.type(), command.pos(), command.button(), command.buttons(), + QPoint globalPos = m_editView3DData.window->mapToGlobal(command.pos()); + QMouseEvent me(command.type(), command.pos(), globalPos, command.button(), command.buttons(), command.modifiers()); // We must use sendEvent in Qt 6, as using postEvent allows the associated position // data stored internally in QMutableEventPoint to potentially be updated by system From 578cc6578373a3ffa6c85766306a6e79c7d8d4af Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:34:18 +0100 Subject: [PATCH 084/322] UnitTests: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: I35005f46d4d73221a37329c520996a771ac136d0 Reviewed-by: Tim Jenssen --- tests/unit/tests/printers/gtest-qt-printing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/tests/printers/gtest-qt-printing.cpp b/tests/unit/tests/printers/gtest-qt-printing.cpp index 970eae8fd96..ef4425e1eb7 100644 --- a/tests/unit/tests/printers/gtest-qt-printing.cpp +++ b/tests/unit/tests/printers/gtest-qt-printing.cpp @@ -58,7 +58,7 @@ std::ostream &operator<<(std::ostream &out, const QVariant &variant) std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format) { out << "(" - << format.fontFamily(); + << format.fontFamilies(); if (format.fontItalic()) out << ", italic"; From 86afae33de110e36c8938e56270d619af424312d Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 5 Nov 2024 15:35:52 +0100 Subject: [PATCH 085/322] Trigger cmake generation when modifying qmldir files Fixes: QDS-13829 Change-Id: I4b5a3cfa24a866a8a5202d0cc49e59a9708d51ec Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- .../projectitem/filefilteritems.cpp | 29 +++++++++++++++++++ .../buildsystem/projectitem/filefilteritems.h | 2 ++ .../projectitem/qmlprojectitem.cpp | 6 ++++ .../buildsystem/projectitem/qmlprojectitem.h | 1 + .../qmlprojectexporter/cmakegenerator.cpp | 20 +++++++++++++ .../qmlprojectexporter/cmakegenerator.h | 1 + .../qmlprojectexporter/exporter.cpp | 1 + 7 files changed, 60 insertions(+) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp index 9e834891949..007889ba3a9 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.cpp @@ -40,6 +40,8 @@ Utils::FileSystemWatcher *FileFilterItem::dirWatcher() m_dirWatcher->setObjectName(QLatin1String("FileFilterBaseItemWatcher")); connect(m_dirWatcher, &Utils::FileSystemWatcher::directoryChanged, this, &FileFilterItem::updateFileList); + connect(m_dirWatcher, &Utils::FileSystemWatcher::fileChanged, + [this](const QString& path) { emit fileModified(path); }); } return m_dirWatcher; } @@ -203,6 +205,31 @@ void FileFilterItem::updateFileList() #endif } +void FileFilterItem::watchFiles(QSet filters, const QSet &add, const QSet &remove) +{ + const QSet mFilterSet = QSet(m_filter.begin(), m_filter.end()); + if (!filters.intersects(mFilterSet)) + return; + + filters = Utils::transform>(filters, [](QString filter) { + if (filter.startsWith("*.")) + filter = filter.mid(2); + return filter; + }); + + for (const auto& fileString : add) { + Utils::FilePath filePath = Utils::FilePath::fromString(fileString); + bool hasName = filters.contains(filePath.fileName()) || filters.contains(filePath.suffix()); + if (hasName && !dirWatcher()->watchesFile(fileString)) + dirWatcher()->addFile(fileString, Utils::FileSystemWatcher::WatchModifiedDate); + } + for (const auto& fileString : remove) { + Utils::FilePath filePath = Utils::FilePath::fromString(fileString); + if (filters.contains(filePath.fileName()) || filters.contains(filePath.suffix())) + dirWatcher()->removeFile(fileString); + } +} + void FileFilterItem::updateFileListNow() { if (m_updateFileListTimer.isActive()) @@ -228,6 +255,8 @@ void FileFilterItem::updateFileListNow() addedFiles.subtract(unchanged); removedFiles.subtract(unchanged); + watchFiles({"qmldir", "*.bla"}, addedFiles, removedFiles); + m_files = newFiles; emit filesChanged(addedFiles, removedFiles); } diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h index e5e3edb2a32..e835f6fae20 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/filefilteritems.h @@ -52,11 +52,13 @@ signals: void directoryChanged(); void recursiveChanged(); void pathsChanged(); + void fileModified(const QString &filePath); void filesChanged(const QSet &added, const QSet &removed); private: void updateFileList(); void updateFileListNow(); + void watchFiles(QSet filters, const QSet &add, const QSet &remove); QString absolutePath(const QString &path) const; QString absoluteDir() const; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 63380fb5b9d..82bbf05b6df 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -94,6 +94,11 @@ void QmlProjectItem::setupFileFilters() &FileFilterItem::filesChanged, this, &QmlProjectItem::filesChanged); + + connect(fileFilterItem.get(), + &FileFilterItem::fileModified, + this, + &QmlProjectItem::fileModified); #endif m_content.push_back(std::move(fileFilterItem)); }; @@ -120,6 +125,7 @@ void QmlProjectItem::setupFileFilters() fileFilterItem->setDirectory(groupDir.toString()); #ifndef TESTS_ENABLED_QMLPROJECTITEM connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, &QmlProjectItem::filesChanged); + connect(fileFilterItem.get(), &FileFilterItem::fileModified, this, &QmlProjectItem::fileModified); #endif m_content.push_back(std::move(fileFilterItem)); }; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 6bfc351edff..33d054d23fc 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -102,6 +102,7 @@ public: void setEnablePythonGeneration(bool enable); signals: + void fileModified(const QString &filePath); void filesChanged(const QSet &, const QSet &); private: diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp index 3cc560a40b8..14cdd884974 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp @@ -107,6 +107,22 @@ bool CMakeGenerator::hasChildModule(const NodePtr &node) const return false; } +void CMakeGenerator::updateModifiedFile(const QString &fileString) +{ + if (!isEnabled() || !m_writer) + return; + + const Utils::FilePath path = Utils::FilePath::fromString(fileString); + if (path.fileName() != "qmldir") + return; + + if (auto node = findOrCreateNode(m_root, path.parentDir())) + insertFile(node, path); + + createCMakeFiles(m_root); + createSourceFiles(); +} + void CMakeGenerator::update(const QSet &added, const QSet &removed) { if (!isEnabled() || !m_writer) @@ -234,6 +250,10 @@ bool CMakeGenerator::isMockModule(const NodePtr &node) const void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const { + node->uri = ""; + node->name = ""; + node->singletons.clear(); + if (isMockModule(node)) node->type = Node::Type::MockModule; else diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h index 43257e17815..c0464001c10 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h @@ -37,6 +37,7 @@ public: bool isRootNode(const NodePtr &node) const; bool hasChildModule(const NodePtr &node) const; + void updateModifiedFile(const QString &file); void update(const QSet &added, const QSet &removed); private: diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/exporter.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/exporter.cpp index d4f7b752588..9c5a849de1b 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/exporter.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/exporter.cpp @@ -30,6 +30,7 @@ void Exporter::updateProject(QmlProject *project) void Exporter::updateProjectItem(QmlProjectItem *item, bool updateEnabled) { connect(item, &QmlProjectItem::filesChanged, m_cmakeGen, &CMakeGenerator::update); + connect(item, &QmlProjectItem::fileModified, m_cmakeGen, &CMakeGenerator::updateModifiedFile); if (updateEnabled) { m_cmakeGen->setEnabled(item->enableCMakeGeneration()); From ca203c544cfff71494c93f43de3f301f101f5367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 8 Nov 2024 17:45:04 +0100 Subject: [PATCH 086/322] qtcreatorcdbext: disable it if BUILD_DESIGNSTUDIO=ON Change-Id: I83d531029e59a2ad4fe3f24c5cf01498743ca28e Reviewed-by: Tim Jenssen Reviewed-by: Eike Ziller --- src/libs/qtcreatorcdbext/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 47eca2cd163..3505933ad4c 100644 --- a/src/libs/qtcreatorcdbext/CMakeLists.txt +++ b/src/libs/qtcreatorcdbext/CMakeLists.txt @@ -15,7 +15,7 @@ if (NOT QT_CREATOR_API_DEFINED) qtc_handle_compiler_cache_support() endif() -if (NOT WIN32 OR NOT MSVC) +if (NOT WIN32 OR NOT MSVC OR BUILD_DESIGNSTUDIO) return() endif() From 90127d1a8f877e29cc3edf9fbcec8a9fd7943ad2 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 11 Nov 2024 14:35:35 +0200 Subject: [PATCH 087/322] Doc: Update icon Change-Id: I7ac047d58c05212280b761fea5b1188eae9f296a Reviewed-by: Johanna Vanhatapio --- doc/qtcreator/images/icons/examples-32x32.png | Bin 0 -> 1777 bytes doc/qtdesignstudio/src/qtdesignstudio.qdoc | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 doc/qtcreator/images/icons/examples-32x32.png diff --git a/doc/qtcreator/images/icons/examples-32x32.png b/doc/qtcreator/images/icons/examples-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..561e9cb56f198896c92c72655e9b9ef3e7fa7195 GIT binary patch literal 1777 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9GG!XV7ZFl&wk z0|U#u%#etZ2wxwokg&dz0$52&wylyQ$U=n(-v9;Y{GwC^OFcu~WCH^u1#?TiB=cl5ljLL_1tS9^ z6MX}7eM2){19K}=ODhut1t?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;nPT|>hVLlY|_Q!7J5Z381K0|SUs-~5!!v`TDBjSZ~~Em4%FmL#SmmLw8XoRVyn zmS2>cSYoS`nVXoNs$YEE1D+&6CoMbdyX>EOjlDlT&rm%ncGvQj?O6QZ3V9`d#vqOF=OU z)1N}3e*dD(^vt}(9J0*;WjIihu=2<(F3B&dgyx9g)IumLCsP5O3ak>7L8;qTDH#;4 zsVPcO;Y_ka0Ag%;Mrt13=!PgO&QB{TPb^AxOi#@#A())>?G$YELHQ8k4}EZ|NAek?jSZtOhYzx(;_ z?pez`|EgYbd=M{~G3(i_(!06rUn=L#4@qF*+>&c8v}~$GM}hx*O^5b0ll>RV&z`@3 zgr#edN6$glJqHA2XI&}%%NG(_y6|V`_48S2ESzg(zbun4Iq~2#PrJfTL8c1lLwBtT zj?DSkDp0vZRJh^whcZ0@sT`HV{KW=N52cR1k6`x~64)Bja_e!}*|zBFeFBSC-;AI3 zH1bj7nY}?O25VHsc3z)q_3~`)%{7cIiYYmM`p3BQ`~-v@k`i0fy$pKN?id^qFgWC; z%Hs6fMXBYtnSeyiEwi&n%}L`vs1N&VHBc`LZW* z)8c-Y68nZ(`Rzyi?k=<3=qjbOai_e%v!;BNx~UEQEj#2VFzMdn;naQ0zUaYg<^|Gg mx_>oBoH5lsZojy>q1V6ld!paBWp8hSDkV=>KbLh*2~7ZGq;Prw literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index dee3622540b..bad0da25798 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -65,7 +65,7 @@ \row \li \inlineimage icons/demo-32x32.png \li \inlineimage icons/build-32x32.png - \li \inlineimage icons/settings-32x32.png + \li \inlineimage icons/examples-32x32.png \row \li Key Concepts \list From 463864c76ad505d71d37340e04595ace78705f10 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 12 Nov 2024 12:40:17 +0200 Subject: [PATCH 088/322] EffectComposer: Keep code editor on top when another window activated Fixes: QDS-14078 Change-Id: I377e02eb845c88b121267dca19aad68923b830ea Reviewed-by: Mahmoud Badri --- .../effectshaderscodeeditor.cpp | 20 ------------------- .../effectcomposer/effectshaderscodeeditor.h | 1 - 2 files changed, 21 deletions(-) diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index b8b9c459fdd..3ead0be4a7c 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -159,8 +159,6 @@ EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() editorWidget->setParent(this); editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); - editorWidget->installEventFilter(this); - return editorWidget; } @@ -186,7 +184,6 @@ void EffectShadersCodeEditor::setupUIComponents() : m_vertexEditor.get(); tabWidget->setCurrentWidget(widgetToSelect); widgetToSelect->setFocus(); - activateWindow(); }); this->resize(660, 240); @@ -210,23 +207,6 @@ void EffectShadersCodeEditor::closeEvent(QCloseEvent *event) setOpened(false); } -bool EffectShadersCodeEditor::eventFilter(QObject *watched, QEvent *event) -{ - if (event->type() == event->FocusOut) { - QFocusEvent *focusEvent = dynamic_cast(event); - if (focusEvent && focusEvent->reason() == Qt::ActiveWindowFocusReason) { - connect( - qApp, - &QApplication::focusWindowChanged, - this, - &EffectShadersCodeEditor::close, - Qt::SingleShotConnection); - } - } - - return QWidget::eventFilter(watched, event); -} - void EffectShadersCodeEditor::writeLiveUpdateSettings() { m_settings->setValue(EFFECTCOMPOSER_LIVE_UPDATE_KEY, m_liveUpdate); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 374524c33e9..a20f5e71fc2 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -49,7 +49,6 @@ protected: void setOpened(bool value); void closeEvent(QCloseEvent *event) override; - bool eventFilter(QObject *watched, QEvent *event) override; private: void writeLiveUpdateSettings(); From e3f22cba2dd4b669213100ac30fda41a5bd6b054 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 6 Nov 2024 20:07:53 +0100 Subject: [PATCH 089/322] QmlDesigner: Add isQtQuickStateGroup Change-Id: I1101e82272283a4844495f1e994d5128cabd79b5 Reviewed-by: Tim Jenssen --- .../libs/designercore/include/nodemetainfo.h | 1 + .../libs/designercore/metainfo/nodemetainfo.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/plugins/qmldesigner/libs/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/libs/designercore/include/nodemetainfo.h index ed2920cac4b..c8e920ff3d7 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/libs/designercore/include/nodemetainfo.h @@ -223,6 +223,7 @@ public: bool isQtQuickRectangle() const; bool isQtQuickRepeater() const; bool isQtQuickState() const; + bool isQtQuickStateGroup() const; bool isQtQuickStateOperation() const; bool isQtQuickStudioComponentsGroupItem() const; bool isQtQuickStudioUtilsJsonListModel() const; diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp index 43bdbcdd875..5da8cdf77ef 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp @@ -3563,6 +3563,23 @@ bool NodeMetaInfo::isQtQuickState() const } } +bool NodeMetaInfo::isQtQuickStateGroup() const +{ + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.StateGroup", category(), keyValue("type id", m_typeId)}; + + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick.StateGroup"); + } +} + + bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { From 7c732e3f20ef48c4b9c2b0749aa9f2ba20d07138 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:35:45 +0100 Subject: [PATCH 090/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: Ief8b895f0318840cae6d05e944b9a17b6248943d Reviewed-by: Tim Jenssen --- .../qmldesigner/instances/capturingconnectionmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/instances/capturingconnectionmanager.cpp b/src/plugins/qmldesigner/instances/capturingconnectionmanager.cpp index 77b087e8c9e..fbe1e265ccb 100644 --- a/src/plugins/qmldesigner/instances/capturingconnectionmanager.cpp +++ b/src/plugins/qmldesigner/instances/capturingconnectionmanager.cpp @@ -55,7 +55,7 @@ void CapturingConnectionManager::writeCommand(const QVariant &command) InteractiveConnectionManager::writeCommand(command); if (m_captureFileForTest.isWritable()) { - qDebug() << "command name: " << QMetaType::typeName(command.typeId()); + qDebug() << "command name: " << QMetaType(command.typeId()).name(); writeCommandToIODevice(command, &m_captureFileForTest, writeCommandCounter()); qDebug() << "\tcatpure file offset: " << m_captureFileForTest.pos(); } From f5eef5029ac1ce92d977e3bc03dfb75c08f76000 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 6 Nov 2024 20:08:35 +0100 Subject: [PATCH 091/322] QmlDesigner: Fix crash in transition view m_selectedProperty can be dangling but then it is not part of the scene. Change-Id: I2fdf56bdf0165215efa0434c948829a8ebb866c4 Reviewed-by: Tim Jenssen --- .../transitioneditor/transitioneditorgraphicsscene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp index e8c04ae38b1..ccfbd45e16f 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp @@ -286,7 +286,7 @@ QRectF TransitionEditorGraphicsScene::selectionBounds() const void TransitionEditorGraphicsScene::clearSelection() { - if (m_selectedProperty) + if (m_selectedProperty && items().contains(m_selectedProperty)) m_selectedProperty->update(); m_selectedProperty = nullptr; @@ -458,7 +458,7 @@ TransitionEditorPropertyItem *TransitionEditorGraphicsScene::selectedPropertyIte void TransitionEditorGraphicsScene::setSelectedPropertyItem(TransitionEditorPropertyItem *item) { - if (m_selectedProperty) + if (m_selectedProperty && items().contains(m_selectedProperty)) m_selectedProperty->update(); m_selectedProperty = item; emit selectionChanged(); From a55c2c20cb88b74e6552ece58c8ff2b0c2f60acb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:35:07 +0100 Subject: [PATCH 092/322] QmlDesigner: Fix StatesView for project storage Change-Id: Ibf5d5087fb6d5a56f5043abcfae6f923db4c2ab9 Reviewed-by: Tim Jenssen --- .../qmldesigner/components/stateseditor/stateseditorview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index eddd8615c46..ff0a65ea619 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -156,7 +156,7 @@ void StatesEditorView::createNewState() { // can happen when root node is e.g. a ListModel if (!QmlVisualNode::isValidQmlVisualNode(activeStatesGroupNode()) - && m_activeStatesGroupNode.type() != "QtQuick.StateGroup") + && !m_activeStatesGroupNode.metaInfo().isQtQuickStateGroup()) return; QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATE_ADDED); From 629b61d40aa1edb1812fe677348cd5d713ba8571 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 1 Nov 2024 19:16:31 +0100 Subject: [PATCH 093/322] QmlDesigner: Implement "dot" property support in property editor Change-Id: If2e5875fa3049aaa04d47ab9f180a065451fd75d Reviewed-by: Thomas Hartmann --- .../propertyeditorqmlbackend.cpp | 30 ++++++++++++++----- .../propertyeditor/propertyeditorqmlbackend.h | 1 + 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 63471bb4bf9..bad6101687c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -452,6 +452,28 @@ PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QStrin return qobject_cast(variantToQObject(backendValuesPropertyMap().value(propertyName))); } +void QmlDesigner::PropertyEditorQmlBackend::createPropertyEditorValues(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor) +{ +#ifndef QDS_USE_PROJECTSTORAGE + for (const auto &property : PropertyEditorUtils::filteredProperties(qmlObjectNode.metaInfo())) { + auto propertyName = property.name(); + createPropertyEditorValue(qmlObjectNode, + propertyName, + qmlObjectNode.instanceValue(propertyName), + propertyEditor); + } +#else + + for (const auto &property : MetaInfoUtils::inflateValueAndReadOnlyProperties(qmlObjectNode.metaInfo().properties())) { + auto propertyName = property.name(); + createPropertyEditorValue(qmlObjectNode, + propertyName, + qmlObjectNode.instanceValue(propertyName), + propertyEditor); + } +#endif +} + void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor) { if (qmlObjectNode.isValid()) { @@ -464,13 +486,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q if (propertyEditorBenchmark().isInfoEnabled()) time.start(); - for (const auto &property : PropertyEditorUtils::filteredProperties(qmlObjectNode.metaInfo())) { - auto propertyName = property.name(); - createPropertyEditorValue(qmlObjectNode, - propertyName, - qmlObjectNode.instanceValue(propertyName), - propertyEditor); - } + createPropertyEditorValues(qmlObjectNode, propertyEditor); setupLayoutAttachedProperties(qmlObjectNode, propertyEditor); setupInsightAttachedProperties(qmlObjectNode, propertyEditor); setupAuxiliaryProperties(qmlObjectNode, propertyEditor); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index bf298010bda..18a46d0c3c3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -97,6 +97,7 @@ private: void setupPropertyEditorValue(PropertyNameView name, PropertyEditorView *propertyEditor, const NodeMetaInfo &type); + void createPropertyEditorValues(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); static QUrl fileToUrl(const QString &filePath); static QString fileFromUrl(const QUrl &url); From a1dddc990eb3e1167779bb2c13a3bb8ab2c96c7c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 27 Sep 2024 13:38:21 +0200 Subject: [PATCH 094/322] QmlDesigner: Add some logs to debugview Change-Id: I183fb0f59826291d3c8e3141ab12c77afe5d6213 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/debugview/debugview.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 059c203c8a6..386db83e2c7 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -463,8 +463,11 @@ void DebugView::instancePropertyChanged(const QList Date: Fri, 1 Nov 2024 19:46:16 +0200 Subject: [PATCH 095/322] QmlDesigner: Show texture name in Texture Editor Fixes: QDS-13479 Change-Id: Ic18bf5a788fa6ec63285811f52704412c03e964f Reviewed-by: Mahmoud Badri --- .../TextureEditorTopSection.qml | 35 +++++++++++++++++++ .../materialbrowsertexturesmodel.cpp | 17 +-------- .../materialeditor/materialeditorview.cpp | 12 +------ .../textureeditor/textureeditorview.cpp | 6 ++++ .../qmldesigner/qmltools/qmlobjectnode.cpp | 19 ++++++++++ .../qmldesigner/qmltools/qmlobjectnode.h | 1 + 6 files changed, 63 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml index 7c03d79571d..02df887ba40 100644 --- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import HelperWidgets as HelperWidgets +import StudioTheme as StudioTheme Column { id: root @@ -42,4 +44,37 @@ Column { source: "image://qmldesigner_thumbnails/" + resolveResourcePath(backendValues.source.valueToString) } } + + HelperWidgets.Section { + id: nameSection + + // Section with hidden header is used so properties are aligned with the other sections' properties + hideHeader: true + implicitWidth: root.width + bottomPadding: StudioTheme.Values.sectionPadding * 2 + collapsible: false + + HelperWidgets.SectionLayout { + HelperWidgets.PropertyLabel { text: qsTr("Name") } + + HelperWidgets.SecondColumnLayout { + HelperWidgets.Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } + + HelperWidgets.LineEdit { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + width: StudioTheme.Values.singleControlColumnWidth + backendValue: backendValues.objectName + placeholderText: qsTr("Texture name") + + showTranslateCheckBox: false + showExtendedFunctionButton: false + + // allow only alphanumeric characters, underscores, no space at start, and 1 space between words + validator: RegularExpressionValidator { regularExpression: /^(\w+\s)*\w+$/ } + } + + HelperWidgets.ExpandingSpacer {} + } + } + } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index ea898446420..1c56ab927aa 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -338,22 +338,7 @@ void MaterialBrowserTexturesModel::setTextureName(int idx, const QString &newNam if (!isValidIndex(idx)) return; - ModelNode node = m_textureList[idx]; - if (!node.isValid()) - return; - - VariantProperty objectNameProperty = node.variantProperty("objectName"); - QString oldName = objectNameProperty.value().toString(); - - if (oldName != newName) { - const Model *model = m_view->model(); - QTC_ASSERT(model, return); - - m_view->executeInTransaction(__FUNCTION__, [&] { - node.setIdWithRefactoring(model->generateNewId(newName, "texture")); - objectNameProperty.setValue(newName); - }); - } + QmlObjectNode(m_textureList[idx]).setNameAndId(newName, "texture"); } void MaterialBrowserTexturesModel::applyToSelectedMaterial(qint64 internalId) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 12cf8ed128b..29db55e2813 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -989,17 +989,7 @@ void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImp void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newName) { QTC_ASSERT(material.isValid(), return); - - QVariant objName = material.variantProperty("objectName").value(); - if (objName.isValid() && objName.toString() == newName) - return; - - executeInTransaction(__FUNCTION__, [&] { - material.setIdWithRefactoring(model()->generateNewId(newName, "material")); - - VariantProperty objNameProp = material.variantProperty("objectName"); - objNameProp.setValue(newName); - }); + QmlObjectNode(material).setNameAndId(newName, "material"); } void MaterialEditorView::duplicateMaterial(const ModelNode &material) diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index f084735c016..d363d3bfaf5 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -117,6 +117,12 @@ void TextureEditorView::changeValue(const QString &name) if (!value) return; + if (propertyName == "objectName") { + QTC_ASSERT(m_selectedTexture.isValid(), return); + QmlObjectNode(m_selectedTexture).setNameAndId(value->value().toString(), "texture"); + return; + } + if (propertyName.endsWith("__AUX")) { commitAuxValueToModel(propertyName, value->value()); return; diff --git a/src/plugins/qmldesigner/qmltools/qmlobjectnode.cpp b/src/plugins/qmldesigner/qmltools/qmlobjectnode.cpp index fcb65295115..33f7e9b494d 100644 --- a/src/plugins/qmldesigner/qmltools/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/qmltools/qmlobjectnode.cpp @@ -669,6 +669,25 @@ void QmlObjectNode::setId(const QString &id) modelNode().setIdWithRefactoring(id); } +void QmlObjectNode::setNameAndId(const QString &newName, const QString &fallbackId) +{ + if (!isValid()) + return; + + VariantProperty objectNameProperty = modelNode().variantProperty("objectName"); + QString oldName = objectNameProperty.value().toString(); + + if (oldName != newName) { + const Model *model = view()->model(); + QTC_ASSERT(model, return); + + view()->executeInTransaction(__FUNCTION__, [&] { + modelNode().setIdWithRefactoring(model->generateNewId(newName, fallbackId)); + objectNameProperty.setValue(newName); + }); + } +} + QString QmlObjectNode::id() const { return modelNode().id(); diff --git a/src/plugins/qmldesigner/qmltools/qmlobjectnode.h b/src/plugins/qmldesigner/qmltools/qmlobjectnode.h index 90424adf626..8773591add9 100644 --- a/src/plugins/qmldesigner/qmltools/qmlobjectnode.h +++ b/src/plugins/qmldesigner/qmltools/qmlobjectnode.h @@ -45,6 +45,7 @@ public: QmlItemNode modelParentItem() const; void setId(const QString &id); + void setNameAndId(const QString &newName, const QString &preferredId); QString id() const; QString validId(); From 626750ab2b0c65dd398660348a52b79370f6be71 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 12 Nov 2024 13:09:06 +0200 Subject: [PATCH 096/322] EffectComposer: Add objectNames to the effect composer buttons Task-number: QDS-14034 Change-Id: I15d1d7c40cf3cca375eeadbf3653350aae6a1e66 Reviewed-by: Mahmoud Badri --- .../EffectComposerPreview.qml | 12 ++++++++++++ .../EffectComposerTopBar.qml | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index c0c47f73661..2e4cedc438d 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -68,12 +68,14 @@ Column { PreviewImagesComboBox { id: imagesComboBox + objectName: "comboPreviewImages" mainRoot: root.mainRoot } StudioControls.ColorEditor { id: colorEditor + objectName: "colorEditPreviewImage" actionIndicatorVisible: false showHexTextField: false @@ -87,6 +89,8 @@ Column { anchors.verticalCenter: parent.verticalCenter HelperWidgets.AbstractButton { + objectName: "btnZoomIn" + enabled: root.previewScale < 3 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomIn_medium @@ -101,6 +105,8 @@ Column { } HelperWidgets.AbstractButton { + objectName: "btnZoomOut" + enabled: root.previewScale > .4 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomOut_medium @@ -115,6 +121,8 @@ Column { } HelperWidgets.AbstractButton { + objectName: "btnResetView" + enabled: root.previewScale !== 1 || imageScaler.x !== root.previewMargin || imageScaler.y !== root.previewMargin style: StudioTheme.Values.viewBarButtonStyle @@ -149,6 +157,8 @@ Column { } HelperWidgets.AbstractButton { + objectName: "btnRestartAnimation" + style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.toStartFrame_medium tooltip: qsTr("Restart Animation") @@ -159,6 +169,8 @@ Column { } HelperWidgets.AbstractButton { + objectName: "btnPlayAnimation" + style: StudioTheme.Values.viewBarButtonStyle buttonIcon: previewAnimationRunning ? StudioTheme.Constants.pause_medium : StudioTheme.Constants.playOutline_medium diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index a9e0d74587e..e14dae8b114 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -25,6 +25,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter HelperWidgets.AbstractButton { + objectName: "btnAddComposition" style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.add_medium tooltip: qsTr("Add new composition") @@ -33,6 +34,7 @@ Rectangle { } HelperWidgets.AbstractButton { + objectName: "btnSaveComposition" style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.save_medium tooltip: qsTr("Save current composition") @@ -45,6 +47,7 @@ Rectangle { } HelperWidgets.AbstractButton { + objectName: "btnSaveAsComposition" style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.saveAs_medium tooltip: qsTr("Save current composition with a new name") @@ -57,6 +60,7 @@ Rectangle { HelperWidgets.AbstractButton { id: openCodeEditorButton + objectName: "btnOpenCodeEditor" property bool codeEditorOpen: root.backendModel && (root.backendModel.codeEditorIndex @@ -84,6 +88,8 @@ Rectangle { } HelperWidgets.AbstractButton { + objectName: "btnAssignCompositionToItem" + style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.assignTo_medium tooltip: qsTr("Assign current composition to selected item") @@ -96,7 +102,6 @@ Rectangle { } } - Text { readonly property string compName: root.backendModel ? root.backendModel.currentComposition : "" From 41f99c09f2d426e7fd6060506b0120cf51813801 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 12 Nov 2024 14:03:37 +0200 Subject: [PATCH 097/322] QmlDesigner: Use StudioControls.Tooltip in assets view So that the tooltip is properly styled with the current theme Change-Id: I67dbc6ad3ec5e69bb2cb7d06c4e89cc7586f3937 Reviewed-by: Shrief Gabr Reviewed-by: Miikka Heikkinen --- .../qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index f59374e2cc9..32485a61a1e 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -4,6 +4,7 @@ import QtQuick import QtQuick.Controls import StudioTheme as StudioTheme +import StudioControls as StudioControls import AssetsLibraryBackend TreeViewDelegate { @@ -195,7 +196,7 @@ TreeViewDelegate { AssetsLibraryBackend.rootView.openEffectComposer(filePath) } - ToolTip { + StudioControls.ToolTip { id: assetTooltip visible: !root.isFont && mouseArea.containsMouse && !root.assetsView.contextMenu.visible text: assetTooltip.__computeText() From 9792f607359b6af42455cce38e2893b9b4209087 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 1 Nov 2024 16:52:15 +0100 Subject: [PATCH 098/322] QmlDesigner: Avoid QML warnings about undefined context objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid the warnings we have to add the context objects before we set the source url. setupContextProperties() is called before we the the QML file url. unfortunately we have to set the modelNodeBackend twice. Change-Id: I14ca94fa037d7d301868ffc4e0d21a95d9ade5c7 Reviewed-by: Henning Gründl --- .../propertyeditor/propertyeditorqmlbackend.cpp | 13 +++++++++---- .../propertyeditor/propertyeditorqmlbackend.h | 2 ++ .../propertyeditor/propertyeditorview.cpp | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index bad6101687c..6982b177aa9 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -493,7 +493,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // model node m_backendModelNode.setup(qmlObjectNode.modelNode()); - context()->setContextProperty(QLatin1String("modelNodeBackend"), &m_backendModelNode); + context()->setContextProperty("modelNodeBackend", &m_backendModelNode); // className auto valueObject = qobject_cast(variantToQObject( @@ -523,9 +523,6 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // anchors m_backendAnchorBinding.setup(qmlObjectNode.modelNode()); - context()->setContextProperties(QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setHasMultiSelection( !qmlObjectNode.view()->singleSelectedModelNode().isValid()); @@ -942,6 +939,14 @@ void PropertyEditorQmlBackend::refreshBackendModel() m_backendModelNode.refresh(); } +void PropertyEditorQmlBackend::setupContextProperties() +{ + context()->setContextProperty("modelNodeBackend", &m_backendModelNode); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); +} + #ifndef QDS_USE_PROJECTSTORAGE TypeName PropertyEditorQmlBackend::qmlFileName(const NodeMetaInfo &nodeInfo) { diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 18a46d0c3c3..f57cb575658 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -89,6 +89,8 @@ public: void refreshBackendModel(); + void setupContextProperties(); + private: void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, PropertyNameView name, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index ce680284a3c..09c4aafff2c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -551,6 +551,7 @@ PropertyEditorQmlBackend *getQmlBackend(QHashaddWidget(currentQmlBackend->widget()); qmlBackendHash.insert(qmlFileName, currentQmlBackend); + currentQmlBackend->setupContextProperties(); currentQmlBackend->setSource(qmlFileUrl); } From d3a16c47c4574369962e202a7f220660dceda320 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Tue, 12 Nov 2024 11:48:50 +0200 Subject: [PATCH 099/322] Doc: Remove wrong info about Effect Composer - remove mentions of applying the effects to 3D components Fixes: QDS-13937 Change-Id: I6b2502939ef4604f43ee1626bf41636f579ff08b Reviewed-by: Tanja Remes Reviewed-by: Mats Honkamaa Reviewed-by: Ali Kianian --- .../src/views/qtquick-effect-maker-view.qdoc | 19 ++++++++++--------- .../effects/qtdesignstudio-effects.qdoc | 8 ++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc index cc89cef7db5..37222939731 100644 --- a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc @@ -13,8 +13,8 @@ \brief Compose custom effects. Use \uicontrol {Effect Composer} to create post-processing effects that - can be applied to 2D or 3D components. The effects created with \uicontrol {Effect Composer} - are shader effects, which can be used in any \QDS projects. + can be applied to 2D components or 3D views. The effects created with + \uicontrol {Effect Composer} are shader effects, which can be used in any \QDS projects. \image studio-effect-composer.webp "The Effect Composer view in Qt Design Studio." @@ -24,8 +24,8 @@ \li To create a new custom effect, do one of the following: \list \li Right-click anywhere in the \uicontrol Assets view (1) and select - \uicontrol {New Effect} in the dialog. Give your new custom - effect a name and select \uicontrol Create. + \uicontrol {New Effect} in the dialog. Name your new custom + effect and select \uicontrol Create. \li Open the \uicontrol {Effect Composer} view (2), select \uicontrol {Add Effect}, and then select the effects you wish to use from the dropdown menu. The added effects appear in the effect stack (3). You cannot add some of the @@ -49,13 +49,12 @@ \section1 Assigning a Custom Effect to a Component - To assign your newly created custom effect to a 2D or 3D component, - do one of the following: + To assign your newly created custom effect to a component, do one of the following: \list - \li Select a component in the \uicontrol Navigator, \uicontrol 2D, or - \uicontrol 3D view, and then select \inlineimage icons/assign-effect-composer.png - in \uicontrol {Effect Composer}. + \li Select a component in \uicontrol Navigator or the \uicontrol 2D view, + and then select \inlineimage icons/assign-effect-composer.png in + \uicontrol {Effect Composer}. \li Drag the custom effect from \uicontrol Assets to the component in \uicontrol Navigator or the \uicontrol {2D} view. \endlist @@ -101,4 +100,6 @@ \uicontrol Navigator, and in \uicontrol Properties > \uicontrol {Exposed Custom Properties}, select or clear the \uicontrol timeRunning checkbox. + + \sa {Effect Composer Example} */ diff --git a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc index 6bd5ce93544..59936cca6f5 100644 --- a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc +++ b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc @@ -20,12 +20,12 @@ \li Description \row \li \l {Effect Composer} - \li A set of ready-made 2D, 3D, and particle effects that you can combine and customize. - Includes effects like \uicontrol {Color Overlay}, \uicontrol Rain, and \uicontrol - Swirl. + \li Compose new effects using a set of ready-made 2D effects that you can combine and + customize. Includes effects like \uicontrol {Color Overlay}, \uicontrol Rain, and + \uicontrol Swirl. \row \li \l {Adding an Effect to Your Project}{Content Library Effects} - \li A set of ready-made particle effects. Includes customizable effects like \uicontrol + \li A set of ready-made 3D particle effects. Includes customizable effects like \uicontrol Bubbles, \uicontrol Explosion, and \uicontrol Shockwave. \row \li \l {Design Effects} From cca00b51d4288399b7ca8ca9f1c72273566ebe9d Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 12 Nov 2024 09:27:49 +0100 Subject: [PATCH 100/322] DesignViewer: Define userinfo as Q_PROPERTY for QML context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I3d679c4e6c280c045636ad1aa854ea80e1cdcda2 Reviewed-by: Henning Gründl --- .../components/designviewer/dvconnector.cpp | 41 +++++++++++++++---- .../components/designviewer/dvconnector.h | 19 ++++++--- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index 8ec76bd4bc2..c73d5d88780 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -104,6 +104,7 @@ void CustomCookieJar::clearCookies() DVConnector::DVConnector(QObject *parent) : QObject{parent} + , m_isWebViewerVisible(false) , m_connectorStatus(ConnectorStatus::FetchingUserInfo) { QLoggingCategory::setFilterRules("qtc.designer.deploymentPlugin.debug=true"); @@ -113,6 +114,7 @@ DVConnector::DVConnector(QObject *parent) m_webEngineView.reset(new QWebEngineView); m_webEngineView->setPage(m_webEnginePage.data()); m_webEngineView->resize(1024, 750); + m_webEngineView->installEventFilter(this); m_networkCookieJar.reset( new CustomCookieJar(this, m_webEngineProfile->persistentStoragePath() + "/dv_cookies.txt")); @@ -135,11 +137,11 @@ DVConnector::DVConnector(QObject *parent) if (cookieName == "jwt") { qCDebug(deploymentPluginLog) << "Got JWT"; m_webEngineView->hide(); - userInfo(); + fetchUserInfo(); } }); - userInfo(); + fetchUserInfo(); } DVConnector::ConnectorStatus DVConnector::connectorStatus() const @@ -147,6 +149,28 @@ DVConnector::ConnectorStatus DVConnector::connectorStatus() const return m_connectorStatus; } +QByteArray DVConnector::userInfo() const +{ + return m_userInfo; +} + +bool DVConnector::isWebViewerVisible() const +{ + return m_isWebViewerVisible; +} + +bool DVConnector::eventFilter(QObject *obj, QEvent *e) +{ + if (obj == m_webEngineView.data()) { + if (m_isWebViewerVisible != m_webEngineView->isVisible()) { + m_isWebViewerVisible = m_webEngineView->isVisible(); + emit webViewerVisibleChanged(); + } + return true; + } + return QObject::eventFilter(obj, e); +} + void DVConnector::projectList() { qCDebug(deploymentPluginLog) << "Fetching project list"; @@ -222,7 +246,7 @@ void DVConnector::uploadProject(const QString &projectId, const QString &filePat evaluatorData.successCallback = [this](const QByteArray &) { emit projectUploaded(); // call userInfo to update storage info in the UI - userInfo(); + fetchUserInfo(); }; evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { emit projectUploadError(errorCode, errorString); @@ -276,7 +300,7 @@ void DVConnector::uploadProjectThumbnail(const QString &projectId, const QString evaluatorData.successCallback = [this](const QByteArray &) { emit thumbnailUploaded(); // call userInfo to update storage info in the UI - userInfo(); + fetchUserInfo(); }; evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { emit thumbnailUploadError(errorCode, errorString); @@ -308,7 +332,7 @@ void DVConnector::deleteProject(const QString &projectId) evaluatorData.successCallback = [this](const QByteArray &) { emit projectDeleted(); // call userInfo to update storage info in the UI - userInfo(); + fetchUserInfo(); }; evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { emit projectDeleteError(errorCode, errorString); @@ -331,7 +355,7 @@ void DVConnector::deleteProjectThumbnail(const QString &projectId) evaluatorData.successCallback = [this](const QByteArray &) { emit thumbnailDeleted(); // call userInfo to update storage info in the UI - userInfo(); + fetchUserInfo(); }; evaluatorData.errorPreCallback = [this](const int errorCode, const QString &errorString) { emit thumbnailDeleteError(errorCode, errorString); @@ -568,7 +592,7 @@ void DVConnector::logout() evaluatorData.connectCallbacks(this); } -void DVConnector::userInfo() +void DVConnector::fetchUserInfo() { qCDebug(deploymentPluginLog) << "Fetching user info"; QUrl url(DVEndpoints::serviceUrl + DVEndpoints::userInfo); @@ -580,10 +604,11 @@ void DVConnector::userInfo() evaluatorData.successCallback = [this](const QByteArray &reply) { m_connectorStatus = ConnectorStatus::LoggedIn; emit connectorStatusUpdated(m_connectorStatus); + m_userInfo = reply; emit userInfoReceived(reply); }; evaluatorData.errorCodeOtherCallback = [this](const int, const QString &) { - QTimer::singleShot(1000, this, &DVConnector::userInfo); + QTimer::singleShot(1000, this, &DVConnector::fetchUserInfo); }; evaluatorData.connectCallbacks(this); diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h index 63377075cde..e9a7a84e020 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.h +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -42,6 +42,8 @@ class DVConnector : public QObject { Q_OBJECT Q_PROPERTY(ConnectorStatus connectorStatus READ connectorStatus NOTIFY connectorStatusUpdated) + Q_PROPERTY(QByteArray userInfo READ userInfo NOTIFY userInfoReceived) + Q_PROPERTY(bool isWebViewerVisible READ isWebViewerVisible NOTIFY webViewerVisibleChanged) public: explicit DVConnector(QObject *parent = nullptr); @@ -49,7 +51,10 @@ public: Q_ENUM(ConnectorStatus) public: + // getters for UI ConnectorStatus connectorStatus() const; + QByteArray userInfo() const; + bool isWebViewerVisible() const; void projectList(); void uploadProject(const QString &projectId, const QString &filePath); @@ -72,7 +77,7 @@ public: void login(); void logout(); - void userInfo(); + void fetchUserInfo(); private: // network @@ -83,9 +88,11 @@ private: QScopedPointer m_webEngineProfile; QScopedPointer m_webEnginePage; QScopedPointer m_webEngineView; + bool m_isWebViewerVisible; // status ConnectorStatus m_connectorStatus; + QByteArray m_userInfo; struct ReplyEvaluatorData { @@ -107,9 +114,10 @@ private: private: void evaluateReply(const ReplyEvaluatorData &evaluator); + bool eventFilter(QObject *obj, QEvent *e) override; signals: - // backend integration - project related signals + // service integration - project related signals void projectListReceived(const QByteArray &reply); void projectUploaded(); void projectUploadError(const int errorCode, const QString &message); @@ -119,7 +127,7 @@ signals: void projectDownloaded(); void projectDownloadError(const int errorCode, const QString &message); - // backend integration - project thumbnail related signals + // service integration - project thumbnail related signals void thumbnailUploaded(); void thumbnailUploadError(const int errorCode, const QString &message); void thumbnailUploadProgress(const double progress); @@ -128,7 +136,7 @@ signals: void thumbnailDownloaded(); void thumbnailDownloadError(const int errorCode, const QString &message); - // backend integration - shared project related signals + // service integration - shared project related signals void sharedProjectListReceived(const QByteArray &reply); void projectShared(const QString &projectId, const QString &shareUUID); void projectShareError(const int errorCode, const QString &message); @@ -141,8 +149,9 @@ signals: void sharedProjectThumbnailDownloaded(); void sharedProjectThumbnailDownloadError(const int errorCode, const QString &message); - // backend integration - login/user related signals + // UI integration - login/user related signals void userInfoReceived(const QByteArray &reply); + void webViewerVisibleChanged(); // internal signals void connectorStatusUpdated(const ConnectorStatus status); From a509bb74feb9bd9a5167116668a7b52ba1f9871c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 8 Nov 2024 17:42:08 +0100 Subject: [PATCH 101/322] QmlDesigner: Add translation support to itemlibrary The data comes from .metainfo files. Therefore we add itemlibrary.qml to define the required strings and use QApplication::translate(). Introduced displayName for ItemLibraryCategory, because the name has to be untranslated. Change-Id: I97e8f1f74f40676cf911806d467e31f08a0e3491 Reviewed-by: Thomas Hartmann --- .../itemLibraryQmlSources/ItemsView.qml | 2 +- .../translationhelper/itemlibrary.qml | 80 +++++++++++++++++++ .../itemlibrary/itemlibrarycategory.cpp | 7 ++ .../itemlibrary/itemlibrarycategory.h | 2 + .../itemlibrary/itemlibraryitem.cpp | 4 +- 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 share/qtcreator/qmldesigner/translationhelper/itemlibrary.qml diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 62c907e76f0..4d11b6ea5e2 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -276,7 +276,7 @@ Item { rightPadding: 0 addTopPadding: categoryModel.rowCount() > 1 addBottomPadding: index !== categoryModel.rowCount() - 1 - caption: categoryName + " (" + itemModel.rowCount() + ")" + caption: displayNMame + " (" + itemModel.rowCount() + ")" visible: categoryVisible expanded: categoryExpanded expandOnClick: false diff --git a/share/qtcreator/qmldesigner/translationhelper/itemlibrary.qml b/share/qtcreator/qmldesigner/translationhelper/itemlibrary.qml new file mode 100644 index 00000000000..a75e72b2818 --- /dev/null +++ b/share/qtcreator/qmldesigner/translationhelper/itemlibrary.qml @@ -0,0 +1,80 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick + +Text { + name1: qsTr("Item") + name2: qsTr("Rectangle") + name3: qsTr("Text") + name4: qsTr("Text Edit") + name5: qsTr("Text Input") + name6: qsTr("Mouse Area") + name7: qsTr("Image") + name8: qsTr("Animated Image") + name9: qsTr("Animated Sprite") + name10: qsTr("Border Image") + name11: qsTr("Flickable") + name12: qsTr("Grid View") + name13: qsTr("List View") + name14: qsTr("Path View") + name15: qsTr("Focus Scope") + name16: qsTr("Column") + name17: qsTr("Row") + name18: qsTr("Grid") + name19: qsTr("Flow") + name20: qsTr("Property Animation") + name21: qsTr("Pause Animation") + name22: qsTr("Sequential Animation") + name23: qsTr("Parallel Animation") + name24: qsTr("Property Action") + name25: qsTr("Script Action") + name26: qsTr("Color Animation") + name27: qsTr("Number Animation") + name28: qsTr("Loader") + name29: qsTr("Repeater") + name30: qsTr("Busy Indicator") + name31: qsTr("Button") + name32: qsTr("Check Box") + name33: qsTr("Check Delegate") + name34: qsTr("Combo Box") + name35: qsTr("Control") + name36: qsTr("Delay Button") + name37: qsTr("Dial") + name38: qsTr("Frame") + name39: qsTr("Group Box") + name40: qsTr("Item Delegate") + name41: qsTr("Label") + name42: qsTr("Page") + name43: qsTr("Page Indicator") + name44: qsTr("Pane") + name45: qsTr("Progress Bar") + name46: qsTr("Radio Button") + name47: qsTr("Radio Delegate") + name48: qsTr("Range Slider") + name49: qsTr("Round Button") + name50: qsTr("Slider") + name51: qsTr("Spin Box") + name52: qsTr("Scroll View") + name53: qsTr("Stack View") + name54: qsTr("Swipe Delegate") + name55: qsTr("Swipe View") + name56: qsTr("Switch") + name57: qsTr("Switch Delegate") + name58: qsTr("Tab Bar") + name59: qsTr("Tab Button") + name60: qsTr("Text Area") + name61: qsTr("Text Field") + name62: qsTr("Tool Bar") + name63: qsTr("Tool Button") + name64: qsTr("Tool Separator") + name65: qsTr("Tumbler") + + category1: qsTr("Views") + category2: qsTr("Positioner") + category3: qsTr("Positioner") + category4: qsTr("Animation") + category5: qsTr("Instancers") + category6: qsTr("Basic") + category7: qsTr("Controls 2") +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp index bcd9f04e6e2..2a49e1a8d1a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp @@ -6,6 +6,8 @@ #include "itemlibraryitem.h" #include "itemlibrarywidget.h" +#include + namespace QmlDesigner { ItemLibraryCategory::ItemLibraryCategory(const QString &groupName, QObject *parent) @@ -20,6 +22,11 @@ QString ItemLibraryCategory::categoryName() const return m_name; } +QString ItemLibraryCategory::displayNMame() const +{ + return QApplication::translate("itemlibrary", m_name.toUtf8()); +} + bool ItemLibraryCategory::categoryExpanded() const { return m_categoryExpanded; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h index 53a06a00945..e6b00d8bb72 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.h @@ -15,6 +15,7 @@ class ItemLibraryCategory : public QObject Q_OBJECT Q_PROPERTY(QString categoryName READ categoryName FINAL) + Q_PROPERTY(QString displayNMame READ displayNMame FINAL) Q_PROPERTY(bool categoryVisible READ isCategoryVisible WRITE setCategoryVisible NOTIFY categoryVisibilityChanged FINAL) Q_PROPERTY(bool categoryExpanded READ categoryExpanded WRITE setExpanded NOTIFY expandedChanged FINAL) Q_PROPERTY(bool categorySelected READ categorySelected WRITE setCategorySelected NOTIFY categorySelectedChanged FINAL) @@ -24,6 +25,7 @@ public: ItemLibraryCategory(const QString &groupName, QObject *parent = nullptr); QString categoryName() const; + QString displayNMame() const; bool categoryExpanded() const; bool categorySelected() const; QString sortingName() const; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp index 1170679101e..518c0188a42 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp @@ -3,6 +3,8 @@ #include "itemlibraryitem.h" +#include + namespace QmlDesigner { ItemLibraryItem::ItemLibraryItem(const ItemLibraryEntry &itemLibraryEntry, bool isUsable, QObject *parent) @@ -16,7 +18,7 @@ ItemLibraryItem::~ItemLibraryItem() = default; QString ItemLibraryItem::itemName() const { - return m_itemLibraryEntry.name(); + return QApplication::translate("itemlibrary", m_itemLibraryEntry.name().toUtf8()); } QString ItemLibraryItem::typeName() const From f4bd68ff779abf4dc170511e1fcb061bf9066e95 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 12 Nov 2024 15:56:33 +0200 Subject: [PATCH 102/322] QmlDesigner: Handle url in a binding property when exporting a bundle Fixes: QDS-14065 Change-Id: I8bae0adcfeb8ddc6267a842d46d0eff2f6f656d4 Reviewed-by: Miikka Heikkinen Reviewed-by: Ali Kianian --- .../components/componentcore/bundlehelper.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp index b7a17ce07f0..89d506773b3 100644 --- a/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/bundlehelper.cpp @@ -733,6 +733,18 @@ QSet BundleHelper::getComponentDependencies(const Utils::FilePath &fi depList.insert({assetPathBase, assetPathRelative}); } } + } else if (p.isBindingProperty()) { + // check if the property value is in this format: Qt.resolvedUrl("path") + static const QRegularExpression regex(R"(Qt\.resolvedUrl\(\"([^\"]+)\"\))"); + QRegularExpressionMatch match = regex.match(p.toBindingProperty().expression()); + + if (match.hasMatch()) { + Utils::FilePath assetPath = filePath.parentDir().resolvePath(match.captured(1)); + QString assetPathRelative = assetPath.relativePathFrom(mainCompDir).toFSPathString(); + + QTC_ASSERT(assetPath.exists(), continue); + depList.insert({mainCompDir, assetPathRelative}); + } } } From 3708609fadec3faf52ba26e8339f1a205c2f76e4 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Nov 2024 16:26:07 +0100 Subject: [PATCH 103/322] QmlDesigner: Update version in project template Change-Id: I7a1a612b5c3d5f72e566a6d00f9c95f5c1bfe3d3 Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/app.qmlproject.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index 615cc8d2348..63f61928da6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -107,7 +107,7 @@ Project { enableCMakeGeneration: true @endif - qdsVersion: "4.6" + qdsVersion: "4.7" quickVersion: "%{QtQuickVersion}" From 5a95c90675e73a43a138bfb6ee930659b9bcd954 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Nov 2024 16:25:41 +0100 Subject: [PATCH 104/322] QmlDesigner: Remove FlowView wizards Task-number: QDS-13931 Change-Id: I9faf79e31677abc10936ef88a44c03109e8fcaed Reviewed-by: Thomas Hartmann --- .../files/flowitem/file.qml.tpl | 25 ------ .../files/flowitem/flow_item.png | Bin 980 -> 0 bytes .../files/flowitem/flow_item@2.png | Bin 1713 -> 0 bytes .../files/flowitem/wizard.json | 83 ------------------ .../files/flowview/file.qml.tpl | 34 ------- .../files/flowview/flow_view.png | Bin 531 -> 0 bytes .../files/flowview/flowview2.png | Bin 817 -> 0 bytes .../files/flowview/wizard.json | 83 ------------------ 8 files changed, 225 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowitem/file.qml.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item.png delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item@2.png delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowitem/wizard.json delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowview/file.qml.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowview/flow_view.png delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowview/flowview2.png delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/flowview/wizard.json diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/file.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/files/flowitem/file.qml.tpl deleted file mode 100644 index 2a5913bdb47..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/file.qml.tpl +++ /dev/null @@ -1,25 +0,0 @@ -/* -This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only. -It is supposed to be strictly declarative and only uses a subset of QML. If you edit -this file manually, you might introduce QML code that is not supported by Qt Design Studio. -Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. -*/ - -import QtQuick -@if %{UseQtQuickControls2} -import QtQuick.Controls -@endif -@if %{UseImport} -import %{ApplicationImport} -@endif -import FlowView - -FlowItem { -@if %{UseImport} - width: Constants.width - height: Constants.height -@else - width: 800 - height: 600 -@endif -} diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item.png b/share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item.png deleted file mode 100644 index fbd3d097329828fd0534233b03d2439ba863a60c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 980 zcmeAS@N?(olHy`uVBq!ia0y~yU}$DwU`XI#W?*38oNQRZz`*Dp;1lA?z`#&G3MdYN z@0Z+`FfcIWlmz(&GcYnSv$1ngATp7oN`w!GH-Ct|(onCfLp!U8(NIxSZ4@ZIn12dC^ zz=4JdSsNDIUC_(G#$uq*fFi@d#xmiu&^q z_akE;;H3Gi3eyzxwyE%Z zmE-YcFpHVx$=Q6qc_EX-jisFbHXTgoId2wpH(6 zAQ7DWLR?fbP_%8gPU5Adhc;;XoeGhNxo2?nSOO0k`hPXx<-q`61tmYPR31kDHZz9h zkCsFpGkAN@fSW=0P)gjf4>8KVANDgH2%M;p*?U@|;N^4`lOvPFzh}?wI`dE6LsB6A z4d;^m_m-abuem*s=T=UQ--qCy1+~-anq6=4ie;KdEV-L7vCMgr0}scM1{kr6=kSud i1|GL~<2ez6@8>3G9@V|9GLwOUfx*+&&t;ucLK6V>3#_I9 diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item@2.png b/share/qtcreator/qmldesigner/studio_templates/files/flowitem/flow_item@2.png deleted file mode 100644 index eadce0c9939e8b92b224cc827e368ba520576344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1713 zcmeAS@N?(olHy`uVBq!ia0y~yU}R%pU^u|R%)r2KQP)XvW z@Zj&W!wd`z^GkyKf*BZ@m|564IJtQE_yq)oMMNberDSB~lzxHnp@i1 zJG#1idM8bvF>}`Zg-chg->`AZw(UE1?%K0=|AB*t4j(^x>hziO7cO4Eb^GqU2M-@T zdG`Fp%U5sSzWea;)0eN`zW@07>(AeRyvI+NF)%Pc^mK6yskrs_?#t+&K#AiYji+vD zf0JD%I@QU;MKW6C)FiLular?8^mIe20AO^FQyt*VbSE zT4{a$s-V(8(Mpf6#tQ>h?Q7y}GtzH#GXG(BUQD=;+MaR#_G$M&d^F+_=e(vZ%YFCrRcoEnZM6?lDkiDU$iLVv z(CW2%?$qQOo_8h(>t;4@W|Ll1{;9su&__?y?9QtP+gGfgq+-Pz!;{{kdtF3AXW~g6 zndgZ!d^Wc$R-clPDfB2^_t~I?WA?hs5+x^7%RU;&Z0>$I$FRalZ}REG8!qd$)gF5w zcao3)aN2H(uAYORDo;+x7k6fqIKaSW(7?!(z`zV<{$XxwE}ALgDsk>~>NSl+7gVRO z3~NcWa@V@Nn1k8$WQf@WEuMLy*OX7zo-(-LuGzvOo8|5W$o^L>eD*SGt_ENo!p`8@aF|6gUr zi*N3JQf0SP>F{^$(?Wkf_sl9kWoUD1vw%l#XwSp6OFC_n_ zeC6SS7^O}3)^i>}dTC{C<(A20I)#B63$zZTiE9*Edr6v`ZurjOd6HE3Pk(Xpxdq>g9{rE8SeZ4aLNa5E*<60b&rgnjINJGb(j6A5 zm(lt>f`!Hcf8XS-v_4yqe!FV1UB;{bY2WsweEiU7COA=;$K{DgfaT1XCW68mzD9EK!Jb@(-nOj9UQ~kIJlU&s*GGh1OhHh zRP1U>({~91-w$ba0ez`@zM;HOrvFi&f%8 eaEjAIe&yxNe$K3w%NQ6K7(8A5T-G@yGywnzXyq;d diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/flowitem/wizard.json deleted file mode 100644 index 6eadcc98294..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/files/flowitem/wizard.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "version": 1, - "supportedProjectTypes": [ ], - "id": "B.QtStudio.QtQuickFlowItem.2", - "category": "B.StudioQtQuickFiles", - "trDescription": "Creates a Flow Item.", - "trDisplayName": "Flow Item", - "trDisplayCategory": "Qt Quick Files", - "icon": "flow_item.png", - "platformIndependent": true, - - "options": - [ - { "key": "QmlFile", "value": "%{Class}.%{JS: Util.preferredSuffix('application/x-qt.ui+qml')}" }, - { "key": "ApplicationImport", "value": "%{QmlProjectName} 1.0" }, - { "key": "UseImportDefault", "value": "%{JS: false}" }, - { "key": "UseQtQuickControls2Default", "value": "%{JS: true}" } - ], - - "pages" : - [ - { - "trDisplayName": "Define Class", - "trShortTitle": "Details", - "typeId": "Fields", - "data" : - [ - { - "name": "Class", - "trDisplayName": "Component name:", - "mandatory": true, - "type": "LineEdit", - "data": { - "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)", - "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }" - } - }, - { - "name": "TargetPath", - "type": "PathChooser", - "trDisplayName": "Path:", - "mandatory": true, - "data": - { - "kind": "existingDirectory", - "basePath": "%{InitialPath}", - "path": "%{InitialPath}" - } - }, - { - "name": "UseImport", - "trDisplayName": "Use Application Import", - "type": "CheckBox", - "data": - { - "checked": "%{UseImportDefault}" - } - }, - { - "name": "UseQtQuickControls2", - "trDisplayName": "Use QtQuick Controls 2", - "type": "CheckBox", - "data": - { - "checked": "%{UseQtQuickControls2Default}" - } - } - ] - } - ], - "generators" : - [ - { - "typeId": "File", - "data": - { - "source": "file.qml.tpl", - "target": "%{TargetPath}/%{QmlFile}", - "openInEditor": true - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowview/file.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/files/flowview/file.qml.tpl deleted file mode 100644 index eb095cf4894..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/files/flowview/file.qml.tpl +++ /dev/null @@ -1,34 +0,0 @@ -/* -This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only. -It is supposed to be strictly declarative and only uses a subset of QML. If you edit -this file manually, you might introduce QML code that is not supported by Qt Design Studio. -Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. -*/ - -import QtQuick -@if %{UseImport} -import %{ApplicationImport} -@endif -import FlowView - -FlowView { -@if %{UseImport} - width: Constants.width - height: Constants.height -@else - width: 800 - height: 600 -@endif - - defaultTransition: FlowTransition { - id: defaultTransition - } - -@if %{UseEventSimulator} - EventListSimulator { - id: eventListSimulator - active: false - } -@endif - -} diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowview/flow_view.png b/share/qtcreator/qmldesigner/studio_templates/files/flowview/flow_view.png deleted file mode 100644 index f8676da79ac6b8bf8f25a1017063568ce6a7a561..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmeAS@N?(olHy`uVBq!ia0y~yU}$DwU`XI#W?*38oNQRZz`#%%;1lA?z`#&WckpnD zx)uWigIYfJXguH^Tfw{edZ$Lt3c1}S_X+=|e$Al?!7Hl|h;o6fAA9qzu zDq>(@RQGgo49U3n_SQ+iBL)Jl7r9vsjCpw(xn3Cj{BJ*zRobEJR_f6mD);WHbuT`$ zO-KF1?$Cd)nFNX$ocJ70FmhNjD6%WmnD^}5y--?_T_MH(VZn`i?!5;;im+|_9+&uG zPlp@Z^@xn*54^`2H)L*GuGL)Z#&f`={^!zT59Utc<#|`Suu<{!1JgY-XWo=6$lZLG z>CWt#d$$?*?5LWyaO1-rEAB39+Y~*S$LJ69;aD$CS%xKx${PMo3ECBTZo{%o-jY!g zerD&+O*fCJD7Vg*Oqt-whLxCRA~SRIWuwD4{-3E?vO96f?nIscc3&&PZaOm_{Qv*( zq*_4^2L*;k1{Nj(4u==1I~MIubS*#Z&&0^Vq9D-Vz;HBFDDGB7YOc)I$z JtaD0e0st1v+eQEY diff --git a/share/qtcreator/qmldesigner/studio_templates/files/flowview/flowview2.png b/share/qtcreator/qmldesigner/studio_templates/files/flowview/flowview2.png deleted file mode 100644 index bd895584e12ea2accd8d65a15f229ced96454af4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 817 zcmeAS@N?(olHy`uVBq!ia0y~yU}R%pU^u|R%)r2KQP)~)y>mP7vV(}jMP|+E4jQ^F1>Os%Y->65zg~e~AlxC3%T8=^ zaJAM-NCj$$lo^{W4$yd$l;N>3 z;KsQNQ=ArV=v`>o%FFZlJ*)GX>8#GpT&;)L+QdY;TDS6Wa-CN{{H4^Ut*1Is=x~X# zl=CU!Kw0N=djh1LFTIJ7biO7PD1LbNnyH(#+VtKEm>NFO{KBKJ&;P2%rOl;v-or;5 z&aOX}d?I8;yJF}u+i9Z_JTIgse*FDs`!5oOQy>!wJuyY?el>TyPX0uW Date: Tue, 12 Nov 2024 09:27:49 +0100 Subject: [PATCH 105/322] DesignViewer: Add function to create the resource on demand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I33e2fb185333c6dc0bf0f53aae4810988ac3add5 Reviewed-by: Henning Gründl --- .../components/designviewer/dvconnector.cpp | 22 +++++++++++++++++-- .../components/designviewer/dvconnector.h | 1 + .../designviewer/resourcegeneratorproxy.cpp | 5 +++-- .../designviewer/resourcegeneratorproxy.h | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index c73d5d88780..23a460fe7bc 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -3,6 +3,9 @@ #include "dvconnector.h" +#include +#include +#include #include #include @@ -12,6 +15,8 @@ #include #include +#include "resourcegeneratorproxy.h" + namespace QmlDesigner::DesignViewer { Q_LOGGING_CATEGORY(deploymentPluginLog, "qtc.designer.deploymentPlugin", QtWarningMsg) @@ -166,7 +171,6 @@ bool DVConnector::eventFilter(QObject *obj, QEvent *e) m_isWebViewerVisible = m_webEngineView->isVisible(); emit webViewerVisibleChanged(); } - return true; } return QObject::eventFilter(obj, e); } @@ -185,6 +189,20 @@ void DVConnector::projectList() evaluatorData.connectCallbacks(this); } +void DVConnector::uploadCurrentProject() +{ + ResourceGeneratorProxy resourceGenerator; + QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName(); + QString resourcePath = resourceGenerator.createResourceFileSync(projectName); + + if (resourcePath.isEmpty()) { + qCWarning(deploymentPluginLog) << "Failed to create resource file"; + return; + } + + uploadProject(projectName, resourcePath); +} + void DVConnector::uploadProject(const QString &projectId, const QString &filePath) { QmlDesigner::QmlDesignerPlugin::emitUsageStatistics( @@ -199,7 +217,7 @@ void DVConnector::uploadProject(const QString &projectId, const QString &filePat const QString newProjectId = projectId.endsWith(".qmlrc") ? projectId : projectId + ".qmlrc"; qCDebug(deploymentPluginLog) << "Uploading project:" << fileInfo.fileName() - << " with projectId: " << newProjectId; + << "with projectId:" << newProjectId; QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); file->setParent(multiPart); diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h index e9a7a84e020..9602b7212ff 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.h +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -57,6 +57,7 @@ public: bool isWebViewerVisible() const; void projectList(); + void uploadCurrentProject(); void uploadProject(const QString &projectId, const QString &filePath); void deleteProject(const QString &projectId); void downloadProject(const QString &projectId, const QString &filePath); diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp index 30bec8a550d..bcd1d74d9cd 100644 --- a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp @@ -45,10 +45,11 @@ void ResourceGeneratorProxy::createResourceFileAsync() }); } -QString ResourceGeneratorProxy::createResourceFileSync() +QString ResourceGeneratorProxy::createResourceFileSync(const QString &projectName) { const auto project = ProjectExplorer::ProjectManager::startupProject(); - const Utils::FilePath tempFilePath = project->projectDirectory().pathAppended("share.qmlrc"); + const Utils::FilePath tempFilePath = project->projectDirectory().pathAppended(projectName + + ".qmlrc"); const bool retVal = ResourceGenerator::createQmlrcFile(tempFilePath); diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h index 4afcad98a30..0efe7ba51d7 100644 --- a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h @@ -15,7 +15,7 @@ class ResourceGeneratorProxy : public QObject public: ~ResourceGeneratorProxy(); Q_INVOKABLE void createResourceFileAsync(); - Q_INVOKABLE QString createResourceFileSync(); + Q_INVOKABLE QString createResourceFileSync(const QString &projectName = "share"); private: QFuture m_future; From 59e49440229261dbd12412a2ee9bf411043197e8 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 12 Nov 2024 15:21:33 +0200 Subject: [PATCH 106/322] QmlDesigner: Fix drag & drop of bundle textures to Navigator image components Fixes: QDS-13793 Change-Id: I1e7ee518aec6b45ec5045d59e56ad3834346839c Reviewed-by: Miikka Heikkinen --- .../components/navigator/navigatortreemodel.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 2c6a02002d4..78a992108d7 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -586,10 +586,11 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, bool moveNodesAfter = false; m_view->executeInTransaction(__FUNCTION__, [&] { - ModelNodeOperations::handleItemLibraryTexture3dDrop(texturePath, - modelNodeForIndex( - rowModelIndex), - moveNodesAfter); + ModelNodeOperations::handleItemLibraryImageDrop(texturePath, + targetProperty, + modelNodeForIndex( + rowModelIndex), + moveNodesAfter); }); } } From a80ca508ffd26be6f233180401f10ebd05fb4071 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Nov 2024 14:59:54 +0100 Subject: [PATCH 107/322] QmlDesigner: Show current state in property editor Task-number: QDS-11160 Change-Id: Ia020998f4b53897867034f0fbcf34e7360f703ad Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann --- .../HelperWidgets/PropertyEditorPane.qml | 46 ++++++++++++++++++- .../propertyeditor/propertyeditorview.cpp | 4 +- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index 1df75525c51..fa0def82c7d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -48,6 +48,50 @@ Rectangle { onClicked: itemPane.forceActiveFocus() } + Rectangle { + id: stateSection + width: itemPane.width + height: StudioTheme.Values.height + StudioTheme.Values.controlGap * 2 + color: StudioTheme.Values.themePanelBackground + z: isBaseState ? -1: 10 + SectionLayout { + y: StudioTheme.Values.controlGap + x: StudioTheme.Values.controlGap + PropertyLabel { + text: qsTr("Current State") + tooltip: tooltipItem.tooltip + } + + SecondColumnLayout { + + Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } + + Item { + + implicitWidth: StudioTheme.Values.singleControlColumnWidth + height: StudioTheme.Values.height + + HelperWidgets.Label { + anchors.fill: parent + anchors.leftMargin: StudioTheme.Values.inputHorizontalPadding + anchors.topMargin: StudioTheme.Values.typeLabelVerticalShift + text: stateName + color: StudioTheme.Values.themeInteraction + } + + ToolTipArea { + id: tooltipItem + anchors.fill: parent + tooltip: qsTr("The current state of the States View.") + } + + } + + ExpandingSpacer {} + } + } + } + HelperWidgets.ScrollView { id: mainScrollView @@ -57,7 +101,7 @@ Rectangle { bottom: itemPane.bottom left: itemPane.left right: itemPane.right - topMargin: dockedHeaderLoader.active ? 2 : 0 + topMargin: dockedHeaderLoader.active ? 2 : 0 + isBaseState ? 0 : stateSection.height } interactive: !Controller.contextMenuOpened diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 09c4aafff2c..b537fca2d03 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -565,8 +565,8 @@ void setupCurrentQmlBackend(PropertyEditorQmlBackend *currentQmlBackend, PropertyEditorView *propertyEditorView, const QString &specificQmlData) { - QString currentStateName = currentState.isBaseState() ? currentState.name() - : QStringLiteral("invalid state"); + QString currentStateName = currentState.isBaseState() ? QStringLiteral("invalid state") + : currentState.name(); QmlObjectNode qmlObjectNode{selectedNode}; if (specificQmlData.isEmpty()) From c75e0edb6f745e0c17bfc261218be133f17d8eb3 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 13 Nov 2024 11:29:20 +0100 Subject: [PATCH 108/322] QmlDesigner: Fix assert Avoid unnecessary reflection if the timeline mode did not actually change. The writer locker did trigger an assert if the reflection was "unnecessary". Change-Id: I69edfb6ad5c53f094b1e1da784953b3e7f797503 Reviewed-by: Marco Bubke Reviewed-by: Knud Dollereder --- src/plugins/qmldesigner/libs/designercore/model/model.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index bc36e8948d7..c4feda2a713 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -1977,6 +1977,9 @@ ModelNode Model::currentStateNode(AbstractView *view) void Model::setCurrentTimelineNode(const ModelNode &timeline) { + if (timeline.internalNode() == d->m_currentTimelineNode) + return; + Internal::WriteLocker locker(this); // unsure about this locker d->m_currentTimelineNode = timeline.internalNode(); From f715c96cae2369d8d645ef36186892fe3f77df20 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 17 Sep 2024 15:34:01 +0200 Subject: [PATCH 109/322] QmlDesigner: Improve QR code generator Add support for sending background and foreground colors for the QR code generator as a JSON payload. Change-Id: Ibd75c8be438935281e9b189220f34e1f0831931b Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- src/libs/3rdparty/qrcodegen/CMakeLists.txt | 11 +-- .../qrcodegen/src/qrcodegenerator_exports.h | 12 +++ .../qrcodegen/src/qrcodeimageprovider.cpp | 74 +++++++++---------- .../qrcodegen/src/qrcodeimageprovider.h | 35 +++------ 4 files changed, 64 insertions(+), 68 deletions(-) create mode 100644 src/libs/3rdparty/qrcodegen/src/qrcodegenerator_exports.h diff --git a/src/libs/3rdparty/qrcodegen/CMakeLists.txt b/src/libs/3rdparty/qrcodegen/CMakeLists.txt index 15a03147ee4..256e08b9a4b 100644 --- a/src/libs/3rdparty/qrcodegen/CMakeLists.txt +++ b/src/libs/3rdparty/qrcodegen/CMakeLists.txt @@ -1,9 +1,10 @@ add_qtc_library(QrCodeGenerator STATIC - CONDITION TARGET Qt6::Quick AND TARGET Qt6::Svg - PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src - DEPENDS - Qt6::Qml Qt6::Quick Qt6::Svg - SOURCES + CONDITION TARGET Qt6::Quick AND TARGET Qt6::Svg + PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src + DEFINES QRCODEGENERATOR_LIBRARY + DEPENDS + Qt6::Qml Qt6::Quick Qt6::Svg + SOURCES src/qrcodegen.cpp src/qrcodegen.h src/qrcodeimageprovider.cpp diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodegenerator_exports.h b/src/libs/3rdparty/qrcodegen/src/qrcodegenerator_exports.h new file mode 100644 index 00000000000..b248d66ed50 --- /dev/null +++ b/src/libs/3rdparty/qrcodegen/src/qrcodegenerator_exports.h @@ -0,0 +1,12 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#if defined(QRCODEGENERATOR_LIBRARY) +#define QRCODEGENERATOR_EXPORT Q_DECL_EXPORT +#elif defined(QRCODEGENERATOR_STATIC_LIBRARY) +#define QRCODEGENERATOR_EXPORT +#else +#define QRCODEGENERATOR_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp index b889b677600..4bf33a3a7e1 100644 --- a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp +++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp @@ -1,36 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2022 The Qt Company Ltd -** All rights reserved. -** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us -** -** This file is part of the Custom Merge QtDesignStudio plugin. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** If you have questions regarding the use of this file, please use -** contact form at http://www.qt.io/contact-us -** -******************************************************************************/ +// Copyright (C) 2024 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 "qrcodeimageprovider.h" #include "qrcodegen.h" + +#include #include #include QrCodeImageProvider::QrCodeImageProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap) +{} + +static QString qrToSvgString( + const qrcodegen::QrCode &qr, + int border, + const QColor &foreground = Qt::black, + const QColor &background = Qt::white) { -} - - -static QString qrToSvgString(const qrcodegen::QrCode &qr, int border) { if (border < 0) throw std::domain_error("Border must be non-negative"); if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) @@ -40,10 +27,12 @@ static QString qrToSvgString(const qrcodegen::QrCode &qr, int border) { QTextStream stream(&svgString); stream << "\n"; - stream << "\n"; + stream << "\n"; stream << "\n"; - stream << "\t\n"; + stream << (qr.getSize() + border * 2) << " " << (qr.getSize() + border * 2) + << "\" stroke=\"none\">\n"; + stream << "\t\n"; stream << "\t\n"; + stream << "\" fill=\"" << foreground.name() << "\"/>\n"; stream << "\n"; return svgString; - } QPixmap QrCodeImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { + QString content = id; + QColor foreground = Qt::black; + QColor background = Qt::white; + + QJsonParseError parseError; + QJsonDocument doc + = QJsonDocument::fromJson(QUrl::fromPercentEncoding(id.toUtf8()).toLatin1(), &parseError); + + if (parseError.error == QJsonParseError::NoError && !doc.isNull()) { // JSON document contained + content = doc["content"].toString(); + foreground = doc["foreground"].toString(); + background = doc["background"].toString(); + } + int width = 1000; int height = 1000; - const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(id.toLatin1(), qrcodegen::QrCode::Ecc::LOW); + const qrcodegen::QrCode qr + = qrcodegen::QrCode::encodeText(content.toLatin1(), qrcodegen::QrCode::Ecc::LOW); - QString svgString = qrToSvgString(qr, 3); + QString svgString = qrToSvgString(qr, 3, foreground, background); QSvgRenderer svgRenderer(svgString.toLatin1()); - if (size) *size = QSize(width, height); - QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width, - requestedSize.height() > 0 ? requestedSize.height() : height); + QPixmap pixmap( + requestedSize.width() > 0 ? requestedSize.width() : width, + requestedSize.height() > 0 ? requestedSize.height() : height); QPainter painter(&pixmap); svgRenderer.render(&painter); return pixmap; - } - - diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h index 1516d214bd8..6df1a3df7f7 100644 --- a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h +++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h @@ -1,36 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2022 The Qt Company Ltd -** All rights reserved. -** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us -** -** This file is part of the Custom Merge QtDesignStudio plugin. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** If you have questions regarding the use of this file, please use -** contact form at http://www.qt.io/contact-us -** -******************************************************************************/ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 -#ifndef QRCODEIMAGEPROVIDER_H -#define QRCODEIMAGEPROVIDER_H +#pragma once + +#include "qrcodegenerator_exports.h" #include #include -class QrCodeImageProvider : public QQuickImageProvider +class QRCODEGENERATOR_EXPORT QrCodeImageProvider : public QQuickImageProvider { + Q_OBJECT + public: QrCodeImageProvider(); - QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; }; - -#endif // QRCODEIMAGEPROVIDER_H From 17d43216fc99a9482c5a0f2a3cc8a0c3cfeebd22 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 7 Nov 2024 16:25:45 +0100 Subject: [PATCH 110/322] QmlDesigner: Improve flashing animation on button Change-Id: I009024c581113f40438dbe0c4028b7b8f61b3318 Reviewed-by: Thomas Hartmann --- .../imports/StudioControls/AbstractButton.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml index a8781707361..163374e73c9 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml @@ -132,13 +132,10 @@ T.AbstractButton { SequentialAnimation { id: highlightAnimation running: false + loops: 3 MyColorAnimation { to: StudioTheme.Values.themeConnectionEditorButtonBorder_hover } MyColorAnimation { to: control.style.background.idle } - MyColorAnimation { to: StudioTheme.Values.themeConnectionEditorButtonBorder_hover } - MyColorAnimation { to: control.style.background.idle } - MyColorAnimation { to: StudioTheme.Values.themeConnectionEditorButtonBorder_hover } - MyColorAnimation { to: control.style.background.idle } } onStateChanged: highlightAnimation.stop() From 537657a874b6d29cf9e99fd0b3b3f93e81fd221f Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 7 Nov 2024 16:18:38 +0100 Subject: [PATCH 111/322] QmlDesigner: Add functionality to device manager * Make active true by default * Add translation for column names * Fix dataChanged call by avoiding role * Add invokables for QML communication * Remove "Remove" column Change-Id: Ifa7ff49a79efe74bdbc05a18e453d102946f2b2d Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../components/devicesharing/deviceinfo.cpp | 2 +- .../devicesharing/devicemanager.cpp | 24 ++++++++++++ .../components/devicesharing/devicemanager.h | 5 +++ .../devicesharing/devicemanagermodel.cpp | 38 ++++++++++++------- .../devicesharing/devicemanagermodel.h | 4 +- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp index f2557100f09..d82d1b10c90 100644 --- a/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/deviceinfo.cpp @@ -29,7 +29,7 @@ IDeviceData::operator QString() const bool DeviceSettings::active() const { - return m_data.value(keyActive).toBool(); + return m_data.value(keyActive).toBool(true); } QString DeviceSettings::alias() const diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 4e41df28d43..0a02dd08d87 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -156,6 +156,24 @@ std::optional DeviceManager::deviceInfo(const QString &deviceId) con return device->deviceInfo(); } +std::optional DeviceManager::deviceSettings(const QString &deviceId) const +{ + auto device = findDevice(deviceId); + if (!device) + return {}; + + return device->deviceSettings(); +} + +std::optional DeviceManager::deviceIsConnected(const QString &deviceId) const +{ + auto device = findDevice(deviceId); + if (!device) + return {}; + + return device->isConnected(); +} + void DeviceManager::setDeviceAlias(const QString &deviceId, const QString &alias) { auto device = findDevice(deviceId); @@ -163,9 +181,15 @@ void DeviceManager::setDeviceAlias(const QString &deviceId, const QString &alias return; auto deviceSettings = device->deviceSettings(); + + if (deviceSettings.alias() == alias) + return; + deviceSettings.setAlias(alias); device->setDeviceSettings(deviceSettings); writeSettings(); + + emit deviceAliasChanged(device->deviceInfo()); } void DeviceManager::setDeviceActive(const QString &deviceId, const bool active) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 5120e407f10..42902267aeb 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -18,7 +18,11 @@ public: // Getters QList> devices() const; + std::optional deviceInfo(const QString &deviceId) const; + std::optional deviceSettings(const QString &deviceId) const; + + std::optional deviceIsConnected(const QString &deviceId) const; // Device management functions void setDeviceAlias(const QString &deviceId, const QString &alias); @@ -66,6 +70,7 @@ signals: void deviceOffline(const DeviceInfo &deviceInfo); void deviceActivated(const DeviceInfo &deviceInfo); void deviceDeactivated(const DeviceInfo &deviceInfo); + void deviceAliasChanged(const DeviceInfo &deviceInfo); void projectStarted(const DeviceInfo &deviceInfo); void projectStopped(const DeviceInfo &deviceInfo); void projectLogsReceived(const DeviceInfo &deviceInfo, const QString &logs); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp index 6614cb3cf48..8f55102ca84 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.cpp @@ -11,17 +11,21 @@ DeviceManagerModel::DeviceManagerModel(DeviceManager &deviceManager, QObject *pa , m_deviceManager(deviceManager) { connect(&m_deviceManager, &DeviceManager::deviceAdded, this, [this](const DeviceInfo &) { + beginResetModel(); endResetModel(); }); connect(&m_deviceManager, &DeviceManager::deviceRemoved, this, [this](const DeviceInfo &) { + beginResetModel(); endResetModel(); }); connect(&m_deviceManager, &DeviceManager::deviceOnline, this, [this](const DeviceInfo &) { + beginResetModel(); endResetModel(); }); connect(&m_deviceManager, &DeviceManager::deviceOffline, this, [this](const DeviceInfo &) { + beginResetModel(); endResetModel(); }); } @@ -96,27 +100,25 @@ QVariant DeviceManagerModel::headerData(int section, Qt::Orientation orientation if (orientation == Qt::Horizontal) { switch (section) { case DeviceColumns::Active: - return "Active"; + return tr("Active"); case DeviceColumns::Status: - return "Status"; + return tr("Status"); case DeviceColumns::Alias: - return "Alias"; + return tr("Alias"); case DeviceColumns::IPv4Addr: - return "IPv4 Address"; + return tr("IPv4 Address"); case DeviceColumns::OS: - return "OS"; + return tr("OS"); case DeviceColumns::OSVersion: - return "OS Version"; + return tr("OS Version"); case DeviceColumns::Architecture: - return "Architecture"; + return tr("Architecture"); case DeviceColumns::ScreenSize: - return "Screen Size"; + return tr("Screen Size"); case DeviceColumns::AppVersion: - return "App Version"; + return tr("App Version"); case DeviceColumns::DeviceId: - return "Device ID"; - case DeviceColumns::Remove: - return "Remove"; + return tr("Device ID"); } } @@ -142,7 +144,7 @@ bool DeviceManagerModel::setData(const QModelIndex &index, const QVariant &value m_deviceManager.setDeviceIP(deviceInfo.deviceId(), value.toString()); break; } - emit dataChanged(index, index, {role}); + emit dataChanged(index, index); return true; } @@ -159,4 +161,14 @@ Qt::ItemFlags DeviceManagerModel::flags(const QModelIndex &index) const return flags; } +bool DeviceManagerModel::addDevice(const QString &ip) +{ + return m_deviceManager.addDevice(ip); +} + +void DeviceManagerModel::removeDevice(const QString &deviceId) +{ + m_deviceManager.removeDevice(deviceId); +} + } // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h index 88f05f1215d..3a6ca79d810 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagermodel.h @@ -28,7 +28,6 @@ public: ScreenSize, AppVersion, DeviceId, - Remove, COLUMN_COUNT }; Q_ENUM(DeviceColumns) @@ -40,6 +39,9 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; + Q_INVOKABLE bool addDevice(const QString &ip); + Q_INVOKABLE void removeDevice(const QString &deviceId); + private: DeviceManager &m_deviceManager; }; From cd7152a64f8a2d34595c97d9916b1e28504df7f8 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 7 Nov 2024 16:24:47 +0100 Subject: [PATCH 112/322] QmlDesigner: Add SplitButton and DeviceManager * Add SplitButton * Remove Run and Live Preview button * Add RunManager * Add DeviceManagerWidget and all necessary QML sources * Add RunManagerModel to ToolBarBackend Task-number: QDS-12266 Task-number: QDS-13596 Task-number: QDS-13460 Change-Id: I19cb1fc52c84147fd5b4928485f531a5dd48a889 Signed-off-by: Henning Gruendl Reviewed-by: Thomas Hartmann --- .../qmldesigner/devicemanager/Main.qml | 532 +++++++++++++++++ ...ItOnGooglePlay_Badge_Web_color_English.png | Bin 0 -> 4698 bytes .../DeviceManagerControls/ButtonInput.qml | 286 +++++++++ .../DeviceManagerControls/Dropdown.qml | 270 +++++++++ .../imports/DeviceManagerControls/Switch.qml | 193 ++++++ .../DeviceManagerControls/TextField.qml | 208 +++++++ .../DeviceManagerControls/ToolbarButton.qml | 166 ++++++ .../imports/DeviceManagerControls/qmldir | 5 + .../imports/StudioTheme/Values.qml | 3 + share/qtcreator/qmldesigner/toolbar/Main.qml | 80 +-- .../qmldesigner/toolbar/SplitButton.qml | 559 ++++++++++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 10 +- .../devicesharing/devicemanager.cpp | 14 + .../components/devicesharing/devicemanager.h | 8 + .../devicesharing/devicemanagerwidget.cpp | 120 ++++ .../devicesharing/devicemanagerwidget.h | 46 ++ .../components/runmanager/runmanager.cpp | 320 ++++++++++ .../components/runmanager/runmanager.h | 94 +++ .../components/toolbar/toolbarbackend.cpp | 122 ++++ .../components/toolbar/toolbarbackend.h | 35 ++ .../qmldesigner/qmldesignerconstants.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 20 +- src/plugins/qmldesigner/qmldesignerplugin.h | 7 + 23 files changed, 3031 insertions(+), 69 deletions(-) create mode 100644 share/qtcreator/qmldesigner/devicemanager/Main.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/images/GetItOnGooglePlay_Badge_Web_color_English.png create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ButtonInput.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Dropdown.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Switch.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ToolbarButton.qml create mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir create mode 100644 share/qtcreator/qmldesigner/toolbar/SplitButton.qml create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.cpp create mode 100644 src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.h create mode 100644 src/plugins/qmldesigner/components/runmanager/runmanager.cpp create mode 100644 src/plugins/qmldesigner/components/runmanager/runmanager.h diff --git a/share/qtcreator/qmldesigner/devicemanager/Main.qml b/share/qtcreator/qmldesigner/devicemanager/Main.qml new file mode 100644 index 00000000000..68e54141a2d --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/Main.qml @@ -0,0 +1,532 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Shapes +import QtQuick.Templates as T +import Qt.labs.qmlmodels + +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +import DeviceManagerControls as DMC + +Rectangle { + id: root + color: StudioTheme.Values.themePanelBackground + + readonly property int sideBarWidth: 50 + readonly property int sideBarExpandedWidth: 350 + + readonly property int popupMargin: 6 + + component Cell: Rectangle { + required property var display + required property int row + required property int column + + required property bool editing + required property bool selected + required property bool current + + color: tableView.currentRow === row ? "#08475B" : StudioTheme.Values.themePanelBackground + implicitWidth: StudioTheme.Values.cellWidth + implicitHeight: StudioTheme.Values.cellHeight + border { + width: StudioTheme.Values.border + color: StudioTheme.Values.themeStateSeparator + } + } + + component CustomPopup: T.Popup { + id: popup + + function openAt(item: Item) { + popup.open() // open first so popup position is writable + + let rootRectangle = row.mapToItem(root, item.x, item.y, item.width, item.height) + + popup.x = Math.max(root.popupMargin, + Math.min(rootRectangle.x - popup.width / 2, + root.width - popup.width - root.popupMargin)) + popup.y = rootRectangle.y + rootRectangle.height + popup.chevronSize.height + + popup.chevronPosition = Qt.point(rootRectangle.x - popup.x + rootRectangle.width / 2, + -popup.chevronSize.height) + } + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding) + + property point chevronPosition: Qt.point(0, 0) + property size chevronSize: Qt.size(20, 10) + + padding: 20 + modal: false + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + background: Rectangle { + color: StudioTheme.Values.themePopupBackground + radius: 4 + + Shape { + id: chevron + + ShapePath { + id: path + strokeWidth: -1 + fillColor: StudioTheme.Values.themePopupBackground + startX: popup.chevronPosition.x - popup.chevronSize.width / 2 + startY: popup.chevronPosition.y + popup.chevronSize.height + + PathLine { + id: peak + x: popup.chevronPosition.x + y: popup.chevronPosition.y + } + + PathLine { + id: end + x: popup.chevronPosition.x + popup.chevronSize.width / 2 + y: popup.chevronPosition.y + popup.chevronSize.height + } + } + } + } + } + + DelegateChooser { + id: chooser + + DelegateChoice { + column: DeviceManagerModel.Status + + Cell { + id: statusDelegate + + Rectangle { + id: statusBackground + + anchors.centerIn: parent + + width: 60 + height: 30 + radius: 10 + color: statusDelegate.display === DeviceManagerModel.Online ? "#1AAA55" + : "#c98014" + + Text { + color: StudioTheme.Values.themeTextColor + text: statusDelegate.display === DeviceManagerModel.Online ? qsTr("Online") + : qsTr("Offline") + anchors.fill: parent + anchors.margins: 8 + + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + } + } + } + } + + DelegateChoice { + column: DeviceManagerModel.Active + + Cell { + id: activeDelegate + + DMC.Switch { + checked: activeDelegate.display === true + anchors.centerIn: parent + + onToggled: { + let index = activeDelegate.TableView.view.index(activeDelegate.row, activeDelegate.column) + activeDelegate.TableView.view.model.setData(index, checked, Qt.EditRole) + } + } + } + } + + DelegateChoice { + column: DeviceManagerModel.Alias + + Cell { + id: aliasDelegate + + Text { + text: aliasDelegate.display + visible: !aliasDelegate.editing + color: StudioTheme.Values.themeTextColor + anchors.fill: parent + anchors.margins: 8 + elide: Text.ElideMiddle + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + } + + TableView.editDelegate: TextField { + anchors.fill: parent + text: aliasDelegate.display + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + padding: 8 + Component.onCompleted: selectAll() + + TableView.onCommit: { + let index = aliasDelegate.TableView.view.index(aliasDelegate.row, aliasDelegate.column) + aliasDelegate.TableView.view.model.setData(index, text, Qt.EditRole) + } + + background: Rectangle { + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeInteraction + border.width: StudioTheme.Values.border + } + } + } + } + + DelegateChoice { + Cell { + Text { + color: StudioTheme.Values.themeTextColor + text: display + anchors.fill: parent + anchors.margins: 8 + elide: Text.ElideMiddle + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + } + } + } + } + + CustomPopup { + id: questionPopup + + contentItem: Item { + implicitWidth: 400 + implicitHeight: questionColumnLayout.height + + ColumnLayout { + id: questionColumnLayout + anchors.left: parent.left + anchors.right: parent.right + + Text { + Layout.fillWidth: true + text: qsTr("How to see a preview on Android device") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + font.bold: true + } + + component CollationItem: RowLayout { + id: collationItem + Layout.fillWidth: true + spacing: 6 + + property int number: 0 + property alias text: textLabel.text + + Label { + id: numberLabel + Layout.alignment: Qt.AlignTop + text: collationItem.number + "." + } + + Label { + id: textLabel + Layout.fillWidth: true + wrapMode: Text.WordWrap + } + } + + CollationItem { + number: 1 + text: qsTr("Scan the QR code below or click on the link to go to the Google Play store with your device and seek for Qt Viewer application.") + } + + RowLayout { + Layout.fillWidth: true + + Image { + Layout.maximumWidth: 200 + Layout.alignment: Qt.AlignHCenter + fillMode: Image.PreserveAspectFit + source: "images/GetItOnGooglePlay_Badge_Web_color_English.png" + + MouseArea {} + } + + Image { + id: playstoreQR + + property string payload: JSON.stringify({ + background: `${StudioTheme.Values.themePopupBackground}`, + foreground: `${StudioTheme.Values.themeTextColor}`, + content: "https://play.google.com/store/apps/details?id=io.qt.qtdesignviewer" + }) + + Layout.maximumWidth: 200 + Layout.alignment: Qt.AlignHCenter + // Requested size + sourceSize.width: 200 + sourceSize.height: 200 + fillMode: Image.PreserveAspectFit + source: "image://QrGenerator/" + playstoreQR.payload + } + } + + CollationItem { + number: 2 + text: qsTr("Install the Qt Viewer application on your phone.") + } + + CollationItem { + number: 3 + text: qsTr("Open up this window again, if you already closed it, and click on \"Add Run Target\" button in this window.") + } + } + } + } + + ColumnLayout { + id: column + anchors.fill: parent + + Rectangle { + id: toolbar + color: StudioTheme.Values.themeToolbarBackground + + Layout.fillWidth: true + Layout.preferredHeight: StudioTheme.Values.toolbarHeight + + RowLayout { + id: row + anchors.fill: parent + anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin + anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + spacing: 6 + + function addRunTarget(ip: string) { + let added = DeviceManagerBackend.deviceManagerModel.addDevice(ip) + if (added) + ipInput.clear() + } + + DMC.ButtonInput { + id: ipInput + + width: 200 + buttonIcon: StudioTheme.Constants.add_medium + placeholderText: qsTr("Run target IP") + validator: RegularExpressionValidator { + regularExpression: /^(\d{1,3}\.){3}\d{1,3}$/ + } + + tooltip: qsTr("Add Run Target") + + style: StudioTheme.SearchControlStyle {} + + onButtonClicked: row.addRunTarget(ipInput.text) + onAccepted: row.addRunTarget(ipInput.text) + } + + DMC.ToolbarButton { + id: removeRunTargetButton + enabled: tableView.currentRow !== -1 + buttonIcon: StudioTheme.Constants.remove_medium + tooltip: qsTr("Remove Run Target") + + onClicked: { + let index = tableView.index(tableView.currentRow, DeviceManagerModel.DeviceId) + let deviceId = tableView.model.data(index, Qt.DisplayRole) + DeviceManagerBackend.deviceManagerModel.removeDevice(deviceId) + } + } + + Item { + Layout.fillWidth: true + } + + DMC.Dropdown { + text: qsTr("Columns") + model: ListModel { + id: columnModel + onDataChanged: { + tableView.forceLayout() + } + } + style: StudioTheme.Values.viewBarControlStyle + + Component.onCompleted: { + tableView.setColumnWidth(DeviceManagerModel.Status, 80) + tableView.setColumnWidth(DeviceManagerModel.Active, 70) + tableView.setColumnWidth(DeviceManagerModel.Alias, 200) + tableView.setColumnWidth(DeviceManagerModel.IPv4Addr, 110) + tableView.setColumnWidth(DeviceManagerModel.OS, 100) + tableView.setColumnWidth(DeviceManagerModel.OSVersion, 100) + tableView.setColumnWidth(DeviceManagerModel.Architecture, 100) + tableView.setColumnWidth(DeviceManagerModel.ScreenSize, 90) + //tableView.setColumnWidth(DeviceManagerModel.AppVersion, 130) + tableView.setColumnWidth(DeviceManagerModel.DeviceId, 250) + + for (let i = 0; i < DeviceManagerBackend.deviceManagerModel.columnCount(); ++i) { + columnModel.append({"name": DeviceManagerBackend.deviceManagerModel.headerData(i, Qt.Horizontal), + "hidden": false}) + } + } + } + + DMC.ToolbarButton { + id: questionButton + buttonIcon: StudioTheme.Constants.help + onClicked: questionPopup.openAt(questionButton) + checkable: true + checked: questionPopup.visible + } + } + } + + ColumnLayout { + id: content + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 20 + + spacing: 20 + + Text { + id: topBar + text: qsTr("Manage target devices to be used to display the project") + Layout.fillWidth: true + verticalAlignment: Text.AlignTop + height: 20 + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: -1 + + HoverHandler { id: hoverHandler } + + HorizontalHeaderView { + id: horizontalHeader + + Layout.fillWidth: true + + syncView: tableView + clip: true + interactive: false + + delegate: Rectangle { + color: StudioTheme.Values.themePanelBackground + implicitWidth: StudioTheme.Values.cellWidth + implicitHeight: StudioTheme.Values.cellHeight + border { + width: StudioTheme.Values.border + color: StudioTheme.Values.themeStateSeparator + } + + Text { + color: StudioTheme.Values.themeTextColor + text: display + anchors.fill: parent + anchors.margins: 8 + elide: Text.ElideRight + font.bold: true + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ScrollView { + id: scrollView + anchors.fill: parent + + contentItem: TableView { + id: tableView + + columnWidthProvider: function(column) { + if (columnModel.get(column).hidden) + return 0 + + const width = explicitColumnWidth(column) + if (width === 0) + return 0 + else if (width > 0) + return width + return implicitColumnWidth(column) + } + + columnSpacing: -StudioTheme.Values.border + rowSpacing: -StudioTheme.Values.border + clip: true + interactive: false + selectionMode: TableView.SingleSelection + selectionBehavior: TableView.SelectRows + selectionModel: ItemSelectionModel {} + model: DeviceManagerBackend.deviceManagerModel + delegate: chooser + } + + ScrollBar.horizontal: StudioControls.TransientScrollBar { + id: horizontalScrollBar + style: StudioTheme.Values.viewStyle + parent: tableView + x: 0 + y: tableView.height - horizontalScrollBar.height + width: tableView.availableWidth - (verticalScrollBar.isNeeded ? verticalScrollBar.thickness : 0) + orientation: Qt.Horizontal + + visible: !tableView.hideHorizontalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && horizontalScrollBar.isNeeded + otherInUse: verticalScrollBar.inUse + } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + style: StudioTheme.Values.viewStyle + parent: tableView + x: tableView.width - verticalScrollBar.width + y: 0 + height: tableView.availableHeight - (horizontalScrollBar.isNeeded ? horizontalScrollBar.thickness : 0) + orientation: Qt.Vertical + + visible: !tableView.hideVerticalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && verticalScrollBar.isNeeded + otherInUse: horizontalScrollBar.inUse + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/devicemanager/images/GetItOnGooglePlay_Badge_Web_color_English.png b/share/qtcreator/qmldesigner/devicemanager/images/GetItOnGooglePlay_Badge_Web_color_English.png new file mode 100644 index 0000000000000000000000000000000000000000..7a06997a57236e18b5bd2a152435911b72371a89 GIT binary patch literal 4698 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 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ButtonInput.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ButtonInput.qml new file mode 100644 index 00000000000..af6cddd6e15 --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ButtonInput.qml @@ -0,0 +1,286 @@ +// Copyright (C) 2024 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 StudioControls as StudioControls +import StudioTheme as StudioTheme + +T.TextField { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + signal buttonClicked + + property bool empty: control.text === "" + + property alias buttonIcon: buttonIcon.text + + function isEmpty() { + return control.text === "" + } + + property string tooltip + + StudioControls.ToolTipExt { id: toolTip } + + HoverHandler { id: hoverHandler } + + Timer { + interval: 1000 + running: control.hovered && control.tooltip.length + onTriggered: toolTip.showText(control, Qt.point(hoverHandler.point.position.x, + hoverHandler.point.position.y), + control.tooltip) + } + + width: control.style.controlSize.width + height: control.style.controlSize.height + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + leftPadding: 32 + rightPadding: 30 + + font.pixelSize: control.style.baseFontSize + + color: control.style.text.idle + selectionColor: control.style.text.selection + selectedTextColor: control.style.text.selectedText + placeholderTextColor: control.style.text.placeholder + + selectByMouse: true + readOnly: false + hoverEnabled: true + clip: true + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape && event.modifiers === Qt.NoModifier) { + control.clear() + event.accepted = true + } + } + + Text { + id: placeholder + x: control.leftPadding + y: control.topPadding + width: control.width - (control.leftPadding + control.rightPadding) + height: control.height - (control.topPadding + control.bottomPadding) + + text: control.placeholderText + font: control.font + color: control.placeholderTextColor + verticalAlignment: control.verticalAlignment + visible: !control.length && !control.preeditText + && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + renderType: control.renderType + } + + background: Rectangle { + id: textFieldBackground + color: control.style.background.idle + //border.color: control.style.border.idle + border.width: 0//control.style.borderWidth + radius: control.style.radius + } + + T.AbstractButton { + id: button + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + width: button.style.squareControlSize.width + height: button.style.squareControlSize.height + + anchors.left: parent.left + + enabled: control.acceptableInput + + onClicked: control.buttonClicked() + + contentItem: T.Label { + id: buttonIcon + width: button.style.squareControlSize.width + height: button.height + font { + family: StudioTheme.Constants.iconFont.family + pixelSize: button.style.baseIconFontSize + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + id: buttonBackground + color: button.style.background.idle + border.color: "transparent"//button.style.border.idle + border.width: button.style.borderWidth + radius: button.style.radius + topRightRadius: 0 + bottomRightRadius: 0 + } + + states: [ + State { + name: "default" + when: button.enabled && !button.hovered && !button.pressed && !button.checked + PropertyChanges { + target: buttonBackground + color: button.style.background.idle + border.color: button.style.border.idle + } + PropertyChanges { + target: buttonIcon + color: button.style.icon.idle + } + }, + State { + name: "hover" + when: button.enabled && button.hovered && !button.pressed && !button.checked + PropertyChanges { + target: buttonBackground + color: button.style.background.hover + border.color: button.style.border.hover + } + PropertyChanges { + target: buttonIcon + color: button.style.icon.hover + } + }, + State { + name: "press" + when: button.enabled && button.hovered && button.pressed + PropertyChanges { + target: buttonBackground + color: button.style.interaction + border.color: button.style.interaction + } + PropertyChanges { + target: buttonIcon + color: button.style.icon.interaction + } + }, + State { + name: "pressNotHover" + when: button.enabled && !button.hovered && button.pressed + extend: "hover" + }, + State { + name: "disable" + when: !button.enabled + PropertyChanges { + target: buttonBackground + color: button.style.background.disabled + border.color: button.style.border.disabled + } + PropertyChanges { + target: buttonIcon + color: button.style.icon.disabled + } + } + ] + } + + Rectangle { + id: controlBorder + anchors.fill: parent + color: "transparent" + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: control.style.radius + } + + Rectangle { // x button + width: 16 + height: 16 + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.verticalCenter: parent.verticalCenter + visible: control.text !== "" + color: "transparent" + + T.Label { + text: StudioTheme.Constants.close_small + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: control.style.baseIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.centerIn: parent + color: control.style.icon.idle + + scale: xMouseArea.containsMouse ? 1.2 : 1 + } + + MouseArea { + id: xMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: control.text = "" + } + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hovered && !control.activeFocus + PropertyChanges { + target: textFieldBackground + color: control.style.background.idle + } + PropertyChanges { + target: controlBorder + border.color: control.style.border.idle + } + + PropertyChanges { + target: control + placeholderTextColor: control.style.text.placeholder + } + }, + State { + name: "hover" + when: control.enabled && control.hovered && !control.activeFocus + PropertyChanges { + target: textFieldBackground + color: control.style.background.hover + } + PropertyChanges { + target: controlBorder + border.color: control.style.border.hover + } + PropertyChanges { + target: control + placeholderTextColor: control.style.text.placeholderHover + } + }, + State { + name: "edit" + when: control.enabled && control.activeFocus + PropertyChanges { + target: textFieldBackground + color: control.style.background.interaction + } + PropertyChanges { + target: controlBorder + border.color: control.style.border.interaction + } + PropertyChanges { + target: control + placeholderTextColor: control.style.text.placeholderInteraction + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: control + placeholderTextColor: control.style.text.disabled + } + } + ] +} diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Dropdown.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Dropdown.qml new file mode 100644 index 00000000000..7b67d79db0d --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Dropdown.qml @@ -0,0 +1,270 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Shapes +import QtQuick.Templates as T + +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +T.AbstractButton { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property alias model: listView.model + + width: control.style.squareControlSize.width + buttonLabel.implicitWidth + height: control.style.squareControlSize.height + + checked: dropdownPopup.visible + + onPressed: control.checked ? dropdownPopup.close() : dropdownPopup.open() + + contentItem: Row { + spacing: 0 + + Text { + id: buttonLabel + height: control.height + leftPadding: 8 + font.pixelSize: control.style.baseFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + visible: control.text !== "" + } + + 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: StudioTheme.Constants.upDownSquare2 + } + } + + background: Rectangle { + id: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: control.style.radius + } + + T.Popup { + id: dropdownPopup + parent: control + x: control.width - dropdownPopup.width + y: control.height + width: 120 + height: 200 + padding: control.style.borderWidth + margins: 0 // If not defined margin will be -1 + closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent + + contentItem: ListView { + id: listView + clip: true + implicitHeight: listView.contentHeight + boundsBehavior: Flickable.StopAtBounds + + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + parent: listView + x: listView.width - verticalScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded + } + + delegate: ItemDelegate { + id: itemDelegate + + required property string name + required property bool hidden + + required property int index + + + width: dropdownPopup.width - dropdownPopup.leftPadding - dropdownPopup.rightPadding + height: control.style.controlSize.height - 2 * control.style.borderWidth + padding: 0 + checkable: true + checked: !itemDelegate.hidden + + onToggled: { + listView.model.setProperty(itemDelegate.index, "hidden", !itemDelegate.checked) + } + + contentItem: Text { + leftPadding: itemDelegateIconArea.width + + text: itemDelegate.name + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + color: { + if (!itemDelegate.enabled) + return control.style.text.disabled + + return itemDelegate.highlighted ? control.style.text.selectedText + : control.style.text.idle + } + } + + Item { + id: itemDelegateIconArea + width: itemDelegate.height + height: itemDelegate.height + + T.Label { + id: itemDelegateIcon + text: StudioTheme.Constants.tickIcon + color: itemDelegate.highlighted ? control.style.text.selectedText + : control.style.text.idle + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: control.style.smallIconFontSize + visible: itemDelegate.checked + anchors.fill: parent + renderType: Text.NativeRendering + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + highlighted: itemDelegate.hovered + + background: Rectangle { + id: itemDelegateBackground + x: 0 + y: 0 + width: itemDelegate.width + height: itemDelegate.height + color: itemDelegate.highlighted ? control.style.interaction : "transparent" + } + } + } + + background: Rectangle { + color: control.style.popup.background + border.width: 0 + } + + enter: Transition {} + exit: Transition {} + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.idle + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.enabled && control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.hover + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.hover + } + }, + State { + 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: "press" + when: control.enabled && control.hovered && control.pressed + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.interaction + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.interaction + } + }, + State { + name: "check" + when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "pressNotHover" + 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/devicemanager/imports/DeviceManagerControls/Switch.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Switch.qml new file mode 100644 index 00000000000..4ccdca4486c --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/Switch.qml @@ -0,0 +1,193 @@ +// Copyright (C) 2024 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.Switch { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + // This property is used to indicate the global hover state + property bool hover: control.hovered && control.enabled + property bool edit: false + + property alias labelVisible: label.visible + property alias labelColor: label.color + + property alias fontFamily: label.font.family + property alias fontPixelSize: label.font.pixelSize + + font.pixelSize: StudioTheme.Values.myFontSize + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + spacing: label.visible ? control.style.controlSpacing : 0 + hoverEnabled: true + activeFocusOnTab: false + + indicator: Rectangle { + id: switchBackground + x: 0 + y: 0 + z: 5 + implicitWidth: 40//control.style.squareControlSize.width * 2 + implicitHeight: 20//control.style.squareControlSize.height + radius: 10//control.style.squareControlSize.height * 0.5 + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + + Rectangle { + id: switchIndicator + + readonly property real gap: 2 + property real size: switchBackground.implicitHeight - switchIndicator.gap * 2 + + x: control.checked ? parent.width - width - switchIndicator.gap + : switchIndicator.gap + y: switchIndicator.gap + width: switchIndicator.size + height: switchIndicator.size + radius: switchIndicator.size * 0.5 + color: control.style.icon.idle + border.width: 0 + } + } + + contentItem: T.Label { + id: label + leftPadding: switchBackground.x + switchBackground.width + control.spacing + rightPadding: 0 + verticalAlignment: Text.AlignVCenter + text: control.text + font: control.font + color: control.style.text.idle + visible: control.text !== "" + } + + property bool __default: control.enabled && !control.hover && !control.pressed + property bool __globalHover: control.enabled && !control.pressed + property bool __hover: control.hover && !control.pressed + property bool __press: control.hover && control.pressed + + states: [ + State { + name: "default" + when: control.__default && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.idle + } + }, + State { + name: "globalHover" + when: control.__globalHover && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.globalHover + border.color: control.style.border.idle + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.__hover && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.hover + } + }, + State { + name: "press" + when: control.__press && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.interaction + border.color: control.style.border.interaction + } + PropertyChanges { + target: switchIndicator + color: control.style.interaction + } + }, + State { + name: "disable" + when: !control.enabled && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.disabled + border.color: control.style.border.disabled + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.disabled + } + PropertyChanges { + target: label + color: control.style.text.disabled + } + }, + + State { + name: "defaultChecked" + when: control.__default && control.checked + extend: "default" + PropertyChanges { + target: switchBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "globalHoverChecked" + when: control.__globalHover && control.checked + extend: "globalHover" + PropertyChanges { + target: switchBackground + color: control.style.interactionHover + border.color: control.style.interactionHover + } + }, + State { + name: "hoverChecked" + when: control.__hover && control.checked + extend: "hover" + PropertyChanges { + target: switchBackground + color: control.style.interactionHover + border.color: control.style.interactionHover + } + }, + State { + name: "pressChecked" + when: control.__press && control.checked + extend: "press" + }, + State { + name: "disableChecked" + when: !control.enabled && control.checked + extend: "disable" + } + ] +} diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml new file mode 100644 index 00000000000..f34186441f6 --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml @@ -0,0 +1,208 @@ +// Copyright (C) 2024 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.TextField { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + // This property is used to indicate the global hover state + property bool hover: (actionIndicator.hover || mouseArea.containsMouse || indicator.hover + || translationIndicator.hover) && control.enabled + property bool edit: control.activeFocus + + signal rejected + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + font.pixelSize: control.style.baseFontSize + + color: control.style.text.idle + selectionColor: control.style.text.selection + selectedTextColor: control.style.text.selectedText + placeholderTextColor: control.style.text.placeholder + + readOnly: false + selectByMouse: true + persistentSelection: contextMenu.visible || control.focus + + width: control.style.controlSize.width + height: control.style.controlSize.height + implicitHeight: control.style.controlSize.height + + leftPadding: control.style.inputHorizontalPadding + actionIndicator.width + rightPadding: control.style.inputHorizontalPadding + translationIndicator.width + indicator.width + + MouseArea { + id: mouseArea + anchors.fill: parent + enabled: true + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } + + onPressed: function(event) { + if (event.button === Qt.RightButton) + contextMenu.popup(control) + } + + onActiveFocusChanged: { + // OtherFocusReason in this case means, if the TextField gets focus after the context menu + // was closed due to an menu item click. + if (control.activeFocus && control.focusReason !== Qt.OtherFocusReason) + control.preFocusText = control.text + + if (!control.activeFocus) + control.deselect() + } + + onEditChanged: { + if (control.edit) + contextMenu.close() + } + + onEditingFinished: control.focus = false + + Text { + id: placeholder + x: control.leftPadding + y: control.topPadding + width: control.width - (control.leftPadding + control.rightPadding) + height: control.height - (control.topPadding + control.bottomPadding) + + text: control.placeholderText + font: control.font + color: control.placeholderTextColor + verticalAlignment: control.verticalAlignment + visible: !control.length && !control.preeditText + && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + renderType: control.renderType + } + + background: Rectangle { + id: textFieldBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + x: actionIndicator.width + width: control.width - actionIndicator.width + height: control.height + } + + Indicator { + id: indicator + style: control.style + visible: false + x: control.width - translationIndicator.width - indicator.width + width: indicator.visible ? control.height : 0 + height: indicator.visible ? control.height : 0 + } + + TranslationIndicator { + id: translationIndicator + style: control.style + __parentControl: control + x: control.width - translationIndicator.width + width: translationIndicator.visible ? __translationIndicatorWidth : 0 + height: translationIndicator.visible ? __translationIndicatorHeight : 0 + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hover && !control.edit && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + PropertyChanges { + target: mouseArea + cursorShape: Qt.PointingHandCursor + } + }, + State { + name: "globalHover" + when: (actionIndicator.hover || translationIndicator.hover || indicator.hover) + && !control.edit && control.enabled && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.globalHover + border.color: control.style.border.idle + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + }, + State { + name: "hover" + when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover + && !indicator.hover && !control.edit && control.enabled && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: control + color: control.style.text.hover + placeholderTextColor: control.style.text.placeholder + } + }, + State { + name: "edit" + when: control.edit || contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.interaction + border.color: control.style.border.interaction + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + PropertyChanges { + target: mouseArea + cursorShape: Qt.IBeamCursor + } + }, + State { + name: "disable" + when: !control.enabled + PropertyChanges { + target: textFieldBackground + color: control.style.background.disabled + border.color: control.style.border.disabled + } + PropertyChanges { + target: control + color: control.style.text.disabled + placeholderTextColor: control.style.text.disabled + } + } + ] + + Keys.onEscapePressed: function(event) { + event.accepted = true + control.text = control.preFocusText + control.rejected() + control.focus = false + } +} diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ToolbarButton.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ToolbarButton.qml new file mode 100644 index 00000000000..6e798c97f0d --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/ToolbarButton.qml @@ -0,0 +1,166 @@ +// Copyright (C) 2024 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 HelperWidgets 2.0 as HelperWidgets +import StudioTheme as StudioTheme + +T.AbstractButton { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + property alias tooltip: toolTipArea.tooltip + property alias buttonIcon: buttonIcon.text + + width: control.style.squareControlSize.width + buttonLabel.implicitWidth + height: control.style.squareControlSize.height + + 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 + } + + contentItem: Row { + spacing: 0 + + T.Label { + id: buttonIcon + width: control.style.squareControlSize.width + height: control.height + font { + family: StudioTheme.Constants.iconFont.family + pixelSize: control.style.baseIconFontSize + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + T.Label { + id: buttonLabel + height: control.height + rightPadding: buttonLabel.visible ? 8 : 0 + font.pixelSize: control.style.baseFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + visible: control.text !== "" + } + } + + background: Rectangle { + id: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: control.style.radius + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.idle + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.enabled && control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.hover + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.hover + } + }, + State { + 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: "press" + when: control.enabled && control.hovered && control.pressed + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.interaction + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.interaction + } + }, + State { + name: "check" + when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "pressNotHover" + 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/devicemanager/imports/DeviceManagerControls/qmldir b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir new file mode 100644 index 00000000000..de636d34458 --- /dev/null +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir @@ -0,0 +1,5 @@ +ButtonInput 1.0 ButtonInput.qml +Dropdown 1.0 Dropdown.qml +Switch 1.0 Switch.qml +TextField 1.0 TextField.qml +ToolbarButton 1.0 ToolbarButton.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 32a078b8d6d..961be1bf706 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -254,6 +254,9 @@ QtObject { readonly property int flowPillHeight: 20 readonly property int flowPillRadius: 4 + readonly property int cellWidth: 200 + readonly property int cellHeight: 40 + // Theme Colors property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index c69a0205e4c..77cc74ed978 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Controls + import StudioControls as StudioControls import StudioTheme as StudioTheme import ToolBar @@ -15,9 +16,7 @@ Rectangle { readonly property int largeBreakpoint: 1200 readonly property bool flyoutEnabled: root.width < root.largeBreakpoint - ToolBarBackend { - id: backend - } + ToolBarBackend { id: backend } Item { id: topToolbarOtherMode @@ -64,11 +63,11 @@ Rectangle { } } - MouseArea{ + MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true - onClicked: homeOther.onClicked() + onClicked: homeOther.clicked() } states: [ @@ -111,69 +110,20 @@ Rectangle { tooltip: qsTr("Switch to Welcome Mode.") } - ToolbarButton { - id: runProject + SplitButton { + id: splitButton + style: StudioTheme.Values.toolbarButtonStyle anchors.verticalCenter: parent.verticalCenter anchors.left: home.right anchors.leftMargin: 10 - buttonIcon: StudioTheme.Constants.runProjOutline_large - style: StudioTheme.ToolbarStyle { - radius: StudioTheme.Values.smallRadius + width: 160 - icon: StudioTheme.ControlStyle.IconColors { - idle: StudioTheme.Values.themeIdleGreen - hover: StudioTheme.Values.themeRunningGreen - interaction: "#ffffff" - disabled: "#636363" - } + runTarget: backend?.runTargetIndex + runManagerState: backend?.runManagerState - background: StudioTheme.ControlStyle.BackgroundColors { - idle: StudioTheme.Values.themeControlBackground_toolbarIdle - hover: StudioTheme.Values.themeControlBackground_topToolbarHover - interaction: StudioTheme.Values.themeInteraction - disabled: StudioTheme.Values.themeControlBackground_toolbarIdle - } - - border: StudioTheme.ControlStyle.BorderColors { - idle: StudioTheme.Values.themeControlBackground_toolbarIdle - hover: StudioTheme.Values.themeControlBackground_topToolbarHover - interaction: StudioTheme.Values.themeInteraction - disabled: StudioTheme.Values.themeControlBackground_toolbarIdle - } - } - - onClicked: backend.runProject() - tooltip: qsTr("Run Project") - } - - ToolbarButton { - id: livePreviewButton - style: StudioTheme.Values.primaryToolbarStyle - width: 96 - anchors.verticalCenter: parent.verticalCenter - anchors.left: runProject.right - anchors.leftMargin: 10 - iconFontFamily: StudioTheme.Constants.font.family - buttonIcon: qsTr("Live Preview") - - onClicked: { - livePreview.trigger() - } - - MouseArea { - acceptedButtons: Qt.RightButton - anchors.fill: parent - - onClicked: { - var p = livePreviewButton.mapToGlobal(0, 0) - backend.showZoomMenu(p.x, p.y) - } - } - - ActionSubscriber { - id: livePreview - actionId: "LivePreview" - } + onClicked: backend.toggleRunning() + onRunTargetSelected: function(targetName) { backend.selectRunTarget(targetName) } + onOpenRunTargets: backend.openDeviceManager() } StudioControls.TopLevelComboBox { @@ -181,7 +131,7 @@ Rectangle { style: StudioTheme.Values.toolbarStyle width: 320 - ((root.width > root.mediumBreakpoint) ? 0 : (root.mediumBreakpoint - root.width)) anchors.verticalCenter: parent.verticalCenter - anchors.left: livePreviewButton.right + anchors.left: splitButton.right anchors.leftMargin: 10 model: backend.documentModel @@ -191,7 +141,7 @@ Rectangle { } Text { - parent:currentFile.contentItem + parent: currentFile.contentItem visible: backend.isDocumentDirty anchors.right: parent.right diff --git a/share/qtcreator/qmldesigner/toolbar/SplitButton.qml b/share/qtcreator/qmldesigner/toolbar/SplitButton.qml new file mode 100644 index 00000000000..a732a562a9d --- /dev/null +++ b/share/qtcreator/qmldesigner/toolbar/SplitButton.qml @@ -0,0 +1,559 @@ +// Copyright (C) 2024 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 as StudioTheme +import HelperWidgets +import ToolBar + +Item { + id: root + property StudioTheme.ControlStyle style: StudioTheme.Values.primaryButtonStyle + + property alias tooltip: toolTipArea.tooltip + property bool hover: primaryButton.hover || menuButton.hover + + signal clicked() + signal runTargetSelected(targetId: string) + signal openRunTargets() + + property int runTarget: 0 + property int runManagerState: RunManager.NotRunning + + property int menuWidth: Math.max(160, root.width) + + width: root.style.squareControlSize.width + height: root.style.squareControlSize.height + + function isRunTargetEnabled(index: int): bool { + let modelIndex = runManagerModel.index(index, 0) + return runManagerModel.data(modelIndex, RunManagerModel.Enabled) + } + + 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 + } + + onRunTargetChanged: { + primaryButton.enabled = root.isRunTargetEnabled(root.runTarget) + } + + Connections { + target: runManagerModel + function onModelReset() { + primaryButton.enabled = root.isRunTargetEnabled(root.runTarget) + } + } + + Row { + T.AbstractButton { + id: primaryButton + + property StudioTheme.ControlStyle style: root.style + + property bool hover: primaryButton.hovered + property bool press: primaryButton.pressed + + property alias backgroundVisible: buttonBackground.visible + property alias backgroundRadius: buttonBackground.radius + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + width: root.width - menuButton.width// TODO + height: primaryButton.style.squareControlSize.height + z: primaryButton.checked ? 10 : 3 + activeFocusOnTab: false + + onClicked: { + window.close() + root.clicked() + } + + background: Rectangle { + id: buttonBackground + color: primaryButton.style.background.idle + border.color: primaryButton.style.border.idle + border.width: 0//primaryButton.style.borderWidth + radius: primaryButton.style.radius + topRightRadius: 0 + bottomRightRadius: 0 + } + + indicator: Item { + x: 0 + y: 0 + width: primaryButton.width + height: primaryButton.height + + Row { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + spacing: 8 + + T.Label { + height: primaryButton.height + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: primaryButton.style.baseIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + color: { + if (root.runManagerState === RunManager.NotRunning) + return primaryButton.press ? primaryButton.style.text.idle + : primaryButton.hover ? "#2eff68" // running green + : "#649a5d" // idle green + else if (startingAnimation.running) + return primaryButton.style.text.idle + else + return primaryButton.press ? primaryButton.style.text.idle + : primaryButton.hover ? "#cc3c34" // recording red + : "#6a4242" // idle red + } + + text: { + if (root.runManagerState === RunManager.NotRunning) + return StudioTheme.Constants.playOutline_medium + else if (root.runManagerState === RunManager.Starting) + return StudioTheme.Constants.properties_medium + else + return StudioTheme.Constants.stop_medium + } + + RotationAnimation on rotation { + id: startingAnimation + running: root.runManagerState === RunManager.Starting + loops: Animation.Infinite + from: 0 + to: 360 + duration: 1000 + alwaysRunToEnd: true + } + } + + T.Label { + height: primaryButton.height + color: primaryButton.enabled ? primaryButton.style.text.idle + : primaryButton.style.text.disabled + font.pixelSize: primaryButton.style.baseFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: { + let index = runManagerModel.index(root.runTarget, 0) + return runManagerModel.data(index, "targetName") + } + } + } + } + + states: [ + State { + // This is only to sync colors with TopLevelComboBox + name: "open" + when: window.visible + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.hover + border.color: primaryButton.style.border.hover + } + }, + State { + name: "default" + when: primaryButton.enabled && !root.hover && !primaryButton.hover + && !primaryButton.press && !primaryButton.checked + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.idle + border.color: primaryButton.style.border.idle + } + PropertyChanges { + target: primaryButton + z: 3 + } + }, + State { + name: "globalHover" + when: root.hover && !primaryButton.hover && !primaryButton.press && primaryButton.enabled + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.globalHover + border.color: primaryButton.style.border.idle + } + }, + State { + name: "hover" + when: !primaryButton.checked && primaryButton.hover && !primaryButton.press && primaryButton.enabled + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.hover + border.color: primaryButton.style.border.hover + } + PropertyChanges { + target: primaryButton + z: 100 + } + }, + State { + name: "hoverCheck" + when: primaryButton.checked && primaryButton.hover && !primaryButton.press && primaryButton.enabled + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.hover + border.color: primaryButton.style.border.hover + } + PropertyChanges { + target: primaryButton + z: 100 + } + }, + State { + name: "press" + when: primaryButton.hover && primaryButton.press && primaryButton.enabled + PropertyChanges { + target: buttonBackground + color: primaryButton.style.interaction + border.color: primaryButton.style.interaction + } + PropertyChanges { + target: primaryButton + z: 100 + } + }, + State { + name: "check" + when: primaryButton.enabled && !primaryButton.press && primaryButton.checked + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.idle + border.color: primaryButton.style.border.idle + } + }, + State { + name: "disable" + when: !primaryButton.enabled + PropertyChanges { + target: buttonBackground + color: primaryButton.style.background.disabled + border.color: primaryButton.style.border.disabled + } + } + ] + } + + T.AbstractButton { + id: menuButton + + property StudioTheme.ControlStyle style: root.style + + property bool hover: menuButton.hovered + property bool press: menuButton.pressed + + property alias backgroundVisible: menuButtonBackground.visible + property alias backgroundRadius: menuButtonBackground.radius + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + width: menuButton.style.squareControlSize.width + height: menuButton.style.squareControlSize.height + z: menuButton.checked ? 10 : 3 + activeFocusOnTab: false + + checkable: false + checked: window.visible + + onClicked: { + if (window.visible) { + window.close() + } else { + var originMapped = root.mapToGlobal(0,0) + window.x = originMapped.x + window.y = originMapped.y + root.height + window.show() + window.requestActivate() + } + } + + background: Rectangle { + id: menuButtonBackground + color: menuButton.style.background.idle + border.color: menuButton.style.border.idle + border.width: 0//menuButton.style.borderWidth + radius: menuButton.style.radius + topLeftRadius: 0 + bottomLeftRadius: 0 + } + + indicator: T.Label { + width: menuButton.width + height: menuButton.height + color: menuButton.style.icon.idle + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: menuButton.style.baseIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: StudioTheme.Constants.upDownSquare2 + } + + states: [ + State { + name: "default" + when: menuButton.enabled && !root.hover && !menuButton.hover + && !menuButton.press && !menuButton.checked + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.background.idle + border.color: menuButton.style.border.idle + } + PropertyChanges { + target: menuButton + z: 3 + } + }, + State { + name: "globalHover" + when: root.hover && !menuButton.hover && !menuButton.press && menuButton.enabled + && !menuButton.checked + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.background.globalHover + border.color: menuButton.style.border.idle + } + }, + State { + name: "hover" + when: !menuButton.checked && menuButton.hover && !menuButton.press && menuButton.enabled + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.background.hover + border.color: menuButton.style.border.hover + } + PropertyChanges { + target: menuButton + z: 100 + } + }, + State { + name: "hoverCheck" + when: menuButton.checked && menuButton.hover && !menuButton.press && menuButton.enabled + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.interaction + border.color: menuButton.style.interaction + } + PropertyChanges { + target: menuButton + z: 100 + } + }, + State { + name: "press" + when: menuButton.hover && menuButton.press && menuButton.enabled + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.interaction + border.color: menuButton.style.interaction + } + PropertyChanges { + target: menuButton + z: 100 + } + }, + State { + name: "check" + when: menuButton.enabled && !menuButton.press && menuButton.checked + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.interaction + border.color: menuButton.style.border.idle + } + }, + State { + name: "disable" + when: !menuButton.enabled + PropertyChanges { + target: menuButtonBackground + color: menuButton.style.background.disabled + border.color: menuButton.style.border.disabled + } + } + ] + } + } + + Rectangle { + id: controlBorder + anchors.fill: parent + color: "transparent" + border.color: menuButton.checked ? StudioTheme.Values.themeInteraction + : root.hover ? menuButton.style.border.hover + : StudioTheme.Values.controlOutline_toolbarIdle + border.width: StudioTheme.Values.border + radius: menuButton.style.radius + } + + component MenuItemDelegate: T.ItemDelegate { + id: menuItemDelegate + + property StudioTheme.ControlStyle style: root.style + + property alias myIcon: iconLabel.text + property alias myText: textLabel.text + + width: root.menuWidth - 2 * window.padding + height: root.style.controlSize.height// - 2 * root.style.borderWidth + + contentItem: Row { + id: row + width: menuItemDelegate.width + height: menuItemDelegate.height + spacing: 0 + + T.Label { + id: iconLabel + width: menuItemDelegate.height + height: menuItemDelegate.height + color: { + if (menuItemDelegate.checked) + return primaryButton.style.icon.selected + + if (!menuItemDelegate.enabled) + return primaryButton.style.icon.disabled + + return primaryButton.style.icon.idle + } + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: primaryButton.style.baseIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: StudioTheme.Constants.playOutline_medium + } + + T.Label { + id: textLabel + width: menuItemDelegate.width - row.spacing - iconLabel.width + height: menuItemDelegate.height + color: { + if (!menuItemDelegate.enabled && menuItemDelegate.checked) + return primaryButton.style.text.idle + + if (menuItemDelegate.checked) + return primaryButton.style.text.selectedText + + if (!menuItemDelegate.enabled) + return primaryButton.style.text.disabled + + return primaryButton.style.text.idle + } + elide: Text.ElideRight + font.pixelSize: primaryButton.style.baseFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + text: listModel.get(root.runTarget).name + } + } + + background: Rectangle { + id: menuItemDelegateBackground + x: 0 + y: 0 + width: menuItemDelegate.width + height: menuItemDelegate.height + opacity: enabled ? 1 : 0.3 + + color: { + if (!menuItemDelegate.enabled && menuItemDelegate.checked) + return root.style.interaction + + if (!menuItemDelegate.enabled) + return "transparent" + + if (menuItemDelegate.hovered && menuItemDelegate.checked) + return root.style.interactionHover + + if (menuItemDelegate.checked) + return root.style.interaction + + if (menuItemDelegate.hovered) + return root.style.background.hover + + return "transparent" + } + } + + onClicked: window.close() + } + + DelegateModel { + id: visualModel + model: RunManagerModel { id: runManagerModel } + delegate: MenuItemDelegate { + required property string targetName + required property string targetId + required property bool targetEnabled + required property int index + + myIcon: "" + myText: targetName + checked: root.runTarget === index + enabled: targetEnabled + + onClicked: root.runTargetSelected(targetId) + } + } + + Window { + id: window + + readonly property int padding: 1 + + width: root.menuWidth + height: column.implicitHeight + visible: false + flags: Qt.FramelessWindowHint | Qt.Tool | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint + modality: Qt.NonModal + transientParent: null + color: "transparent" + + onActiveFocusItemChanged: { + if (window.activeFocusItem === null && !root.hover) + window.close() + } + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePopupBackground + + Column { + id: column + padding: window.padding + anchors.fill: parent + spacing: 0 + + Repeater { model: visualModel } + + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + width: column.width - 10 + height: StudioTheme.Values.border + color: "#636363" // shadowGrey + } + + MenuItemDelegate { + myText: qsTr("Manage run targets") + myIcon: StudioTheme.Constants.settings_medium + + onClicked: root.openRunTargets() + } + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 41173237254..4ca9b40c99d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -734,12 +734,20 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/devicesharing DEPENDS - Qt::WebSockets + QtCreator::QrCodeGenerator Qt::WebSockets SOURCES device.cpp device.h deviceinfo.cpp deviceinfo.h devicemanager.cpp devicemanager.h devicemanagermodel.cpp devicemanagermodel.h + devicemanagerwidget.cpp devicemanagerwidget.h +) + +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/runmanager + SOURCES + runmanager.cpp + runmanager.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 0a02dd08d87..20df0a3ffbe 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -3,12 +3,16 @@ #include "devicemanager.h" +#include "devicemanagerwidget.h" + #include #include #include #include #include +#include + namespace QmlDesigner::DeviceShare { DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath) @@ -23,6 +27,8 @@ DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath) initUdpDiscovery(); } +DeviceManager::~DeviceManager() = default; + void DeviceManager::initUdpDiscovery() { const QList interfaces = QNetworkInterface::allInterfaces(); @@ -392,4 +398,12 @@ bool DeviceManager::stopRunningProject(const QString &deviceId) return device->sendProjectStopped(); } +DeviceManagerWidget *DeviceManager::widget() +{ + if (!m_widget) + m_widget = new DeviceManagerWidget(*this, Core::ICore::instance()->dialogParent()); + + return m_widget.get(); +} + } // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 42902267aeb..8bb0b9de406 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -10,11 +11,14 @@ namespace QmlDesigner::DeviceShare { +class DeviceManagerWidget; + class DeviceManager : public QObject { Q_OBJECT public: explicit DeviceManager(QObject *parent = nullptr, const QString &settingsPath = "settings.json"); + ~DeviceManager(); // Getters QList> devices() const; @@ -35,6 +39,8 @@ public: bool sendProjectFile(const QString &deviceId, const QString &projectFile); bool stopRunningProject(const QString &deviceId); + DeviceManagerWidget *widget(); + private: // Devices management QList> m_devices; @@ -44,6 +50,8 @@ private: const QString m_settingsPath; QString m_uuid; + QPointer m_widget; + private: // internal slots void initUdpDiscovery(); diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.cpp new file mode 100644 index 00000000000..cb932d85fc2 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2024 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 "devicemanagerwidget.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace QmlDesigner::DeviceShare { + +static 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(); +} + +DeviceManagerWidget::DeviceManagerWidget(DeviceManager &deviceManager, QWidget *parent) + : StudioQuickWidget(parent) + , m_deviceManagerModel(deviceManager) +{ + engine()->addImageProvider(QLatin1String("QrGenerator"), new QrCodeImageProvider); + engine()->addImportPath(qmlSourcesPath()); + engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + engine()->addImportPath(qmlSourcesPath() + "/imports"); + + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F10), this); + connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &DeviceManagerWidget::reloadQmlSource); + + quickWidget()->setObjectName(Constants::OBJECT_NAME_DEVICE_MANAGER); + setResizeMode(QQuickWidget::SizeRootObjectToView); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto map = registerPropertyMap("DeviceManagerBackend"); + map->setProperties({{"deviceManagerModel", QVariant::fromValue(&m_deviceManagerModel)}}); + + Theme::setupTheme(engine()); + + setWindowFlags(Qt::Dialog); + setWindowTitle(tr("Device Manager", "Title of device manager widget")); + setMinimumSize(QSize(195, 195)); + resize(1000, 600); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +DeviceManagerWidget::~DeviceManagerWidget() = default; + +QString DeviceManagerWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/devicemanager"; +#endif + return Core::ICore::resourcePath("qmldesigner/devicemanager").toString(); +} + +void DeviceManagerWidget::showEvent(QShowEvent *event) +{ + StudioQuickWidget::showEvent(event); + update(); +} + +void DeviceManagerWidget::focusOutEvent(QFocusEvent *focusEvent) +{ + QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_DEVICEMANAGER_TIME, + m_usageTimer.elapsed()); + StudioQuickWidget::focusOutEvent(focusEvent); +} + +void DeviceManagerWidget::focusInEvent(QFocusEvent *focusEvent) +{ + m_usageTimer.restart(); + StudioQuickWidget::focusInEvent(focusEvent); +} + +void DeviceManagerWidget::reloadQmlSource() +{ + QString statesListQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml"); + QTC_ASSERT(QFileInfo::exists(statesListQmlFilePath), return ); + setSource(QUrl::fromLocalFile(statesListQmlFilePath)); + + if (!rootObject()) { + QString errorString; + for (const QQmlError &error : errors()) + errorString += "\n" + error.toString(); + + Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), + tr("StatesEditorWidget: %1 cannot be created.%2") + .arg(qmlSourcesPath(), errorString)); + return; + } +} + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.h b/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.h new file mode 100644 index 00000000000..386f8b33fc4 --- /dev/null +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanagerwidget.h @@ -0,0 +1,46 @@ +// Copyright (C) 2024 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 "devicemanager.h" +#include "devicemanagermodel.h" + +QT_BEGIN_NAMESPACE +class QShortcut; +QT_END_NAMESPACE + +namespace QmlDesigner::DeviceShare { + +class DeviceManagerWidget : public StudioQuickWidget +{ + Q_OBJECT + +public: + DeviceManagerWidget(DeviceManager &deviceManager, QWidget *parent = nullptr); + ~DeviceManagerWidget() override; + + static QString qmlSourcesPath(); + +protected: + void showEvent(QShowEvent *) override; + void focusOutEvent(QFocusEvent *focusEvent) override; + void focusInEvent(QFocusEvent *focusEvent) override; + +private: + void reloadQmlSource(); + +private: + QShortcut *m_qmlSourceUpdateShortcut; + QElapsedTimer m_usageTimer; + + DeviceManagerModel m_deviceManagerModel; +}; + +} // namespace QmlDesigner::DeviceShare diff --git a/src/plugins/qmldesigner/components/runmanager/runmanager.cpp b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp new file mode 100644 index 00000000000..768c493f330 --- /dev/null +++ b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp @@ -0,0 +1,320 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "runmanager.h" + +#include +#include +#include + +#include + +namespace QmlDesigner { + +Q_LOGGING_CATEGORY(runManagerLog, "qtc.designer.runManager", QtWarningMsg) + +namespace { +// helper type for the visitor +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; + +} // namespace + +RunManager::RunManager(DeviceShare::DeviceManager &deviceManager) + : QObject() + , m_deviceManager(deviceManager) +{ + // Connect DeviceManager with internal target generation + connect(&m_deviceManager, &DeviceShare::DeviceManager::deviceAdded, this, &RunManager::udpateTargets); + connect(&m_deviceManager, + &DeviceShare::DeviceManager::deviceRemoved, + this, + &RunManager::udpateTargets); + connect(&m_deviceManager, + &DeviceShare::DeviceManager::deviceActivated, + this, + &RunManager::udpateTargets); + connect(&m_deviceManager, + &DeviceShare::DeviceManager::deviceDeactivated, + this, + &RunManager::udpateTargets); + connect(&m_deviceManager, + &DeviceShare::DeviceManager::deviceAliasChanged, + this, + &RunManager::udpateTargets); + + // TODO If device going offline is currently running force stop + + // Connect Android/Device run/stop project signals + connect(&m_deviceManager, + &DeviceShare::DeviceManager::projectStarted, + this, + [this](const DeviceShare::DeviceInfo &info) { + qCDebug(runManagerLog) << "Project started." << info; + + m_runningTargets.append(info.deviceId()); + + m_state = TargetState::Running; + emit stateChanged(); + }); + connect(&m_deviceManager, + &DeviceShare::DeviceManager::projectStopped, + this, + [this](const DeviceShare::DeviceInfo &info) { + qCDebug(runManagerLog) << "Project stopped." << info; + + auto findRunningTarget = [&](const auto &runningTarget) { + return std::visit(overloaded{[](const QPointer) { + return false; + }, + [&](const QString &arg) { + return arg == info.deviceId(); + }}, + runningTarget); + }; + + const auto [first, last] = std::ranges::remove_if(m_runningTargets, findRunningTarget); + m_runningTargets.erase(first, last); + + if (!m_runningTargets.isEmpty()) + return; + + m_state = TargetState::NotRunning; + emit stateChanged(); + }); + + // Connect Creator run/stop project signals + auto projectExplorerPlugin = ProjectExplorer::ProjectExplorerPlugin::instance(); + connect(projectExplorerPlugin, + &ProjectExplorer::ProjectExplorerPlugin::runControlStarted, + this, + [this](ProjectExplorer::RunControl *runControl) { + qCDebug(runManagerLog) << "Run Control started."; + + m_runningTargets.append(QPointer(runControl)); + + m_state = TargetState::Running; + emit stateChanged(); + }); + connect(projectExplorerPlugin, + &ProjectExplorer::ProjectExplorerPlugin::runControlStoped, + this, + [this](ProjectExplorer::RunControl *runControl) { + qCDebug(runManagerLog) << "Run Control stoped."; + + auto findRunningTarget = [&](const auto &runningTarget) { + return std::visit(overloaded{[&](const QPointer arg) { + return arg.get() == runControl; + }, + [](const QString &) { return false; }}, + runningTarget); + }; + + const auto [first, last] = std::ranges::remove_if(m_runningTargets, findRunningTarget); + m_runningTargets.erase(first, last); + + if (!m_runningTargets.isEmpty()) + return; + + m_state = TargetState::NotRunning; + emit stateChanged(); + }); + + udpateTargets(); +} + +void RunManager::udpateTargets() +{ + m_targets.clear(); + + m_targets.append(NormalTarget()); + m_targets.append(LivePreviewTarget()); + + for (const auto &device : m_deviceManager.devices()) { + if (device->deviceSettings().active()) + m_targets.append(AndroidTarget(device->deviceInfo().deviceId())); + } + + emit targetsChanged(); + + bool currentTargetReset = false; + if (m_currentTargetId.isValid()) + currentTargetReset = selectRunTarget(m_currentTargetId); + + if (!currentTargetReset) // default run target + selectRunTarget(NormalTarget().id()); +} + +const QList RunManager::targets() const +{ + return m_targets; +} + +void RunManager::toggleCurrentTarget() +{ + if (!m_runningTargets.isEmpty()) { + for (auto runningTarget : m_runningTargets) { + std::visit(overloaded{[](const QPointer arg) { + if (!arg.isNull()) + arg.get()->initiateStop(); + }, + [](const QString arg) { + if (!arg.isEmpty()) + QmlDesignerPlugin::deviceManager().stopRunningProject(arg); + }}, + runningTarget); + } + return; + } + + auto target = runTarget(m_currentTargetId); + + if (!target) + return; + + bool enabled = std::visit([&](const auto &arg) { return arg.enabled(); }, *target); + + if (!enabled) { + qCDebug(runManagerLog) << "Can't start run target" << m_currentTargetId << "not enabled."; + return; + } + + std::visit([&](const auto &arg) { arg.run(); }, *target); + + m_state = TargetState::Starting; + emit stateChanged(); +} + +int RunManager::currentTargetIndex() const +{ + return runTargetIndex(m_currentTargetId); +} + +bool RunManager::selectRunTarget(Utils::Id id) +{ + if (m_currentTargetId == id) + return true; + + int index = runTargetIndex(id); + + if (index == -1) { + qCDebug(runManagerLog) << "Couldn't find run target" << id; + return false; + } + + m_currentTargetId = id; + emit runTargetChanged(); + + return true; +} + +bool RunManager::selectRunTarget(const QString &targetName) +{ + return selectRunTarget(Utils::Id::fromString(targetName)); +} + +std::optional RunManager::runTarget(Utils::Id targetId) const +{ + auto find_id = [&](const auto &target) { + return std::visit([&](const auto &arg) { return arg.id() == targetId; }, target); + }; + + auto result = std::ranges::find_if(m_targets, find_id); + + if (result != m_targets.end()) + return *result; + + qCDebug(runManagerLog) << "Couldn't find run target" << targetId; + + return {}; +} + +int RunManager::runTargetIndex(Utils::Id targetId) const +{ + auto find_id = [&](const auto &target) { + return std::visit([&](const auto &arg) { return arg.id() == targetId; }, target); + }; + + auto result = std::ranges::find_if(m_targets, find_id); + + if (result != m_targets.end()) + return std::distance(m_targets.begin(), result); + + return -1; +} + +QString NormalTarget::name() const +{ + return "Default"; +} + +Utils::Id NormalTarget::id() const +{ + return ProjectExplorer::Constants::NORMAL_RUN_MODE; +} + +bool NormalTarget::enabled() const +{ + return bool(ProjectExplorer::ProjectExplorerPlugin::canRunStartupProject(id())); +} + +void NormalTarget::run() const +{ + ProjectExplorer::ProjectExplorerPlugin::runStartupProject(id()); +} + +QString LivePreviewTarget::name() const +{ + return "Live Preview"; +} + +Utils::Id LivePreviewTarget::id() const +{ + return ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE; +} + +bool LivePreviewTarget::enabled() const +{ + return bool(ProjectExplorer::ProjectExplorerPlugin::canRunStartupProject(id())); +} + +void LivePreviewTarget::run() const +{ + ProjectExplorer::ProjectExplorerPlugin::runStartupProject(id()); +} + +AndroidTarget::AndroidTarget(const QString &deviceId) + : m_deviceId(deviceId) +{} + +QString AndroidTarget::name() const +{ + if (auto devcieSettings = QmlDesignerPlugin::deviceManager().deviceSettings(m_deviceId)) + return devcieSettings->alias(); + + return {}; +} + +Utils::Id AndroidTarget::id() const +{ + if (auto deviceInfo = QmlDesignerPlugin::deviceManager().deviceInfo(m_deviceId)) + return Utils::Id::fromString(deviceInfo->deviceId()); + + return {}; +} + +bool AndroidTarget::enabled() const +{ + return QmlDesignerPlugin::deviceManager().deviceIsConnected(m_deviceId).value_or(false); +} + +void AndroidTarget::run() const +{ + auto qmlrcPath = DesignViewer::ResourceGeneratorProxy().createResourceFileSync(); + QmlDesignerPlugin::deviceManager().sendProjectFile(m_deviceId, qmlrcPath); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/runmanager/runmanager.h b/src/plugins/qmldesigner/components/runmanager/runmanager.h new file mode 100644 index 00000000000..e658bab79c9 --- /dev/null +++ b/src/plugins/qmldesigner/components/runmanager/runmanager.h @@ -0,0 +1,94 @@ +// Copyright (C) 2024 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 + +namespace QmlDesigner { + +struct NormalTarget +{ + QString name() const; + Utils::Id id() const; + bool enabled() const; + + void run() const; +}; + +struct LivePreviewTarget +{ + QString name() const; + Utils::Id id() const; + bool enabled() const; + + void run() const; +}; + +struct AndroidTarget +{ + AndroidTarget(const QString &deviceId); + QString name() const; + Utils::Id id() const; + bool enabled() const; + + void run() const; + +private: + QString m_deviceId; +}; + +using Target = std::variant; + +using RunningTarget = std::variant, QString>; + +class RunManager : public QObject +{ + Q_OBJECT + +public: + explicit RunManager(DeviceShare::DeviceManager &deviceManager); + + enum TargetState { Starting, Running, NotRunning }; + Q_ENUM(TargetState) + + void udpateTargets(); + + const QList targets() const; + + void toggleCurrentTarget(); + + int currentTargetIndex() const; + + bool selectRunTarget(Utils::Id id); + bool selectRunTarget(const QString &targetName); + + TargetState state() const { return m_state; } + +private: + std::optional runTarget(Utils::Id targetId) const; + int runTargetIndex(Utils::Id targetId) const; + + DeviceShare::DeviceManager &m_deviceManager; + + QList m_targets; + Utils::Id m_currentTargetId; + //std::unique_ptr m_runningTarget; + + QList m_runningTargets; + + TargetState m_state = TargetState::NotRunning; + +signals: + void runTargetChanged(); + void stateChanged(); + void targetsChanged(); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 1460eb00e95..4407c6a22b1 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -14,9 +14,13 @@ #include #include #include +#include #include #include +#include +#include + #include #include #include @@ -205,6 +209,79 @@ QVariant WorkspaceModel::data(const QModelIndex &index, int role) const return QVariant(); } +RunManagerModel::RunManagerModel(QObject *) +{ + connect(&QmlDesignerPlugin::runManager(), &RunManager::targetsChanged, this, &RunManagerModel::reset); + + connect(ProjectExplorer::KitManager::instance(), + &ProjectExplorer::KitManager::kitsChanged, + this, + &RunManagerModel::reset); + connect(ProjectExplorer::KitManager::instance(), + &ProjectExplorer::KitManager::kitsLoaded, + this, + &RunManagerModel::reset); + + connect(&QmlDesignerPlugin::deviceManager(), + &DeviceShare::DeviceManager::deviceOnline, + this, + &RunManagerModel::reset); + connect(&QmlDesignerPlugin::deviceManager(), + &DeviceShare::DeviceManager::deviceOffline, + this, + &RunManagerModel::reset); + + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + this, + &RunManagerModel::reset); + + connect(ProjectExplorer::ProjectExplorerPlugin::instance(), + &ProjectExplorer::ProjectExplorerPlugin::runActionsUpdated, + this, + &RunManagerModel::reset); +} + +int RunManagerModel::rowCount(const QModelIndex &) const +{ + return QmlDesignerPlugin::runManager().targets().size(); +} + +QHash RunManagerModel::roleNames() const +{ + static QHash roleNames{{DisplayNameRole, "targetName"}, + {TargetNameRole, "targetId"}, + {Enabled, "targetEnabled"}}; + + return roleNames; +} + +QVariant RunManagerModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + auto target = QmlDesignerPlugin::runManager().targets()[index.row()]; + + if (role == DisplayNameRole) + return std::visit([](const auto &arg) { return arg.name(); }, target); + else if (role == TargetNameRole) + return std::visit([](const auto &arg) { return arg.id().toSetting(); }, target); + else if (role == Enabled) + return std::visit([](const auto &arg) { return arg.enabled(); }, target); + else + qWarning() << Q_FUNC_INFO << "invalid role"; + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +void RunManagerModel::reset() +{ + beginResetModel(); + endResetModel(); +} + ActionSubscriber::ActionSubscriber(QObject *parent) : QObject(parent) { @@ -402,6 +479,17 @@ ToolBarBackend::ToolBarBackend(QObject *parent) &ProjectExplorer::KitManager::kitsChanged, this, &ToolBarBackend::kitsChanged); + + // RunManager connections + + connect(&QmlDesignerPlugin::runManager(), + &RunManager::runTargetChanged, + this, + &ToolBarBackend::runTargetIndexChanged); + connect(&QmlDesignerPlugin::runManager(), + &RunManager::stateChanged, + this, + &ToolBarBackend::runManagerStateChanged); } void ToolBarBackend::registerDeclarativeType() @@ -410,10 +498,19 @@ void ToolBarBackend::registerDeclarativeType() qmlRegisterType("ToolBar", 1, 0, "ActionSubscriber"); qmlRegisterType("ToolBar", 1, 0, "CrumbleBarModel"); qmlRegisterType("ToolBar", 1, 0, "WorkspaceModel"); + qmlRegisterType("ToolBar", 1, 0, "RunManagerModel"); qmlRegisterType("OutputPane", 1, 0, "MessageModel"); qmlRegisterType("OutputPane", 1, 0, "AppOutputParentModel"); qmlRegisterType("OutputPane", 1, 0, "AppOutputChildModel"); + + qmlRegisterUncreatableType("ToolBar", + 1, + 0, + "RunManager", + "RunManager shouldn't be instantiated."); + qmlRegisterUncreatableType( + "ToolBar", 1, 0, "DeviceManagerModel", "DeviceManagerModel shouldn't be instantiated."); } void ToolBarBackend::triggerModeChange() @@ -594,6 +691,21 @@ void ToolBarBackend::setCurrentKit(int index) emit currentKitChanged(); } +void ToolBarBackend::openDeviceManager() +{ + QmlDesignerPlugin::deviceManager().widget()->show(); +} + +void ToolBarBackend::selectRunTarget(const QString &targetName) +{ + QmlDesignerPlugin::runManager().selectRunTarget(targetName); +} + +void ToolBarBackend::toggleRunning() +{ + QmlDesignerPlugin::runManager().toggleCurrentTarget(); +} + bool ToolBarBackend::canGoBack() const { QTC_ASSERT(designModeWidget(), return false); @@ -775,6 +887,16 @@ bool ToolBarBackend::isLiteModeEnabled() const return QmlDesignerBasePlugin::isLiteModeEnabled(); } +int ToolBarBackend::runTargetIndex() const +{ + return QmlDesignerPlugin::runManager().currentTargetIndex(); +} + +int ToolBarBackend::runManagerState() const +{ + return QmlDesignerPlugin::runManager().state(); +} + void ToolBarBackend::launchGlobalAnnotations() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 1ffc28aec9d..dc9a4c4de7a 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -40,6 +40,28 @@ public: QVariant data(const QModelIndex &index, int role = DisplayNameRole) const override; }; +class RunManagerModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum RunManagerRoles { + DisplayNameRole = Qt::DisplayRole, + TargetNameRole = Qt::UserRole, + Enabled + }; + Q_ENUM(RunManagerRoles) + + explicit RunManagerModel(QObject *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; + +private: + void reset(); +}; + class ActionSubscriber : public QObject { Q_OBJECT @@ -101,6 +123,9 @@ class ToolBarBackend : public QObject Q_PROPERTY(bool isLiteModeEnabled READ isLiteModeEnabled CONSTANT) + Q_PROPERTY(int runTargetIndex READ runTargetIndex NOTIFY runTargetIndexChanged) + Q_PROPERTY(int runManagerState READ runManagerState NOTIFY runManagerStateChanged) + public: ToolBarBackend(QObject *parent = nullptr); static void registerDeclarativeType(); @@ -120,6 +145,10 @@ public: Q_INVOKABLE void setCurrentStyle(int index); Q_INVOKABLE void setCurrentKit(int index); + Q_INVOKABLE void openDeviceManager(); + Q_INVOKABLE void selectRunTarget(const QString &targetName); + Q_INVOKABLE void toggleRunning(); + bool canGoBack() const; bool canGoForward() const; @@ -154,6 +183,9 @@ public: bool isLiteModeEnabled() const; + int runTargetIndex() const; + int runManagerState() const; + static void launchGlobalAnnotations(); signals: @@ -176,6 +208,9 @@ signals: void isSharingEnabledChanged(); void isDocumentDirtyChanged(); + void runTargetIndexChanged(); + void runManagerStateChanged(); + private: void setupWorkspaces(); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 8aef417b3bc..0a8e6243af6 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -128,6 +128,7 @@ inline constexpr char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser"; inline constexpr char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary"; inline constexpr char EVENT_INSIGHT_TIME[] = "insight"; inline constexpr char EVENT_MODELEDITOR_TIME[] = "modelEditor"; +inline constexpr char EVENT_DEVICEMANAGER_TIME[] = "deviceManager"; inline constexpr char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange"; inline constexpr char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings"; inline constexpr char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject"; @@ -175,6 +176,7 @@ inline constexpr char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog"; inline constexpr char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen"; inline constexpr char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage"; inline constexpr char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor"; +inline constexpr char OBJECT_NAME_DEVICE_MANAGER[] = "QQuickWidgetDeviceManager"; inline constexpr char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 23f4120f3f4..e6f3ab07279 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -30,14 +30,16 @@ #ifndef QDS_USE_PROJECTSTORAGE # include #endif +#include #include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #include @@ -174,6 +176,8 @@ public: ViewManager viewManager{projectManager.asynchronousImageCache(), externalDependencies}; DocumentManager documentManager{projectManager, externalDependencies}; ShortCutManager shortCutManager; + DeviceShare::DeviceManager deviceManager; + RunManager runManager{deviceManager}; SettingsPage settingsPage{externalDependencies}; DesignModeWidget mainWidget; QtQuickDesignerFactory m_qtQuickDesignerFactory; @@ -915,6 +919,16 @@ ViewManager &QmlDesignerPlugin::viewManager() return instance()->d->viewManager; } +DeviceShare::DeviceManager &QmlDesignerPlugin::deviceManager() +{ + return instance()->d->deviceManager; +} + +RunManager &QmlDesignerPlugin::runManager() +{ + return instance()->d->runManager; +} + DesignerActionManager &QmlDesignerPlugin::designerActionManager() { return d->viewManager.designerActionManager(); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index 33fecc7a574..b88665be024 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -24,9 +24,14 @@ namespace Core { namespace QmlDesigner { +namespace DeviceShare { + class DeviceManager; +} + class QmlDesignerPluginPrivate; class AsynchronousImageCache; class ExternalDependenciesInterface; +class RunManager; namespace Internal { class DesignModeWidget; } @@ -50,6 +55,8 @@ public: const DocumentManager &documentManager() const; static ViewManager &viewManager(); + static DeviceShare::DeviceManager &deviceManager(); + static RunManager &runManager(); DesignerActionManager &designerActionManager(); const DesignerActionManager &designerActionManager() const; From 730cca053508b12f80eeab6fe4563846eda6cc42 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 12 Nov 2024 10:11:54 +0200 Subject: [PATCH 113/322] EffectComposer: Implement basic AutoComplete for the code editor Uniforms are available for AutoComplete Task-number: QDS-14038 Change-Id: Ib89224aa2520fc4d791a8356c356c87208fbfa04 Reviewed-by: Shrief Gabr Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/CMakeLists.txt | 1 + .../effectcomposer/compositionnode.cpp | 1 + .../effectcomposer/effectcodeeditorwidget.cpp | 53 +++- .../effectcomposer/effectcodeeditorwidget.h | 10 + .../effectcomposeruniformsmodel.h | 28 +- .../effectcomposer/effectsautocomplete.cpp | 295 ++++++++++++++++++ .../effectcomposer/effectsautocomplete.h | 54 ++++ .../effectshaderscodeeditor.cpp | 18 ++ .../effectcomposer/effectshaderscodeeditor.h | 2 + 9 files changed, 445 insertions(+), 17 deletions(-) create mode 100644 src/plugins/effectcomposer/effectsautocomplete.cpp create mode 100644 src/plugins/effectcomposer/effectsautocomplete.h diff --git a/src/plugins/effectcomposer/CMakeLists.txt b/src/plugins/effectcomposer/CMakeLists.txt index 4e3e7283b3b..7fa8726ad14 100644 --- a/src/plugins/effectcomposer/CMakeLists.txt +++ b/src/plugins/effectcomposer/CMakeLists.txt @@ -13,6 +13,7 @@ add_qtc_plugin(EffectComposer effectcomposermodel.cpp effectcomposermodel.h effectcomposernodesmodel.cpp effectcomposernodesmodel.h effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h + effectsautocomplete.cpp effectsautocomplete.h effectshaderscodeeditor.cpp effectshaderscodeeditor.h effectnode.cpp effectnode.h effectnodescategory.cpp effectnodescategory.h diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 86455fcc200..818b877a943 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -166,6 +166,7 @@ void CompositionNode::ensureShadersCodeEditor() return; m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr(name()); + m_shadersCodeEditor->setUniformsModel(&m_unifomrsModel); m_shadersCodeEditor->setFragmentValue(fragmentCode()); m_shadersCodeEditor->setVertexValue(vertexCode()); diff --git a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp index c6fcf7c952e..a5920b48607 100644 --- a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp +++ b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp @@ -3,6 +3,8 @@ #include "effectcodeeditorwidget.h" +#include "effectsautocomplete.h" + #include #include @@ -19,10 +21,13 @@ #include +#include #include +#include #include #include +#include namespace EffectComposer { @@ -55,6 +60,11 @@ EffectCodeEditorWidget::EffectCodeEditorWidget() connect(m_completionAction, &QAction::triggered, this, [this] { invokeAssist(TextEditor::Completion); }); + + setLineNumbersVisible(true); + setMarksVisible(false); + setCodeFoldingSupported(false); + setTabChangesFocus(true); } EffectCodeEditorWidget::~EffectCodeEditorWidget() @@ -76,6 +86,12 @@ void EffectCodeEditorWidget::setEditorTextWithIndentation(const QString &text) auto *doc = document(); doc->setPlainText(text); + QString errorStr; + textDocument()->save(&errorStr); + + if (!errorStr.isEmpty()) + qWarning() << __FUNCTION__ << errorStr; + // We don't need to indent an empty text but is also needed for safer text.length()-1 below if (text.isEmpty()) return; @@ -84,10 +100,39 @@ void EffectCodeEditorWidget::setEditorTextWithIndentation(const QString &text) modifier->indent(0, text.length()-1); } +std::unique_ptr EffectCodeEditorWidget::createAssistInterface( + TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const +{ + return std::make_unique( + textCursor(), + Utils::FilePath(), + assistReason, + qmlJsEditorDocument()->semanticInfo(), + getUniforms()); +} + +void EffectCodeEditorWidget::setUniformsCallback(const std::function &callback) +{ + m_getUniforms = callback; +} + +QStringList EffectCodeEditorWidget::getUniforms() const +{ + if (m_getUniforms) + return m_getUniforms(); + return {}; +} + EffectDocument::EffectDocument() : QmlJSEditor::QmlJSEditorDocument(EFFECTEDITOR_CONTEXT_ID) , m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this)) -{} +{ + auto *tmpFile = new Utils::TemporaryFile("EffectDoc.js"); + tmpFile->setParent(this); + tmpFile->open(); + tmpFile->close(); + setFilePath(Utils::FilePath::fromString(tmpFile->fileName())); +} EffectDocument::~EffectDocument() { @@ -120,14 +165,16 @@ EffectCodeEditorFactory::EffectCodeEditorFactory() setDocumentCreator([]() { return new EffectDocument; }); setEditorWidgetCreator([]() { return new EffectCodeEditorWidget; }); - setEditorCreator([]() { return new QmlJSEditor::QmlJSEditor; }); + setEditorCreator([]() { + return new QmlJSEditor::QmlJSEditor; + }); setAutoCompleterCreator([]() { return new QmlJSEditor::AutoCompleter; }); setCommentDefinition(Utils::CommentDefinition::CppStyle); setParenthesesMatchingEnabled(true); setCodeFoldingSupported(true); addHoverHandler(new QmlJSEditor::QmlJSHoverHandler); - setCompletionAssistProvider(new QmlJSEditor::QmlJSCompletionAssistProvider); + setCompletionAssistProvider(new EffectsCompeletionAssistProvider); } void EffectCodeEditorFactory::decorateEditor(TextEditor::TextEditorWidget *editor) diff --git a/src/plugins/effectcomposer/effectcodeeditorwidget.h b/src/plugins/effectcomposer/effectcodeeditorwidget.h index 1ea043dc68e..28c76ba2f9e 100644 --- a/src/plugins/effectcomposer/effectcodeeditorwidget.h +++ b/src/plugins/effectcomposer/effectcodeeditorwidget.h @@ -29,6 +29,11 @@ public: void unregisterAutoCompletion(); void setEditorTextWithIndentation(const QString &text); + std::unique_ptr createAssistInterface( + TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const override; + + void setUniformsCallback(const std::function &callback); + signals: void returnKeyClicked(); @@ -36,6 +41,11 @@ public: Core::IContext *m_context = nullptr; QAction *m_completionAction = nullptr; bool m_isMultiline = true; + +private: + QStringList getUniforms() const; + + std::function m_getUniforms; }; class EffectDocument : public QmlJSEditor::QmlJSEditorDocument diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index fc82194fdf3..00d41a9204d 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -14,6 +14,20 @@ class EffectComposerUniformsModel : public QAbstractListModel Q_OBJECT public: + enum Roles { + NameRole = Qt::UserRole + 1, + DisplayNameRole, + DescriptionRole, + ValueRole, + BackendValueRole, + DefaultValueRole, + MaxValueRole, + MinValueRole, + TypeRole, + ControlTypeRole, + UseCustomValueRole + }; + EffectComposerUniformsModel(QObject *parent = nullptr); QHash roleNames() const override; @@ -29,20 +43,6 @@ public: QList uniforms() const; private: - enum Roles { - NameRole = Qt::UserRole + 1, - DisplayNameRole, - DescriptionRole, - ValueRole, - BackendValueRole, - DefaultValueRole, - MaxValueRole, - MinValueRole, - TypeRole, - ControlTypeRole, - UseCustomValueRole - }; - QList m_uniforms; }; diff --git a/src/plugins/effectcomposer/effectsautocomplete.cpp b/src/plugins/effectcomposer/effectsautocomplete.cpp new file mode 100644 index 00000000000..00a2b8261d0 --- /dev/null +++ b/src/plugins/effectcomposer/effectsautocomplete.cpp @@ -0,0 +1,295 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectsautocomplete.h" + +#include +#include + +#include + +#include + +namespace { + +enum CompletionOrder { + EnumValueOrder = -5, + SnippetOrder = -15, + PropertyOrder = -10, + SymbolOrder = -20, + KeywordOrder = -25, + TypeOrder = -30 +}; + +/*! + Returns a number defining how well \a searchStr matches \a str. + + Quite simplistic, looks only at the first match, and prefers contiguous + matches, or matches to capitalized or separated words. + Match to the last character is also preferred. +*/ +int matchStrength(const QString &searchStr, const QString &str) +{ + QString::const_iterator i = searchStr.constBegin(), iEnd = searchStr.constEnd(), + j = str.constBegin(), jEnd = str.constEnd(); + bool lastWasNotUpper = true, lastWasSpacer = true, lastWasMatch = false, didJump = false; + int res = 0; + while (i != iEnd && j != jEnd) { + bool thisIsUpper = (*j).isUpper(); + bool thisIsLetterOrNumber = (*j).isLetterOrNumber(); + if ((*i).toLower() == (*j).toLower()) { + if (lastWasMatch || (lastWasNotUpper && thisIsUpper) || (thisIsUpper && (*i).isUpper()) + || (lastWasSpacer && thisIsLetterOrNumber)) + ++res; + lastWasMatch = true; + ++i; + } else { + didJump = true; + lastWasMatch = false; + } + ++j; + lastWasNotUpper = !thisIsUpper; + lastWasSpacer = !thisIsLetterOrNumber; + } + if (i != iEnd) + return i - iEnd; + if (j == jEnd) + ++res; + if (!didJump) + res += 2; + return res; +} + +bool isIdentifierChar(const QChar &c, bool atStart, bool acceptDollar) +{ + switch (c.unicode()) { + case '_': + return true; + case '$': + if (acceptDollar) + return true; + return false; + + default: + if (atStart) + return c.isLetter(); + else + return c.isLetterOrNumber(); + } +} + +class CompleteFunctionCall +{ +public: + CompleteFunctionCall(bool hasArguments = true) + : hasArguments(hasArguments) + {} + bool hasArguments; +}; + +class QmlJSLessThan +{ + using AssistProposalItemInterface = TextEditor::AssistProposalItemInterface; + +public: + QmlJSLessThan(const QString &searchString) + : m_searchString(searchString) + {} + bool operator()(const AssistProposalItemInterface *a, const AssistProposalItemInterface *b) + { + if (a->order() != b->order()) + return a->order() > b->order(); + else if (a->text().isEmpty() && !b->text().isEmpty()) + return true; + else if (b->text().isEmpty()) + return false; + else if (a->text().at(0).isUpper() && b->text().at(0).isLower()) + return false; + else if (a->text().at(0).isLower() && b->text().at(0).isUpper()) + return true; + int m1 = ::matchStrength(m_searchString, a->text()); + int m2 = ::matchStrength(m_searchString, b->text()); + if (m1 != m2) + return m1 > m2; + return a->text() < b->text(); + } + +private: + QString m_searchString; +}; + +} // namespace + +namespace EffectComposer { + +class EffectsCodeAssistProposalItem final : public TextEditor::AssistProposalItem +{ + using TextEditorSettings = TextEditor::TextEditorSettings; + +public: + bool prematurelyApplies(const QChar &c) const final + { + if (data().canConvert()) // snippet + return false; + + return (text().endsWith(QLatin1String(": ")) && c == QLatin1Char(':')) + || (text().endsWith(QLatin1Char('.')) && c == QLatin1Char('.')); + } + + void applyContextualContent( + TextEditor::TextDocumentManipulatorInterface &manipulator, int basePosition) const final + { + const int currentPosition = manipulator.currentPosition(); + manipulator.replace(basePosition, currentPosition - basePosition, QString()); + + QString content = text(); + int cursorOffset = 0; + + const bool autoInsertBrackets + = TextEditorSettings::completionSettings().m_autoInsertBrackets; + + if (autoInsertBrackets && data().canConvert()) { + CompleteFunctionCall function = data().value(); + content += QLatin1String("()"); + if (function.hasArguments) + cursorOffset = -1; + } + + QString replaceable = content; + int replacedLength = 0; + for (int i = 0; i < replaceable.length(); ++i) { + const QChar a = replaceable.at(i); + const QChar b = manipulator.characterAt(manipulator.currentPosition() + i); + if (a == b) + ++replacedLength; + else + break; + } + const int length = manipulator.currentPosition() - basePosition + replacedLength; + manipulator.replace(basePosition, length, content); + if (cursorOffset) { + manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset); + manipulator.setAutoCompleteSkipPosition(manipulator.currentPosition()); + } + } +}; + +class EffectsAssistProposalModel : public TextEditor::GenericProposalModel +{ + using AssistProposalItemInterface = TextEditor::AssistProposalItemInterface; + using AssistReason = TextEditor::AssistReason; + using enum TextEditor::AssistReason; + +public: + EffectsAssistProposalModel(const QList &items) + { + loadContent(items); + } + + void filter(const QString &prefix) override; + void sort(const QString &prefix) override; + bool keepPerfectMatch(TextEditor::AssistReason reason) const override; +}; + +void EffectsAssistProposalModel::filter(const QString &prefix) +{ + GenericProposalModel::filter(prefix); + if (prefix.startsWith(QLatin1String("__"))) + return; + QList newCurrentItems; + newCurrentItems.reserve(m_currentItems.size()); + for (AssistProposalItemInterface *item : std::as_const(m_currentItems)) { + if (!item->text().startsWith(QLatin1String("__"))) + newCurrentItems << item; + } + m_currentItems = newCurrentItems; +} + +void EffectsAssistProposalModel::sort(const QString &prefix) +{ + std::sort(m_currentItems.begin(), m_currentItems.end(), QmlJSLessThan(prefix)); +} + +bool EffectsAssistProposalModel::keepPerfectMatch(AssistReason reason) const +{ + return reason == ExplicitlyInvoked; +} + +void addCompletion( + QList *completions, + const QString &text, + const QIcon &icon, + int order, + const QVariant &data = QVariant()) +{ + if (text.isEmpty()) + return; + + TextEditor::AssistProposalItem *item = new EffectsCodeAssistProposalItem; + item->setText(text); + item->setIcon(icon); + item->setOrder(order); + item->setData(data); + completions->append(item); +} + +void addCompletions( + QList *completions, + const QStringList &newCompletions, + const QIcon &icon, + int order) +{ + for (const QString &text : newCompletions) + addCompletion(completions, text, icon, order); +} + +EffectsCompletionAssistProcessor::EffectsCompletionAssistProcessor() + : m_startPosition(0) +{} + +TextEditor::IAssistProposal *EffectsCompletionAssistProcessor::performAsync() +{ + using QmlJSEditor::QmlJSCompletionAssistInterface; + + auto completionInterface = static_cast(interface()); + QTC_ASSERT(completionInterface, return {}); + + m_startPosition = completionInterface->position(); + QTextDocument *textDocument = completionInterface->textDocument(); + + while (isIdentifierChar(textDocument->characterAt(m_startPosition - 1), false, false)) + --m_startPosition; + + m_completions.clear(); + + // The completionOperator is the character under the cursor or directly before the + // identifier under cursor. Use in conjunction with onIdentifier. Examples: + // a + b -> ' ' + // a + -> '+' + // a +b -> '+' + QChar completionOperator; + if (m_startPosition > 0) + completionOperator = textDocument->characterAt(m_startPosition - 1); + + if (completionOperator != QLatin1Char('.')) { + addCompletions( + &m_completions, + completionInterface->uniformNames(), + QmlJSCompletionAssistInterface::keywordIcon(), + KeywordOrder); + } + + if (!m_completions.isEmpty()) { + TextEditor::GenericProposalModelPtr model(new EffectsAssistProposalModel(m_completions)); + return new TextEditor::GenericProposal(m_startPosition, model); + } + return nullptr; +} + +TextEditor::IAssistProcessor *EffectsCompeletionAssistProvider::createProcessor( + const TextEditor::AssistInterface *) const +{ + return new EffectsCompletionAssistProcessor; +} + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectsautocomplete.h b/src/plugins/effectcomposer/effectsautocomplete.h new file mode 100644 index 00000000000..40ea8b06b91 --- /dev/null +++ b/src/plugins/effectcomposer/effectsautocomplete.h @@ -0,0 +1,54 @@ +// Copyright (C) 2024 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 TextEditor { +class AssistProposalItemInterface; +class IAssistProposal; +} // namespace TextEditor + +namespace EffectComposer { + +class EffectsCompletionAssistInterface : public QmlJSEditor::QmlJSCompletionAssistInterface +{ +public: + EffectsCompletionAssistInterface( + const QTextCursor &cursor, + const Utils::FilePath &fileName, + TextEditor::AssistReason reason, + const QmlJSTools::SemanticInfo &info, + const QStringList &uniforms) + : QmlJSEditor::QmlJSCompletionAssistInterface(cursor, fileName, reason, info) + , m_uniformNames(uniforms) + {} + + QStringList uniformNames() const { return m_uniformNames; } + +private: + QStringList m_uniformNames; +}; + +class EffectsCompletionAssistProcessor : public TextEditor::AsyncProcessor +{ +public: + EffectsCompletionAssistProcessor(); + ~EffectsCompletionAssistProcessor() override = default; + + TextEditor::IAssistProposal *performAsync() override; + +private: + int m_startPosition; + QList m_completions; +}; + +class EffectsCompeletionAssistProvider : public QmlJSEditor::QmlJSCompletionAssistProvider +{ + TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; +}; + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 3ead0be4a7c..33eea2bef8b 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -3,6 +3,7 @@ #include "effectshaderscodeeditor.h" #include "effectcodeeditorwidget.h" +#include "effectcomposeruniformsmodel.h" #include #include @@ -138,6 +139,23 @@ bool EffectShadersCodeEditor::isOpened() const return m_opened; } +void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsModel *uniforms) +{ + std::function uniformNames = [uniforms]() -> QStringList { + if (!uniforms) + return {}; + int count = uniforms->rowCount(); + QStringList result; + for (int i = 0; i < count; ++i) { + const QModelIndex mIndex = uniforms->index(i, 0); + result.append(mIndex.data(EffectComposerUniformsModel::NameRole).toString()); + } + return result; + }; + m_fragmentEditor->setUniformsCallback(uniformNames); + m_vertexEditor->setUniformsCallback(uniformNames); +} + EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() { static EffectCodeEditorFactory f; diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index a20f5e71fc2..02f19aabe46 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -11,6 +11,7 @@ QT_FORWARD_DECLARE_CLASS(QToolBar) namespace EffectComposer { class EffectCodeEditorWidget; +class EffectComposerUniformsModel; class EffectShadersCodeEditor : public QWidget { @@ -34,6 +35,7 @@ public: void setLiveUpdate(bool liveUpdate); bool isOpened() const; + void setUniformsModel(EffectComposerUniformsModel *uniforms); signals: void liveUpdateChanged(bool); From 0a1fac9f11f28fec582a34b78b089ba7dcc27b75 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Tue, 12 Nov 2024 18:07:30 +0200 Subject: [PATCH 114/322] Doc: Update Scene Environment docs Fixes: QDS-13940 Change-Id: If24f8929a379b9ff43ac7d89ce8c81e1f72d93f0 Reviewed-by: Mahmoud Badri --- .../qtdesignstudio-3d-scene-environment.qdoc | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc index 5d18f39a836..32eb650ed42 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc @@ -16,20 +16,31 @@ you select when \l {Creating Projects}{creating your project}. \note \uicontrol {Extended Scene Environment} is available in projects created with - Qt 6.5 or higher as the \uicontrol {Target Qt Version}. + Qt 6.5 or higher as \uicontrol {Target Qt Version}. - \section1 Adding Scene Environments to Projects + \section1 Creating a New Project with a Scene Environment - Add the scene environment components to projects by selecting a suitable preset when - \l{Creating Projects}{creating your project}. + To include a scene environment component in your project, select a suitable preset when + \l{Creating Projects}{creating your project}: + \list + \li Choose the \uicontrol {3D} preset to create a project with a \uicontrol View3D + component that includes a \uicontrol {Scene Environment}. + \li Choose the \uicontrol {3D Extended} preset to create a project with an + \uicontrol {Extended View3D} component that includes an + \uicontrol {Extended Scene Environment}. + \endlist - Use the \uicontrol {3D} preset to create a project with a \uicontrol View3D - component that includes a scene environment. If you need to add it manually, it is - available in \uicontrol Components > \uicontrol {Qt Quick 3D}. + \section1 Adding a Scene Environment to an Existing Project - Use the \uicontrol {Extended 3D} preset to create a project with a 3D view - component that includes an extended scene environment. It is also - available in \uicontrol Components > \uicontrol {Qt Quick 3D Helpers}. + To add a 3D view component, which includes a scene environment, drag it from + \uicontrol Components > \uicontrol {Qt Quick 3D} > \uicontrol Items to + \uicontrol Navigator or the \uicontrol {2D} view. + + To add a scene environment component separately to your project, drag it + from \uicontrol Components to a 3D view component in \uicontrol Navigator. + The \uicontrol {Scene Environment} component is available in \uicontrol {Qt Quick 3D} > + \uicontrol Components and the \uicontrol {Extended Scene Environment} in + \uicontrol {Qt Quick 3D Helpers}. \section1 Setting the Scene Environment From 8a74617b6106427aa1e60576db9200ddd7f08a66 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Nov 2024 11:46:02 +0100 Subject: [PATCH 115/322] QmlDesigner: Fix compile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit deduction guide Change-Id: Ice9cf30047b6fbd7da230682c8e8d26c01387dfc Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/components/runmanager/runmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/runmanager/runmanager.cpp b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp index 768c493f330..a6cbba04464 100644 --- a/src/plugins/qmldesigner/components/runmanager/runmanager.cpp +++ b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp @@ -21,6 +21,9 @@ struct overloaded : Ts... using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; + } // namespace RunManager::RunManager(DeviceShare::DeviceManager &deviceManager) From edf18292d09c38c5691d64f930200a18608e9def Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 7 Nov 2024 11:47:59 +0200 Subject: [PATCH 116/322] Doc: Update the custom effects and materials docs - Edited the old custom effects and materials doc to make it Qt 5 specific. - Tried to explain the use of custom shader utilities in Qt 5 vs. Qt 6 custom material/effects system - Sidenote about the built-in-effects for Extended Scene Environment Fixes: QDS-10498 Change-Id: I2d242f4311350fe15f06a4079ba26be1f78f153c Reviewed-by: Mats Honkamaa Reviewed-by: Miikka Heikkinen --- .../config/style/qt5-sidebar.html | 6 +- ...dio-qtquick-3d-custom-effect-navigator.png | Bin 7845 -> 0 bytes ...io-qtquick-3d-custom-effect-navigator.webp | Bin 0 -> 3630 bytes .../studio-qtquick-3d-shader-properties.png | Bin 4849 -> 0 bytes .../studio-qtquick-3d-shader-properties.webp | Bin 0 -> 10654 bytes ...tudio-qtquick-3d-shader-utilities-qt5.webp | Bin 0 -> 4406 bytes .../components/qtquick-preset-components.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 2 +- ...ignstudio-3d-custom-effects-materials.qdoc | 251 ++++++++---------- .../qtdesignstudio-3d-custom-shaders.qdoc | 156 ++++++++--- .../qtdesignstudio-3d-effects.qdoc | 2 +- .../qtdesignstudio-3d-materials-shaders.qdoc | 2 +- .../qtdesignstudio-3d-materials.qdoc | 2 +- .../qtdesignstudio-3d-scene-environment.qdoc | 4 +- .../effects/qtdesignstudio-effects.qdoc | 13 +- 15 files changed, 243 insertions(+), 197 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png create mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.webp delete mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-shader-properties.png create mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-shader-properties.webp create mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-shader-utilities-qt5.webp diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index 872828b8f0d..93e32708931 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -137,7 +137,7 @@
  • Overview
  • 2D Effects
  • 3D Effects
  • -
  • Custom Effects and Materials
  • +
  • Custom Effects in Qt 5
  • Design Effects
  • Effect Composer
  • Particles
  • @@ -184,10 +184,10 @@ @@ -370,7 +370,7 @@
    • Overview
    • Cameras
    • -
    • Custom Effects and Materials
    • +
    • Custom Effects and Materials in Qt 5
    • Custom Shaders
    • Effects
    • Group
    • diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png b/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png deleted file mode 100644 index 9f3fd2f272b9dddff14e529e1e9a2c0d69cb7645..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7845 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 diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-shader-properties.webp b/doc/qtdesignstudio/images/studio-qtquick-3d-shader-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..acba3cb61ae37bdc957a8b5af8acc955fce64d26 GIT binary patch literal 10654 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 uu_*;UBdr^RCAL literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-shader-utilities-qt5.webp b/doc/qtdesignstudio/images/studio-qtquick-3d-shader-utilities-qt5.webp new file mode 100644 index 0000000000000000000000000000000000000000..6ed30a050760cbcc6f798a2bf312441c7fc24cbf GIT binary patch literal 4406 zcmWIYbaT@aWMBw)bqWXzu<%h5WMI&*I;g^6dUNa6+>KA3P5M{-i)XH}l-z1lo20aB z8?Gv+9eemx(J;9Gc#%1ta^a~n3S7Rz zn)9RsmT*ffZ@N?AqZllyHt7lL!&}=-Pg+mdV6FMrE-9(pJGcIqar&hjo;Pl|9{=XG zV_R&`p#wge-CQSUH5XC_-E6{7Z&?m>5iVw?6uf(i}nR$OW~N;xeGm?t-NwT z_CcWA>m#|$QT|Mk<&R6&E7>wL?R>~7=l)E0!{+S!ZtwnHcRm~Qc*`d>gDw4%^0OC& zro81i68e_&d+_G;9^F^#wJvM3YBU>kE{aSq@w<`}Ow7k6fi}M+TQ%huJlE{^c`C%yQp5 zca>w`Wrj9p;X7Z#7b_`De!Anm_qos&6?eYxHhny0Vw67jmNLeUP>GIh8Pn{ld#CUB zG@7>Yxj{-X}v z`Se~s(Kr9>k;csvP2Y&T-+a4%%ki?%J$HUgy_;4)J9gdYC6W%o8TNkXo*Z0iXO~m9 zIlSnfT!O`38|hVQSN>SOS8M++y4UEJU+Z>{SJ5+<21>nf+1dD0>$hvq?1x@*oeS&c zIF!t73o?8z-0Bnn_A%GGDf{F5wY^HyZsuO4VNtLJe$)Lu0_EpRK%{5PaoBwuWxHkW{ z=X+ka<(@8|_UU#tgV+A|IW?K_6P}-Eb+~fu`Rad7=656$GUn#qwO?tjFF5~R?DLCz zO2Ut2FJU-)>D14kwHMp{GDCKiu6F8I?Ou86S!ldln9*~t3wjI-xvtiVZ?W4e-FA5H z;)myqzD;Lb@#90@QXks~8T(3h%$~YL{q?u2+qIHpKfd%|z293|;@+`8Q8&K*OXavf zf1zJ8_qMdXwpS8Xa9LP0NSX>h(fB5OLut*GFMoZQe|5TV^OTB|ghgUwm>7lz&h&TMx35SXQr<(G& zzpmh3&&XnYajV;mHMgdH;tOCfIo-H_jpCn0j*DKq#D|}}o!wE|uyEbMcfVD$dbz)G z%d8Tc+NfxKF)t_Y_|;$PXYFiw9nL(`uR6@nwpHVk_#LAkSC+qzFcUoc=zOK?k%k{v za6_D0Z=1pDvV*O%|%i0pNmfgIQxouMFtBtMK+u4Nw9m=>TazD`e zz}h=gzeH&;v?X_MY%M&hl;AZ(C%!EVEnBk7kw7ALg+blw^2c3t5s(k@raoKV=BRC4%1>ET^pkM=Yz zmlimYw^p_`V^L)61TL9%$6nslW}g4(ht|8$ZEKu6)Gz0;E{`~Imp^Hm!||{m98Rg* zTYhYxtGjN=Chg}771)l%hucr?I&a66V6l@&`WzcW+d9kZ=4u~AFS~IbStftlz*g$q z>`T>YvYLk9EzU~`GB}?;BmFRKwd69lz9!F=^ZAWJzRbIJ)9;L}VvNfT0}u0nEwAR; zXSSGMyZL)d?99kJTdu3`+|^@rLGmU;(NFg36I0GSvD|$swBmO4XRTWHTFdj7IA)eE zZIL(<|LNYE>yp=RweQ_`_qfE}`5c$7tk_=26xy9v^x}rg@?4IB1B|!DW>`7t@&1jU zSUS~-ao_Fxr8SEmT|eLvoN%D{-R+|~uP0qltGIZ)OX9A^0+nwUEWNh%?%UESe#)nR zp#q!B(~y$N6D89hzDev?me%+>N%{MWzh)cP=pF6hSS-0!AaCsg!;LOc{M`Pm2QJ(z zIvaj|_WmA!F14lm>`oYNJhD~lVI;SZnPtt4Rdt2TwQH}Ou>GnVa7}#r*XBB%{1VUA zT>CYyD&3iNR&)JSrC_7U>>2@Y@++R*eEPgFB41YJ^#5r7&nh0LOaG_q?Tn1Pes#(2 ziDJo`^Xn2rPg<;apAb^txYb}xnf%UjpObK5P{qC@tH%GRs3XjjTDpSUgDq=xf?nuGak&y7b` zE|K3O(^gxYQ?V@O%sqaAGcMr_Gm~W|O#MBnUV*2~$zy`k&#zy@Z-b(+g z=&cW4Qp?MdXKveKy=cC2z}7=^~aQ!km8pPSvFS zuXxLs-M&A|kL#@6tjbk6O0Gv5Ui_Y=b8Sw_U5y7**@eolM}4^ex%?Z$l=_}^L8q!U zO|ABPdeRi_{h7ax`^(nA1tyauKIoWD=rqn^VV%5N|ID=Iv(Hvbzr1PczgYToe7(M4 z_}sXh?1gI|NAtI-*>o*@ZyvRz&N$|#^3l-a(QnsmD-@~OtJM$}uk-JnS={7_53QXHMjWx zSs?oV?P`q(nY@SJb-awWcivm`<`t{+=_`}+9~Y%tENl|ww`2iOvvR}EZz9#%%6#^vD&Z98g`%hwKl5n z*Zg1e*?Q_GWIXk-3q84{=GN|SuK%~0o|?Ae_MR}g6hD`eq@Wejh>me zc$sy0fot%-j>6A}r`{3bN}m>PEipG$>wPApf}+Sx!^L?EQn%@6ezw~8)cnh$*)m_= zuUKTQl(V*WX6Kvl7k;xbG|fD}j%(dk=ZmrRGv01J#TQ!kciQ56%Tg3i&u_YTG*N_s&($X4$J)0{GRD8 zP36g`$~^MmOO1m*^Mw6L8nKT1w{fRDTW!QDP$MJyN^6Fp%R$k?(Atwm!4ZoqEEtaH z^y@8kU|h?ja9REF{|PT08T`JlVi35}!Tlh#Bu+H-#E+C~rvn*2XuVl#!hN#j+05ya zmdLf9)K7MI@Nn5QAu8pcuKlCv`?mslIC=!S;;v6iJbOa8#kf%cy`?G-)8;^XIK^_ zW%ROdEI$&+xZI`U+vf9?>BlF3IiG#z+*>nSMH7Y`z5wnIH+vcSD!=9Gzxr3Q**Bl# zGE)fS7ez6b8nX{?R=>OY^@she?0I!H`#WrvmQ8c7Da(4bx!>2f_UcD}^W-b+5@G=o zr3_o1EqNCkJm1#zz`sLUTeH7U{&F^Z&pFK&@eZNbt;-&}e)qa7U()zD_f@Vah5ZK%&zqHU z|253*j`rFzyE~doVomX>M2iEff2`=7pj6krud?^L6j&zHO^{d3L6-^(Ax8yYpqw6PEd`PgIOIiZ2x0wfC(>-|7`gzg~ML zD=9o%SK%w$m2%UON&oevsdFE)2*%u=T71FhP8omMBn^2RHNPr7-w)?>=BBtR zTRYmEoUl&Yx3<;mW-sR+9Z46xWtXCN%q=Z%c?StI9jmyVD}V>71i=YPhCBD_T`d| z>jN~cZLgo(9J}Ic-_BDeYEJDZDqr@_v=(~&Z^igc zH7i%s^fVH@T4@?na&pt%H$2A+IW14}{Mfdp+3ZnFfP=?^$!lU{D^%C*UpsX@Q@_v= zrw?U1agmY4A%3~+_dc)l^#YpQ(*URSE z%uqk`=f?5+Zxb1l|9KYIW~XYLJ9a1D@qUzK*Ji_V(Tm%|+8*Vc*<12u?bL-9(#64X zP$g5sAxg}jUWF*}t^5j85}Tc>bMDyfDckopeKdQTyIzN9n`Zv+XWJ$6gYSJi6`!)l zx3c&5$~iOZ84sV?pOgM9evR4J_(eNcCOL1s+m*{`82%=pq+q&EaJTN}$(KFFHg?LM z(XE`*JXg;r=0w4zV$Ch-GMP!oPdKr?O}Ngj_}xEkLS0v~mA>8WLeJYe-g!b*$9C@N zopmADFRNDawDTF$xwF|PuPfq;*r2)O^PF5x+nqU|J98(_o>XwpxOnqq2^Wh^!PV0~ z$1QvMcjsG;?eDk59-SQQaYW{p+ZDyR4?EgfEj^xF@(Rp)wX$#B($!M!&AU@pek%Rs zWWD}{pH+_c;ciw#bCwov&z$_(i~7^MUcAlPy>QOkP0fXd;tdg=yeD2Rv39uxtHz)?|9C?)qZ*I&B}lr#x)m? ze@_T+YM-Fcq$&6yVdl5heNH8FV>rd0oGUwZ;nhvOlnoiKq2Iq;tx^(?JhPPV`O1V+v2HZGVY4I2?ct%>$B%$7(HS=9xq+Fcb*D1&T{Nm?1e;9dx IH`p-%0DDQ8sQ>@~ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 267517cb6c7..10e33f973c2 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -56,7 +56,7 @@ \li \l {3D Materials} \li \l {3D Effects} \li \l {Custom Shaders} - \li \l {Custom Effects and Materials} + \li \l {Custom Effects and Materials in Qt 5} \li \l {Lights} \li \l {Cameras} \li \l {Scene Environments} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 3146ce633ec..26145cb013b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -77,7 +77,7 @@ \li \l{3D Materials} \li \l{3D Effects} \li \l{Custom Shaders} - \li \l{Custom Effects and Materials} + \li \l{Custom Effects and Materials in Qt 5} \li \l{Lights} \li \l{Cameras} \li \l{Scene Environments} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-effects-materials.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-effects-materials.qdoc index 806308869d2..0b18db0df08 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-effects-materials.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-effects-materials.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,192 +6,157 @@ \previouspage studio-3d-custom-shaders.html \nextpage studio-3d-lights.html - \title Custom Effects and Materials + \title Custom Effects and Materials in Qt 5 - The \l{3D Effects}{Qt Quick 3D Effects} and \l{3D Materials} - {Qt Quick 3D Materials} modules contain a set of ready-made effects and - materials that you can apply to 3D models. If the ready-made effects and - materials don't meet your needs, you can create custom effects and - materials. Each effect or material must have a fragment shader that - implements all the functions needed to calculate the shaded color. The - material system also offers ready-made functions to help you implement - the material. If the 3D effects and materials are not displayed in - \l Components, you should add the \uicontrol QtQuick3D.Effects and - \uicontrol QtQuick3D.Materials modules to your project, as described in - \l {Adding and Removing Modules}. + Use custom shader utilities to create custom effects in previous \QDS + versions built with \uicontrol {Qt 5} and \uicontrol {Qt 5.15}. - The material system supports dielectric, metallic, and transparent - materials, point lights, area lights, ambient occlusion, shadowing, - two-sided polygons, index-of-refraction, and fragment cutoff (masking). - For more information, see \l {Qt Quick 3D Custom Material Reference}. + \note To learn how to use and create effects in \QDS projects built + with \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, see \l Effects. - Use the components in \uicontrol Components > - \uicontrol QtQuick3D > \uicontrol {Qt Quick 3D Custom Shader Utils} to - create custom effects and materials. You can find the \uicontrol Effect - component in \uicontrol Components > \uicontrol {QtQuick3D Effects} > \uicontrol - {Qt Quick 3D Custom Shader Utils}, and the \uicontrol {Custom Material} - component in \uicontrol Components> \uicontrol {QtQuick3D Materials} > - \uicontrol {Qt Quick 3D Custom Shader Utils}. For more information about the - shader utilities and commands and their properties, see - \l {Custom Shaders}. + \section1 Creating Custom Effects and Materials - \note You must create the actual shader source files with some other tool - and copy them to your project folder. You can then specify the source file - names in the custom effect or material properties. To use custom \e uniforms - in the shader files, you must specify them as QML properties for the custom - effect or material component. \QDS automatically generates the uniforms for - the shaders based on the property values. + Create custom effects and materials using the \l{Custom Shaders in Qt 5} + {custom shader utilities} available in \uicontrol Components > + \uicontrol {Qt Quick 3D Custom Shader Utilities}. Each effect or material + must have a fragment shader that completes all the functions needed to calculate + the shaded color. The material system also offers ready-made functions to help you + implement the material. - \section1 Creating Custom Effects + You can create dielectric, metallic, and transparent materials, point lights, + area lights, ambient occlusion, shadowing, two-sided polygons, index-of-refraction, + and fragment cutoff (masking). For more information, see + \l {Qt Quick 3D Custom Material Reference}. - By default, a custom effect component contains a Pass component and a Shader - component in the fragment stage. You can add passes, shaders, and other - shader utilities to the effect. + \section2 Creating Custom Effects - \image studio-qtquick-3d-custom-effect-navigator.png "Custom effect in Navigator" + \note Use the method described here to create custom effects in \QDS 4.0 or older + versions in projects created with \uicontrol {Qt 5} or \uicontrol {Qt 5.15} as + \uicontrol {Target Qt Version}. - The fragment shader component is created with a placeholder for the path - to the shader file. Specify the path to the shader file to use in the - shader properties. + By default, the custom effect component contains a \uicontrol Pass component + and a \uicontrol Shader component in the fragment stage. You can add passes, + shaders, and other shader utilities to the effect. + + \image studio-qtquick-3d-custom-effect-navigator.webp "Custom effect in Navigator" + + The fragment shader component has a placeholder for the path to the shader file. + You need to create shader files in code. For more information, + see \l {Creating Shader Files}. Specify the path to the shader file to use in the + \uicontrol Properties view. To create a custom effect: \list 1 - \li Drag-and-drop an \uicontrol {Effect} component from the - \uicontrol {Qt Quick 3D Custom Shader Utils} tab of - \uicontrol Component to a Model component in \l Navigator. - \li Select the custom effect component in \uicontrol Navigator to edit - the values of its properties in the \l Properties view. + \li Drag an \uicontrol Effect component from \uicontrol Components + > \uicontrol {Qt Quick 3D Effects} > + \uicontrol {Qt Quick 3D Custom Shader Utilities} to a model + component in \l Navigator. + \li Select the \uicontrol Effect component in \uicontrol Navigator + to adjust the values of its properties in the \l Properties view. \image studio-qtquick-3d-custom-effect.png "Custom effect properties" - \li In the \uicontrol Passes field, select the pass components for + \li Use the \uicontrol Passes property to select the pass components for the effect. - \li Select the pass component in \uicontrol Navigator to specify values - for its properties in \uicontrol Properties. + \li Select the \uicontrol renderPass component in \uicontrol Navigator to + specify values for its properties in \uicontrol Properties. \image studio-qtquick-3d-pass.png "Pass properties" - \li To execute commands during the pass, drag-and-drop the following - command components from \uicontrol Component to the custom material in - \uicontrol Navigator: \uicontrol Blending, \uicontrol {Buffer Blit}, - \uicontrol {Buffer Input}, \uicontrol {Cull Mode}, - \uicontrol {Depth Input}, \uicontrol {Render State}, and - \uicontrol {Set Uniform Value}. Then select the commands in the - \uicontrol Commands field. - \li To allocate a buffer for the pass, drag-and-drop a \uicontrol Buffer - component to the custom material. Then select the buffer in the - \uicontrol Buffer field. - \li Select the shader component in \uicontrol Navigator to set the path - to the shader files in the \uicontrol Source field in - \uicontrol Properties. - \image studio-qtquick-3d-shader-properties.png "Shader properties" + \li To run commands during the pass, drag the following + command components from \uicontrol Components > + \uicontrol {Qt Quick 3D Custom Shader Utilities} to \uicontrol Effect + > \uicontrol renderPass in \uicontrol Navigator: + \list + \li \uicontrol Blending + \li \uicontrol Buffer + \li \uicontrol {Buffer Blit} + \li \uicontrol {Buffer Input} + \li \uicontrol {Cull Mode} + \li \uicontrol {Depth Input} + \li \uicontrol {Render State} + \li \uicontrol {Set Uniform Value} + \endlist + \li Select the \uicontrol fragShader component in \uicontrol Navigator > + \uicontrol Effect to set the path to the shader files using the + \uicontrol Source property in \uicontrol Properties. + \image studio-qtquick-3d-shader-properties.webp "Shader properties" \endlist \section1 Creating Custom Materials - By default, a Custom Material component contains two Shader components, a - Shader Info component, and a Pass component. You can add shaders, passes, - and other shader utilities to the material. + \note Use the method described here to create custom materials in \QDS 3.4 or + older versions in projects created with \uicontrol {Qt 5} or \uicontrol {Qt 5.15} + as \uicontrol {Target Qt Version}. + + By default, the \uicontrol {Custom Material} component contains two \uicontrol Shader + components, a \uicontrol {Shader Info}, and a \uicontrol Pass component. You can add + shaders, passes, and other shader utilities to the material. \image studio-qtquick-3d-custom-material-navigator.png "Custom material in Navigator" - By default, fragment and vertex shaders are created with placeholders for - the paths to the shader files. Specify the paths to the shader files to use - in the shader properties. + By default, fragment and vertex shaders have placeholders for the paths to the + shader files. Specify the paths to the shader files to use in the shader properties. - The Shader Info component specifies the shader component and version, as - well as the options used by the shader based on the selected shader key + The \uicontrol {Shader Info} component specifies the shader component and version, + as well as the options used by the shader based on the selected shader key values, such as diffuse or specular lighting, refraction, transparency, displacement, transmissiveness, glossiness, and alpha cutout. - The shaders are used with the Pass component to create the resulting - material. A pass can contain multiple rendering passes and other commands. - You can use a Buffer component to allocate a buffer for storing intermediate + Use shaders with the \uicontrol Pass component to customize the material. + A pass can contain multiple rendering passes and other commands. Use a + \uicontrol Buffer component to allocate a buffer for storing intermediate rendering results. To create a custom material: \list 1 - \li Drag-and-drop a \uicontrol {Custom Material} component from the - \uicontrol {Qt Quick 3D Custom Shader Utils} tab of - \uicontrol Component to a Model component in \uicontrol Navigator. - \li Select the custom material component in \uicontrol Navigator to - edit the values of its properties in the \uicontrol Properties view. + \li Drag a \uicontrol {Custom Material} component from + \uicontrol Components > \uicontrol {Qt Quick 3D Custom Shader Utils} + to a model component in \uicontrol Navigator. + \li Select the \uicontrol {Custom Material} component in \uicontrol Navigator + to adjust the values of its properties in the \uicontrol Properties view. \image studio-qtquick-3d-custom-material.png "Custom material properties" \li Select the \uicontrol Transparency check box to make the material - transparent. - \li Select the \uicontrol Refraction check box to specify that the + transparent, and \uicontrol Refraction to specify that the material is \l{Using Highlights and Reflections}{reflective}. - \li Select the \uicontrol {Always dirty} check box to determine that - the material needs to be refreshed every time it is used. + \li Select the \uicontrol {Always dirty} check box to refresh the + the material every time it is used. \li In the \uicontrol {Shader Info} field, select the shader info - component to use. - \li In the \uicontrol Passes field, select the pass components for - the effect. + component to use, and in the \uicontrol Passes field, select the pass + components for the effect. \li In the \uicontrol Material group, select the \l{Using Highlights and Reflections}{light probe}, \l{Simulating Geometry Displacement}{displacement map and amount}, and \l{Culling Faces}{culling mode} to use. - \li Select the shader info component in \uicontrol Navigator to specify - values for its properties in \uicontrol Properties. + \li Select the \uicontrol shaderInfo component in \uicontrol Navigator + to adjust the values for its properties in \uicontrol Properties. \image studio-qtquick-3d-shader-info.png "Shader Info properties" - \li Select the pass component in \uicontrol Navigator to specify values - for its properties in \uicontrol Properties. + \li Select the \uicontrol renderPass component in \uicontrol Navigator + to adjust the values for its properties in \uicontrol Properties. \image studio-qtquick-3d-pass.png "Pass properties" - \li To execute commands during the pass, drag-and-drop the following - command components from \uicontrol Component to the pass component in - \uicontrol Navigator: \uicontrol Blending, \uicontrol {Buffer Blit}, - \uicontrol {Buffer Input}, \uicontrol {Cull Mode}, - \uicontrol {Depth Input}, \uicontrol {Render State}, and - \uicontrol {Set Uniform Value}. The command components are created - at the same level as the pass component and automatically added to - the \uicontrol Commands field. - \li To allocate a buffer for the pass, drag-and-drop a \uicontrol Buffer - component to the custom material. Then select the buffer in the - \uicontrol Buffer field. - \li To add a shader to the pass, drag-and-drop the \uicontrol Shader - component from the \uicontrol Component to the pass component in - \uicontrol Navigator. The shader components are created at the same - level as the pass component and automatically added to the + \li To execute commands during the pass, drag the following command + components from \uicontrol Components to the \uicontrol renderPass + component in \uicontrol Navigator: + \list + \li \uicontrol Blending + \li \uicontrol {Buffer Blit} + \li \uicontrol {Buffer Input} + \li \uicontrol {Cull Mode}, + \li \uicontrol {Depth Input} + \li \uicontrol {Render State} + \li \uicontrol {Set Uniform Value} + \endlist + \QDS creates the command components at the same level as the pass component + and automatically adds them to the \uicontrol Commands field. + \li To allocate a buffer for the pass, drag a \uicontrol Buffer + component to the \uicontrol {Custom Material} component. Then select + the buffer in the \uicontrol Buffer field. + \li To add a shader to a pass, drag a \uicontrol Shader + component from \uicontrol Components to the pass component in + \uicontrol Navigator. \QDS creates the shader components at the same + level as the pass component and automatically adds them to the \uicontrol Shaders field. \li Select the shader components in \uicontrol Navigator to set the paths to the shader files in the \uicontrol Source field in \uicontrol Properties. - \image studio-qtquick-3d-shader-properties.png "Shader properties" + \image studio-qtquick-3d-shader-properties.webp "Shader properties" \endlist - - \section1 Creating Shader Files - - The requirements set for shaders that you can use in custom effects and - materials are described in \l {Qt Quick 3D Custom Material Reference}. - - If you use custom uniforms in the shader files, you must specify them - as QML properties for the custom effect or material component. \QDS - automatically generates the uniforms based on the property values. - - For example, the following code snippet shows fragment shader code that - uses two uniforms: \c uTextureInUse and \c uInputTexture. - - \code - out vec4 fragColor; - - in vec3 pos; - in vec3 texCoord0; - - void main() { - - vec4 textCol; - if (uTextureInUse) - textCol = texture( uInputTexture, texCoord0.xy ); - - fragColor = vec4(pos.x * 0.02 * textCol.x, pos.y * 0.02 * textCol.y, pos.z * 0.02, 1.0); - } - \endcode - - To use the above fragment shader in a custom effect or material component, - you must remove the uniforms from the shader code and define them as - properties for the component on the \uicontrol Properties tab in the - \l {Connections} view. - - \image studio-custom-material-uniform-properties.png "Uniforms as properties in Connections view Properties tab" - - For more information about adding properties, see - \l{Specifying Custom Properties}. */ diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc index f053834f0dc..229dec4b953 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -8,50 +8,127 @@ \title Custom Shaders - You can use the 3D shader utilities and commands available in - > \uicontrol Components > \uicontrol {Qt Quick 3D} > - \uicontrol {Qt Quick 3D Custom Shader Utilities} to create your own effects - and materials. + In \QDS projects created with \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, + you create custom shaders in code and use custom shader utilities to create post- + processing effects. In project created with \uicontrol {Qt 5} as \uicontrol + {Target Qt Version}, you can use custom shader utilities to to create custom + materials and effects. + + This topic details the creation of custom shaders in code in \QDS projects created with + \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, the use of custom shader utilities + \l{Custom Effects and Materials in Qt 5}{to create custom materials and effects} + and the creation of custom shaders in code in \QDS projects created with \uicontrol + {Qt 6} as \uicontrol {Target Qt Version}. + + \note Creating custom effects and materials as described here is not supported + in the \QDS \l{Creating Projects} {projects} created with \uicontrol {Qt 6} as + \uicontrol {Target Qt Version}. + + \section1 Custom Shader Utilities in Qt 6 + + In projects created with \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, + you create custom shaders used for custom materials in code. In \uicontrol {Qt 6}, + the custom materials no longer support multipass rendering, and therefore + you don't need to use the custom shader utilities to create custom materials. + + Use the custom shader utilities available in \uicontrol Components > \uicontrol QtQuick3D + to customize the \uicontrol Effect component used for post-processing + effects. + + \section2 Creating Shader Files + + In \uicontrol {Qt 6}, you need to create custom shaders for custom materials in code + using \uicontrol {Text Editor} or the \uicontrol Edit mode. For more information about + creating custom materials, see \l {Qt Quick 3D - Custom Materials Example} in the + Qt Quick 3D documentation. + + The requirements set for shaders that you can use in custom effects and + materials are described in \l {Qt Quick 3D Custom Material Reference}. + + If you use custom uniforms in the shader files, you must specify them + as QML properties for the custom effect or material component. \QDS + automatically generates the uniforms based on the property values. + + For example, the following code snippet shows fragment shader code that + uses two uniforms: \c uTextureInUse and \c uInputTexture. + + \code + out vec4 fragColor; + + in vec3 pos; + in vec3 texCoord0; + + void main() { + + vec4 textCol; + if (uTextureInUse) + textCol = texture( uInputTexture, texCoord0.xy ); + + fragColor = vec4(pos.x * 0.02 * textCol.x, pos.y * 0.02 * textCol.y, pos.z * 0.02, 1.0); + } + \endcode + + To use the above fragment shader in a custom effect or material component, + you must remove the uniforms from the shader code and define them as + properties for the component on the \uicontrol Properties tab in the + \l {Connections} view. + + \image studio-custom-material-uniform-properties.png "Uniforms as properties in Connections view Properties tab" + + For more information about adding properties, see + \l{Specifying Custom Properties}. + + \section2 Creating Post-Processing Effects + + You can use the custom shader utilities available in \uicontrol Components to create custom + post-processing effects. + + \note \uicontrol ExtendedSceneEnvironment provides a set of built-in effects, such as depth + of field, glow/bloom, lens flare, color grading, and vignette. These effects can be defined + as properties, as described in \l {Applying Post-Processing Effects in the Extended Scene Environment}. + To ensure optimal performance for your application, we recommend using these built-in effects + instead of applying custom post-processing effects. + + To create a post-processing effect, drag an \uicontrol Effect from \uicontrol Components > + \uicontrol QtQuick3D > \uicontrol Components to \uicontrol a scene environment in + \uicontrol Navigator. To customize the effect, drag the required shader utilities from + \uicontrol Components > \uicontrol QtQuick3d > \uicontrol {Custom Shader Utils} to + \uicontrol Effect in \uicontrol Navigator. To define settings for a shader utility, select + it in \uicontrol Navigator and edit its properties in the \uicontrol Properties view. + + \section1 Custom Shaders in Qt 5 + + Use the 3D shader utilities and commands available in \uicontrol Components + > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D Custom Shader Utilities} + to create your own effects and materials. The \uicontrol Effect component, used + as the base component for custom effects, is located in + \uicontrol {Qt Quick 3D Effects} > \uicontrol {Qt Quick 3D Custom Shader Utilities}. + To use the \uicontrol Effect component, you may need to add add the + \uicontrol QtQuick3D.Effects module to your project. The \uicontrol {Custom Material} + is located in \uicontrol {Qt Quick 3D Materials} > + \uicontrol {Qt Quick 3D Custom Shader Utilities}. To use the + \uicontrol {Custom Material} component, you may need to add the + \uicontrol QtQuick3D.Materials module to your project. If the custom shader utilities are not displayed in \uicontrol {Components}, add the \uicontrol QtQuick3D module to your project, as described in - \l {Adding and Removing Modules}. + \l {Adding and Removing Modules}. - \note If you select \uicontrol {Qt 5} as the \uicontrol {Target Qt Version} - when \l {Creating Projects}{creating your project}, the available custom - shader utilities and their properties will be slightly different, and some - of the components can be found in different locations in - \uicontrol {Components}. + \note In some \QDS versions the location of the custom shader utilities in + \uicontrol Components may differ from what is described here. Use the search + function in \uicontrol Components to locate any available component. - \image studio-qtquick-3d-shader-utilities.png "Custom shader utilities in Components" - - You can find additional shader utilities, the \uicontrol Effect and - \uicontrol {Custom Material} components, in - \uicontrol Components > \uicontrol {Qt Quick3D} > \uicontrol {Qt Quick 3D}. - - \image studio-qtquick-3d-components.webp "Effect and Custom Material Components in Components" - - \note In Qt 5 the \uicontrol Effect component is located in - \uicontrol {Qt Quick 3D Effects} > - \uicontrol {Qt Quick 3D Custom Shader Utilities}. To use the - \uicontrol Effect component, add the \uicontrol QtQuick3D.Effects module to - your project. - - \note In Qt 5 the \uicontrol {Custom Material} component can be found in - \uicontrol {Qt Quick 3D Materials} > - \uicontrol {Qt Quick 3D Custom Shader Utilities}. To use the - \uicontrol {Custom Material} component, add the - \uicontrol QtQuick3D.Materials module to your project. + \image studio-qtquick-3d-shader-utilities-qt5.webp "Qt 5 custom shader utilities in Components" For more information about using the shaders, see - \l {Custom Effects and Materials}. + \l {Custom Effects and Materials in Qt 5}. See the following tables for available shader utilities and commands. - \section1 Available Custom Shader Utilities + \section1 Available Custom Shader Utilities and Base Components \table \header - \li Custom Shader + \li Component \li Qt 5 Only \li Description @@ -118,12 +195,11 @@ \uicontrol {Always dirty} properties. The \uicontrol {Shader Info} specifies the shader info of the - material. For more information, see \l {Custom Effects and Materials}. + material. For more information, see \l {Custom Effects and Materials in Qt 5}. - \note In Qt 5 you can also define render passes for - \uicontrol {Custom Material} instances by using the - \uicontrol Passes property, which lists render passes implemented - by the material. + \note In \uicontrol {Qt 5} you can also define render passes for + \uicontrol {Custom Material} by using the \uicontrol Passes property, + which lists render passes implemented by the material. \row \li \l Effect @@ -133,7 +209,7 @@ The \uicontrol Passes property contains a list of render passes implemented by the effect. You can add more entry fields to the list by selecting \inlineimage icons/plus.png - . For more information, see \l {Custom Effects and Materials}. + . For more information, see \l {Custom Effects and Materials in Qt 5}. \row \li \l Pass @@ -218,7 +294,7 @@ \row \li \l Blending - \li + \li \inlineimage ok.png \li A pass command that specifies the source blending function. The \uicontrol Source property specifies the source blending diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc index 95eb3232d7b..ec8586eebed 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc @@ -17,7 +17,7 @@ You can use the \l Effect component available in \uicontrol Components > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D} as the base component for creating custom post-processing effects. For more information, - see \l {Custom Effects and Materials} and \l {Custom Shaders}. + see \l {Custom Effects and Materials in Qt 5} and \l {Custom Shaders}. \note In \uicontrol {Qt 5}, the \uicontrol Effect component is located in \uicontrol {Qt Quick 3D Effects} > \uicontrol {Qt Quick 3D Custom Shader Utilities}. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials-shaders.qdoc index debb5473eac..e4f4c9571e5 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials-shaders.qdoc @@ -28,7 +28,7 @@ \list \li Default material \li Principled material - \li \l{Custom Effects and Materials}{Custom material} + \li \l{Custom Effects and Materials in Qt 5}{Custom material} \li Texture \endlist diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc index ba8169a4824..5347c19718b 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc @@ -47,7 +47,7 @@ You can use the \l {CustomMaterial}{Custom Material} component available in \uicontrol {Qt Quick 3D Effects} > \uicontrol {Custom Shader Utils} as the base component for creating custom materials used to shade models. For - more information, see \l {Custom Effects and Materials} and + more information, see \l {Custom Effects and Materials in Qt 5} and \l {Custom Shaders}. \section1 Metal Materials diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc index 32eb650ed42..1e964d91da1 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc @@ -339,12 +339,12 @@ These settings are not relevant at other times, such as when using already generated lightmaps to render a scene. - \section1 Setting the Extended Scene Environment + \section1 Applying Post-Processing Effects in the Extended Scene Environment In addition to properties described above, in the extended scene environment you can apply effects to your scene by defining them as properties. When enabling one or more of these effects, the result is similar to manually adding - \l {3D Effects}{effects} to \uicontrol {Scene Environment}. + \l {Creating Post-Processing Effects}{custom effects} to \uicontrol {Scene Environment}. Use \uicontrol {Extended Scene Environment} instead of \uicontrol {Scene Environment} to add multiple and complex effects to your scene. Because the \uicontrol diff --git a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc index 59936cca6f5..bbe004dc98f 100644 --- a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc +++ b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc @@ -9,8 +9,9 @@ \title Effects \QDS includes various ready-made effects that you can use in your applications. These effects - are customizable to meet your specific requirements. Alternatively, you can use your own shader - files to create a custom effect. + are customizable to meet your specific requirements. In projects created with + \uicontrol {Qt 5} as the \uicontrol {Target Qt Version}, you can use your own shader files + and \QDS custom shader utilities to create custom effects. The following table summarizes the available effects and their corresponding use cases. @@ -48,8 +49,12 @@ \li \l {Using Qt Quick Effect Maker Effects}{Qt Quick Effect Maker} \li Import effects made with \QQEM to \QDS. \row - \li \l {Custom Effects and Materials}{Custom Effects} - \li Use your own shader files to create a custom effect. + \li \l {Creating Custom Effects}{Custom Effects in Qt 5} + \li Use your own shader files and \QDS custom shader utilities to create + custom effects. + \row + \li \l {Applying Post-Processing Effects in the Extended Scene Environment}{Post-Processing Effects} + \li Use the extended scene environment to apply built-in post-processing effects. \endtable */ From e4eda2cfc25d788717e31d83a22e96fbbe06b2da Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Nov 2024 14:08:08 +0100 Subject: [PATCH 117/322] QmlDesigner: Fix compile using enum is not supported on all compilers, yet. Change-Id: I59f87c58eb749b70a33ddacc30c64270e71e0ce0 Reviewed-by: Ali Kianian Reviewed-by: Thomas Hartmann --- src/plugins/effectcomposer/effectsautocomplete.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/effectcomposer/effectsautocomplete.cpp b/src/plugins/effectcomposer/effectsautocomplete.cpp index 00a2b8261d0..f1752e45fbb 100644 --- a/src/plugins/effectcomposer/effectsautocomplete.cpp +++ b/src/plugins/effectcomposer/effectsautocomplete.cpp @@ -178,7 +178,6 @@ class EffectsAssistProposalModel : public TextEditor::GenericProposalModel { using AssistProposalItemInterface = TextEditor::AssistProposalItemInterface; using AssistReason = TextEditor::AssistReason; - using enum TextEditor::AssistReason; public: EffectsAssistProposalModel(const QList &items) @@ -212,7 +211,7 @@ void EffectsAssistProposalModel::sort(const QString &prefix) bool EffectsAssistProposalModel::keepPerfectMatch(AssistReason reason) const { - return reason == ExplicitlyInvoked; + return reason == TextEditor::ExplicitlyInvoked; } void addCompletion( From f37e7100fadbca3f124827ca0fba3bdb433fd223 Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Thu, 7 Nov 2024 10:08:14 +0100 Subject: [PATCH 118/322] qmlprojectmanager: support for MCU.Config.fileSelector Task-number: QDS-9838 Change-Id: I084ec9b3e1e30d122883564979218c30ef531825 Reviewed-by: Burak Hancerli Reviewed-by: Thomas Hartmann --- .../buildsystem/projectitem/converters.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 9ec1c957135..74ee4365da3 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -526,6 +526,16 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) shaderToolObject.insert("files", childNode->property("files").value.toJsonValue()); } else if (childNode->name().contains("config", Qt::CaseInsensitive)) { mcuConfigObject = nodeToJsonObject(childNode); + if (const auto fileSelector = childNode->property("fileSelector"); fileSelector.isValid()) { + auto currentSelectors = runConfigObject.value("fileSelectors").toArray(); + const auto mcuSelectors = fileSelector.value.toJsonArray(); + for (const auto &elem : mcuSelectors) { + if (!currentSelectors.contains(elem)) { + currentSelectors.append(elem); + } + } + runConfigObject.insert("fileSelectors", currentSelectors); + } } else if (childNode->name().contains("module", Qt::CaseInsensitive)) { mcuModuleObject = nodeToJsonObject(childNode); } else { From 1689fb5b0d471b2b7041db94286c12576c5f09cc Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Thu, 14 Nov 2024 13:07:33 +0200 Subject: [PATCH 119/322] Doc: Update tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update tables to align with MS style guide, mainly: - Fill empty cells - Convert table titles to sentence-case capitalization Task-number: QDS-14096 Change-Id: I76146294a85baf9a80ff2284cb637879300e9e18 Reviewed-by: Teea Põldsam Reviewed-by: Johanna Vanhatapio --- .../examples/doc/DesignEffect.qdoc | 2 +- .../examples/doc/effectComposerExample.qdoc | 2 +- .../components/qtquick-animation-types.qdoc | 2 +- .../src/components/qtquick-controls.qdoc | 48 ++++---- .../src/components/qtquick-data-models.qdoc | 22 ++-- .../src/components/qtquick-images.qdoc | 12 +- .../src/components/qtquick-shapes.qdoc | 18 +-- .../src/components/qtquick-text.qdoc | 16 +-- .../qtquick-user-interaction-methods.qdoc | 10 +- .../studio-designer-developer-workflow.qdoc | 4 +- ...ignstudio-compatibility-with-mcu-sdks.qdoc | 4 +- ...signstudio-creating-projects-for-mcus.qdoc | 2 +- ...designstudio-features-on-mcu-projects.qdoc | 106 +++++++++--------- .../overviews/qtquick-animation-overview.qdoc | 6 +- .../overviews/qtquick-creating-ui-logic.qdoc | 4 +- ...signstudio-finding-qt-runtime-version.qdoc | 2 +- .../src/qtdesignstudio-projects.qdoc | 10 +- .../qtdesignstudio-3d-custom-shaders.qdoc | 32 +++--- .../qtdesignstudio-3d-editor.qdoc | 2 +- .../qtdesignstudio-3d-effects.qdoc | 2 +- .../qtdesignstudio-3d-lights.qdoc | 12 +- .../qtdesignstudio-3d-materials.qdoc | 2 +- .../qtdesignstudio-3d-scene-environment.qdoc | 8 +- .../qtdesignstudio-optimized-3d-scenes.qdoc | 2 +- .../qtdesignstudio-logic-helpers.qdoc | 2 +- .../qtdesignstudio-visual-effects.qdoc | 42 +++---- .../qtquick-connection-editor-signals.qdoc | 4 +- .../src/views/qtquick-properties-view.qdoc | 2 +- 28 files changed, 190 insertions(+), 190 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc b/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc index 4311b802cc2..3e80fe2c6e6 100644 --- a/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc +++ b/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc @@ -18,7 +18,7 @@ principles: \table \header - \li Design Effect + \li Design effect \li Description \row \li Neumorphism diff --git a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc index d8e347b898f..eda2329886e 100644 --- a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc +++ b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc @@ -33,7 +33,7 @@ \table \header - \li Effect Name + \li Effect name \li Description \row \li \uicontrol {Displace} diff --git a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc index a8bddc66ff8..11b0b7a75e6 100644 --- a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc @@ -186,7 +186,7 @@ \table \header \li Component - \li Use Case + \li Use case \row \li \uicontrol {Property Animation} \li Applying animation when the value of a property changes. Color diff --git a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc index 15b2189d2ac..4999061e675 100644 --- a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc @@ -651,115 +651,115 @@ The following table lists preset UI controls with links to their developer documentation. They are available in \uicontrol Components > - \uicontrol {Qt Quick Controls}. The \e MCU column indicates which controls + \uicontrol {Qt Quick Controls}. The \e {MCU support} column indicates which controls are supported on MCUs. \table \header \li Icon \li Name - \li MCU + \li MCU support \li Purpose \row \li \inlineimage icons/busyindicator-icon16.png \li \l [QtQuickControls]{BusyIndicator}{Busy Indicator} - \li + \li No \li Indicates activity while content is being loaded. \row \li \inlineimage icons/button-icon16.png \li \l [QtQuickControls]{Button} - \li \inlineimage ok.png + \li Yes \li A push button that you can associate with an action. \row \li \inlineimage icons/checkbox-icon16.png \li \l [QtQuickControls]{CheckBox}{Check Box} - \li \inlineimage ok.png + \li Yes \li An option button that can be toggled on (checked) or off (unchecked). \row \li \inlineimage icons/checkbox-icon16.png \li \l [QtQuickControls]{CheckDelegate}{Check Delegate} - \li + \li No \li An item delegate that can be toggled on (checked) or off (unchecked). \row \li \inlineimage icons/combobox-icon16.png \li \l [QtQuickControls]{ComboBox}{Combo Box} - \li + \li No \li A combined button and popup list that is populated by using a data model. \row \li \inlineimage icons/delaybutton-icon16.png \li \l [QtQuickControls]{DelayButton}{Delay Button} - \li + \li No \li An option button that is triggered when held down long enough. \row \li \inlineimage icons/dial-icon16.png \li \l [QtQuickControls]{Dial} - \li \inlineimage ok.png + \li Yes \li A circular dial that is rotated to set a value. \row \li \inlineimage icons/pageindicator-icon16.png \li \l [QtQuickControls]{PageIndicator}{Page Indicator} - \li + \li No \li Indicates the indicate the currently active page in a container of multiple pages. \row \li \inlineimage icons/progressbar-icon16.png \li \l [QtQuickControls]{ProgressBar}{Progress Bar} - \li \inlineimage ok.png + \li Yes \li Indicates the progress of an operation. \row \li \inlineimage icons/radiobutton-icon16.png \li \l [QtQuickControls]{RadioButton}{Radio Button} - \li \inlineimage ok.png + \li Yes \li An option button that can be switched on (checked) or off (unchecked). \row \li \inlineimage icons/radiobutton-icon16.png \li \l [QtQuickControls]{RadioDelegate}{Radio Delegate} - \li + \li No \li An item delegate that can be toggled on (checked) or off (unchecked). \row \li \inlineimage icons/rangeslider-icon16.png \li \l [QtQuickControls]{RangeSlider}{Range Slider} - \li + \li No \li Enables users to select a range of values by sliding two handles along a track. \row \li \inlineimage icons/roundbutton-icon16.png \li \l [QtQuickControls]{RoundButton}{Round Button} - \li + \li No \li A push button with rounded corners that you can associate with an action. \row \li \inlineimage icons/slider-icon16.png \li \l [QtQuickControls]{Slider} - \li \inlineimage ok.png + \li Yes \li Enables users to select a value by sliding a handle along a track. \row \li \inlineimage icons/spinbox-icon16.png \li \l [QtQuickControls]{SpinBox}{Spin Box} - \li + \li No \li Enables users to specify a value by clicking the up or down buttons, by pressing up or down on the keyboard, or by entering a value in the box. \row \li \inlineimage icons/switch-icon16.png \li \l [QtQuickControls]{Switch} - \li \inlineimage ok.png + \li Yes \li An option button that can be toggled on or off. \row \li \inlineimage icons/switch-icon16.png \li \l [QtQuickControls]{SwitchDelegate}{Switch Delegate} - \li + \li No \li An item delegate with a switch indicator that can be toggled on or off. \row \li \inlineimage icons/toolbar-icon16.png \li \l [QtQuickControls] {TabBar}{Tab Bar} - \li + \li No \li Enables users to switch between different views or subtasks. \row \li \inlineimage icons/toolbutton-icon16.png @@ -770,25 +770,25 @@ \row \li \inlineimage icons/toolbar-icon16.png \li \l [QtQuickControls]{ToolBar}{Tool Bar} - \li + \li No \li A container of application-wide and context sensitive actions and controls, such as navigation buttons and search fields. \row \li \inlineimage icons/toolbutton-icon16.png \li \l [QtQuickControls]{ToolButton}{Tool Button} - \li + \li No \li A button that is functionally similar to \uicontrol Button, but provides a look that is more suitable for a \uicontrol {Tool Bar}. \row \li \inlineimage icons/toolseparator-icon16.png \li \l [QtQuickControls]{ToolSeparator}{Tool Separator} - \li + \li No \li Separates a group of items from adjacent items on a \uicontrol {Tool Bar}. \row \li \inlineimage icons/tumbler-icon16.png \li \l [QtQuickControls]{Tumbler} - \li + \li No \li A spinnable wheel of items that can be selected. \endtable */ diff --git a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc index 2aa89a744dd..092a5449536 100644 --- a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc @@ -169,7 +169,7 @@ The following table lists the components that you can use to add data models to UIs. The \e Location column indicates the location of the component in - \uicontrol Components. The \e MCU column indicates which + \uicontrol Components. The \e {MCU support} column indicates which components are supported on MCUs. \table @@ -177,57 +177,57 @@ \li Icon \li Name \li Location - \li MCU + \li MCU support \li Purpose \row \li \inlineimage gridview-icon16.png \li \l{GridView}{Grid View} \li Default Components - Views - \li + \li No \li A grid vizualization of a model. \row \li \inlineimage icons/itemdelegate-icon16.png \li \l{ItemDelegate}{Item Delegate} \li Qt Quick Controls - \li + \li No \li A standard view item that can be used as a delegate in various views and controls, such as \l ListView and \l ComboBox. \row \li \inlineimage listview-icon16.png \li \l{ListView}{List View} \li Default Components - Views - \li \inlineimage ok.png + \li Yes \li A list vizualization of a model. \row \li \inlineimage pathview-icon16.png \li \l{Path View} \li Default Components - Views - \li + \li No \li Vizualizes the contents of a model along a path. \row \li \inlineimage icons/scrollview-icon16.png \li \l [QtQuickControls] {ScrollView}{Scroll View} \li Qt Quick Controls - \li + \li No \li Provides scrolling for user-defined content. It can be used instead of a \l Flickable component. \row \li \inlineimage icons/stackview-icon16.png \li \l [QtQuickControls] {StackView}{Stack View} \li Qt Quick Controls - \li + \li No \li A stack-based navigation model. \row \li \inlineimage icons/item-svg-16px.png \li \l{SVG Path Item} \li Qt Quick Studio Components - \li + \li No \li An SVG path data string that is used to draw a path as a line. \row \li \inlineimage icons/itemdelegate-icon16.png \li \l{SwipeDelegate}{Swipe Delegate} \li Qt Quick Controls - \li + \li No \li A view item that can be swiped left or right to expose more options or information. It is used as a delegate in views such as \l ListView. @@ -235,7 +235,7 @@ \li \inlineimage icons/swipeview-icon16.png \li \l[QtQuickControls] {SwipeView}{Swipe View} \li Qt Quick Controls - \li \inlineimage ok.png + \li Yes \li Enables users to navigate pages by swiping sideways. \endtable */ diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index cbece71a4b5..bc5228bb118 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -214,7 +214,7 @@ The following table lists the components that you can use to add images. The \e Location column contains the tab name where you can find the - component in \uicontrol Components. The \e MCU column + component in \uicontrol Components. The \e {MCU support} column indicates which components are supported on MCUs. \table @@ -222,33 +222,33 @@ \li Icon \li Name \li Location - \li MCU + \li MCU support \li Purpose \row \li \inlineimage animated-image-icon16.png \li \l [QtQuick]{AnimatedImage}{Animated Image} \li Default Components - Basic - \li + \li No \li An images that stores animations containing a series of frames, such as those stored in GIF files. \row \li \inlineimage border-image-icon16.png \li \l [QtQuick]{BorderImage}{Border Image} \li Default Components - Basic - \li \inlineimage ok.png + \li Yes \li An image that is used as a border or background. \row \li \inlineimage image-icon16.png \li \l [QtQuick]{Image} \li Default Components - Basic - \li \inlineimage ok.png + \li Yes \li An image in one of the supported formats, including bitmap formats such as PNG and JPEG and vector graphics formats such as SVG. \row \li \inlineimage icons/iso-icons-16px.png \li \l{Iso Icon} \li Qt Quick Studio Components - \li + \li No \li An icon from an ISO 7000 icon library specified as a \l Picture component. You can select the icon to use and its color. diff --git a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc index 73ccc3971a5..b8c1bd4581a 100644 --- a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc @@ -210,7 +210,7 @@ The following table lists the components that you can use to draw shapes. The \e Location column indicates the location of the component in - \uicontrol Components. The \e MCU column indicates which + \uicontrol Components. The \e {MCU support} column indicates which components are supported on MCUs. \table @@ -218,38 +218,38 @@ \li Icon \li Name \li Location - \li MCU + \li MCU support \li Purpose \row \li \inlineimage icons/item-arc-16px.png \li \l Arc \li Qt Quick Studio Components - \li + \li No \li An arc that begins and ends at given positions. \row \li \inlineimage icons/custom-border-16px.png \li \l Border \li Qt Quick Studio Components - \li + \li No \li A line with four segments that you can show and shape individually. \row \li \inlineimage icons/item-pie-16px.png \li \l Pie \li Qt Quick Studio Components - \li + \li No \li A pie slice or a pie with a slice missing from it. \row \li \inlineimage icons/item-flippable-16px.png \li \l Flipable \li Qt Quick Studio Components - \li + \li No \li A component that can be visibly \e flipped between its front and back sides, like a card. \row \li \inlineimage rect-icon16.png \li \l Rectangle \li Default Components - Basic - \li \inlineimage ok.png + \li Yes \li A rectangle that is painted with a solid fill color or linear gradient and an optional border. You can use the radius property to draw circles. @@ -257,7 +257,7 @@ \li \inlineimage icons/custom-rectangle-16px.png \li \l{Studio Rectangle}{Rectangle} \li Qt Quick Studio Components - \li + \li No \li An extended rectangle that is painted with a solid fill color or linear, conical, or radial gradients, and corners that you can shape independently of each other. @@ -265,7 +265,7 @@ \li \inlineimage icons/item-triangle-16px.png \li \l Triangle \li Qt Quick Studio Components - \li + \li No \li A triangle with different dimensions and shapes that is enclosed in an invisible rectangle. \endtable diff --git a/doc/qtdesignstudio/src/components/qtquick-text.qdoc b/doc/qtdesignstudio/src/components/qtquick-text.qdoc index 0e8695b5101..09635c142ba 100644 --- a/doc/qtdesignstudio/src/components/qtquick-text.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-text.qdoc @@ -392,7 +392,7 @@ The following table lists the components that you can use to add text to UIs. The \e Location column contains the tab name where you can find the - component in \uicontrol Components. The \e MCU column + component in \uicontrol Components. The \e {MCU support} column indicates which components are supported on MCUs. \table @@ -400,43 +400,43 @@ \li Icon \li Name \li Location - \li MCU + \li MCU support \li Purpose \row \li \inlineimage icons/label-icon16.png \li \l [QtQuickControls]{Label} \li Qt Quick Controls - \li + \li No \li A text label with inherited styling and font. \row \li \inlineimage text-icon16.png \li \l [QtQuick]{Text} \li Default Components - Basic - \li \inlineimage ok.png + \li Yes \li Formatted read-only text. \row \li \inlineimage icons/textarea-icon16.png \li \l [QtQuickControls]{TextArea}{Text Area} \li Qt Quick Controls - \li + \li No \li Multiple lines of editable formatted text. \row \li \inlineimage text-edit-icon16.png \li \l [QtQuick]{TextEdit}{Text Edit} \li Default Components - Basic - \li + \li No \li A single line of editable formatted text that can be validated. \row \li \inlineimage icons/textfield-icon16.png \li \l [QtQuickControls]{TextField}{Text Field} \li Qt Quick Controls - \li + \li No \li A single line of editable plain text. \row \li \inlineimage text-input-icon16.png \li \l [QtQuick]{TextInput}{Text Input} \li Default Components - Basic - \li + \li No \li A single line of editable plain text that can be validated. \endtable */ diff --git a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc index 71b3fee6305..1fec407e956 100644 --- a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc @@ -292,30 +292,30 @@ The following table lists the components that you can use to add basic interaction methods to UIs with links to their developer documentation. They are availabe in \uicontrol Components > - \uicontrol {Default Components} > \uicontrol Basic. The \e MCU column + \uicontrol {Default Components} > \uicontrol Basic. The \e {MCU support} column indicates which components are supported on MCUs. \table \header \li Icon \li Name - \li MCU + \li MCU support \li Purpose \row \li \inlineimage flickable-icon16.png \li \l [QML]{Flickable} - \li \inlineimage ok.png + \li Yes \li Enables flicking components horizontally or vertically. \row \li \inlineimage focusscope-icon16.png \li \l{FocusScope}{Focus Scope} - \li + \li No \li Assists in keyboard focus handling when building reusable components. \row \li \inlineimage mouse-area-icon16.png \li \l [QtQuick]{MouseArea}{Mouse Area} - \li \inlineimage ok.png + \li Yes \li Enables simple mouse handling. \endtable */ diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 03f3b840b7a..d98b0499c1f 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -167,8 +167,8 @@ \table \row - \li {2,1} \image studio-project-export-python-folder.webp "The generated Python folder in the Qt Design Studio project" - \li {2,2} \image studio-project-export-python-file.webp "The generated Python file in the Qt Design Studio project" + \li \image studio-project-export-python-folder.webp "The generated Python folder in the Qt Design Studio project" + \li \image studio-project-export-python-file.webp "The generated Python file in the Qt Design Studio project" \endtable \list 1 \li If you don't have Python installed on your computer, install it. 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 e86eb5b19da..aba73cf69eb 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -13,8 +13,8 @@ \table \header - \li \QDS Version - \li \QMCU SDK Version + \li \QDS version + \li \QMCU SDK version \row \li 4.7 or later \li 2.9 or later 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 40995ec84a9..624edffe6b5 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc @@ -92,7 +92,7 @@ \table \header \li Category - \li Wizard Template + \li Wizard template \li Purpose \row \li {1,5} Qt Quick Files 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 b4864debbf8..558ef9f4aa0 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc @@ -14,63 +14,63 @@ \table \header \li View - \li Fully Supported - \li Partially Supported - \li Not Supported + \li Fully supported + \li Partially supported + \li Not supported \li Comments \row \li \l 2D - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li A scene in the \uicontrol 2D view is rendered by the regular Qt Quick and QML, and not as \QUL and \QMCU, so some imperfections or inaccuracies can occur. Note that the default font used in \QDS preview and \QUL are different, and the developer must confirm both fonts are the same. \row \li \l 3D - \li \b - - \li \b - - \li \b X + \li \e {Not applicable} + \li \e {Not applicable} + \li Yes \li The \uicontrol 3D view is not a part of \QUL or \QMCU. \row \li \l {Material Editor and Browser} - \li \b - - \li \b - - \li \b X + \li \e {Not applicable} + \li \e {Not applicable} + \li Yes \li The \uicontrol {Material Editor} and \uicontrol {Material Browser} views are not a part of \QUL or \QMCU. \row \li \l {Components} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li Shows only the components and modules available for MCU projects. \row \li \l {Assets} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li Shows all the listed assets in the \c QmlProject file. \row \li \l {Navigator} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li Displays the composition of the current component file as a tree structure. \row \li \l {Properties} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li Shows only the preset properties available for MCU projects (such as by Qt Quick and its modules). \row \li \l {Connections} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li The \uicontrol Connections view displays all signal handlers in the current file, but it doesn't filter available signals, so you can still see and select signals available in Qt Quick, but not in \QUL. @@ -79,56 +79,56 @@ to filter available signal/function. \row \li \l {States} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li The feature is fully supported, but there are some limitations listed in \l {\QMCU Known Issues or Limitations}. In addition, StateGroup is disabled. \row \li \l {Transitions} - \li \b X - \li \b - - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} + \li \e {None} \row \li \l {Translations} - \li \b - - \li \b X - \li \b - + \li \e {Not applicable} + \li Yes + \li \e {Not applicable} \li The \uicontrol Translations view previews with regular Qt Quick instead of \QUL, and it can be inaccurate in calculating the text overflow in some translations. \row \li \l {Timeline} - \li \b X - \li \b - - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} + \li \e {None} \row \li \l {Curves} - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li Linear interpolation works, and \QMCU supports the \c easing.bezierCurve property of a keyframe in \QMCU 2.6 or higher. \row \li \l Code - \li \b X - \li \b - - \li \b - + \li Yes + \li \e {Not applicable} + \li \e {Not applicable} \li The \uicontrol Code view uses regular Qt Quick instead of \QUL, so it may not show an error if you are using or assigning an unsupported property. \row \li \l {Content Library} - \li \b - - \li \b - - \li \b X + \li \e {Not applicable} + \li \e {Not applicable} + \li Yes \li The \uicontrol {Content Library} view is not a part of \QUL or \QMCU. \row \li \l {Texture Editor} - \li \b - - \li \b - - \li \b X + \li \e {Not applicable} + \li \e {Not applicable} + \li Yes \li The \uicontrol {Texture Editor} view is not a part of \QUL or \QMCU. \endtable diff --git a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc index e6ae0295788..23de84abcaa 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc @@ -27,7 +27,7 @@ \table \header \li Technique - \li Use Case + \li Use case \row \li \l{Timeline}{Timeline animation} \li Linear interpolation through intermediate values at specified @@ -129,7 +129,7 @@ \table \header \li Technique - \li Use Case + \li Use case \row \li Data-driven timeline animation \li Using real or mock data from a backend to control motion. @@ -180,7 +180,7 @@ \table \header \li Component - \li Use Case + \li Use case \row \li \uicontrol {Property Animation} \li Applying animation when the value of a property changes. Color diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index 1ea19bac4b7..a13a2282c1e 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -77,8 +77,8 @@ \table \header - \li To Learn About - \li Go To + \li To learn about + \li Go to \row \li Responding to application events \li \l{Connecting Components to Signals} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc index 10f780ce468..97901839b64 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc @@ -15,7 +15,7 @@ \table \header \li \QDS - \li Qt Runtime Version + \li Qt runtime version \row \li 4.0 \li 6.4.1 and 5.15.5 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index c65469d8cd0..54215eb3661 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -34,7 +34,7 @@ \li Purpose \row \li Recents - \li + \li \e {Not applicable} \li Lists your most recently used presets. \row \li {1,3} General @@ -77,7 +77,7 @@ images, and text, and defines a launcher application. \row \li Custom - \li + \li \e {Not applicable} \li Lists your saved custom presets. \note This tab is not visible if there are no saved custom presets. \endtable @@ -127,7 +127,7 @@ \table \header - \li File/Folder + \li File or folder \li Description \row \li ProjectName.qmlproject @@ -184,7 +184,7 @@ \table \header - \li File/Folder + \li File or folder \li Description \row \li \c {Generated} (folder) @@ -244,7 +244,7 @@ \table \header \li Category - \li Wizard Template + \li Wizard template \li Purpose \row \li {1,4} Qt Quick Files diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc index 229dec4b953..d9f4004c353 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc @@ -129,12 +129,12 @@ \table \header \li Component - \li Qt 5 Only + \li Qt 5 only \li Description \row \li \l Buffer - \li + \li No \li A buffer to be used for a pass of \uicontrol {Custom Material} or \uicontrol Effect instances. @@ -168,7 +168,7 @@ \row \li \l {CustomMaterial} {Custom Material} - \li + \li No \li The base component for creating custom materials used to shade model instances. @@ -203,7 +203,7 @@ \row \li \l Effect - \li + \li No \li A base component for creating post-processing effects. The \uicontrol Passes property contains a list of render passes @@ -213,7 +213,7 @@ \row \li \l Pass - \li + \li No \li A render pass of an \uicontrol Effect instance. In Qt 5 you can also use render passes for \uicontrol {Custom Materials}. @@ -227,7 +227,7 @@ \uicontrol Pass instance. \row \li \l Shader - \li + \li No \li A container component for defining shader code used by \uicontrol Effect instances. @@ -240,7 +240,7 @@ \row \li \l {ShaderInfo} {Shader Info} - \li \inlineimage ok.png + \li Yes \li Basic information about custom shader code for Custom Materials. The \uicontrol Version property specifies the shader code version, @@ -273,7 +273,7 @@ \endlist \row \li \l {TextureInput} {Texture Input} - \li + \li No \li A texture channel for \uicontrol {Custom Material} and Effect instances. @@ -289,12 +289,12 @@ \table \header \li Command - \li Qt 5 Only + \li Qt 5 only \li Description \row \li \l Blending - \li \inlineimage ok.png + \li Yes \li A pass command that specifies the source blending function. The \uicontrol Source property specifies the source blending @@ -302,7 +302,7 @@ destination for it. \row \li \l {BufferBlit} {Buffer Blit} - \li \inlineimage ok.png + \li Yes \li A copy operation between two buffers in a pass of a Custom Material or an Effect. @@ -310,7 +310,7 @@ and the destination buffers for the copy-operation. \row \li \l {BufferInput} {Buffer Input} - \li + \li No \li An input buffer to be used for a pass of a Custom Material or an Effect. @@ -320,7 +320,7 @@ \row \li \l {CullMode} {Cull Mode} - \li \inlineimage ok.png + \li Yes \li A culling mode for a render pass. The \uicontrol Mode property specifies the culling mode in a pass @@ -331,7 +331,7 @@ \row \li \l {DepthInput} {Depth Input} - \li \inlineimage ok.png + \li Yes \li An output texture for the depth buffer. The \uicontrol Parameter property specifies the name of the texture @@ -339,7 +339,7 @@ \row \li \l {RenderState} {Render State} - \li \inlineimage ok.png + \li Yes \li The render state to be enabled or disabled in a pass of a \uicontrol {Custom Material} or an \uicontrol Effect instance. @@ -355,7 +355,7 @@ \row \li \l {SetUniformValue} {Set Uniform Value} - \li + \li No \li A value to be set during a single pass. The \uicontrol Target property specifies the name of the uniform diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 69a3f70ef58..9e45dc1b25a 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -386,7 +386,7 @@ \header \li Action \li Description - \li Keyboard Shortcut + \li Keyboard shortcut \row \li Show Grid \li Toggles the visibility of the helper grid. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc index ec8586eebed..e65341a5928 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc @@ -37,7 +37,7 @@ \table \header \li 3D Effect - \li Example Image + \li Example image \li Description \row \li Additive Color Gradient diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc index c7ae1e955ef..360b4a7b5ca 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc @@ -37,31 +37,31 @@ \header \li Icon \li Name - \li Qt 5 Only - \li More Information + \li Qt 5 only + \li More information \row \li \inlineimage icons/directional.png \li Directional Light - \li + \li No \li \l{DirectionalLight}{Light Directional} \row \li \inlineimage icons/point.png \li Point Light - \li + \li No \li \l{PointLight}{Light Point} \row \li \inlineimage icons/spot.png \li Spot Light - \li + \li No \li \l{SpotLight}{Light Spot} \row \li \inlineimage icons/area.png \li Area Light - \li \inlineimage ok.png + \li Yes \li \l{AreaLight}{Light Area} \endtable diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc index 5347c19718b..fdbac629850 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc @@ -319,7 +319,7 @@ \table \header \li Material - \li Example Image + \li Example image \li Description \row diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc index 1e964d91da1..822c00e34e5 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc @@ -65,7 +65,7 @@ Use the dropdown menu to select one of the available modes: \table \header - \li Background Mode + \li Background mode \li Description \row \li Transparent @@ -135,7 +135,7 @@ modes using the dropdown menu: \table \header - \li Mode of Antialiasing + \li Mode of antialiasing \li Description \row \li MSAA @@ -169,8 +169,8 @@ final image. \table \header - \li Level of Antialiasing - \li Supersampling Resolution used in SSAA + \li Level of antialiasing + \li Supersampling resolution used in SSAA \li Number of samples per pixel in MSAA \li Number of frames for final image in ProgressiveAA \row diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc index 535bb392cab..e0d51cc4ad4 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-optimized-3d-scenes.qdoc @@ -132,7 +132,7 @@ \table \header - \li How to... + \li How to \li Solution \row \li Get best performance from the 3D scene. diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc index 72f57f0c174..f41935432f5 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc @@ -337,7 +337,7 @@ \table \header \li Icon - \li Logic Helper + \li Logic helper \li Description \row \li \inlineimage icons/lc-and-operator-16px.png diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc index 52ef110a626..7c68c80f23f 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc @@ -79,7 +79,7 @@ \row \li \inlineimage icons/blend-mode-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-blend.html}{Blend} - \li + \li No \li Merges two source components by using a blend mode. The default \uicontrol Mode is \c subtract, where the pixel value @@ -95,13 +95,13 @@ \li \inlineimage icons/brightness-contrast-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-brightnesscontrast.html} {Brightness Contrast} - \li \inlineimage ok.png + \li Yes \li Adjusts \uicontrol Brightness and \uicontrol Contrast. \row \li \inlineimage icons/colourize-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-coloroverlay.html} {Color Overlay} - \li \inlineimage ok.png + \li Yes \li Alters the colors of the source component by applying an \uicontrol {Overlay color}. You can use the color picker to \l{Picking Colors}{select the color}. @@ -109,14 +109,14 @@ \li \inlineimage icons/colourize-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-colorize.html} {Colorize} - \li \inlineimage ok.png + \li Yes \li Sets the color in the HSL color space by specifying \uicontrol Hue, \uicontrol Lightness, and \uicontrol Saturation values. \row \li \inlineimage icons/desaturation-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-desaturate.html} {Desaturation} - \li \inlineimage ok.png + \li Yes \li Reduces the saturation of the colors by the value set in the \uicontrol Desaturation field. The value ranges from \c 0.0 (no change) to \c 1.0 (desaturated). Desaturated pixel values are @@ -126,7 +126,7 @@ \li \inlineimage icons/directional-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-directionalblur.html} {Directional Blur} - \li \inlineimage ok.png + \li Yes \li Applies a blur effect to the specified direction. The value of the \uicontrol Angle field defines the direction of the blur. This effect makes the source component appear to be moving in the direction of @@ -144,7 +144,7 @@ \li \inlineimage icons/displace-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-displace.html} {Displace} - \li \inlineimage ok.png + \li Yes \li Moves the pixels of the source component according to the displacement map specified in the \uicontrol {Displacement source} field. @@ -158,7 +158,7 @@ \li \inlineimage icons/drop-shadow-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-dropshadow.html} {Drop Shadow} - \li \inlineimage ok.png + \li Yes \li Generates a soft shadow behind the source component using a gaussian blur. This effect blurs the alpha channel of the input and colorizes the result, which it then places behind the source component @@ -181,7 +181,7 @@ \li \inlineimage icons/fast-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-fastblur.html} {Fast Blur} - \li \inlineimage ok.png + \li Yes \li Applies a fast blur effect to one or more source components. \uicontrol {Fast Blur} offers lower blur quality than \uicontrol {Gaussian Blur}, but it is faster to render. @@ -200,7 +200,7 @@ \li \inlineimage icons/gamma-adjust-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-gammaadjust.html} {Gamma Adjust} - \li \inlineimage ok.png + \li Yes \li Alters the luminance of the source component. This effect is applied to each pixel according to the curve that is pre-defined as a power-law expression, where the value of the \uicontrol Gamma @@ -209,7 +209,7 @@ \li \inlineimage icons/gaussian-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-gaussianblur.html} {Gaussian Blur} - \li + \li No \li Applies a gaussian blur effect to one or more source components. The effect softens the image by blurring it with an algorithm that uses the gaussian function to calculate the effect. The effect @@ -231,7 +231,7 @@ \row \li \inlineimage icons/glow-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-glow.html}{Glow} - \li \inlineimage ok.png + \li Yes \li Generates a halo-like glow around the source component. This effect blurs the alpha channel of the source and colorizes it with \uicontrol {Glow color}. It then places the alpha channel behind the @@ -243,7 +243,7 @@ \li \inlineimage icons/hue-saturation-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-huesaturation.html} {Hue Saturation} - \li \inlineimage ok.png + \li Yes \li Alters the source component colors in the HSL color space. This effect is similar to the \uicontrol Colorize effect, but the \uicontrol Hue and \uicontrol Saturation values are handled @@ -254,7 +254,7 @@ \li \inlineimage icons/inner-shadow-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-innershadow.html} {Inner Shadow} - \li + \li No \li Generates a colorized and blurred shadow inside the source using the color that you specify in the \uicontrol {Inner shadow color} field. @@ -277,7 +277,7 @@ \li \inlineimage icons/levels-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-leveladjust.html} {Level Adjust} - \li \inlineimage ok.png + \li Yes \li Adjusts color levels in the RGBA color space. This effect adjusts the source component colors separately for each color channel. Source component contrast can be adjusted and color balance altered. @@ -308,7 +308,7 @@ \li \inlineimage icons/mask-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-maskedblur.html} {Masked Blur} - \li + \li No \li Softens the image by blurring it. The intensity of the blur can be controlled for each pixel by specifying a \uicontrol {Mask source}, so that some parts of the source are blurred more than others. @@ -324,7 +324,7 @@ \li \inlineimage icons/opacity-mask-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-opacitymask.html} {Opacity Mask} - \li \inlineimage ok.png + \li Yes \li Masks the source component with another component specified in the \uicontrol {Mask source} field. The mask component gets rendered into an intermediate pixel buffer and the alpha values from the result are @@ -340,7 +340,7 @@ \li \inlineimage icons/radial-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-radialblur.html} {Radial Blur} - \li + \li No \li Applies a directional blur effect in a circular direction around the component's center point. This effect makes the source component appear to be rotating into the direction of the blur. Other available @@ -356,7 +356,7 @@ \li \inlineimage icons/recursive-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-recursiveblur.html} {Recursive Blur} - \li + \li No \li Blurs repeatedly, providing a strong blur effect. This effect softens the image by blurring it with an algorithm that uses a recursive feedback loop to blur the source as many times as @@ -373,7 +373,7 @@ \li \inlineimage icons/threshold-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-thresholdmask.html} {Threshold Mask} - \li \inlineimage ok.png + \li Yes \li Masks the source component with another component specified by \uicontrol {Mask source}. The value of the \uicontrol Spread field determines the smoothness of the mask edges near the @@ -385,7 +385,7 @@ \li \inlineimage icons/zoom-blur-16px.png \li \l {https://doc.qt.io/qt-5/qml-qtgraphicaleffects-zoomblur.html} {Zoom Blur} - \li + \li No \li Applies a directional blur effect towards source component's center point. This effect makes the source component appear to be moving towards the center point in Z-direction or the camera diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc index fe7e83141da..1c2f1b39d13 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc @@ -126,8 +126,8 @@ \table \header \li Action - \li 1st Property - \li 2nd Property + \li 1st property + \li 2nd property \row \li \uicontrol {Call Function} \li \uicontrol {Item}: [Sets the component that is affected by the action of the diff --git a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc index 2d660c99511..1a154bb9a8b 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc @@ -34,7 +34,7 @@ \table \header - \li Custom Property Type + \li Custom property type \li Description \row \li Local Custom Property From fdb24c48c3b03c7a76109fdeb2fbb74d58101f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 15 Nov 2024 09:09:19 +0100 Subject: [PATCH 120/322] TextEditor: fix crash Found in Qt Design -Studio sentry: https://the-qt-company-00.sentry.io/issues/6066221570 Change-Id: I51c2cab276bafe3775c2a46fd343cb52276d5801 Reviewed-by: Tim Jenssen --- src/plugins/texteditor/texteditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 8ced95c0fc0..63145510660 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -9444,6 +9444,7 @@ BaseTextEditor::~BaseTextEditor() TextDocument *BaseTextEditor::textDocument() const { TextEditorWidget *widget = editorWidget(); + QTC_CHECK(widget); QTC_CHECK(!widget->d->m_document.isNull()); return widget->d->m_document.data(); } From 51e5955e5bee3c41701c49c76e07c0c52fdd5e83 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 15 Nov 2024 11:11:53 +0200 Subject: [PATCH 121/322] EffectComposer: Add objectName for EffectComposer buttons Task-number: QDS-14034 Change-Id: I5fda8628d3c170b04a88a96533083c7f2a8f70f1 Reviewed-by: Miikka Heikkinen --- .../qmldesigner/effectComposerQmlSources/EffectComposer.qml | 4 ++++ .../effectComposerQmlSources/EffectComposerTopBar.qml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 16525678be8..75c1aac037d 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -254,6 +254,8 @@ Item { } HelperWidgets.AbstractButton { + objectName: "btnRemoveAllEffects" + anchors.right: parent.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter @@ -272,6 +274,8 @@ Item { } HelperWidgets.AbstractButton { + objectName: "btnOpenShaderInCodeEditor" + anchors.right: parent.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index e14dae8b114..a484a6cde80 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -112,6 +112,8 @@ Rectangle { } HelperWidgets.AbstractButton { + objectName: "btnEffectComposerHelp" + anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: 5 anchors.right: parent.right From d2c607c7c956e2510d8ff02951c2a471ac38be5f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Nov 2024 19:40:46 +0100 Subject: [PATCH 122/322] QmlDesigner: Give more horizontal space to items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives more horizontal space to items and adds a more intelligent layout using the available space. Task-number: QDS-13933 Change-Id: I81bd5fdc9d11ed8162a603c779848bfcc761901b Reviewed-by: Henning Gründl --- .../itemLibraryQmlSources/ItemsView.qml | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 4d11b6ea5e2..e22f23dc12e 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -84,7 +84,7 @@ Item { onIsHorizontalViewChanged: closeContextMenu() Item { id: styleConstants - property int textWidth: 58 + property int textWidth: 72 property int textHeight: Theme.smallFontPixelSize() * 2 property int cellHorizontalMargin: 1 property int cellVerticalSpacing: 2 @@ -296,15 +296,20 @@ Item { property real actualWidth: parent.width - itemGrid.leftPadding - itemGrid.rightPadding leftPadding: 6 rightPadding: 6 - columns: itemGrid.actualWidth / styleConstants.cellWidth + columns: itemGrid.columnNumber rowSpacing: 7 + + property int columnNumber: Math.floor(itemGrid.actualWidth / (styleConstants.cellWidth + 7)) + + property int realWidth: itemGrid.actualWidth / itemGrid.columnNumber - 7 + Repeater { model: itemModel delegate: ItemDelegate { visible: itemVisible textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor - width: styleConstants.cellWidth + width: itemGrid.realWidth height: styleConstants.cellHeight onShowContextMenu: { if (!itemUsable || itemComponentSource) { @@ -443,7 +448,12 @@ Item { leftPadding: 9 rightPadding: 9 bottomPadding: 15 - columns: hItemGrid.actualWidth / styleConstants.cellWidth + columns: hItemGrid.columnNumber + + property int columnNumber: Math.floor(hItemGrid.actualWidth / (styleConstants.cellWidth + 7)) + + property int realWidth: hItemGrid.actualWidth / hItemGrid.columnNumber - 7 + rowSpacing: 7 Repeater { model: ItemLibraryBackend.itemLibraryModel.itemsModel @@ -451,7 +461,7 @@ Item { visible: itemVisible textColor: ItemLibraryBackend.itemLibraryModel.importUnimportedSelected ? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor - width: styleConstants.cellWidth + width: hItemGrid.realWidth height: styleConstants.cellHeight onShowContextMenu: { if (!itemUsable || itemComponentSource) { From 1e244d28e4441f7a35fd4c4261432f44a38153a6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Nov 2024 12:36:36 +0100 Subject: [PATCH 123/322] QmlDesigner: Fix word wrap behavior in item library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Word wrap and eliding together had unpredictable results. We added an invisible text item without eliding to determine if eliding is required if we wrap the text. Removing verticalAlignment: Qt.AlignVCenter Limiting line count to 2 Task-number: QDS-13933 Change-Id: I4e6f0923f7f8cd58b697c2316bba092812fecd55 Reviewed-by: Henning Gründl --- .../itemLibraryQmlSources/ItemDelegate.qml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 266dba995dc..0e3099361a9 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -47,7 +47,7 @@ Item { Text { id: text font.pixelSize: Theme.smallFontPixelSize() - elide: Text.ElideMiddle + elide: fontMetric.elide ? Text.ElideMiddle : Text.ElideNone wrapMode: Text.WordWrap anchors.top: itemIcon.bottom anchors.topMargin: styleConstants.cellVerticalSpacing @@ -56,13 +56,25 @@ Item { anchors.right: parent.right anchors.rightMargin: styleConstants.cellHorizontalMargin anchors.bottom: parent.bottom - anchors.bottomMargin: styleConstants.cellHorizontalMargin + anchors.bottomMargin: styleConstants.cellVerticalSpacing - verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: itemName // to be set by model color: StudioTheme.Values.themeTextColor renderType: Text.NativeRendering + + maximumLineCount: 2 + + Text { + visible: false + font: parent.font + text: parent.text + id: fontMetric + anchors.fill: parent + wrapMode: Text.WordWrap + property bool elide: (fontMetric.implicitWidth > fontMetric.width && fontMetric.lineCount !== 2) || fontMetric.lineCount === 3 + horizontalAlignment: Qt.AlignHCenter + } } ImagePreviewTooltipArea { From eeb2d9dd75e58796ba6e35309536e30671af01e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 15 Nov 2024 13:23:39 +0100 Subject: [PATCH 124/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: Ifc4972976f6e0bf1d73ebba00592998ae6490d6f Reviewed-by: Tim Jenssen Reviewed-by: Thomas Hartmann --- .../libs/designercore/rewriter/texttomodelmerger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp index 3cbf33e391c..6b0907712ca 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp @@ -271,15 +271,15 @@ QVariant convertDynamicPropertyValueToVariant(const QString &astValue, if (astType.isEmpty()) return QString(); - const int type = propertyType(astType); - if (type == QMetaType::fromName("QVariant").id()) { + const QMetaType type = static_cast(propertyType(astType)); + if (type == QMetaType::fromType()) { if (cleanedValue.isNull()) // Explicitly isNull, NOT isEmpty! - return QVariant(static_cast(type)); + return QVariant(type); else return QVariant(cleanedValue); } else { QVariant value = QVariant(cleanedValue); - value.convert(static_cast(type)); + value.convert(type); return value; } } From a02550ee989da4782b27d0fecb7e502fa21c95ea Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 14 Nov 2024 16:44:01 +0200 Subject: [PATCH 125/322] Doc: Remove extra paragraph Change-Id: I02b9f6a81e69124eb11b08ee58e73a36fd468d26 Reviewed-by: Mats Honkamaa --- .../qtdesignstudio-3d-custom-shaders.qdoc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc index d9f4004c353..907271082c4 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc @@ -10,16 +10,10 @@ In \QDS projects created with \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, you create custom shaders in code and use custom shader utilities to create post- - processing effects. In project created with \uicontrol {Qt 5} as \uicontrol + processing effects. In projects created with \uicontrol {Qt 5} as \uicontrol {Target Qt Version}, you can use custom shader utilities to to create custom materials and effects. - This topic details the creation of custom shaders in code in \QDS projects created with - \uicontrol {Qt 6} as \uicontrol {Target Qt Version}, the use of custom shader utilities - \l{Custom Effects and Materials in Qt 5}{to create custom materials and effects} - and the creation of custom shaders in code in \QDS projects created with \uicontrol - {Qt 6} as \uicontrol {Target Qt Version}. - \note Creating custom effects and materials as described here is not supported in the \QDS \l{Creating Projects} {projects} created with \uicontrol {Qt 6} as \uicontrol {Target Qt Version}. From 3c7909ba5ce8e58e685b12d150663b52e6a60686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 15 Nov 2024 13:28:35 +0100 Subject: [PATCH 126/322] QmlDesigner: QVariant::Type -> QMetaType::Type fixes QVariant::Type deprecation warnings Change-Id: I052535e330e66e08a14d52e5461fa3b90412425c Reviewed-by: Tim Jenssen --- .../materialeditor/materialeditorview.cpp | 6 +++--- .../textureeditor/textureeditorqmlbackend.cpp | 6 +++--- .../qmldesigner/instances/nodeinstance.cpp | 18 +++++++++--------- .../rewriter/propertycontainer.cpp | 2 +- .../rewriter/texttomodelmerger.cpp | 4 ++-- src/shared/modeltest/modeltest.cpp | 4 ++-- .../qt5informationnodeinstanceserver.cpp | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 29db55e2813..13eee6eed65 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -189,9 +189,9 @@ void MaterialEditorView::changeValue(const QString &name) } else { // QVector*D(0, 0, 0) detects as null variant though it is valid value if (castedValue.isValid() - && (!castedValue.isNull() || castedValue.typeId() == QVariant::Vector2D - || castedValue.typeId() == QVariant::Vector3D - || castedValue.typeId() == QVariant::Vector4D)) { + && (!castedValue.isNull() || castedValue.typeId() == QMetaType::QVector2D + || castedValue.typeId() == QMetaType::QVector3D + || castedValue.typeId() == QMetaType::QVector4D)) { commitVariantValueToModel(propertyName, castedValue); } } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index adaa0410505..0e73e068eff 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -112,7 +112,7 @@ void TextureEditorQmlBackend::setValue(const QmlObjectNode &, const QVariant &value) { // Vector*D values need to be split into their subcomponents - if (value.typeId() == QVariant::Vector2D) { + if (value.typeId() == QMetaType::QVector2D) { const char *suffix[2] = {"_x", "_y"}; auto vecValue = value.value(); for (int i = 0; i < 2; ++i) { @@ -123,7 +123,7 @@ void TextureEditorQmlBackend::setValue(const QmlObjectNode &, if (propertyValue) propertyValue->setValue(QVariant(vecValue[i])); } - } else if (value.typeId() == QVariant::Vector3D) { + } else if (value.typeId() == QMetaType::QVector3D) { const char *suffix[3] = {"_x", "_y", "_z"}; auto vecValue = value.value(); for (int i = 0; i < 3; ++i) { @@ -134,7 +134,7 @@ void TextureEditorQmlBackend::setValue(const QmlObjectNode &, if (propertyValue) propertyValue->setValue(QVariant(vecValue[i])); } - } else if (value.typeId() == QVariant::Vector4D) { + } else if (value.typeId() == QMetaType::QVector4D) { const char *suffix[4] = {"_x", "_y", "_z", "_w"}; auto vecValue = value.value(); for (int i = 0; i < 4; ++i) { diff --git a/src/plugins/qmldesigner/instances/nodeinstance.cpp b/src/plugins/qmldesigner/instances/nodeinstance.cpp index 17104f645f5..49666fb60c9 100644 --- a/src/plugins/qmldesigner/instances/nodeinstance.cpp +++ b/src/plugins/qmldesigner/instances/nodeinstance.cpp @@ -310,7 +310,7 @@ QVariant NodeInstance::property(PropertyNameView name) const if (index != -1) { PropertyNameView parentPropName = name.left(index); QVariant varValue = value(d->propertyValues, parentPropName); - if (varValue.typeId() == QVariant::Vector2D) { + if (varValue.typeId() == QMetaType::QVector2D) { auto value = varValue.value(); char subProp = name.right(1)[0]; float subValue = 0.f; @@ -326,7 +326,7 @@ QVariant NodeInstance::property(PropertyNameView name) const break; } return QVariant(subValue); - } else if (varValue.typeId() == QVariant::Vector3D) { + } else if (varValue.typeId() == QMetaType::QVector3D) { auto value = varValue.value(); char subProp = name.right(1)[0]; float subValue = 0.f; @@ -345,7 +345,7 @@ QVariant NodeInstance::property(PropertyNameView name) const break; } return QVariant(subValue); - } else if (varValue.typeId() == QVariant::Vector4D) { + } else if (varValue.typeId() == QMetaType::QVector4D) { auto value = varValue.value(); char subProp = name.right(1)[0]; float subValue = 0.f; @@ -431,9 +431,9 @@ void NodeInstance::setProperty(PropertyNameView name, const QVariant &value) QVariant oldValue = QmlDesigner::value(d->propertyValues, parentPropName); QVariant newValueVar; bool update = false; - if (oldValue.typeId() == QVariant::Vector2D) { + if (oldValue.typeId() == QMetaType::QVector2D) { QVector2D newValue; - if (oldValue.typeId() == QVariant::Vector2D) + if (oldValue.typeId() == QMetaType::QVector2D) newValue = oldValue.value(); if (name.endsWith(".x")) { newValue.setX(value.toFloat()); @@ -443,9 +443,9 @@ void NodeInstance::setProperty(PropertyNameView name, const QVariant &value) update = true; } newValueVar = newValue; - } else if (oldValue.typeId() == QVariant::Vector3D) { + } else if (oldValue.typeId() == QMetaType::QVector3D) { QVector3D newValue; - if (oldValue.typeId() == QVariant::Vector3D) + if (oldValue.typeId() == QMetaType::QVector3D) newValue = oldValue.value(); if (name.endsWith(".x")) { newValue.setX(value.toFloat()); @@ -458,9 +458,9 @@ void NodeInstance::setProperty(PropertyNameView name, const QVariant &value) update = true; } newValueVar = newValue; - } else if (oldValue.typeId() == QVariant::Vector4D) { + } else if (oldValue.typeId() == QMetaType::QVector4D) { QVector4D newValue; - if (oldValue.typeId() == QVariant::Vector4D) + if (oldValue.typeId() == QMetaType::QVector4D) newValue = oldValue.value(); if (name.endsWith(".x")) { newValue.setX(value.toFloat()); diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/propertycontainer.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/propertycontainer.cpp index cd7cebbb73a..8eaa7947deb 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/propertycontainer.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/propertycontainer.cpp @@ -41,7 +41,7 @@ PropertyName PropertyContainer::name() const QVariant PropertyContainer::value() const { - if (m_value.typeId() == QVariant::String) + if (m_value.typeId() == QMetaType::QString) m_value = PropertyParser::read(m_type, m_value.toString()); return m_value; } diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp index 6b0907712ca..dac70e2ea86 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp @@ -269,7 +269,7 @@ QVariant convertDynamicPropertyValueToVariant(const QString &astValue, const QString cleanedValue = fixEscapedUnicodeChar(deEscape(stripQuotes(astValue.trimmed()))); if (astType.isEmpty()) - return QString(); + return QVariant(QString()); const QMetaType type = static_cast(propertyType(astType)); if (type == QMetaType::fromType()) { @@ -382,7 +382,7 @@ bool smartVeryFuzzyCompare(const QVariant &value1, const QVariant &value2) } bool smartColorCompare(const QVariant &value1, const QVariant &value2) { - if ((value1.typeId() == QVariant::Color) || (value2.typeId() == QVariant::Color)) + if ((value1.typeId() == QMetaType::QColor) || (value2.typeId() == QMetaType::QColor)) return value1.value().rgba() == value2.value().rgba(); return false; } diff --git a/src/shared/modeltest/modeltest.cpp b/src/shared/modeltest/modeltest.cpp index 0db247755e7..9d36a09fbe6 100644 --- a/src/shared/modeltest/modeltest.cpp +++ b/src/shared/modeltest/modeltest.cpp @@ -413,11 +413,11 @@ void ModelTest::data() // General Purpose roles that should return a QColor QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundRole); if (colorVariant.isValid()) - Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + Q_ASSERT(colorVariant.canConvert(QMetaType::QColor)); colorVariant = model->data(model->index(0, 0), Qt::ForegroundRole); if (colorVariant.isValid()) - Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + Q_ASSERT(colorVariant.canConvert(QMetaType::QColor)); // Check that the "check state" is one we know about. QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index aa3b4386c61..24b797d64dc 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -810,7 +810,7 @@ Qt5InformationNodeInstanceServer::propertyToPropertyValueTriples( QVector result; InstancePropertyValueTriple propTriple; - if (variant.typeId() == QVariant::Vector3D) { + if (variant.typeId() == QMetaType::QVector3D) { auto vector3d = variant.value(); const PropertyName dot = propertyName.isEmpty() ? "" : "."; propTriple.instance = instance; From 18a0a863fe255b3e509aaf0e0c4c11870ff2cb42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Jen=C3=9Fen?= Date: Fri, 15 Nov 2024 15:27:20 +0100 Subject: [PATCH 127/322] QmlDesigner: NodeMetaInfo: QVariant::Type -> QMetaType::Type fixes QVariant::Type deprecation warnings Change-Id: I7885593c10360cb26545a639fe3af416cdffbeb0 Reviewed-by: Marco Bubke --- .../designercore/metainfo/nodemetainfo.cpp | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp index 5da8cdf77ef..0c4d53780ef 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp @@ -623,7 +623,7 @@ public: bool isPropertyEnum(const PropertyName &propertyName) const; QStringList keysForEnum(const QString &enumName) const; bool cleverCheckType(const TypeName &otherType) const; - QVariant::Type variantTypeId(const PropertyName &properyName) const; + QMetaType::Type variantTypeId(const PropertyName &properyName) const; int majorVersion() const; int minorVersion() const; @@ -1137,50 +1137,50 @@ static TypeName toSimplifiedTypeName(const TypeName &typeName) return typeName.split('.').constLast(); } -QVariant::Type NodeMetaInfoPrivate::variantTypeId(const PropertyName &propertyName) const +QMetaType::Type NodeMetaInfoPrivate::variantTypeId(const PropertyName &propertyName) const { TypeName typeName = toSimplifiedTypeName(propertyType(propertyName)); if (typeName == "string") - return QVariant::String; + return QMetaType::QString; if (typeName == "color") - return QVariant::Color; + return QMetaType::QColor; if (typeName == "int") - return QVariant::Int; + return QMetaType::Int; if (typeName == "url") - return QVariant::Url; + return QMetaType::QUrl; if (typeName == "real") - return QVariant::Double; + return QMetaType::Double; if (typeName == "bool") - return QVariant::Bool; + return QMetaType::Bool; if (typeName == "boolean") - return QVariant::Bool; + return QMetaType::Bool; if (typeName == "date") - return QVariant::Date; + return QMetaType::QDate; if (typeName == "alias") - return QVariant::UserType; + return QMetaType::User; if (typeName == "var") - return QVariant::UserType; + return QMetaType::User; if (typeName == "vector2d") - return QVariant::Vector2D; + return QMetaType::QVector2D; if (typeName == "vector3d") - return QVariant::Vector3D; + return QMetaType::QVector3D; if (typeName == "vector4d") - return QVariant::Vector4D; + return QMetaType::QVector4D; - return QVariant::nameToType(typeName.data()); // This is deprecated + return QMetaType::UnknownType; } int NodeMetaInfoPrivate::majorVersion() const @@ -4477,15 +4477,15 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const if (isEnumType() || variant.canConvert() || typeName.endsWith("Flags")) return variant; - QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); + QMetaType::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); if (variant.typeId() == ModelNode::variantTypeId()) { return variant; - } else if (typeId == QVariant::UserType && typeName == "QVariant") { + } else if (typeId == QMetaType::User && typeName == "QVariant") { return variant; - } else if (typeId == QVariant::UserType && typeName == "variant") { + } else if (typeId == QMetaType::User && typeName == "variant") { return variant; - } else if (typeId == QVariant::UserType && typeName == "var") { + } else if (typeId == QMetaType::User && typeName == "var") { return variant; } else if (variant.typeId() == QMetaType::QVariantList) { // TODO: check the contents of the list @@ -4523,7 +4523,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const static constexpr auto qUrlType = QMetaType::fromType(); static constexpr auto qColorType = QMetaType::fromType(); - if (value.typeId() == QVariant::UserType && value.typeId() == ModelNode::variantTypeId()) { + if (value.typeId() == QMetaType::User && value.typeId() == ModelNode::variantTypeId()) { return value; } else if (typeId == m_projectStorage->builtinTypeId()) { return value; From 7dc8eb7da92947aefd68f655365368f4c827c28a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:32:02 +0100 Subject: [PATCH 128/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: Ic40906804a08cb24ae9673416fe20b6d04792f59 Reviewed-by: Ali Kianian Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/edit3d/edit3dcanvas.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 14344a03be5..480a8eb8c61 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -156,7 +156,7 @@ void Edit3DCanvas::mousePressEvent(QMouseEvent *e) { m_contextMenuPending = false; if (!m_flyMode && e->modifiers() == Qt::NoModifier && e->buttons() == Qt::RightButton) { - setFlyMode(true, e->globalPos()); + setFlyMode(true, e->globalPosition().toPoint()); m_parent->view()->startContextMenu(e->pos()); m_contextMenuPending = true; } @@ -186,12 +186,15 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) QWidget::mouseMoveEvent(e); - if (m_flyMode && e->globalPos() != m_hiddenCursorPos) { + const QPoint globalPos = e->globalPosition().toPoint(); + + if (m_flyMode && globalPos != m_hiddenCursorPos) { if (!m_flyModeFirstUpdate) { // We notify explicit camera rotation need for puppet rather than rely in mouse events, // as mouse isn't grabbed on puppet side and can't handle fast movements that go out of // edit camera mouse area. This also simplifies split view handling. - QPointF diff = m_isQDSTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos()); + QPointF diff = m_isQDSTrusted ? (m_hiddenCursorPos - globalPos) + : (m_lastCursorPos - e->globalPosition().toPoint()); if (auto model = m_parent->view()->model()) { if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) { @@ -209,7 +212,7 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) if (m_isQDSTrusted) QCursor::setPos(m_hiddenCursorPos); else - m_lastCursorPos = e->globalPos(); + m_lastCursorPos = globalPos; } } From 9be095cdb09f6952dd5cb89c2dcbad6a9f424c4d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 5 Nov 2024 17:21:33 +0200 Subject: [PATCH 129/322] EffectComposer: Add custom effect handling Effects can be now customized by adding and removing new properties or editing existing ones. Built-in properties of the effects can not be removed. Fixes: QDS-14023 Change-Id: I9ac7daac5d0e56d29880b02684b3db0a0d49f129 Reviewed-by: Mahmoud Badri --- .../AddPropertyForm.qml | 778 ++++++++++++++++++ .../EffectCompositionNode.qml | 69 +- .../EffectCompositionNodeUniform.qml | 159 ++-- .../effectComposerQmlSources/ValueBool.qml | 12 +- .../effectComposerQmlSources/ValueChannel.qml | 12 +- .../effectComposerQmlSources/ValueColor.qml | 8 +- .../effectComposerQmlSources/ValueDefine.qml | 11 +- .../effectComposerQmlSources/ValueFloat.qml | 17 +- .../effectComposerQmlSources/ValueImage.qml | 8 +- .../effectComposerQmlSources/ValueInt.qml | 18 +- .../effectComposerQmlSources/ValueVec2.qml | 16 +- .../effectComposerQmlSources/ValueVec3.qml | 21 +- .../effectComposerQmlSources/ValueVec4.qml | 26 +- .../imports/HelperWidgets/Section.qml | 1 + .../effectcomposer/compositionnode.cpp | 49 +- src/plugins/effectcomposer/compositionnode.h | 11 +- .../effectcomposer/effectcomposermodel.cpp | 75 ++ .../effectcomposer/effectcomposermodel.h | 13 +- .../effectcomposernodesmodel.cpp | 13 +- .../effectcomposer/effectcomposernodesmodel.h | 2 +- .../effectcomposeruniformsmodel.cpp | 32 + .../effectcomposeruniformsmodel.h | 6 +- .../effectcomposer/effectcomposerwidget.cpp | 3 +- src/plugins/effectcomposer/effectnode.cpp | 1 + src/plugins/effectcomposer/effectnode.h | 2 + src/plugins/effectcomposer/uniform.cpp | 15 +- src/plugins/effectcomposer/uniform.h | 15 +- 27 files changed, 1285 insertions(+), 108 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml new file mode 100644 index 00000000000..934a65b6e2e --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml @@ -0,0 +1,778 @@ +// Copyright (C) 2024 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 as StudioControls +import StudioTheme as StudioTheme +import HelperWidgets as HelperWidgets +import EffectComposerBackend + +Item { + id: root + + height: column.implicitHeight + (root.verticalSpacing * 4) + + readonly property real horizontalSpacing: 10 + readonly property real verticalSpacing: 5 + readonly property real labelWidth: 140 + readonly property real controlWidth: (root.width - labelWidth - root.horizontalSpacing + - (StudioTheme.Values.popupMargin * 2)) + + property var backendModel: EffectComposerBackend.effectComposerModel + + property string effectNodeName + + property int typeIndex: 1 + property string displayName + property string uniName + property string description + property var minValue: 0 + property var maxValue: 1 + property var defaultValue: 0 + property bool showMinMax: false + property var minPossibleValue: 0 + property var maxPossibleValue: 0 + property bool userAdded: false + property bool uniNameEdited: false + + property real minFloat: root.backendModel.valueLimit("float", false) + property real maxFloat: root.backendModel.valueLimit("float", true) + property int minInt: root.backendModel.valueLimit("int", false) + property int maxInt: root.backendModel.valueLimit("int", true) + + property var reservedDispNames: [] + property var reservedUniNames: [] + property bool propNameError: root.reservedDispNames.includes(nameText.text) + property bool uniNameError: root.reservedUniNames.includes(uniNameText.text) + + property var typeList: [ + {"type": "int", "controlType": "int"}, + {"type": "float", "controlType": "float"}, + {"type": "bool", "controlType": "bool"}, + {"type": "vec2", "controlType": "vec2"}, + {"type": "vec3", "controlType": "vec3"}, + {"type": "vec4", "controlType": "vec4"}, + {"type": "color", "controlType": "color"}, + {"type": "int", "controlType": "channel"}, + {"type": "sampler2D", "controlType": "sampler2D"}, + {"type": "define", "controlType": "int"}, + {"type": "define", "controlType": "bool"} + ] + + property var displayTypes: [ + qsTr("Integer"), + qsTr("Float"), + qsTr("Boolean"), + qsTr("Vector 2D"), + qsTr("Vector 3D"), + qsTr("Vector 4D"), + qsTr("Color"), + qsTr("Color channel"), + qsTr("Texture sampler"), + qsTr("Define (integer)"), + qsTr("Define (boolean)") + ] + + property vector4d compareMatches: Qt.vector4d(0, 0, 0, 0) + + // Backend value for sampler controls (UrlChooser needs this to function) + property var urlChooserBackendValue: QtObject { + id: urlChooserValue + property bool isBound: false + property var expression + property string valueToString + property var value + + signal valueChangedQml() + + onValueChanged: { + urlChooserValue.valueToString = value.toString() + urlChooserValue.valueChangedQml() + } + } + + signal accepted() + signal canceled() + + // If vector is assigned to variant, it just creates reference by default, which breaks the + // min/max logic. This function creates a deep copy of vectors. + function copyValue(v) + { + if (root.typeList[root.typeIndex].type === "vec2") + return Qt.vector2d(v.x, v.y) + else if (root.typeList[root.typeIndex].type === "vec3") + return Qt.vector3d(v.x, v.y, v.z) + else if (root.typeList[root.typeIndex].type === "vec4") + return Qt.vector4d(v.x, v.y, v.z, v.w) + + return v + } + + // For vectors, copies values of previous comparison matched subcomponents only + function copyMatchedSubcomponents(v, orig) + { + let isVec2 = root.typeList[root.typeIndex].type === "vec2" + let isVec3 = root.typeList[root.typeIndex].type === "vec3" + let isVec4 = root.typeList[root.typeIndex].type === "vec4" + if (isVec2 || isVec3 || isVec4) { + let vx = root.compareMatches.x > 0 ? v.x : orig.x + let vy = root.compareMatches.y > 0 ? v.y : orig.y + if (isVec3 || isVec4) { + let vz = root.compareMatches.z > 0 ? v.z : orig.z + if (isVec4) { + let vw = root.compareMatches.w > 0 ? v.w : orig.w + return Qt.vector4d(vx, vy, vz, vw) + } else { + return Qt.vector3d(vx, vy, vz) + } + } else { + return Qt.vector2d(vx, vy) + } + } + + return v + } + + function showForAdd() + { + root.uniNameEdited = false + + if (typeCombo.currentIndex === 0) + reloadType() + else + typeCombo.currentIndex = 0 + + root.displayName = root.backendModel.getUniqueDisplayName(root.reservedDispNames) + root.uniName = root.backendModel.generateUniformName(root.effectNodeName, root.displayName, "") + root.description = "" + root.userAdded = true + + titleLabel.text = qsTr("Add property") + root.visible = true + } + + function showForEdit(type, controlType, dispName, name, desc, defVal, min, max, user) + { + root.uniNameEdited = true + + let targetIndex = typeList.findIndex(function(element) { + return element.type === type && element.controlType === controlType; + }) + + if (targetIndex < 0) + return; + + root.userAdded = user + + if (typeCombo.currentIndex === targetIndex) + reloadType() + else + typeCombo.currentIndex = targetIndex + + root.displayName = dispName + root.description = desc + root.uniName = name + + if (root.showMinMax) { + root.minValue = root.copyValue(root.minPossibleValue) + root.maxValue = root.copyValue(root.maxPossibleValue) + root.maxValue = root.copyValue(max) + root.minValue = root.copyValue(min) + minValueLoader.uniformValue = root.copyValue(min) + maxValueLoader.uniformValue = root.copyValue(max) + } + root.defaultValue = root.copyValue(defVal) + if (root.urlChooserBackendValue) + root.urlChooserBackendValue.value = root.copyValue(root.defaultValue) + defaultValueLoader.uniformValue = root.copyValue(defVal) + + titleLabel.text = qsTr("Edit property") + root.visible = true + } + + function propertyData() + { + let propData = { + "type": root.typeList[root.typeIndex].type, + "controlType": root.typeList[root.typeIndex].controlType, + "displayName": root.displayName, + "name": root.uniName, + "description": root.description, + "minValue": root.minValue, + "maxValue": root.maxValue, + "defaultValue": root.defaultValue, + "userAdded": root.userAdded + } + + return propData; + } + + // Returns true if variant a is considered larger than variant b. + // In case of vectors, any subcomponent being larger qualifies. + // root.compareMatches subproperties are set to non-zero to indicate which subproperties + // were matched. + function compareValues(a, b) + { + let isVec2 = root.typeList[root.typeIndex].type === "vec2" + let isVec3 = root.typeList[root.typeIndex].type === "vec3" + let isVec4 = root.typeList[root.typeIndex].type === "vec4" + if (isVec2 || isVec3 || isVec4) { + root.compareMatches = Qt.vector4d(0, 0, 0, 0) + let match = false + if (a.x > b.x) { + match = true + root.compareMatches.x = 1 + } + if (a.y > b.y) { + match = true + root.compareMatches.y = 1 + } + if (isVec3 || isVec4) { + if (a.z > b.z) { + match = true + root.compareMatches.z = 1 + } + if (isVec4) { + if (a.w > b.w) { + match = true + root.compareMatches.w = 1 + } + } + } + return match + } else { + return a > b + } + } + + function reloadType() + { + defaultValueLoader.source = "" + minValueLoader.source = "" + maxValueLoader.source = "" + + defaultValueLoader.uniformBackendValue = null + + var sourceQml + let hasMinMax = false + if (root.typeList[root.typeIndex].controlType === "int") { + root.minPossibleValue = root.minInt + root.maxPossibleValue = root.maxInt + root.defaultValue = 0 + root.minValue = 0 + root.maxValue = 100 + hasMinMax = true + sourceQml = "ValueInt.qml" + } else if (root.typeList[root.typeIndex].controlType === "channel") { + root.minPossibleValue = root.minInt + root.maxPossibleValue = root.maxInt + root.defaultValue = 3 + root.minValue = 0 + root.maxValue = 3 + hasMinMax = false // Color channels have hardcoded min/max, so don't show controls + sourceQml= "ValueChannel.qml" + } else if (root.typeList[root.typeIndex].controlType === "vec2") { + root.minPossibleValue = Qt.vector2d(root.minFloat, root.minFloat) + root.maxPossibleValue = Qt.vector2d(root.maxFloat, root.maxFloat) + root.defaultValue = Qt.vector2d(0, 0) + root.minValue = Qt.vector2d(0, 0) + root.maxValue = Qt.vector2d(1, 1) + hasMinMax = true + sourceQml = "ValueVec2.qml" + } else if (root.typeList[root.typeIndex].controlType === "vec3") { + root.minPossibleValue = Qt.vector3d(root.minFloat, root.minFloat, root.minFloat) + root.maxPossibleValue = Qt.vector3d(root.maxFloat, root.maxFloat, root.maxFloat) + root.defaultValue = Qt.vector3d(0, 0, 0) + root.minValue = Qt.vector3d(0, 0, 0) + root.maxValue = Qt.vector3d(1, 1, 1) + hasMinMax = true + sourceQml = "ValueVec3.qml" + } else if (root.typeList[root.typeIndex].controlType === "vec4") { + root.minPossibleValue = Qt.vector4d(root.minFloat, root.minFloat, root.minFloat, root.minFloat) + root.maxPossibleValue = Qt.vector4d(root.maxFloat, root.maxFloat, root.maxFloat, root.maxFloat) + root.defaultValue = Qt.vector4d(0, 0, 0, 0) + root.minValue = Qt.vector4d(0, 0, 0, 0) + root.maxValue = Qt.vector4d(1, 1, 1, 1) + hasMinMax = true + sourceQml = "ValueVec4.qml" + } else if (root.typeList[root.typeIndex].controlType === "bool") { + root.defaultValue = false + sourceQml = "ValueBool.qml" + } else if (root.typeList[root.typeIndex].controlType === "color") { + root.defaultValue = Qt.rgba(0, 0, 0, 1) + sourceQml = "ValueColor.qml" + } else if (root.typeList[root.typeIndex].controlType === "sampler2D") { + root.defaultValue = "" + sourceQml = "ValueImage.qml" + defaultValueLoader.uniformBackendValue = root.urlChooserBackendValue + root.urlChooserBackendValue.value = root.copyValue(root.defaultValue) + } else { + root.minPossibleValue = root.minFloat + root.maxPossibleValue = root.maxFloat + root.defaultValue = 0 + root.minValue = 0 + root.maxValue = 1 + hasMinMax = true + sourceQml = "ValueFloat.qml" + } + + if (hasMinMax) { + minValueLoader.uniformValue = root.copyValue(root.minValue) + maxValueLoader.uniformValue = root.copyValue(root.maxValue) + minValueLoader.source = sourceQml + maxValueLoader.source = sourceQml + } + + defaultValueLoader.uniformValue = root.copyValue(root.defaultValue) + defaultValueLoader.source = sourceQml + + root.showMinMax = hasMinMax + } + + onMinValueChanged: { + if (!root.showMinMax) + return + + if (root.compareValues(root.minValue, root.maxValue)) { + root.maxValue = root.copyMatchedSubcomponents(root.minValue, root.maxValue) + maxValueLoader.uniformValue = root.copyValue(root.maxValue) + } + if (root.compareValues(root.minValue, root.defaultValue)) { + root.defaultValue = root.copyMatchedSubcomponents(root.minValue, root.defaultValue) + defaultValueLoader.uniformValue = root.copyValue(root.defaultValue) + } + } + onMaxValueChanged: { + if (!root.showMinMax) + return + + if (root.compareValues(root.minValue, root.maxValue)) { + root.minValue = root.copyMatchedSubcomponents(root.maxValue, root.minValue) + minValueLoader.uniformValue = root.copyValue(root.minValue) + } + if (root.compareValues(root.defaultValue, root.maxValue)) { + root.defaultValue = root.copyMatchedSubcomponents(root.maxValue, root.defaultValue) + defaultValueLoader.uniformValue = root.copyValue(root.defaultValue) + } + } + + onTypeIndexChanged: reloadType() + + Rectangle { + anchors.fill: parent + anchors.topMargin: 8 + anchors.rightMargin: 8 + border.width: 1 + border.color: StudioTheme.Values.themeControlOutline + color: StudioTheme.Values.themeSectionHeadBackground + + Column { + id: column + anchors.fill: parent + anchors.topMargin: root.verticalSpacing + anchors.bottomMargin: root.verticalSpacing + spacing: root.verticalSpacing + + Text { + id: titleLabel + color: StudioTheme.Values.themeControlOutlineInteraction + font.bold: true + font.pixelSize: StudioTheme.Values.baseFontSize + width: parent.width + height: 30 + horizontalAlignment: Text.AlignHCenter + } + + Row { + spacing: root.horizontalSpacing + width: parent.width + + Text { + id: nameLabel + color: StudioTheme.Values.themeTextColor + text: qsTr("Display Name") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the display name of the property.") + } + } + + StudioControls.TextField { + id: nameText + + width: root.controlWidth + anchors.verticalCenter: parent.verticalCenter + + actionIndicatorVisible: false + translationIndicatorVisible: false + + text: root.displayName + + KeyNavigation.tab: uniNameText + + onEditingFinished: { + root.displayName = nameText.text + if (!root.uniNameEdited) { + root.uniName = root.backendModel.generateUniformName( + root.effectNodeName, root.displayName, root.uniName) + } + } + } + } + + // Error line (for invalid name) + Row { + width: parent.width + spacing: root.horizontalSpacing + + Item { // Spacer + width: root.labelWidth + height: 1 + } + + Text { + text: qsTr("Display name of the property has to be unique.") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.controlWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + visible: root.propNameError + color: StudioTheme.Values.themeWarning + } + } + + Row { + spacing: root.horizontalSpacing + width: parent.width + + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Uniform Name") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the uniform name of the property.") + } + } + + StudioControls.TextField { + id: uniNameText + + width: root.controlWidth + anchors.verticalCenter: parent.verticalCenter + + actionIndicatorVisible: false + translationIndicatorVisible: false + + text: root.uniName + + KeyNavigation.tab: descriptionEdit + + onEditingFinished:{ + if (root.uniName !== uniNameText.text) { + root.uniName = uniNameText.text + root.uniNameEdited = true + } + } + } + } + + // Error line (for invalid uniform name) + Row { + width: parent.width + spacing: root.horizontalSpacing + + Item { // Spacer + width: root.labelWidth + height: 1 + } + + Text { + text: qsTr("Uniform name has to be unique.") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.controlWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + visible: root.uniNameError + color: StudioTheme.Values.themeWarning + } + } + + Row { + spacing: root.horizontalSpacing + width: parent.width + + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Description") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the property description.") + } + } + + Rectangle { + color: StudioTheme.Values.controlStyle.background.idle + border.color: StudioTheme.Values.controlStyle.border.idle + border.width: StudioTheme.Values.controlStyle.borderWidth + height: descriptionEdit.height + width: root.controlWidth + clip: true + + TextEdit { + id: descriptionEdit + padding: StudioTheme.Values.controlStyle.inputHorizontalPadding + width: parent.width + height: descriptionEdit.contentHeight + descriptionEdit.topPadding + + descriptionEdit.bottomPadding + + font.pixelSize: StudioTheme.Values.baseFontSize + color: StudioTheme.Values.themeTextColor + wrapMode: TextEdit.WordWrap + + text: root.description + + KeyNavigation.tab: typeCombo + + onEditingFinished: root.description = descriptionEdit.text + } + } + } + + Row { + width: parent.width + spacing: root.horizontalSpacing + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Type") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the property type.") + } + } + + StudioControls.ComboBox { + id: typeCombo + style: StudioTheme.Values.connectionPopupControlStyle + width: root.controlWidth + + model: root.displayTypes + actionIndicatorVisible: false + + onCurrentIndexChanged: root.typeIndex = currentIndex + + delegate: ItemDelegate { + required property int index + required property var modelData + + width: typeCombo.width + highlighted: typeCombo.highlightedIndex === index + contentItem: Text { + text: modelData + color: StudioTheme.Values.themeTextColor + font: typeCombo.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + } + } + + Row { + width: parent.width + spacing: root.horizontalSpacing + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Default Value") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the default value of the property.") + } + } + + Loader { + id: defaultValueLoader + + property var uniformValue + + // No need to deep copy min/max as these bindings do not change the value + property var uniformMinValue: root.minValue + property var uniformMaxValue: root.maxValue + + // These are needed for sampler controls (ValueImage type) + property var uniformBackendValue + property string uniformName: root.uniName + property var uniformDefaultValue + + width: root.controlWidth + + Connections { + target: defaultValueLoader.item + + function onValueChanged() { + root.defaultValue = root.copyValue(defaultValueLoader.uniformValue) + } + } + } + } + + Row { + width: parent.width + spacing: root.horizontalSpacing + visible: root.showMinMax + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Min Value") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the minimum value of the property.") + } + } + + Loader { + id: minValueLoader + + property var uniformValue + // No need to deep copy min/max as these bindings do not change the value + property var uniformMinValue: root.minPossibleValue + property var uniformMaxValue: root.maxValue + + width: root.controlWidth + + Connections { + target: minValueLoader.item + + function onValueChanged() { + root.minValue = root.copyValue(minValueLoader.uniformValue) + } + } + + onLoaded: { + if (root.typeList[root.typeIndex].controlType === "int" + || root.typeList[root.typeIndex].controlType === "float") { + item.hideSlider = true + } + } + } + } + + Row { + width: parent.width + spacing: root.horizontalSpacing + visible: root.showMinMax + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Max Value") + font.pixelSize: StudioTheme.Values.baseFontSize + anchors.verticalCenter: parent.verticalCenter + width: root.labelWidth + elide: Text.ElideRight + horizontalAlignment: Text.AlignRight + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("Sets the maximum value of the property.") + } + } + + Loader { + id: maxValueLoader + + property var uniformValue + // No need to deep copy min/max as these bindings do not change the value + property var uniformMinValue: root.minValue + property var uniformMaxValue: root.maxPossibleValue + + width: root.controlWidth + + Connections { + target: maxValueLoader.item + + function onValueChanged() { + root.maxValue = root.copyValue(maxValueLoader.uniformValue) + } + } + + onLoaded: { + if (root.typeList[root.typeIndex].controlType === "int" + || root.typeList[root.typeIndex].controlType === "float") { + item.hideSlider = true + } + } + } + } + + Row { + width: acceptButton.width + root.horizontalSpacing + cancelButton.width + spacing: root.horizontalSpacing + anchors.horizontalCenter: parent.horizontalCenter + height: 35 + + HelperWidgets.Button { + id: cancelButton + width: 130 + height: 35 + text: qsTr("Cancel") + padding: 4 + + onClicked: { + root.canceled() + root.visible = false + } + } + + HelperWidgets.Button { + id: acceptButton + width: 130 + height: 35 + text: qsTr("Apply") + padding: 4 + enabled: !root.propNameError && !root.uniNameError + + onClicked: { + root.accepted() + root.visible = false + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index fe19390a0f1..0c56893bd39 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -15,6 +15,7 @@ HelperWidgets.Section { readonly property bool codeEditorOpen: root.backendModel.codeEditorIndex === root.modelIndex property int modelIndex: 0 + property int editedUniformIndex: -1 caption: nodeName category: "EffectComposer" @@ -23,7 +24,8 @@ HelperWidgets.Section { fillBackground: true showCloseButton: !isDependency closeButtonToolTip: qsTr("Remove") - visible: repeater.count > 0 || !isDependency + closeButtonIcon: StudioTheme.Constants.deletepermanently_medium + visible: repeater.count > 0 || !isDependency || isCustom onCloseButtonClicked: { root.backendModel.removeNode(root.modelIndex) @@ -72,9 +74,72 @@ HelperWidgets.Section { model: nodeUniformsModel EffectCompositionNodeUniform { - width: root.width + id: effectCompositionNodeUniform + width: root.width - StudioTheme.Values.scrollBarThicknessHover + removable: uniformUserAdded + editing: root.editedUniformIndex === index + disableMoreMenu: root.editedUniformIndex >= 0 onReset: nodeUniformsModel.resetData(index) + onRemove: nodeUniformsModel.remove(index) + onEdit: { + addPropertyForm.parent = effectCompositionNodeUniform.editPropertyFormParent + let dispNames = nodeUniformsModel.displayNames() + let filteredDispNames = dispNames.filter(name => name !== uniformDisplayName); + addPropertyForm.reservedDispNames = filteredDispNames + let uniNames = root.backendModel.uniformNames() + let filteredUniNames = uniNames.filter(name => name !== uniformName); + addPropertyForm.reservedUniNames = filteredUniNames + root.editedUniformIndex = index + addPropertyForm.showForEdit(uniformType, uniformControlType, uniformDisplayName, + uniformName, uniformDescription, uniformDefaultValue, + uniformMinValue, uniformMaxValue, uniformUserAdded) + } + } + } + } + + Item { + id: addProperty + width: root.width - StudioTheme.Values.scrollBarThicknessHover + height: addPropertyForm.visible && addPropertyForm.parent === addProperty + ? addPropertyForm.height : 50 + + HelperWidgets.Button { + id: addPropertyButton + width: 130 + height: 35 + text: qsTr("Add Property") + visible: !addPropertyForm.visible + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + onClicked: { + root.editedUniformIndex = -1 + addPropertyForm.parent = addProperty + addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() + addPropertyForm.reservedUniNames = root.backendModel.uniformNames() + addPropertyForm.showForAdd() + } + } + + AddPropertyForm { + id: addPropertyForm + visible: false + width: parent.width + + effectNodeName: nodeName + + onAccepted: { + root.backendModel.addOrUpdateNodeUniform(root.modelIndex, + addPropertyForm.propertyData(), + root.editedUniformIndex) + addPropertyForm.parent = addProperty + root.editedUniformIndex = -1 + } + + onCanceled: { + addPropertyForm.parent = addProperty + root.editedUniformIndex = -1 } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 7c3214c5fa7..92868eb19c5 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -12,10 +12,17 @@ import EffectComposerBackend Item { id: root - height: layout.implicitHeight + property bool removable: false + property bool editing: false + property bool disableMoreMenu: false + property alias editPropertyFormParent: editPropertyFormPlaceholder + + height: layout.implicitHeight + editPropertyFormPlaceholder.height + column.spacing visible: !uniformUseCustomValue signal reset() + signal remove() + signal edit() Component.onCompleted: { if (uniformType === "int") { @@ -47,59 +54,119 @@ Item { } } - RowLayout { - id: layout - + Column { + id: column anchors.fill: parent + spacing: 5 - Text { - id: textName + RowLayout { + id: layout - text: uniformDisplayName - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignRight - Layout.maximumWidth: 140 - Layout.minimumWidth: 140 - Layout.preferredWidth: 140 - elide: Text.ElideRight + width: parent.width - HelperWidgets.ToolTipArea { - id: tooltipArea + Text { + id: textName - anchors.fill: parent - tooltip: uniformDescription + text: (root.editing ? qsTr("[Editing] ") : "") + uniformDisplayName + color: root.editing ? StudioTheme.Values.themeControlOutlineInteraction + : StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignRight + Layout.preferredWidth: 140 + Layout.maximumWidth: Layout.preferredWidth + Layout.minimumWidth: Layout.preferredWidth + elide: Text.ElideRight + + HelperWidgets.ToolTipArea { + id: tooltipArea + + anchors.fill: parent + tooltip: uniformDescription + } + } + + Item { + Layout.preferredHeight: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: Layout.preferredWidth + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + } + + HelperWidgets.IconButton { + id: iconButton + + buttonSize: 24 + icon: StudioTheme.Constants.reload_medium + iconSize: 16 + anchors.centerIn: parent + visible: mouseArea.containsMouse || iconButton.containsMouse + tooltip: qsTr("Reset value") + onClicked: root.reset() + } + } + + Loader { + id: valueLoader + Layout.fillWidth: true + } + + Item { + Layout.preferredHeight: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: Layout.preferredWidth + + HelperWidgets.IconButton { + id: moreButton + buttonSize: 24 + icon: StudioTheme.Constants.more_medium + iconSize: 10 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + tooltip: root.disableMoreMenu ? qsTr("Additional actions disabled while editing existing property.") + : qsTr("Access additional property actions.") + enabled: !root.disableMoreMenu + + onClicked: menuLoader.show() + } + + Loader { + id: menuLoader + + active: false + + function show() { + menuLoader.active = true + item.popup() + } + + sourceComponent: Component { + StudioControls.Menu { + id: menu + + StudioControls.MenuItem { + text: qsTr("Edit") + onTriggered: root.edit() + } + + StudioControls.MenuItem { + text: qsTr("Remove") + onTriggered: root.remove() + enabled: root.removable + } + } + } + } } } - Item { - Layout.preferredHeight: 30 - Layout.preferredWidth: 30 - - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: true - } - - HelperWidgets.IconButton { - id: iconButton - - buttonSize: 24 - icon: StudioTheme.Constants.reload_medium - iconSize: 16 - anchors.centerIn: parent - visible: mouseArea.containsMouse || iconButton.containsMouse - tooltip: qsTr("Reset value") - onClicked: root.reset() - } - - } - - Loader { - id: valueLoader - Layout.fillWidth: true + id: editPropertyFormPlaceholder + width: parent.width + height: childrenRect.height } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueBool.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueBool.qml index 9d2a518dcf0..b68f4040aa3 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueBool.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueBool.qml @@ -3,14 +3,20 @@ import QtQuick import StudioControls as StudioControls -import StudioTheme as StudioTheme -import EffectComposerBackend Item { // The wrapper Item is used to limit hovering and clicking the CheckBox to its area + id: root + height: 30 + + signal valueChanged() + StudioControls.CheckBox { actionIndicatorVisible: false checked: uniformValue - onToggled: uniformValue = checked + onToggled: { + uniformValue = checked + root.valueChanged() + } anchors.verticalCenter: parent.verticalCenter } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml index a089caf3efb..e7316369b1b 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml @@ -2,14 +2,20 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import StudioTheme as StudioTheme import StudioControls as StudioControls Row { + id: root + + signal valueChanged() + StudioControls.ComboBox { model: ["R", "G", "B", "A"] actionIndicatorVisible: false - Component.onCompleted: currentIndex = uniformValue ?? 3 - onActivated: uniformValue = currentIndex + currentIndex: uniformValue ?? 3 + onActivated: { + uniformValue = currentIndex + root.valueChanged() + } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml index f7482260b58..4eefa1b9f3c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml @@ -3,8 +3,6 @@ import QtQuick import StudioControls as StudioControls -import StudioTheme as StudioTheme -import EffectComposerBackend Row { id: itemPane @@ -12,6 +10,8 @@ Row { width: parent.width spacing: 5 + signal valueChanged() + StudioControls.ColorEditor { actionIndicatorVisible: false @@ -21,8 +21,10 @@ Row { onResetValueChanged: color = uniformValue Component.onCompleted: color = uniformValue onColorChanged: { - if (uniformValue !== color) + if (uniformValue !== color) { uniformValue = color + itemPane.valueChanged() + } } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueDefine.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueDefine.qml index b854cd890cb..195f9ab18da 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueDefine.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueDefine.qml @@ -3,12 +3,14 @@ import QtQuick import StudioControls as StudioControls -import StudioTheme as StudioTheme -import EffectComposerBackend Row { + id: root + width: parent.width + signal valueChanged() + StudioControls.TextField { id: textField @@ -19,6 +21,9 @@ Row { text: uniformValue - onEditingFinished: uniformValue = text + onEditingFinished: { + uniformValue = text + root.valueChanged() + } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index 6b723a9bc59..a52e0846c28 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -4,13 +4,16 @@ import QtQuick import HelperWidgets as HelperWidgets import StudioControls as StudioControls -import StudioTheme as StudioTheme -import EffectComposerBackend Row { + id: root + property bool hideSlider: false + width: parent.width spacing: 5 + signal valueChanged() + HelperWidgets.DoubleSpinBox { id: spinBox @@ -26,14 +29,17 @@ Row { value: uniformValue stepSize: .01 decimals: 2 - onValueModified: uniformValue = value + onValueModified: { + uniformValue = value + root.valueChanged() + } } StudioControls.Slider { id: slider - width: parent.width - 100 - visible: width > 20 + width: parent.width - spinBox.width - root.spacing + visible: !hideSlider && width > 20 labels: false decimals: 2 actionIndicatorVisible: false @@ -45,6 +51,7 @@ Row { let fixedValue = Number.parseFloat(value).toFixed(slider.decimals) uniformValue = fixedValue spinBox.value = fixedValue // binding isn't working for this property so update it + root.valueChanged() } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml index bed9dfdce0c..824686045b4 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml @@ -3,7 +3,6 @@ import QtQuick import HelperWidgets as HelperWidgets -import StudioTheme as StudioTheme import EffectComposerBackend Row { @@ -11,6 +10,8 @@ Row { spacing: 5 + signal valueChanged() + HelperWidgets.UrlChooser { backendValue: uniformBackendValue resourcesPath: EffectComposerBackend.rootView.imagesPath() @@ -18,7 +19,10 @@ Row { actionIndicatorVisible: false comboBox.width: Math.min(parent.width - 70, 300) - onAbsoluteFilePathChanged: uniformValue = absoluteFilePath + onAbsoluteFilePathChanged: { + uniformValue = absoluteFilePath + itemPane.valueChanged() + } function defaultAsString(defaultPath) { if (!defaultPath) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml index d67929168a9..ca632435e6a 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml @@ -4,13 +4,17 @@ import QtQuick import HelperWidgets as HelperWidgets import StudioControls as StudioControls -import StudioTheme as StudioTheme -import EffectComposerBackend Row { + id: root + + property bool hideSlider: false + width: parent.width spacing: 5 + signal valueChanged() + HelperWidgets.DoubleSpinBox { id: spinBox @@ -26,14 +30,17 @@ Row { value: uniformValue stepSize: 1 decimals: 0 - onValueModified: uniformValue = Math.round(value) + onValueModified: { + uniformValue = Math.round(value) + root.valueChanged() + } } StudioControls.Slider { id: slider - width: parent.width - 100 - visible: width > 20 + width: parent.width - spinBox.width - root.spacing + visible: !hideSlider && width > 20 labels: false actionIndicatorVisible: false handleLabelVisible: false @@ -43,6 +50,7 @@ Row { onMoved: { uniformValue = Math.round(value) spinBox.value = Math.round(value) + root.valueChanged() } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml index adb4fe99056..b051cfad297 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml @@ -4,14 +4,16 @@ import QtQuick import QtQuick.Layouts import HelperWidgets as HelperWidgets -import StudioControls as StudioControls import StudioTheme as StudioTheme -import EffectComposerBackend RowLayout { + id: root + width: parent.width spacing: 0 + signal valueChanged() + HelperWidgets.DoubleSpinBox { id: vX @@ -30,7 +32,10 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueModified: { + uniformValue.x = value + root.valueChanged() + } } Item { // spacer @@ -70,7 +75,10 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueModified: { + uniformValue.y = value + root.valueChanged() + } } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml index 78573c48f67..7f8d2d5aeec 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml @@ -4,14 +4,16 @@ import QtQuick import QtQuick.Layouts import HelperWidgets as HelperWidgets -import StudioControls as StudioControls import StudioTheme as StudioTheme -import EffectComposerBackend RowLayout { + id: root + width: parent.width spacing: 0 + signal valueChanged() + HelperWidgets.DoubleSpinBox { id: vX @@ -30,7 +32,10 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueModified: { + uniformValue.x = value + root.valueChanged() + } } Item { // spacer @@ -70,7 +75,10 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueModified: { + uniformValue.y = value + root.valueChanged() + } } Item { // spacer @@ -110,7 +118,10 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueModified: uniformValue.z = value + onValueModified: { + uniformValue.z = value + root.valueChanged() + } } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml index 61ce8e63893..ef1ba1f518c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml @@ -4,14 +4,16 @@ import QtQuick import QtQuick.Layouts import HelperWidgets as HelperWidgets -import StudioControls as StudioControls import StudioTheme as StudioTheme -import EffectComposerBackend RowLayout { + id: root + width: parent.width spacing: 0 + signal valueChanged() + HelperWidgets.DoubleSpinBox { id: vX @@ -30,7 +32,10 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueModified: { + uniformValue.x = value + root.valueChanged() + } } Item { // spacer @@ -70,7 +75,10 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueModified: { + uniformValue.y = value + root.valueChanged() + } } Item { // spacer @@ -110,7 +118,10 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueModified: uniformValue.z = value + onValueModified: { + uniformValue.z = value + root.valueChanged() + } } Item { // spacer @@ -150,7 +161,10 @@ RowLayout { value: uniformValue.w stepSize: .01 decimals: 2 - onValueModified: uniformValue.w = value + onValueModified: { + uniformValue.w = value + root.valueChanged() + } } Item { // spacer diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 232574a2207..a24edc4904d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -21,6 +21,7 @@ Item { property alias showLeftBorder: leftBorder.visible property alias showCloseButton: closeButton.visible property alias closeButtonToolTip: closeButton.tooltip + property alias closeButtonIcon: closeButton.icon property alias showEyeButton: eyeButton.visible property alias eyeButtonToolTip: eyeButton.tooltip property alias spacing: column.spacing diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 818b877a943..567d63e6b39 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -43,6 +43,13 @@ CompositionNode::CompositionNode(const QString &effectName, const QString &qenPa else { parse(effectName, "", jsonObject); } + + connect(&m_uniformsModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, + [this](const QModelIndex &, int row, int) { + QTC_ASSERT(m_uniforms.size() > row, return); + m_uniforms.removeAt(row); + emit rebakeRequested(); + }); } CompositionNode::~CompositionNode() @@ -72,7 +79,7 @@ QString CompositionNode::id() const QObject *CompositionNode::uniformsModel() { - return &m_unifomrsModel; + return &m_uniformsModel; } QStringList CompositionNode::requiredNodes() const @@ -98,6 +105,11 @@ bool CompositionNode::isDependency() const return m_refCount > 0; } +bool CompositionNode::isCustom() const +{ + return m_isCustom; +} + CompositionNode::NodeType CompositionNode::type() const { return m_type; @@ -125,6 +137,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c if (json.contains("enabled")) m_isEnabled = json["enabled"].toBool(); + if (json.contains("custom")) + m_isCustom = json["custom"].toBool(); + m_id = json.value("id").toString(); if (m_id.isEmpty() && !qenPath.isEmpty()) { QString fileName = qenPath.split('/').last(); @@ -136,7 +151,7 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c QJsonArray jsonProps = json.value("properties").toArray(); for (const QJsonValueConstRef &prop : jsonProps) { const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); - m_unifomrsModel.addUniform(uniform); + m_uniformsModel.addUniform(uniform); m_uniforms.append(uniform); g_propertyData.insert(uniform->name(), uniform->value()); if (uniform->type() == Uniform::Type::Define) { @@ -166,7 +181,7 @@ void CompositionNode::ensureShadersCodeEditor() return; m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr(name()); - m_shadersCodeEditor->setUniformsModel(&m_unifomrsModel); + m_shadersCodeEditor->setUniformsModel(&m_uniformsModel); m_shadersCodeEditor->setFragmentValue(fragmentCode()); m_shadersCodeEditor->setVertexValue(vertexCode()); @@ -270,10 +285,38 @@ void CompositionNode::closeCodeEditor() m_shadersCodeEditor->close(); } +void CompositionNode::addUniform(const QVariantMap &data) +{ + const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {}); + m_uniforms.append(uniform); + g_propertyData.insert(uniform->name(), uniform->value()); + m_uniformsModel.addUniform(uniform); +} + +void CompositionNode::updateUniform(int index, const QVariantMap &data) +{ + QTC_ASSERT(index < m_uniforms.size() && index >= 0, return); + + const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {}); + + m_uniforms[index] = uniform; + g_propertyData.insert(uniform->name(), uniform->value()); + m_uniformsModel.updateUniform(index, uniform); +} + QString CompositionNode::name() const { return m_name; } +void CompositionNode::setName(const QString &name) +{ + if (m_name == name) + return; + + m_name = name; + emit nameChanged(); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index a57d4cb4eba..9b87f385d94 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -18,9 +18,10 @@ class CompositionNode : public QObject { Q_OBJECT - Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT) + Q_PROPERTY(QString nodeName READ name WRITE setName NOTIFY nameChanged CONSTANT) Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged) + Q_PROPERTY(bool isCustom READ isCustom CONSTANT) Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) Q_PROPERTY( QString fragmentCode @@ -54,8 +55,10 @@ public: void setIsEnabled(bool newIsEnabled); bool isDependency() const; + bool isCustom() const; QString name() const; + void setName(const QString &name); QList uniforms() const; @@ -70,6 +73,8 @@ public: void openCodeEditor(); void closeCodeEditor(); + void addUniform(const QVariantMap &data); + void updateUniform(int index, const QVariantMap &data); signals: void uniformsModelChanged(); @@ -79,6 +84,7 @@ signals: void fragmentCodeChanged(); void vertexCodeChanged(); void codeEditorVisibilityChanged(bool); + void nameChanged(); private: void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); @@ -93,12 +99,13 @@ private: QStringList m_requiredNodes; QString m_id; bool m_isEnabled = true; + bool m_isCustom = false; int m_refCount = 0; int m_extraMargin = 0; QList m_uniforms; - EffectComposerUniformsModel m_unifomrsModel; + EffectComposerUniformsModel m_uniformsModel; Utils::UniqueObjectLatePtr m_shadersCodeEditor; }; diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 98247d389ee..67c54fe1a39 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -78,6 +78,7 @@ QHash EffectComposerModel::roleNames() const {EnabledRole, "nodeEnabled"}, {UniformsRole, "nodeUniformsModel"}, {Dependency, "isDependency"}, + {Custom, "isCustom"} }; return roles; } @@ -255,6 +256,13 @@ QString EffectComposerModel::getUniqueEffectName() const }); } +QString EffectComposerModel::getUniqueDisplayName(const QStringList reservedNames) const +{ + return QmlDesigner::UniqueName::generate(tr("New Property"), [&reservedNames] (const QString &name) { + return reservedNames.contains(name); + }); +} + bool EffectComposerModel::nameExists(const QString &name) const { const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); @@ -641,6 +649,7 @@ QJsonObject nodeToJson(const CompositionNode &node) nodeObject.insert("enabled", node.isEnabled()); nodeObject.insert("version", 1); nodeObject.insert("id", node.id()); + nodeObject.insert("custom", node.isCustom()); if (node.extraMargin()) nodeObject.insert("extraMargin", node.extraMargin()); @@ -653,6 +662,8 @@ QJsonObject nodeToJson(const CompositionNode &node) QString type = Uniform::stringFromType(uniform->type()); uniformObject.insert("type", type); + if (uniform->userAdded()) + uniformObject.insert("userAdded", true); QString controlType = Uniform::stringFromType(uniform->controlType()); if (controlType != type) @@ -1226,6 +1237,23 @@ void EffectComposerModel::openMainCodeEditor() m_nodes.at(oldIndex)->closeCodeEditor(); } +QVariant EffectComposerModel::valueLimit(const QString &type, bool max) const +{ + static const int intMin = std::numeric_limits::lowest(); + static const int intMax = std::numeric_limits::max(); + static const float floatMin = std::numeric_limits::lowest(); + static const float floatMax = std::numeric_limits::max(); + + if (type == "float") + return max ? floatMax : floatMin; + if (type == "int") + return max ? intMax : intMin; + + qWarning() << __FUNCTION__ << "Invalid type for limit:" << type; + + return {}; +} + void EffectComposerModel::openComposition(const QString &path) { clear(true); @@ -2461,6 +2489,23 @@ QStringList EffectComposerModel::uniformNames() const return usedList; } +QString EffectComposerModel::generateUniformName(const QString &nodeName, + const QString &propertyName, + const QString &oldName) const +{ + const QStringList allNames = uniformNames(); + QString uniformName = nodeName; + if (!propertyName.isEmpty()) { + QString fixedPropName = propertyName; + fixedPropName[0] = fixedPropName[0].toUpper(); + uniformName.append(fixedPropName); + } + + return QmlDesigner::UniqueName::generateId(uniformName, [&] (const QString &name) { + return allNames.contains(name) && name != oldName; + }); +} + bool EffectComposerModel::isDependencyNode(int index) const { if (m_nodes.size() > index) @@ -2468,6 +2513,15 @@ bool EffectComposerModel::isDependencyNode(int index) const return false; } +bool EffectComposerModel::hasCustomNode() const +{ + for (const auto *node : std::as_const(m_nodes)) { + if (node->isCustom()) + return true; + } + return false; +} + void EffectComposerModel::updateQmlComponent() { // Clear possible QML runtime errors @@ -2484,4 +2538,25 @@ QString EffectComposerModel::stripFileFromURL(const QString &urlString) const return filePath; } +void EffectComposerModel::addOrUpdateNodeUniform(int idx, const QVariantMap &data, int updateIndex) +{ + QTC_ASSERT(m_nodes.size() > idx && idx >= 0, return); + + // Convert values to Uniform digestible strings + auto fixedData = data; + auto type = Uniform::typeFromString(data["type"].toString()); + auto controlType = Uniform::typeFromString(data["controlType"].toString()); + fixedData["defaultValue"] = variantAsDataString(type, controlType, data["defaultValue"]); + fixedData["minValue"] = variantAsDataString(type, controlType, data["minValue"]); + fixedData["maxValue"] = variantAsDataString(type, controlType, data["maxValue"]); + + if (updateIndex >= 0) + m_nodes[idx]->updateUniform(updateIndex, fixedData); + else + m_nodes[idx]->addUniform(fixedData); + + setHasUnsavedChanges(true); + startRebakeTimer(); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 44480640d27..3b41572905b 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -82,10 +82,12 @@ public: Q_INVOKABLE void clear(bool clearName = false); Q_INVOKABLE void assignToSelected(); Q_INVOKABLE QString getUniqueEffectName() const; + Q_INVOKABLE QString getUniqueDisplayName(const QStringList reservedNames) const; Q_INVOKABLE bool nameExists(const QString &name) const; Q_INVOKABLE void chooseCustomPreviewImage(); Q_INVOKABLE void previewComboAboutToOpen(); Q_INVOKABLE void removeCustomPreviewImage(const QUrl &url); + Q_INVOKABLE void addOrUpdateNodeUniform(int idx, const QVariantMap &data, int updateIndex); bool shadersUpToDate() const; void setShadersUpToDate(bool newShadersUpToDate); @@ -120,6 +122,8 @@ public: Q_INVOKABLE void openCodeEditor(int idx); Q_INVOKABLE void openMainCodeEditor(); + Q_INVOKABLE QVariant valueLimit(const QString &type, bool max) const; + void openComposition(const QString &path); QString currentComposition() const; @@ -137,10 +141,14 @@ public: bool hasUnsavedChanges() const; void setHasUnsavedChanges(bool val); - QStringList uniformNames() const; + Q_INVOKABLE QStringList uniformNames() const; + Q_INVOKABLE QString generateUniformName(const QString &nodeName, const QString &propertyName, + const QString &oldName) const; Q_INVOKABLE bool isDependencyNode(int index) const; + bool hasCustomNode() const; + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -165,7 +173,8 @@ private: NameRole = Qt::UserRole + 1, EnabledRole, UniformsRole, - Dependency + Dependency, + Custom }; enum ErrorTypes { diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp index 1d1d405e819..6b04045bc00 100644 --- a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp @@ -79,8 +79,13 @@ void EffectComposerNodesModel::loadModel() m_categories.push_back(category); } + const QString customCatName = "Custom"; std::sort(m_categories.begin(), m_categories.end(), - [](EffectNodesCategory *a, EffectNodesCategory *b) { + [&customCatName](EffectNodesCategory *a, EffectNodesCategory *b) { + if (a->name() == customCatName) + return false; + if (b->name() == customCatName) + return true; return a->name() < b->name(); }); @@ -95,7 +100,7 @@ void EffectComposerNodesModel::resetModel() endResetModel(); } -void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms) +void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms, bool hasCustom) { for (const EffectNodesCategory *cat : std::as_const(m_categories)) { const QList nodes = cat->nodes(); @@ -106,6 +111,10 @@ void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms) if (match) break; } + // For now we limit custom nodes to one as renaming nodes is not yet properly supported + if (!match) + match = hasCustom && node->isCustom(); + node->setCanBeAdded(!match); } } diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.h b/src/plugins/effectcomposer/effectcomposernodesmodel.h index ce914a5e6da..db16c304c72 100644 --- a/src/plugins/effectcomposer/effectcomposernodesmodel.h +++ b/src/plugins/effectcomposer/effectcomposernodesmodel.h @@ -30,7 +30,7 @@ public: QList categories() const { return m_categories; } - void updateCanBeAdded(const QStringList &uniforms); + void updateCanBeAdded(const QStringList &uniforms, bool hasCustom); QHash defaultImagesForNode(const QString &name) const; diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index c2c162a87ee..0ea39b03605 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -29,6 +29,7 @@ QHash EffectComposerUniformsModel::roleNames() const roles[TypeRole] = "uniformType"; roles[ControlTypeRole] = "uniformControlType"; roles[UseCustomValueRole] = "uniformUseCustomValue"; + roles[UserAdded] = "uniformUserAdded"; return roles; } @@ -83,6 +84,26 @@ bool EffectComposerUniformsModel::resetData(int row) return setData(idx, idx.data(DefaultValueRole), ValueRole); } +bool EffectComposerUniformsModel::remove(int row) +{ + QModelIndex idx = index(row, 0); + QTC_ASSERT(idx.isValid(), return false); + + beginRemoveRows({}, row, row); + m_uniforms.removeAt(row); + endRemoveRows(); + + return true; +} + +QStringList EffectComposerUniformsModel::displayNames() const +{ + QStringList displayNames; + for (Uniform *u : std::as_const(m_uniforms)) + displayNames.append(u->displayName()); + return displayNames; +} + void EffectComposerUniformsModel::resetModel() { beginResetModel(); @@ -96,6 +117,17 @@ void EffectComposerUniformsModel::addUniform(Uniform *uniform) endInsertRows(); } +void EffectComposerUniformsModel::updateUniform(int uniformIndex, Uniform *uniform) +{ + QTC_ASSERT(uniformIndex < m_uniforms.size() && uniformIndex >= 0, return); + QModelIndex idx = index(uniformIndex, 0); + + beginResetModel(); + delete m_uniforms[uniformIndex]; + m_uniforms[uniformIndex] = uniform; + endResetModel(); +} + QList EffectComposerUniformsModel::uniforms() const { return m_uniforms; diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index 00d41a9204d..62eaf2530de 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -25,7 +25,8 @@ public: MinValueRole, TypeRole, ControlTypeRole, - UseCustomValueRole + UseCustomValueRole, + UserAdded }; EffectComposerUniformsModel(QObject *parent = nullptr); @@ -35,10 +36,13 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; Q_INVOKABLE bool resetData(int row); + Q_INVOKABLE bool remove(int row); + Q_INVOKABLE QStringList displayNames() const; void resetModel(); void addUniform(Uniform *uniform); + void updateUniform(int uniformIndex, Uniform *uniform); QList uniforms() const; diff --git a/src/plugins/effectcomposer/effectcomposerwidget.cpp b/src/plugins/effectcomposer/effectcomposerwidget.cpp index 5f7d40d8a87..394c64ba5e6 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.cpp +++ b/src/plugins/effectcomposer/effectcomposerwidget.cpp @@ -105,7 +105,8 @@ EffectComposerWidget::EffectComposerWidget(EffectComposerView *view) {"rootView", QVariant::fromValue(this)}}); connect(m_effectComposerModel.data(), &EffectComposerModel::nodesChanged, this, [this]() { - m_effectComposerNodesModel->updateCanBeAdded(m_effectComposerModel->uniformNames()); + m_effectComposerNodesModel->updateCanBeAdded(m_effectComposerModel->uniformNames(), + m_effectComposerModel->hasCustomNode()); }); connect(m_effectComposerModel.data(), &EffectComposerModel::resourcesSaved, diff --git a/src/plugins/effectcomposer/effectnode.cpp b/src/plugins/effectcomposer/effectnode.cpp index 6128e3a1969..b6be6e90362 100644 --- a/src/plugins/effectcomposer/effectnode.cpp +++ b/src/plugins/effectcomposer/effectnode.cpp @@ -29,6 +29,7 @@ EffectNode::EffectNode(const QString &qenPath) m_name = node.name(); m_description = node.description(); + m_isCustom = node.isCustom(); const QList uniforms = node.uniforms(); for (const Uniform *uniform : uniforms) { diff --git a/src/plugins/effectcomposer/effectnode.h b/src/plugins/effectcomposer/effectnode.h index dff553a2de5..609b37e207f 100644 --- a/src/plugins/effectcomposer/effectnode.h +++ b/src/plugins/effectcomposer/effectnode.h @@ -26,6 +26,7 @@ public: QString description() const; QString qenPath() const; QHash defaultImagesHash() const { return m_defaultImagesHash; } + bool isCustom() const { return m_isCustom; } void setCanBeAdded(bool enabled); @@ -39,6 +40,7 @@ private: QString m_description; QString m_qenPath; QUrl m_iconPath; + bool m_isCustom = false; bool m_canBeAdded = true; QSet m_uniformNames; QHash m_defaultImagesHash; diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index c0129f85557..3afd169a683 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -24,6 +24,9 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS m_type = Uniform::typeFromString(propObj.value("type").toString()); defaultValue = propObj.value("defaultValue").toString(); + if (propObj.contains("userAdded")) + m_userAdded = getBoolValue(propObj.value("userAdded"), false); + m_displayName = propObj.value("displayName").toString(); if (m_displayName.isEmpty()) m_displayName = m_name; @@ -150,6 +153,11 @@ QString Uniform::displayName() const return m_displayName; } +bool Uniform::userAdded() const +{ + return m_userAdded; +} + QString Uniform::customValue() const { return m_customValue; @@ -411,14 +419,15 @@ bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue) // Used with sampler types QString Uniform::getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const { - QString filePath = value; - if (qenPath.isEmpty()) { + if (Utils::FilePath::fromString(value).isAbsolutePath()) { + return value; + } else if (qenPath.isEmpty()) { const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); return effectsResDir.pathAppended(effectName).pathAppended(value).toString(); } else { QDir dir(m_qenPath); dir.cdUp(); - QString absPath = dir.absoluteFilePath(filePath); + QString absPath = dir.absoluteFilePath(value); absPath = QDir::cleanPath(absPath); absPath = QUrl::fromLocalFile(absPath).toString(); return absPath; diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index d3a39006510..a316ee2a7b1 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -18,17 +18,18 @@ class Uniform : public QObject { Q_OBJECT - Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) - Q_PROPERTY(QString uniformDisplayName MEMBER m_displayName CONSTANT) + Q_PROPERTY(QString uniformName READ name CONSTANT) + Q_PROPERTY(QString uniformDisplayName READ displayName CONSTANT) Q_PROPERTY(QString uniformType READ typeName CONSTANT) Q_PROPERTY(QString uniformControlType READ controlTypeName CONSTANT) Q_PROPERTY(QString uniformDescription READ description CONSTANT) Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged) - Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) - Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) - Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue NOTIFY uniformDefaultValueChanged) - Q_PROPERTY(QVariant uniformUseCustomValue MEMBER m_useCustomValue CONSTANT) + Q_PROPERTY(QVariant uniformMinValue READ minValue CONSTANT) + Q_PROPERTY(QVariant uniformMaxValue READ maxValue CONSTANT) + Q_PROPERTY(QVariant uniformDefaultValue READ defaultValue NOTIFY uniformDefaultValueChanged) + Q_PROPERTY(QVariant uniformUseCustomValue READ useCustomValue CONSTANT) + Q_PROPERTY(bool uniformUserAdded READ userAdded CONSTANT) public: enum class Type @@ -66,6 +67,7 @@ public: QString name() const; QString description() const; QString displayName() const; + bool userAdded() const; QString customValue() const; void setCustomValue(const QString &newCustomValue); @@ -107,6 +109,7 @@ private: QString m_displayName; QString m_description; QString m_customValue; + bool m_userAdded = false; bool m_useCustomValue = false; bool m_enabled = true; bool m_enableMipmap = false; From 2d59d704d8fc7fb289192aa1d87129ff1dad7f1e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 7 Nov 2024 16:32:50 +0100 Subject: [PATCH 130/322] QmlDesigner: Fix deprication warning Task-number: QTCREATORBUG-31945 Change-Id: Ibcca954073d1db3f4eb31ab23d3bf5ff7b965009 Reviewed-by: Ali Kianian Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/components/formeditor/seekerslider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp index a6bfd25873f..4911656f0d6 100644 --- a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp +++ b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp @@ -42,7 +42,7 @@ void SeekerSlider::mousePressEvent(QMouseEvent *event) QStyleOptionSlider os; initStyleOption(&os); QRect handleRect = style()->subControlRect(QStyle::CC_Slider, &os, QStyle::SC_SliderHandle, this); - m_moving = handleRect.contains(event->localPos().toPoint()); + m_moving = handleRect.contains(event->position().toPoint()); if (m_moving) QSlider::mousePressEvent(event); else From 119f4d2bf20ca0d6253b134aec354855869c2e7e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 15 Nov 2024 15:27:08 +0200 Subject: [PATCH 131/322] EffectComposer: Show errors instead of preview if there are errors Fixes: QDS-14087 Change-Id: I6becc3290f3532532960bbe09447902582b075a1 Reviewed-by: Mats Honkamaa Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri --- .../EffectComposerPreview.qml | 405 ++++++++++-------- .../effectcomposer/effectcomposermodel.cpp | 225 +++++----- .../effectcomposer/effectcomposermodel.h | 29 +- 3 files changed, 362 insertions(+), 297 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index 2e4cedc438d..e4f45e77334 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -42,15 +42,14 @@ Column { ); effectComposerModel.resetEffectError(0); } catch (error) { - let errorString = "QML: ERROR: "; + let errorString = ""; let errorLine = -1; - if (error.qmlErrors.length > 0) { - // Show the first QML error - let e = error.qmlErrors[0]; - errorString += e.lineNumber + ": " + e.message; + for (var i = 0; i < error.qmlErrors.length; ++i) { + let e = error.qmlErrors[i]; + errorString += "\n" + e.lineNumber + ": " + e.message; errorLine = e.lineNumber; } - effectComposerModel.setEffectError(errorString, 0, errorLine); + effectComposerModel.setEffectError(errorString, 1, true, errorLine); placeHolder.visible = true; } } @@ -183,228 +182,266 @@ Column { } } - Rectangle { // preview image - id: preview - - color: colorEditor.color + Item { width: parent.width height: root.height - y - clip: true - MouseArea { - id: mouseArea + // error message + Rectangle { + id: rect anchors.fill: parent - acceptedButtons: Qt.LeftButton + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + visible: !preview.visible - property real pressX: 0 - property real pressY: 0 - property bool panning: false + HelperWidgets.ScrollView { + id: scrollView - onPressed: { - pressX = mouseX - imageScaler.x - pressY = mouseY - imageScaler.y - panning = true + anchors.fill: parent + anchors.margins: 4 + + clip: true + + TextEdit { + id: errorText + width: scrollView.width + font.pixelSize: StudioTheme.Values.myFontSize + color: StudioTheme.Values.themeTextColor + readOnly: true + selectByMouse: true + selectByKeyboard: true + wrapMode: TextEdit.WordWrap + text: root.effectComposerModel ? root.effectComposerModel.effectErrors : "" + } } + } - onReleased: { - panning = false - } + Rectangle { // preview image + id: preview - onWheel: (wheel) => { - let oldPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) + anchors.fill: parent + color: colorEditor.color + clip: true + visible: !root.effectComposerModel || root.effectComposerModel.effectErrors === "" - if (wheel.angleDelta.y > 0) { - if (root.previewScale < 3) - root.previewScale += .2 - } else { - if (root.previewScale > .4) - root.previewScale -= .2 + MouseArea { + id: mouseArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + property real pressX: 0 + property real pressY: 0 + property bool panning: false + + onPressed: { + pressX = mouseX - imageScaler.x + pressY = mouseY - imageScaler.y + panning = true } - let newPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) - imageScaler.x -= (oldPoint.x - newPoint.x) * imageScaler.scale - imageScaler.y -= (oldPoint.y - newPoint.y) * imageScaler.scale + onReleased: { + panning = false + } - imageScaler.checkBounds() - zoomIndicator.show() - } + onWheel: (wheel) => { + let oldPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) - Timer { // pan timer - running: parent.panning - interval: 16 - repeat: true + if (wheel.angleDelta.y > 0) { + if (root.previewScale < 3) + root.previewScale += .2 + } else { + if (root.previewScale > .4) + root.previewScale -= .2 + } + + let newPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) + imageScaler.x -= (oldPoint.x - newPoint.x) * imageScaler.scale + imageScaler.y -= (oldPoint.y - newPoint.y) * imageScaler.scale - onTriggered: { - imageScaler.x = mouseArea.mouseX - mouseArea.pressX - imageScaler.y = mouseArea.mouseY - mouseArea.pressY imageScaler.checkBounds() + zoomIndicator.show() + } + + Timer { // pan timer + running: parent.panning + interval: 16 + repeat: true + + onTriggered: { + imageScaler.x = mouseArea.mouseX - mouseArea.pressX + imageScaler.y = mouseArea.mouseY - mouseArea.pressY + imageScaler.checkBounds() + } } } - } - - Image { - id: placeHolder - anchors.fill: parent - anchors.margins: root.previewMargin - fillMode: Image.PreserveAspectFit - source: imagesComboBox.selectedImage - smooth: true - } - - Item { // Source item as a canvas (render target) for effect - id: source - width: sourceImage.sourceSize.width - height: sourceImage.sourceSize.height - layer.enabled: true - layer.mipmap: true - layer.smooth: true - layer.sourceRect: Qt.rect(-root.extraMargin, -root.extraMargin, - width + root.extraMargin * 2, height + root.extraMargin * 2) - visible: false Image { - id: sourceImage - - onSourceChanged: imageScaler.resetTransforms() - - fillMode: Image.Pad - + id: placeHolder + anchors.fill: parent + anchors.margins: root.previewMargin + fillMode: Image.PreserveAspectFit source: imagesComboBox.selectedImage smooth: true } - } - BlurHelper { - id: blurHelper - source: source - property int blurMax: g_propertyData.blur_helper_max_level ? g_propertyData.blur_helper_max_level : 64 - property real blurMultiplier: g_propertyData.blurMultiplier ? g_propertyData.blurMultiplier : 0 - } + Item { // Source item as a canvas (render target) for effect + id: source + width: sourceImage.sourceSize.width + height: sourceImage.sourceSize.height + layer.enabled: true + layer.mipmap: true + layer.smooth: true + layer.sourceRect: Qt.rect(-root.extraMargin, -root.extraMargin, + width + root.extraMargin * 2, height + root.extraMargin * 2) + visible: false - Item { - id: imageScaler - x: root.previewMargin - y: root.previewMargin - width: parent.width - root.previewMargin * 2 - height: parent.height - root.previewMargin * 2 + Image { + id: sourceImage - scale: root.previewScale * (width > height ? height / sourceImage.sourceSize.height - : width / sourceImage.sourceSize.width) + onSourceChanged: imageScaler.resetTransforms() - Behavior on x { - id: xBehavior + fillMode: Image.Pad - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad + source: imagesComboBox.selectedImage + smooth: true } } - Behavior on y { - id: yBehavior - - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad - } - } - - Behavior on scale { - id: scaleBehavior - - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad - } - } - - function checkBounds() { - let edgeMargin = 10 - // correction factor to account for an observation that edgeMargin decreases - // with increased zoom - let corrFactor = 10 * imageScaler.scale - let imgW2 = sourceImage.paintedWidth * imageScaler.scale * .5 - let imgH2 = sourceImage.paintedHeight * imageScaler.scale * .5 - let srcW2 = width * .5 - let srcH2 = height * .5 - - if (imageScaler.x < -srcW2 - imgW2 + edgeMargin + corrFactor) - imageScaler.x = -srcW2 - imgW2 + edgeMargin + corrFactor - else if (x > srcW2 + imgW2 - edgeMargin - corrFactor) - imageScaler.x = srcW2 + imgW2 - edgeMargin - corrFactor - - if (imageScaler.y < -srcH2 - imgH2 + edgeMargin + corrFactor) - imageScaler.y = -srcH2 - imgH2 + edgeMargin + corrFactor - else if (y > srcH2 + imgH2 - edgeMargin - corrFactor) - imageScaler.y = srcH2 + imgH2 - edgeMargin - corrFactor - } - - function resetTransforms() { - imageScaler.enableAnim(true) - root.previewScale = 1 - imageScaler.x = root.previewMargin - imageScaler.y = root.previewMargin - imageScaler.enableAnim(false) - } - - function enableAnim(flag) { - xBehavior.enabled = flag - yBehavior.enabled = flag - scaleBehavior.enabled = flag + BlurHelper { + id: blurHelper + source: source + property int blurMax: g_propertyData.blur_helper_max_level ? g_propertyData.blur_helper_max_level : 64 + property real blurMultiplier: g_propertyData.blurMultiplier ? g_propertyData.blurMultiplier : 0 } Item { - id: componentParent - width: source.width - height: source.height - anchors.centerIn: parent - } - } + id: imageScaler + x: root.previewMargin + y: root.previewMargin + width: parent.width - root.previewMargin * 2 + height: parent.height - root.previewMargin * 2 - Rectangle { - id: zoomIndicator + scale: root.previewScale * (width > height ? height / sourceImage.sourceSize.height + : width / sourceImage.sourceSize.width) - width: 40 - height: 20 - color: StudioTheme.Values.themeDialogBackground - visible: false + Behavior on x { + id: xBehavior - function show() { - zoomIndicator.visible = true - zoomIndicatorTimer.start() + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + Behavior on y { + id: yBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + Behavior on scale { + id: scaleBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + function checkBounds() { + let edgeMargin = 10 + // correction factor to account for an observation that edgeMargin decreases + // with increased zoom + let corrFactor = 10 * imageScaler.scale + let imgW2 = sourceImage.paintedWidth * imageScaler.scale * .5 + let imgH2 = sourceImage.paintedHeight * imageScaler.scale * .5 + let srcW2 = width * .5 + let srcH2 = height * .5 + + if (imageScaler.x < -srcW2 - imgW2 + edgeMargin + corrFactor) + imageScaler.x = -srcW2 - imgW2 + edgeMargin + corrFactor + else if (x > srcW2 + imgW2 - edgeMargin - corrFactor) + imageScaler.x = srcW2 + imgW2 - edgeMargin - corrFactor + + if (imageScaler.y < -srcH2 - imgH2 + edgeMargin + corrFactor) + imageScaler.y = -srcH2 - imgH2 + edgeMargin + corrFactor + else if (y > srcH2 + imgH2 - edgeMargin - corrFactor) + imageScaler.y = srcH2 + imgH2 - edgeMargin - corrFactor + } + + function resetTransforms() { + imageScaler.enableAnim(true) + root.previewScale = 1 + imageScaler.x = root.previewMargin + imageScaler.y = root.previewMargin + imageScaler.enableAnim(false) + } + + function enableAnim(flag) { + xBehavior.enabled = flag + yBehavior.enabled = flag + scaleBehavior.enabled = flag + } + + Item { + id: componentParent + width: source.width + height: source.height + anchors.centerIn: parent + } } - Text { - text: Math.round(root.previewScale * 100) + "%" - color: StudioTheme.Values.themeTextColor - anchors.centerIn: parent + Rectangle { + id: zoomIndicator + + width: 40 + height: 20 + color: StudioTheme.Values.themeDialogBackground + visible: false + + function show() { + zoomIndicator.visible = true + zoomIndicatorTimer.start() + } + + Text { + text: Math.round(root.previewScale * 100) + "%" + color: StudioTheme.Values.themeTextColor + anchors.centerIn: parent + } + + Timer { + id: zoomIndicatorTimer + interval: 1000 + onTriggered: zoomIndicator.visible = false + } + } + + Connections { + target: effectComposerModel + function onShadersBaked() { + updateTimer.restart() + } } Timer { - id: zoomIndicatorTimer - interval: 1000 - onTriggered: zoomIndicator.visible = false - } - } - - Connections { - target: effectComposerModel - function onShadersBaked() { - updateTimer.restart() - } - } - - Timer { - id: updateTimer - interval: updateDelay - onTriggered: { - effectComposerModel.updateQmlComponent() - createNewComponent() + id: updateTimer + interval: updateDelay + onTriggered: { + effectComposerModel.updateQmlComponent() + createNewComponent() + } } } } } + diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 67c54fe1a39..16c62a69c0b 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -37,30 +37,9 @@ using namespace Qt::StringLiterals; namespace EffectComposer { -enum class FileType -{ - Binary, - Text -}; - static constexpr int INVALID_CODE_EDITOR_INDEX = -1; static constexpr int MAIN_CODE_EDITOR_INDEX = -2; -static bool writeToFile(const QByteArray &buf, const QString &filename, FileType fileType) -{ - QDir().mkpath(QFileInfo(filename).path()); - QFile f(filename); - QIODevice::OpenMode flags = QIODevice::WriteOnly | QIODevice::Truncate; - if (fileType == FileType::Text) - flags |= QIODevice::Text; - if (!f.open(flags)) { - qWarning() << "Failed to open file for writing:" << filename; - return false; - } - f.write(buf); - return true; -} - EffectComposerModel::EffectComposerModel(QObject *parent) : QAbstractListModel{parent} , m_codeEditorIndex(INVALID_CODE_EDITOR_INDEX) @@ -231,6 +210,7 @@ void EffectComposerModel::clear(bool clearName) resetRootVertexShader(); } + resetEffectError(); setHasUnsavedChanges(!m_currentComposition.isEmpty()); setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); @@ -533,24 +513,27 @@ QString EffectComposerModel::detectErrorMessage(const QString &errorMessage) return QString(); } -// Return first error message (if any) -EffectError EffectComposerModel::effectError() const +void EffectComposerModel::resetEffectError(int type, bool notify) { - for (const EffectError &e : std::as_const(m_effectErrors)) { - if (!e.m_message.isEmpty()) - return e; + if (type < 0 && !m_effectErrors.isEmpty()) { + m_effectErrors.clear(); + if (notify) + Q_EMIT effectErrorsChanged(); + } else if (m_effectErrors.contains(type)) { + m_effectErrors.remove(type); + if (notify) + Q_EMIT effectErrorsChanged(); } - 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 EffectComposerModel::setEffectError(const QString &errorMessage, int type, int lineNumber) +void EffectComposerModel::setEffectError(const QString &errorMessage, int type, + bool notify, int lineNumber) { EffectError error; error.m_type = type; - if (type == 1 || type == 2) { + if (type == 2) { // For shaders, get the line number from baker output. // Which is something like "ERROR: :15: message" int glslErrorLineNumber = -1; @@ -570,9 +553,41 @@ void EffectComposerModel::setEffectError(const QString &errorMessage, int type, QString additionalErrorInfo = detectErrorMessage(errorMessage); error.m_message = additionalErrorInfo + errorMessage; - m_effectErrors.insert(type, error); + QList &errors = m_effectErrors[type]; + errors.append(error); + qWarning() << QString("Effect error (line: %2): %1").arg(error.m_message).arg(error.m_line); - Q_EMIT effectErrorChanged(); + + if (notify) + Q_EMIT effectErrorsChanged(); +} + +QString EffectComposerModel::effectErrors() const +{ + static const QStringList errorTypeStrings { + "Common", + "QML parsing", + "Shader", + "Preprocessor" + }; + static const QString messageTemplate = tr("%1 error: %2\n"); + QString retval; + + for (const QList &errors : std::as_const(m_effectErrors)) { + for (const EffectError &e : errors) { + if (!e.m_message.isEmpty()) { + int index = e.m_type; + if (index < 0 || index >= errorTypeStrings.size()) + index = 0; + retval.append(messageTemplate.arg(errorTypeStrings[index], e.m_message)); + } + } + } + + if (!retval.isEmpty()) + retval.chop(1); // Remove last newline + + return retval; } QString variantAsDataString(const Uniform::Type type, const Uniform::Type controlType, const QVariant &variant) @@ -1096,9 +1111,11 @@ R"( void EffectComposerModel::saveComposition(const QString &name) { + resetEffectError(ErrorCommon); + resetEffectError(ErrorQMLParsing); + if (name.isEmpty() || name.size() < 3 || name[0].isLower()) { - QString error = QString("Error: Couldn't save composition '%1', name is invalid").arg(name); - qWarning() << error; + setEffectError(QString("Failed to save composition '%1', name is invalid").arg(name)); return; } @@ -1109,8 +1126,7 @@ void EffectComposerModel::saveComposition(const QString &name) auto saveFile = QFile(path); if (!saveFile.open(QIODevice::WriteOnly)) { - QString error = QString("Error: Couldn't save composition file: '%1'").arg(path); - qWarning() << error; + setEffectError(QString("Failed to save composition file: '%1'").arg(path)); return; } @@ -1266,9 +1282,7 @@ void EffectComposerModel::openComposition(const QString &path) QFile compFile(path); if (!compFile.open(QIODevice::ReadOnly)) { - QString error = QString("Couldn't open composition file: '%1'").arg(path); - qWarning() << qPrintable(error); - setEffectError(error); + setEffectError(QString("Failed to open composition file: '%1'").arg(path)); return; } @@ -1280,16 +1294,12 @@ void EffectComposerModel::openComposition(const QString &path) QJsonParseError parseError; QJsonDocument jsonDoc(QJsonDocument::fromJson(data, &parseError)); if (parseError.error != QJsonParseError::NoError) { - QString error = QString("Error parsing the project file: %1").arg(parseError.errorString()); - qWarning() << error; - setEffectError(error); + setEffectError(QString("Failed to parse the project file: %1").arg(parseError.errorString())); return; } QJsonObject rootJson = jsonDoc.object(); if (!rootJson.contains("QEP")) { - QString error = QStringLiteral("Error: Invalid project file"); - qWarning() << error; - setEffectError(error); + setEffectError(QStringLiteral("Invalid project file")); return; } @@ -1300,10 +1310,7 @@ void EffectComposerModel::openComposition(const QString &path) : ""_L1; if (!toolName.isEmpty() && toolName != "EffectComposer") { - const QString error - = tr("Error: '%1' effects are not compatible with 'Effect Composer'").arg(toolName); - qWarning() << error; - setEffectError(error); + setEffectError(QString("'%1' effects are not compatible with 'Effect Composer'").arg(toolName)); return; } @@ -1312,9 +1319,7 @@ void EffectComposerModel::openComposition(const QString &path) version = json["version"].toInt(-1); if (version != 1) { - QString error = QString("Error: Unknown project version (%1)").arg(version); - qWarning() << error; - setEffectError(error); + setEffectError(QString("Unknown project version (%1)").arg(version)); return; } @@ -1537,8 +1542,8 @@ void EffectComposerModel::saveResources(const QString &name) newFileNames.append(target.fileName()); if (target.exists() && source.fileName() != target.fileName()) target.removeFile(); // Remove existing file for update - if (!source.copyFile(target)) - qWarning() << __FUNCTION__ << " Failed to copy file: " << source; + if (!source.copyFile(target) && !target.exists()) + setEffectError(QString("Failed to copy file: %1").arg(source.toFSPathString())); if (fileNameToUniformHash.contains(dests[i])) { Uniform *uniform = fileNameToUniformHash[dests[i]]; @@ -1564,14 +1569,6 @@ void EffectComposerModel::saveResources(const QString &name) emit resourcesSaved(QString("%1.%2.%2").arg(m_effectTypePrefix, name).toUtf8(), effectPath); } -void EffectComposerModel::resetEffectError(int type) -{ - if (m_effectErrors.contains(type)) { - m_effectErrors.remove(type); - Q_EMIT effectErrorChanged(); - } -} - // Get value in QML format that used for exports QString EffectComposerModel::valueAsString(const Uniform &uniform) { @@ -1603,7 +1600,8 @@ QString EffectComposerModel::valueAsString(const Uniform &uniform) return uniform.value().toBool() ? QString("1") : QString("0"); return uniform.value().toString(); } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + setEffectError(QString("Unhandled const variable type: %1").arg(int(uniform.type())), + ErrorQMLParsing); return QString(); } } @@ -1636,7 +1634,8 @@ QString EffectComposerModel::valueAsBinding(const Uniform &uniform) } else if (uniform.type() == Uniform::Type::Sampler) { return getImageElementName(uniform); } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + setEffectError(QString("Unhandled const variable type: %1").arg(int(uniform.type())), + ErrorQMLParsing); return QString(); } } @@ -1665,7 +1664,8 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform) } else if (uniform.type() == Uniform::Type::Channel) { return QString::number(uniform.value().toInt()); } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + setEffectError(QString("Unhandled const variable type: %1").arg(int(uniform.type())), + ErrorQMLParsing); return QString(); } } @@ -1902,31 +1902,38 @@ QString EffectComposerModel::generateFragmentShader(bool includeUniforms) return s; } -void EffectComposerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview) +void EffectComposerModel::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, + bool preview, int bakeCounter) { - --m_remainingQsbTargets; + if (bakeCounter == m_currentBakeCounter) { + if (m_remainingQsbTargets == 2) + resetEffectError(ErrorShader, false); - const QString errStr = qsbProcess->errorString(); - const QByteArray errStd = qsbProcess->readAllRawStandardError(); - QString previewStr; - if (preview) - previewStr = QStringLiteral("preview"); + --m_remainingQsbTargets; - if (!errStr.isEmpty()) { - qWarning() << QString("Failed to generate %3 QSB file for: %1 %2") - .arg(shader, errStr, previewStr); - } + const QString errStr = qsbProcess->errorString(); + const QByteArray errStd = qsbProcess->readAllRawStandardError(); + QString previewStr; + if (preview) + previewStr = QStringLiteral("preview"); - if (!errStd.isEmpty()) { - qWarning() << QString("Failed to generate %3 QSB file for: %1 %2") - .arg(shader, QString::fromUtf8(errStd), previewStr); - } + if (!errStr.isEmpty() || !errStd.isEmpty()) { + const QString failMessage = "Failed to generate %3 QSB file for: %1\n%2"; + QString error; + if (!errStr.isEmpty()) + error = failMessage.arg(shader, errStr, previewStr); + if (!errStd.isEmpty()) + error = failMessage.arg(shader, QString::fromUtf8(errStd), previewStr); + setEffectError(error, ErrorShader, false); + } - if (m_remainingQsbTargets <= 0) { - Q_EMIT shadersBaked(); - setShadersUpToDate(true); + if (m_remainingQsbTargets <= 0) { + Q_EMIT shadersBaked(); + setShadersUpToDate(true); + Q_EMIT effectErrorsChanged(); - // TODO: Mark shaders as baked, required by export later + // TODO: Mark shaders as baked, required by export later + } } qsbProcess->deleteLater(); @@ -2002,9 +2009,8 @@ void EffectComposerModel::updateCustomUniforms() void EffectComposerModel::initShaderDir() { - static int count = 0; static const QString fileNameTemplate = "%1_%2.%3"; - const QString countStr = QString::number(count); + const QString countStr = QString::number(m_currentBakeCounter); auto resetFile = [&countStr, this](QString &fileName, const QString prefix, const QString suffix) { // qsb generation is done in separate process, so it is not guaranteed all of the old files @@ -2026,22 +2032,13 @@ void EffectComposerModel::initShaderDir() resetFile(m_vertexShaderPreviewFilename, "compiled_prev", "vert.qsb"); resetFile(m_fragmentShaderPreviewFilename, "compiled_prev", "frag.qsb"); - ++count; + ++m_currentBakeCounter; } void EffectComposerModel::bakeShaders() { - const QString failMessage = "Shader baking failed: "; - - const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); - if (!target) { - qWarning() << failMessage << "Target not found"; - return; - } - initShaderDir(); - resetEffectError(ErrorPreprocessor); if (Utils::FilePath::fromString(m_vertexSourceFilename).exists() && Utils::FilePath::fromString(m_fragmentSourceFilename).exists() && m_vertexShader == generateVertexShader() @@ -2050,6 +2047,18 @@ void EffectComposerModel::bakeShaders() return; } + const QString failMessage = "Shader baking failed: %1"; + // Don't reset shader errors yet to avoid UI flicker + resetEffectError(ErrorCommon); + resetEffectError(ErrorQMLParsing); + resetEffectError(ErrorPreprocessor); + + const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); + if (!target) { + setEffectError(failMessage.arg("Target not found")); + return; + } + setShadersUpToDate(false); // First update the features based on shader content @@ -2069,13 +2078,13 @@ void EffectComposerModel::bakeShaders() QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()); if (!qtVer) { - qWarning() << failMessage << "Qt version not found"; + setEffectError(failMessage.arg("Qt version not found")); return; } Utils::FilePath qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix(); if (!qsbPath.exists()) { - qWarning() << failMessage << "QSB tool for target kit not found"; + setEffectError(failMessage.arg("QSB tool for target kit not found")); return; } @@ -2083,7 +2092,7 @@ void EffectComposerModel::bakeShaders() QLibraryInfo::path(QLibraryInfo::BinariesPath)); Utils::FilePath qsbPrevPath = binPath.pathAppended("qsb").withExecutableSuffix(); if (!qsbPrevPath.exists()) { - qWarning() << failMessage << "QSB tool for preview shaders not found"; + setEffectError(failMessage.arg("QSB tool for preview shaders not found")); return; } @@ -2101,8 +2110,8 @@ void EffectComposerModel::bakeShaders() auto qsbProcess = new Utils::Process(this); connect(qsbProcess, &Utils::Process::done, this, - [this, qsbProcess, path = srcPaths[i], preview] { - handleQsbProcessExit(qsbProcess, path, preview); + [this, qsbProcess, path = srcPaths[i], bakeCounter = m_currentBakeCounter, preview] { + handleQsbProcessExit(qsbProcess, path, preview, bakeCounter); }); qsbProcess->setWorkingDirectory(workDir.absolutePath()); qsbProcess->setCommand({qsbPath, args}); @@ -2524,8 +2533,6 @@ bool EffectComposerModel::hasCustomNode() const void EffectComposerModel::updateQmlComponent() { - // Clear possible QML runtime errors - resetEffectError(ErrorQMLRuntime); m_qmlComponentString = getQmlComponentString(false); } @@ -2559,4 +2566,16 @@ void EffectComposerModel::addOrUpdateNodeUniform(int idx, const QVariantMap &dat startRebakeTimer(); } +bool EffectComposerModel::writeToFile(const QByteArray &buf, const QString &fileName, + FileType fileType) +{ + Utils::FilePath fp = Utils::FilePath::fromString(fileName); + fp.absolutePath().createDir(); + if (!fp.writeFileContents(buf)) { + setEffectError(QString("Failed to open file for writing: %1").arg(fileName)); + return false; + } + return true; +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 3b41572905b..b2fbe808bec 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -59,6 +59,7 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(QList previewImages READ previewImages NOTIFY previewImagesChanged) Q_PROPERTY(int customPreviewImageCount READ customPreviewImageCount NOTIFY customPreviewImageCountChanged) Q_PROPERTY(int mainCodeEditorIndex READ mainCodeEditorIndex CONSTANT) + Q_PROPERTY(QString effectErrors READ effectErrors NOTIFY effectErrorsChanged) public: EffectComposerModel(QObject *parent = nullptr); @@ -114,8 +115,10 @@ public: Q_INVOKABLE void updateQmlComponent(); - Q_INVOKABLE void resetEffectError(int type); - Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); + Q_INVOKABLE void resetEffectError(int type = -1, bool notify = true); + Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = 0, + bool notify = true, int lineNumber = -1); + QString effectErrors() const; Q_INVOKABLE void saveComposition(const QString &name); @@ -153,7 +156,7 @@ signals: void isEmptyChanged(); void selectedIndexChanged(int idx); void codeEditorIndexChanged(int idx); - void effectErrorChanged(); + void effectErrorsChanged(); void shadersUpToDateChanged(); void isEnabledChanged(); void hasValidTargetChanged(); @@ -178,11 +181,9 @@ private: }; enum ErrorTypes { - ErrorCommon = -1, + ErrorCommon, ErrorQMLParsing, - ErrorVert, - ErrorFrag, - ErrorQMLRuntime, + ErrorShader, ErrorPreprocessor }; @@ -195,7 +196,6 @@ private: const QString getFSUniforms(); QString detectErrorMessage(const QString &errorMessage); - EffectError effectError() const; QString valueAsString(const Uniform &uniform); QString valueAsBinding(const Uniform &uniform); @@ -211,7 +211,8 @@ private: QString getCustomShaderVaryings(bool outState); QString generateVertexShader(bool includeUniforms = true); QString generateFragmentShader(bool includeUniforms = true); - void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview); + void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview, + int bakeCounter); QString stripFileFromURL(const QString &urlString) const; QString getQmlEffectString(); @@ -235,6 +236,13 @@ private: Utils::FilePath customPreviewImagesPath() const; QList defaultPreviewImages() const; + enum class FileType + { + Binary, + Text + }; + bool writeToFile(const QByteArray &buf, const QString &filename, FileType fileType); + QList m_nodes; int m_selectedIndex = -1; @@ -244,7 +252,7 @@ private: // True when shaders haven't changed since last baking bool m_shadersUpToDate = true; int m_remainingQsbTargets = 0; - QMap m_effectErrors; + QMap> m_effectErrors; ShaderFeatures m_shaderFeatures; QStringList m_shaderVaryingVariables; QString m_fragmentShader; @@ -277,6 +285,7 @@ private: Utils::UniqueObjectLatePtr m_shadersCodeEditor; QUrl m_currentPreviewImage; QList m_customPreviewImages; + int m_currentBakeCounter = 0; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; From 6f1e8932927a8cb9423cd11e22e03566f043de8c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 15 Nov 2024 18:03:46 +0100 Subject: [PATCH 132/322] QmlDesigner: Connections is in QtQml.Base Change-Id: Ie37569f704e2f54c1d90b7ec1106f7e555d5a8b7 Reviewed-by: Marco Bubke --- .../qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp | 2 +- src/plugins/qmldesigner/libs/designercore/model/model.cpp | 2 +- .../libs/designercore/projectstorage/commontypecache.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp index 0c4d53780ef..3b4410c32a8 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp @@ -2676,7 +2676,7 @@ bool NodeMetaInfo::isQtQmlConnections() const NanotraceHR::Tracer tracer{"is Qt Qml connections", category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; - return isBasedOnCommonType(m_projectStorage, m_typeId); + return isBasedOnCommonType(m_projectStorage, m_typeId); #else return isValid() && simplifiedTypeName() == "Connections"; #endif diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index c4feda2a713..261c4767a35 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -2689,7 +2689,7 @@ NodeMetaInfo Model::qtQmlConnectionsMetaInfo() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return createNodeMetaInfo(); + return createNodeMetaInfo(); } else { return metaInfo("QtQml.Connections"); } diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/commontypecache.h index 618c9418c18..33d22f649c5 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/commontypecache.h @@ -90,6 +90,7 @@ inline constexpr char QQuickStateOperation[] = "QQuickStateOperation"; inline constexpr char QtMultimedia[] = "QtMultimedia"; inline constexpr char QtObject[] = "QtObject"; inline constexpr char QtQml[] = "QtQml"; +inline constexpr char QtQml_Base[] = "QtQml.Base"; inline constexpr char QtQml_Models[] = "QtQml.Models"; inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel"; inline constexpr char QtQuick3D[] = "QtQuick3D"; @@ -173,11 +174,11 @@ class CommonTypeCache CacheType, CacheType, CacheType, - CacheType, CacheType, CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, From 6df2bc1ba98669cbd1d87cd36429b87f700ab168 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Nov 2024 11:32:06 +0100 Subject: [PATCH 133/322] QmlDesigner: Fix NodeMetaInfoPrivate::variantTypeId() The fallback was actually required for converting C++ type names. The explicit conversion in this function are only for QML types, since the code model mixes C++ types (usually qmltypes) and QML types from QML. Task-number: QDS-14128 Change-Id: Id4f8ed4bb924717cfd7f3ae0df65a3d4442fd7a0 Reviewed-by: Tim Jenssen --- .../qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp index 3b4410c32a8..740253b87dc 100644 --- a/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/libs/designercore/metainfo/nodemetainfo.cpp @@ -1180,7 +1180,7 @@ QMetaType::Type NodeMetaInfoPrivate::variantTypeId(const PropertyName &propertyN if (typeName == "vector4d") return QMetaType::QVector4D; - return QMetaType::UnknownType; + return static_cast(QMetaType::fromName(typeName.data()).id()); } int NodeMetaInfoPrivate::majorVersion() const From e56bea6560bb4543c36b138f2862ffe7808c8759 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Nov 2024 11:48:30 +0100 Subject: [PATCH 134/322] QmlDesigner: Do not offer FlowView as module Task-number: QDS-13931 Change-Id: I798a1ffcdc279add20b819b2fe6eab95ddbe30db Reviewed-by: Thomas Hartmann --- .../qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp index dac70e2ea86..07731f01087 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp @@ -724,6 +724,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), Equals(u"QtPurchasing"), Equals(u"QtBluetooth"), Equals(u"Enginio"), + Equals(u"FlowView"), StartsWith(u"Qt.labs."), StartsWith(u"Qt.test.controls"), StartsWith(u"QmlTime"), From 94a247c7dd34eb4819b3fe113f1785b7fc2c99f3 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 15 Nov 2024 13:28:22 +0100 Subject: [PATCH 135/322] QmlProjectExporter: Remove empty qmldir files from the folder tree Fixes: QDS-14098 Change-Id: I57e1ad661dd773d28a31339583e3dd2361d39ca5 Reviewed-by: Thomas Hartmann --- .../qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp index 14cdd884974..960fa8e0ef4 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp @@ -116,8 +116,12 @@ void CMakeGenerator::updateModifiedFile(const QString &fileString) if (path.fileName() != "qmldir") return; - if (auto node = findOrCreateNode(m_root, path.parentDir())) + if (path.fileSize() == 0) { + if (auto node = findNode(m_root, path.parentDir())) + removeFile(node, path); + } else if (auto node = findOrCreateNode(m_root, path.parentDir())) { insertFile(node, path); + } createCMakeFiles(m_root); createSourceFiles(); From 36431c2e7be979353945ade74b3533f4190eacb0 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 14 Nov 2024 13:23:26 +0100 Subject: [PATCH 136/322] QmlProjectExporter: Add more modules to the find_package call This patch adds QuickTimeline and ShaderTools unconditionally to the find_package call of the generated CMakeLists.txt file and if either meshes are found in the project or an "import Quick3d" in the mainUI file Quick3D gets added as well. Fixes: QDS-13709 Change-Id: I5ef13d621431915ff49680b8aba3e537f5989eed Reviewed-by: Thomas Hartmann --- .../qmlprojectexporter/cmakegenerator.cpp | 4 -- .../qmlprojectexporter/cmakegenerator.h | 1 - .../qmlprojectexporter/cmakewriter.cpp | 39 ++++++++++++++++++- .../qmlprojectexporter/cmakewriter.h | 6 ++- .../qmlprojectexporter/cmakewriterv1.cpp | 2 +- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp index 960fa8e0ef4..601eca8b0fb 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.cpp @@ -60,7 +60,6 @@ void CMakeGenerator::updateProject(QmlProject *project) if (!isEnabled()) return; - m_moduleNames.clear(); m_writer = CMakeWriter::create(this); m_root = std::make_shared(); @@ -484,9 +483,6 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, if (m_writer) m_writer->transformNode(generatorNode); - - if (generatorNode->type == Node::Type::Module) - m_moduleNames.push_back(generatorNode->name); } void CMakeGenerator::parseSourceTree() diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h index c0464001c10..588b8d72a7e 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakegenerator.h @@ -69,7 +69,6 @@ private: QString m_projectName = {}; NodePtr m_root = {}; - QStringList m_moduleNames = {}; }; } // namespace QmlProjectExporter diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp index c0dc80a0b6b..a9454880804 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp @@ -166,10 +166,13 @@ QString CMakeWriter::getEnvironmentVariable(const QString &key) const return value; } -QString CMakeWriter::makeFindPackageBlock(const QmlBuildSystem* buildSystem) const +QString CMakeWriter::makeFindPackageBlock(const NodePtr &node, const QmlBuildSystem *buildSystem) const { QString head = "find_package(Qt" + buildSystem->versionQt(); - const QString tail = " REQUIRED COMPONENTS Core Gui Qml Quick)\n"; + QString tail = " REQUIRED COMPONENTS Core Gui Qml Quick QuickTimeline ShaderTools"; + if (hasMesh(node) || hasQuick3dImport(buildSystem->mainUiFilePath())) + tail.append(" Quick3D"); + tail.append(")\n"); const QStringList versions = buildSystem->versionQtQuick().split('.', Qt::SkipEmptyParts); if (versions.size() < 2) @@ -337,6 +340,38 @@ void CMakeWriter::collectResources(const NodePtr &node, QStringList &res, QStrin } } +bool CMakeWriter::hasMesh(const NodePtr &node) const +{ + for (const auto &path : node->assets) { + if (path.suffix()=="mesh") + return true; + } + + for (const auto &child : node->subdirs) { + if (hasMesh(child)) + return true; + } + + return false; +} + +bool CMakeWriter::hasQuick3dImport(const Utils::FilePath &filePath) const +{ + QFile f(filePath.toString()); + if (!f.open(QIODevice::ReadOnly)) + return false; + + QTextStream stream(&f); + while (!stream.atEnd()) { + QString line = stream.readLine(); + if (line.contains("{")) + break; + if (line.contains("import") && line.contains("QtQuick3D")) + return true; + } + return false; +} + } // End namespace QmlProjectExporter. } // End namespace QmlProjectManager. diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.h b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.h index e783a3ca4d9..bc53ead02db 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.h +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.h @@ -86,7 +86,7 @@ protected: QString getEnvironmentVariable(const QString &key) const; - QString makeFindPackageBlock(const QmlBuildSystem* buildSystem) const; + QString makeFindPackageBlock(const NodePtr &node, const QmlBuildSystem *buildSystem) const; QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const; QString makeQmlFilesBlock(const NodePtr &node) const; QString makeSingletonBlock(const NodePtr &node) const; @@ -98,6 +98,10 @@ protected: private: void collectPlugins(const NodePtr &node, std::vector &out) const; void collectResources(const NodePtr &node, QStringList &res, QStringList &bigRes) const; + + bool hasMesh(const NodePtr &node) const; + bool hasQuick3dImport(const Utils::FilePath &file) const; + const CMakeGenerator *m_parent = nullptr; }; diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp index 0e76546c6f3..2604d517cc3 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp @@ -67,7 +67,7 @@ void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); if (!file.exists()) { const QString appName = parent()->projectName() + "App"; - const QString findPackage = makeFindPackageBlock(parent()->buildSystem()); + const QString findPackage = makeFindPackageBlock(node, parent()->buildSystem()); QString fileSection = ""; const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); From 0f4933ed840f1a059ff109b91c76d46555b58627 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 18 Nov 2024 12:54:14 +0200 Subject: [PATCH 137/322] QmlDesigner: Add a theme color for table current cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-14135 Change-Id: I0a46ac17491f0fd68dff315216a1b36744fc44ca Reviewed-by: Henning Gründl --- share/qtcreator/qmldesigner/devicemanager/Main.qml | 2 +- .../propertyEditorQmlSources/imports/StudioTheme/Values.qml | 2 ++ share/qtcreator/themes/dark.creatortheme | 3 +++ share/qtcreator/themes/default.creatortheme | 3 +++ share/qtcreator/themes/design-light.creatortheme | 3 +++ share/qtcreator/themes/design.creatortheme | 3 +++ share/qtcreator/themes/flat-dark.creatortheme | 3 +++ share/qtcreator/themes/flat-light.creatortheme | 3 +++ share/qtcreator/themes/flat.creatortheme | 3 +++ src/libs/utils/theme/theme.h | 1 + 10 files changed, 25 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/devicemanager/Main.qml b/share/qtcreator/qmldesigner/devicemanager/Main.qml index 68e54141a2d..ed83c164a01 100644 --- a/share/qtcreator/qmldesigner/devicemanager/Main.qml +++ b/share/qtcreator/qmldesigner/devicemanager/Main.qml @@ -31,7 +31,7 @@ Rectangle { required property bool selected required property bool current - color: tableView.currentRow === row ? "#08475B" : StudioTheme.Values.themePanelBackground + color: tableView.currentRow === row ? StudioTheme.Values.themeTableCellCurrent : StudioTheme.Values.themePanelBackground implicitWidth: StudioTheme.Values.cellWidth implicitHeight: StudioTheme.Values.cellHeight border { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 961be1bf706..9d857513488 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -474,6 +474,8 @@ QtObject { property color themePillTextSelected: Theme.color(Theme.DSpillTextSelected) property color themePillTextEdit: Theme.color(Theme.DspillTextEdit) + property color themeTableCellCurrent: Theme.color(Theme.DStableCellCurrent) + // Control Style Mapping property ControlStyle controlStyle: DefaultStyle {} property ControlStyle connectionPopupControlStyle: ConnectionPopupControlStyle {} diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index 463204fd513..7e223b560f5 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -53,6 +53,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -204,6 +206,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds800 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff242424 diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index e6cdab1bc30..c39d42a370b 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -45,6 +45,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -199,6 +201,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds300 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff323232 diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 5271d5fc57a..4f0e274e2de 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -58,6 +58,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -212,6 +214,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds300 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff323232 diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index 874c5116e03..db9bed53a40 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -56,6 +56,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -229,6 +231,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds800 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff242424 diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index bdf46614b05..1e5cadbd824 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -57,6 +57,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -209,6 +211,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds800 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff242424 diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index a85d3654d88..253aa29f4e1 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -57,6 +57,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -211,6 +213,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds300 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff323232 diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 7fd222e168a..5998ff0fd5d 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -54,6 +54,8 @@ splitterBlue=ff64daff rgbBlue=ff64daff highlightBlue=ff57b9fc highlightHover=ff74CBFC +ds300=ff71d1ef +ds800=ff08475B ;DS Theme Palette END [Colors] @@ -206,6 +208,7 @@ DSactionKeyframe=ffe0e01b DSactionJIT=ff2db543 DStableHeaderBackground=ffff0000 +DStableCellCurrent=ds800 DStableHeaderText=ff00ff00 DSdockContainerBackground=ff242424 diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 8d7e922e79d..38bc9763af6 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -414,6 +414,7 @@ public: DSactionJIT, DStableHeaderBackground, + DStableCellCurrent, DStableHeaderText, DSdockContainerBackground, From 634aeaa396c11882763022b15d6acc73fa38bedf Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 4 Nov 2024 15:05:51 +0200 Subject: [PATCH 138/322] QmlDesigner: Enhance "Folder move failure" message box Fixes: QDS-13956 Change-Id: I99d5665c556e3f32002053b50a858bacd48ea3bf Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen --- .../components/assetslibrary/assetslibrarywidget.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 3af1ebcde99..aa657848ec3 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -422,11 +422,10 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList &urls, const QStrin } if (!src.renameFile(dest) && src.isDir()) { - QMessageBox errBox; - QString message = QString("Failed to move folder \"%1\".\nThe folder might contain subfolders or one of its files is in use.") + QString message = tr("Failed to move folder \"%1\". " + "The folder might contain subfolders or one of its files is in use.") .arg(src.fileName()); - errBox.setInformativeText(message); - errBox.exec(); + Core::AsynchronousMessageBox::warning(tr("Folder move failure"), message); } } From 601fd6155a5bc66b5ad1da73847f0d11d724f028 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 8 Nov 2024 15:26:08 +0200 Subject: [PATCH 139/322] QmlDesigner: Add images folder to project structure Fixes: QDS-13955 Change-Id: I8fee552188d315d031367bfe8a606de2a70b9b3a Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../studio_templates/projects/application/wizard.json | 4 ++++ .../qmldesigner/studio_templates/projects/common/images.txt | 1 + .../studio_templates/projects/desktop-launcher/wizard.json | 4 ++++ .../studio_templates/projects/mobile-scroll/wizard.json | 4 ++++ .../studio_templates/projects/mobile-stack/wizard.json | 4 ++++ 5 files changed, 17 insertions(+) create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/images.txt diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index a7eb00ae0f5..5bb30da8380 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -339,6 +339,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, + { + "source": "../common/images.txt", + "target": "%{ProjectDirectory}/%{ContentDir}/images/images.txt" + }, { "source": "../common/asset_imports.txt", "target": "%{ProjectDirectory}/%{AssetDir}/Quick3DAssets.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/images.txt b/share/qtcreator/qmldesigner/studio_templates/projects/common/images.txt new file mode 100644 index 00000000000..f8b999661ea --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/images.txt @@ -0,0 +1 @@ +Default folder for image assets. diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index 0115ddb920c..952675c332d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -329,6 +329,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, + { + "source": "../common/images.txt", + "target": "%{ProjectDirectory}/%{ContentDir}/images/images.txt" + }, { "source": "../common/asset_imports.txt", "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index 158df87b14f..2b5300aec22 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -288,6 +288,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, + { + "source": "../common/images.txt", + "target": "%{ProjectDirectory}/%{ContentDir}/images/images.txt" + }, { "source": "../common/asset_imports.txt", "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index c7d6ab9ec0a..be4b8d50b1e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -290,6 +290,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, + { + "source": "../common/images.txt", + "target": "%{ProjectDirectory}/%{ContentDir}/images/images.txt" + }, { "source": "../common/asset_imports.txt", "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" From db2db9f3f4e5982622c091384376aba6ae0b8d46 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 13 Nov 2024 18:58:30 +0200 Subject: [PATCH 140/322] EffectComposer: Add uniform view to the code editor Task-number: QDS-14101 Change-Id: I0bcef4016fb54a0e53eb02406d6bfdf959327ba3 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../CodeEditorHeader.qml | 14 + .../CodeEditorUniformsView.qml | 150 +++++++++ src/plugins/effectcomposer/CMakeLists.txt | 1 + .../effectcomposeruniformstablemodel.cpp | 301 ++++++++++++++++++ .../effectcomposeruniformstablemodel.h | 64 ++++ .../effectshaderscodeeditor.cpp | 65 +++- .../effectcomposer/effectshaderscodeeditor.h | 9 + 7 files changed, 600 insertions(+), 4 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml create mode 100644 src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp create mode 100644 src/plugins/effectcomposer/effectcomposeruniformstablemodel.h diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml new file mode 100644 index 00000000000..2071bae15f2 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -0,0 +1,14 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioTheme as StudioTheme + +Rectangle { + color: StudioTheme.Values.themeToolbarBackground + + CodeEditorUniformsView { + anchors.fill: parent + model: uniformsTableModel + } +} diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml new file mode 100644 index 00000000000..901da46365e --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -0,0 +1,150 @@ +// Copyright (C) 2024 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 StudioControls as StudioControls +import StudioTheme as StudioTheme + +ColumnLayout { + property alias model: tableView.model + + spacing: -1 + + HoverHandler { id: hoverHandler } + + HorizontalHeaderView { + id: horizontalHeader + + Layout.fillWidth: true + + syncView: tableView + clip: true + interactive: false + + delegate: Rectangle { + color: StudioTheme.Values.themePanelBackground + implicitWidth: StudioTheme.Values.cellWidth + implicitHeight: StudioTheme.Values.cellHeight + border { + width: StudioTheme.Values.border + color: StudioTheme.Values.themeStateSeparator + } + + Text { + color: StudioTheme.Values.themeTextColor + text: display + anchors.fill: parent + anchors.margins: 8 + elide: Text.ElideRight + font.bold: true + + verticalAlignment: Qt.AlignVCenter + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ScrollView { + id: scrollView + + anchors.fill: parent + + contentItem: TableView { + id: tableView + + columnSpacing: -StudioTheme.Values.border + rowSpacing: -StudioTheme.Values.border + clip: true + interactive: false + selectionMode: TableView.SingleSelection + selectionBehavior: TableView.SelectRows + selectionModel: ItemSelectionModel {} + delegate: Cell { + id: dataScope + + Text { + id: labelView + + text: dataScope.display ?? "" + visible: !dataScope.editing + color: StudioTheme.Values.themeTextColor + anchors.fill: parent + anchors.margins: 8 + elide: Text.ElideMiddle + wrapMode: Text.WordWrap + maximumLineCount: 1 + verticalAlignment: Qt.AlignVCenter + clip: true + + StudioControls.ToolTipArea { + anchors.fill: parent + text: labelView.text + enabled: labelView.truncated + } + } + } + } + + ScrollBar.horizontal: StudioControls.TransientScrollBar { + id: horizontalScrollBar + + style: StudioTheme.Values.viewStyle + parent: tableView + x: 0 + y: tableView.height - horizontalScrollBar.height + width: tableView.availableWidth - (verticalScrollBar.isNeeded ? verticalScrollBar.thickness : 0) + orientation: Qt.Horizontal + + visible: !tableView.hideHorizontalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && horizontalScrollBar.isNeeded + otherInUse: verticalScrollBar.inUse + } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + + style: StudioTheme.Values.viewStyle + parent: tableView + x: tableView.width - verticalScrollBar.width + y: 0 + height: tableView.availableHeight - (horizontalScrollBar.isNeeded ? horizontalScrollBar.thickness : 0) + orientation: Qt.Vertical + + visible: !tableView.hideVerticalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && verticalScrollBar.isNeeded + otherInUse: horizontalScrollBar.inUse + } + } + } + + component Cell: Rectangle { + id: cell + + required property var display + required property int row + required property int column + + required property bool editing + required property bool selected + required property bool current + + color: tableView.currentRow === row ? StudioTheme.Values.themeTableCellCurrent : StudioTheme.Values.themePanelBackground + implicitWidth: StudioTheme.Values.cellWidth + implicitHeight: StudioTheme.Values.cellHeight + border { + width: StudioTheme.Values.border + color: StudioTheme.Values.themeStateSeparator + } + } +} diff --git a/src/plugins/effectcomposer/CMakeLists.txt b/src/plugins/effectcomposer/CMakeLists.txt index 7fa8726ad14..f81fb0159a5 100644 --- a/src/plugins/effectcomposer/CMakeLists.txt +++ b/src/plugins/effectcomposer/CMakeLists.txt @@ -13,6 +13,7 @@ add_qtc_plugin(EffectComposer effectcomposermodel.cpp effectcomposermodel.h effectcomposernodesmodel.cpp effectcomposernodesmodel.h effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h + effectcomposeruniformstablemodel.cpp effectcomposeruniformstablemodel.h effectsautocomplete.cpp effectsautocomplete.h effectshaderscodeeditor.cpp effectshaderscodeeditor.h effectnode.cpp effectnode.h diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp new file mode 100644 index 00000000000..5189a631dfe --- /dev/null +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp @@ -0,0 +1,301 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectcomposeruniformstablemodel.h" + +#include "uniform.h" + +#include + +#include +#include +#include + +namespace EffectComposer { + +namespace { + +struct Tr +{ + static inline QString tr(const auto &text) + { + return EffectComposerUniformsTableModel::tr(text); + } +}; + +struct RoleColMap +{ + using UniformRole = EffectComposerUniformsModel::Roles; + + struct UniformRoleData + { + UniformRole role; + QString description; + }; + + static QList tableCols() + { + static const QList list{ + {UniformRole::NameRole, Tr::tr("Uniform Name")}, + {UniformRole::DisplayNameRole, Tr::tr("Property Name")}, + {UniformRole::TypeRole, Tr::tr("Type")}, + {UniformRole::MinValueRole, Tr::tr("Min")}, + {UniformRole::MaxValueRole, Tr::tr("Max")}, + {UniformRole::DescriptionRole, Tr::tr("Description")}, + }; + return list; + }; + + static UniformRole colToRole(int col) + { + static const QList &list = tableCols(); + return list.at(col).role; + } + + static int roleToCol(int role) + { + static const QHash colOfRole = [] { + QHash result; + int col = -1; + for (const UniformRoleData &roleData : tableCols()) + result.insert(roleData.role, ++col); + return result; + }(); + return colOfRole.value(role, -1); + } + + static QString colDescription(int col) { return tableCols().at(col).description; } +}; + +struct ValueDisplay +{ + QString operator()(const QVector2D &vec) const + { + return QString("(%1, %2)").arg(vec.x()).arg(vec.y()); + } + + QString operator()(const QVector3D &vec) const + { + return QString("(%1, %2, %3)").arg(vec.x()).arg(vec.y()).arg(vec.z()); + } + + QString operator()(const QVector4D &vec) const + { + return QString("(%1, %2, %3, %4)").arg(vec.x()).arg(vec.y()).arg(vec.z()).arg(vec.w()); + } + + QString operator()(const QColor &color) const + { + return QString("(%1, %2, %3, %4)") + .arg(color.redF()) + .arg(color.greenF()) + .arg(color.blueF()) + .arg(color.alphaF()); + } + + QString operator()(const auto &) const { return Tr::tr("Unsupported type"); } +}; + +bool isValueTypedRole(EffectComposerUniformsModel::Roles role) +{ + using UniformRole = EffectComposerUniformsModel::Roles; + switch (role) { + case UniformRole::ValueRole: + case UniformRole::DefaultValueRole: + case UniformRole::MinValueRole: + case UniformRole::MaxValueRole: + return true; + default: + return false; + } +} + +} // namespace + +EffectComposerUniformsTableModel::EffectComposerUniformsTableModel( + EffectComposerUniformsModel *sourceModel, QObject *parent) + : QAbstractTableModel(parent) + , m_sourceModel(sourceModel) +{ + connect( + sourceModel, + &QAbstractItemModel::modelAboutToBeReset, + this, + &EffectComposerUniformsTableModel::modelAboutToBeReset); + connect( + sourceModel, + &QAbstractItemModel::modelReset, + this, + &EffectComposerUniformsTableModel::modelReset); + connect( + sourceModel, + &QAbstractItemModel::rowsAboutToBeInserted, + this, + &EffectComposerUniformsTableModel::onSourceRowsAboutToBeInserted); + connect( + sourceModel, + &QAbstractItemModel::rowsInserted, + this, + &EffectComposerUniformsTableModel::endInsertRows); + connect( + sourceModel, + &QAbstractItemModel::rowsAboutToBeRemoved, + this, + &EffectComposerUniformsTableModel::onSourceRowsAboutToBeRemoved); + connect( + sourceModel, + &QAbstractItemModel::rowsRemoved, + this, + &EffectComposerUniformsTableModel::endRemoveRows); + + connect( + sourceModel, + &QAbstractItemModel::rowsAboutToBeMoved, + this, + &EffectComposerUniformsTableModel::onSourceRowsAboutToBeMoved); + connect( + sourceModel, + &QAbstractItemModel::rowsMoved, + this, + &EffectComposerUniformsTableModel::endMoveRows); + connect( + sourceModel, + &QAbstractItemModel::dataChanged, + this, + &EffectComposerUniformsTableModel::onSourceDataChanged); + + connect(sourceModel, &QObject::destroyed, this, &QObject::deleteLater); +} + +EffectComposerUniformsTableModel::SourceIndex EffectComposerUniformsTableModel::mapToSource( + const QModelIndex &proxyIndex) const +{ + return {m_sourceModel->index(proxyIndex.row(), 0), RoleColMap::colToRole(proxyIndex.column())}; +} + +QHash EffectComposerUniformsTableModel::roleNames() const +{ + return { + {Qt::DisplayRole, "display"}, + {Role::ValueRole, "value"}, + {Role::ValueTypeRole, "valueType"}, + }; +} + +int EffectComposerUniformsTableModel::rowCount(const QModelIndex &) const +{ + return m_sourceModel->rowCount(); +} + +int EffectComposerUniformsTableModel::columnCount(const QModelIndex &parent) const +{ + return RoleColMap::tableCols().size(); +} + +QVariant EffectComposerUniformsTableModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + if (role == Role::ValueRole) + return mapToSource(index).value(); + + if (role == Qt::DisplayRole) + return mapToSource(index).display(); + + if (role == Role::ValueTypeRole) + return mapToSource(index).valueTypeString(); + + return {}; +} + +QVariant EffectComposerUniformsTableModel::headerData( + int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) { + if (section >= 0 && section < rowCount()) + return section; + } else if (orientation == Qt::Horizontal) { + if (section >= 0 && section < columnCount()) + return RoleColMap::colDescription(section); + } + return {}; +} + +void EffectComposerUniformsTableModel::onSourceRowsAboutToBeInserted( + const QModelIndex &, int first, int last) +{ + beginInsertRows({}, first, last); +} + +void EffectComposerUniformsTableModel::onSourceRowsAboutToBeRemoved( + const QModelIndex &, int first, int last) +{ + beginRemoveRows({}, first, last); +} + +void EffectComposerUniformsTableModel::onSourceRowsAboutToBeMoved( + const QModelIndex &, int sourceStart, int sourceEnd, const QModelIndex &, int destinationRow) +{ + beginMoveRows({}, sourceStart, sourceEnd, {}, destinationRow); +} + +void EffectComposerUniformsTableModel::onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) +{ + const int startRow = topLeft.row(); + const int endRow = bottomRight.row(); + + int minCol = 0; + int maxCol = 0; + if (roles.empty()) { + minCol = 0; + maxCol = columnCount(); + } else { + minCol = std::numeric_limits::max(); + maxCol = std::numeric_limits::min(); + for (int role : roles) { + const int col = RoleColMap::roleToCol(role); + if (col < 0) + continue; + minCol = std::min(minCol, col); + maxCol = std::max(maxCol, col); + } + } + emit dataChanged(index(startRow, minCol), index(endRow, maxCol)); +} + +QString EffectComposerUniformsTableModel::SourceIndex::valueTypeString() const +{ + using namespace Qt::StringLiterals; + if (isValueTypedRole(role)) + return index.data(UniformRole::TypeRole).toString(); + + return "string"_L1; +} + +QString EffectComposerUniformsTableModel::SourceIndex::display() const +{ + // For descriptive types, we can return string value + if (!isValueTypedRole(role)) + return value().toString(); + + // For value types, we should stringify them based on uniform type + const QString uniformTypeStr = index.data(UniformRole::TypeRole).toString(); + Uniform::Type uniformType = Uniform::typeFromString(uniformTypeStr); + const QVariant val = value(); + switch (uniformType) { + case Uniform::Type::Vec2: + return ValueDisplay{}(val.value()); + case Uniform::Type::Vec3: + return ValueDisplay{}(val.value()); + case Uniform::Type::Vec4: + return ValueDisplay{}(val.value()); + case Uniform::Type::Color: + return ValueDisplay{}(val.value()); + default: + return val.toString(); + } +} + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h new file mode 100644 index 00000000000..68a11eaa168 --- /dev/null +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h @@ -0,0 +1,64 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "effectcomposeruniformsmodel.h" + +#include +#include + +namespace EffectComposer { + +class EffectComposerUniformsTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + using UniformRole = EffectComposerUniformsModel::Roles; + + struct SourceIndex + { + QVariant value() const { return index.data(role); } + QString valueTypeString() const; + QString display() const; + + QModelIndex index; + UniformRole role = UniformRole::NameRole; + }; + + enum Role { + ValueRole = Qt::UserRole + 1, + ValueTypeRole, + }; + + explicit EffectComposerUniformsTableModel( + EffectComposerUniformsModel *sourceModel, QObject *parent = nullptr); + + SourceIndex mapToSource(const QModelIndex &proxyIndex) const; + + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role) const override; + + QVariant headerData( + int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + +private: + void onSourceRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void onSourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); + void onSourceRowsAboutToBeMoved( + const QModelIndex &sourceParent, + int sourceStart, + int sourceEnd, + const QModelIndex &destinationParent, + int destinationRow); + void onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles); + + QPointer m_sourceModel; +}; + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 33eea2bef8b..fce41f27c4a 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -2,25 +2,32 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "effectshaderscodeeditor.h" + #include "effectcodeeditorwidget.h" #include "effectcomposeruniformsmodel.h" +#include "effectcomposeruniformstablemodel.h" +#include "effectcomposerwidget.h" +#include "effectutils.h" #include #include +#include + #include #include #include #include +#include + #include #include -#include - -#include #include +#include #include +#include #include #include #include @@ -30,12 +37,23 @@ namespace { using IconId = QmlDesigner::DesignerIcons::IconId; inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate"; +inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER[] + = "QQuickWidgetEffectComposerCodeEditorHeader"; QIcon toolbarIcon(IconId iconId) { return QmlDesigner::DesignerActionManager::instance().toolbarIcon(iconId); }; +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(); +} + } // namespace namespace EffectComposer { @@ -76,6 +94,7 @@ EffectShadersCodeEditor::~EffectShadersCodeEditor() void EffectShadersCodeEditor::showWidget() { readAndApplyLiveUpdateSettings(); + reloadQml(); show(); raise(); setOpened(true); @@ -154,6 +173,15 @@ void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsModel *unif }; m_fragmentEditor->setUniformsCallback(uniformNames); m_vertexEditor->setUniformsCallback(uniformNames); + + if (m_headerWidget && uniforms) { + m_uniformsTableModel + = Utils::makeUniqueObjectLatePtr(uniforms, this); + EffectComposerUniformsTableModel *uniformsTable = m_uniformsTableModel.get(); + + m_headerWidget->rootContext() + ->setContextProperty("uniformsTableModel", QVariant::fromValue(uniformsTable)); + } } EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() @@ -183,14 +211,24 @@ EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() void EffectShadersCodeEditor::setupUIComponents() { QVBoxLayout *verticalLayout = new QVBoxLayout(this); + QSplitter *splitter = new QSplitter(this); QTabWidget *tabWidget = new QTabWidget(this); + splitter->setOrientation(Qt::Vertical); + + createHeader(); + tabWidget->addTab(m_fragmentEditor, tr("Fragment Shader")); tabWidget->addTab(m_vertexEditor, tr("Vertex Shader")); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(createToolbar()); - verticalLayout->addWidget(tabWidget); + verticalLayout->addWidget(splitter); + splitter->addWidget(m_headerWidget.get()); + splitter->addWidget(tabWidget); + + splitter->setCollapsible(0, false); + splitter->setCollapsible(1, false); connect(this, &EffectShadersCodeEditor::openedChanged, tabWidget, [this, tabWidget](bool opened) { if (!opened) @@ -272,4 +310,23 @@ QToolBar *EffectShadersCodeEditor::createToolbar() return toolbar; } +void EffectShadersCodeEditor::createHeader() +{ + m_headerWidget = new StudioQuickWidget(this); + m_headerWidget->quickWidget()->setObjectName(OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER); + m_headerWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + QmlDesigner::Theme::setupTheme(m_headerWidget->engine()); + m_headerWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_headerWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common"); + m_headerWidget->setClearColor(QmlDesigner::Theme::getColor( + QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); +} + +void EffectShadersCodeEditor::reloadQml() +{ + const QString headerQmlPath = EffectComposerWidget::qmlSourcesPath() + "/CodeEditorHeader.qml"; + QTC_ASSERT(QFileInfo::exists(headerQmlPath), return); + m_headerWidget->setSource(QUrl::fromLocalFile(headerQmlPath)); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 02f19aabe46..2c2e136bd69 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -5,13 +5,18 @@ #include +#include + QT_FORWARD_DECLARE_CLASS(QSettings) QT_FORWARD_DECLARE_CLASS(QToolBar) +class StudioQuickWidget; + namespace EffectComposer { class EffectCodeEditorWidget; class EffectComposerUniformsModel; +class EffectComposerUniformsTableModel; class EffectShadersCodeEditor : public QWidget { @@ -56,10 +61,14 @@ private: void writeLiveUpdateSettings(); void readAndApplyLiveUpdateSettings(); QToolBar *createToolbar(); + void createHeader(); + void reloadQml(); QSettings *m_settings = nullptr; QPointer m_fragmentEditor; QPointer m_vertexEditor; + QPointer m_headerWidget; + Utils::UniqueObjectLatePtr m_uniformsTableModel; bool m_liveUpdate = false; bool m_opened = false; From 92bd7a1f0105c087dccb8bbb05b43bfe3816d574 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Sun, 17 Nov 2024 00:29:04 +0200 Subject: [PATCH 141/322] EffectComposer: Add a column chooser for the code editor uniforms view Task-number: QDS-14122 Change-Id: Ie93016138599773e8497b60d6abafa059381ea6b Reviewed-by: Mahmoud Badri --- .../CodeEditorHeader.qml | 26 +- .../CodeEditorUniformsView.qml | 2 + .../ColumnChooser.qml | 276 ++++++++++++++++++ .../HeaderColumnController.qml | 38 +++ src/plugins/effectcomposer/CMakeLists.txt | 1 + .../effectcomposer/effectcomposerplugin.cpp | 6 + .../effectcomposer/effectcomposerview.cpp | 7 + .../effectcomposer/effectcomposerview.h | 2 + .../effectcomposer/tableheaderlengthmodel.cpp | 254 ++++++++++++++++ .../effectcomposer/tableheaderlengthmodel.h | 91 ++++++ 10 files changed, 701 insertions(+), 2 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/ColumnChooser.qml create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/HeaderColumnController.qml create mode 100644 src/plugins/effectcomposer/tableheaderlengthmodel.cpp create mode 100644 src/plugins/effectcomposer/tableheaderlengthmodel.h diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 2071bae15f2..7a4b5a87b7c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -2,13 +2,35 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Layouts import StudioTheme as StudioTheme Rectangle { color: StudioTheme.Values.themeToolbarBackground - CodeEditorUniformsView { + ColumnLayout { anchors.fill: parent - model: uniformsTableModel + + RowLayout { + Item { // Spacer + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + + ColumnChooser { + table: uniformsView.tableView + text: "Columns" + style: StudioTheme.Values.viewBarControlStyle + Layout.topMargin: StudioTheme.Values.marginTopBottom + } + } + + CodeEditorUniformsView { + id: uniformsView + + Layout.fillWidth: true + Layout.fillHeight: true + model: uniformsTableModel + } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index 901da46365e..5d86c6d1ac7 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -6,9 +6,11 @@ import QtQuick.Controls import QtQuick.Layouts import StudioControls as StudioControls import StudioTheme as StudioTheme +import TableModules as TableModules ColumnLayout { property alias model: tableView.model + readonly property alias tableView: tableView spacing: -1 diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ColumnChooser.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ColumnChooser.qml new file mode 100644 index 00000000000..cb1f3f4cf0e --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ColumnChooser.qml @@ -0,0 +1,276 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Shapes +import QtQuick.Templates as T + +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import TableModules as TableModules + +T.AbstractButton { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + required property TableView table + + width: control.style.squareControlSize.width + buttonLabel.implicitWidth + height: control.style.squareControlSize.height + + checked: dropdownPopup.visible + + onPressed: control.checked ? dropdownPopup.close() : dropdownPopup.open() + + HeaderColumnController { + id: columnController + view: table + } + + contentItem: Row { + spacing: 0 + + Text { + id: buttonLabel + height: control.height + leftPadding: 8 + font.pixelSize: control.style.baseFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + visible: control.text !== "" + } + + 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: StudioTheme.Constants.upDownSquare2 + } + } + + background: Rectangle { + id: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: control.style.radius + } + + T.Popup { + id: dropdownPopup + parent: control + x: control.width - dropdownPopup.width + y: control.height + width: 120 + height: contentHeight + padding: control.style.borderWidth + margins: 0 // If not defined margin will be -1 + closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent + + contentItem: ListView { + id: listView + clip: true + implicitHeight: Math.min(listView.contentHeight, Window.height, 200) + boundsBehavior: Flickable.StopAtBounds + model: columnController.model + + HoverHandler { id: hoverHandler } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + parent: listView + x: listView.width - verticalScrollBar.width + y: 0 + height: listView.availableHeight + orientation: Qt.Vertical + + show: (hoverHandler.hovered || verticalScrollBar.inUse) + && verticalScrollBar.isNeeded + } + + delegate: ItemDelegate { + id: itemDelegate + + required property string name + required property bool hidden + + required property int index + + width: dropdownPopup.width - dropdownPopup.leftPadding - dropdownPopup.rightPadding + height: control.style.controlSize.height - 2 * control.style.borderWidth + padding: 0 + checkable: true + checked: !itemDelegate.hidden + + onToggled: { + columnController.model.setVisible(itemDelegate.index, itemDelegate.checked) + } + + contentItem: Text { + leftPadding: itemDelegateIconArea.width + + text: itemDelegate.name + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + color: { + if (!itemDelegate.enabled) + return control.style.text.disabled + + return itemDelegate.highlighted ? control.style.text.selectedText + : control.style.text.idle + } + } + + Item { + id: itemDelegateIconArea + width: itemDelegate.height + height: itemDelegate.height + + T.Label { + id: itemDelegateIcon + text: StudioTheme.Constants.tickIcon + color: itemDelegate.highlighted ? control.style.text.selectedText + : control.style.text.idle + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: control.style.smallIconFontSize + visible: itemDelegate.checked + anchors.fill: parent + renderType: Text.NativeRendering + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + highlighted: itemDelegate.hovered + + background: Rectangle { + id: itemDelegateBackground + x: 0 + y: 0 + width: itemDelegate.width + height: itemDelegate.height + color: itemDelegate.highlighted ? control.style.interaction : "transparent" + } + } + } + + background: Rectangle { + color: control.style.popup.background + border.width: 0 + } + + enter: Transition {} + exit: Transition {} + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.idle + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.enabled && control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.hover + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.hover + } + }, + State { + 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: "press" + when: control.enabled && control.hovered && control.pressed + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.interaction + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.interaction + } + }, + State { + name: "check" + when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "pressNotHover" + 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/effectComposerQmlSources/HeaderColumnController.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/HeaderColumnController.qml new file mode 100644 index 00000000000..dd6958126f9 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/HeaderColumnController.qml @@ -0,0 +1,38 @@ +// Copyright (C) 2024 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 TableModules as TableModules + +QtObject{ + id: controller + + required property TableView view + + readonly property TableModules.TableHeaderLengthModel model: TableModules.TableHeaderLengthModel { + id: internalModel + + orientation: Qt.Horizontal + sourceModel: controller.view.model + + onSectionVisibilityChanged: controller.view.forceLayout() + + Component.onCompleted: { + controller.view.columnWidthProvider = controller.lengthProvider + } + } + + function lengthProvider(section) { + if (!internalModel.isVisible(section)) + return 0 + + let len = controller.view.explicitColumnWidth(section) + if (len < 0) + len = controller.view.implicitColumnWidth(section) + len = Math.max(len, internalModel.minimumLength(section)) + + internalModel.setLength(section, len) + return len + } +} diff --git a/src/plugins/effectcomposer/CMakeLists.txt b/src/plugins/effectcomposer/CMakeLists.txt index f81fb0159a5..6719fcad1dd 100644 --- a/src/plugins/effectcomposer/CMakeLists.txt +++ b/src/plugins/effectcomposer/CMakeLists.txt @@ -19,6 +19,7 @@ add_qtc_plugin(EffectComposer effectnode.cpp effectnode.h effectnodescategory.cpp effectnodescategory.h compositionnode.cpp compositionnode.h + tableheaderlengthmodel.cpp tableheaderlengthmodel.h uniform.cpp uniform.h effectutils.cpp effectutils.h effectcomposercontextobject.cpp effectcomposercontextobject.h diff --git a/src/plugins/effectcomposer/effectcomposerplugin.cpp b/src/plugins/effectcomposer/effectcomposerplugin.cpp index 13c29bb943d..75cf7c9872a 100644 --- a/src/plugins/effectcomposer/effectcomposerplugin.cpp +++ b/src/plugins/effectcomposer/effectcomposerplugin.cpp @@ -25,6 +25,12 @@ public: EffectComposerPlugin() {} ~EffectComposerPlugin() override {} + bool initialize(const QStringList &arguments, QString *errorString) override + { + EffectComposerView::registerDeclarativeTypes(); + return ExtensionSystem::IPlugin::initialize(arguments, errorString); + } + bool delayedInitialize() override { if (m_delayedInitialized) diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index c0478790715..064786749b9 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -7,6 +7,7 @@ #include "effectcomposernodesmodel.h" #include "effectcomposerwidget.h" #include "studioquickwidget.h" +#include "tableheaderlengthmodel.h" #include #include @@ -19,6 +20,7 @@ #include #include +#include namespace EffectComposer { @@ -261,4 +263,9 @@ void EffectComposerView::dragEnded() highlightSupportedProperties(false); } +void EffectComposer::EffectComposerView::registerDeclarativeTypes() +{ + qmlRegisterType("TableModules", 1, 0, "TableHeaderLengthModel"); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h index 338960f1f08..d9eef5559e2 100644 --- a/src/plugins/effectcomposer/effectcomposerview.h +++ b/src/plugins/effectcomposer/effectcomposerview.h @@ -46,6 +46,8 @@ public: void highlightSupportedProperties(bool highlight, const QString &suffix = {}); + static void registerDeclarativeTypes(); + private: void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; diff --git a/src/plugins/effectcomposer/tableheaderlengthmodel.cpp b/src/plugins/effectcomposer/tableheaderlengthmodel.cpp new file mode 100644 index 00000000000..fe621abfdd6 --- /dev/null +++ b/src/plugins/effectcomposer/tableheaderlengthmodel.cpp @@ -0,0 +1,254 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "tableheaderlengthmodel.h" + +TableHeaderLengthModel::TableHeaderLengthModel(QObject *parent) + : QAbstractListModel(parent) +{} + +QHash TableHeaderLengthModel::roleNames() const +{ + static const QHash result = { + {LengthRole, "length"}, + {HiddenRole, "hidden"}, + {NameRole, "name"}, + }; + return result; +} + +int TableHeaderLengthModel::rowCount(const QModelIndex &) const +{ + return m_data.size(); +} + +QVariant TableHeaderLengthModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + switch (role) { + case Roles::LengthRole: + return m_data.at(index.row()).length; + case Roles::HiddenRole: + return !m_data.at(index.row()).visible; + case Roles::NameRole: + return m_sourceModel->headerData(index.row(), orientation(), Qt::DisplayRole); + default: + return {}; + } +} + +bool TableHeaderLengthModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (data(index, role) == value) + return false; + + switch (role) { + case Roles::LengthRole: { + m_data[index.row()].length = value.toInt(); + emit dataChanged(index, index, {role}); + } break; + case Roles::HiddenRole: { + m_data[index.row()].visible = !value.toBool(); + emit dataChanged(index, index, {role}); + emit sectionVisibilityChanged(index.row()); + } break; + default: + return false; + } + return true; +} + +void TableHeaderLengthModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + if (m_sourceModel.get() == sourceModel) + return; + + // Disconnect old model if exists + if (m_sourceModel) + disconnect(m_sourceModel.get(), 0, this, nullptr); + + m_sourceModel = sourceModel; + emit sourceModelChanged(); + setupModel(); +} + +QAbstractItemModel *TableHeaderLengthModel::sourceModel() const +{ + return m_sourceModel; +} + +void TableHeaderLengthModel::setOrientation(Qt::Orientation orientation) +{ + if (m_orientation == orientation) + return; + + m_orientation = orientation; + emit orientationChanged(); + setupModel(); +} + +Qt::Orientation TableHeaderLengthModel::orientation() const +{ + return m_orientation; +} + +void TableHeaderLengthModel::onSourceItemsInserted(const QModelIndex &parent, int first, int last) +{ + beginInsertRows({}, first, last); + const Item defaultItem{true, m_defaultLength}; + m_data.insert(first, last - first + 1, defaultItem); + endInsertRows(); +} + +void TableHeaderLengthModel::onSourceItemsRemoved(const QModelIndex &parent, int first, int last) +{ + beginRemoveRows({}, first, last); + m_data.remove(first, last - first + 1); + endRemoveRows(); +} + +void TableHeaderLengthModel::onSourceItemsMoved( + const QModelIndex &sourceParent, + int sourceStart, + int sourceEnd, + const QModelIndex &destinationParent, + int destinationRow) +{ + beginMoveRows({}, sourceStart, sourceEnd, {}, destinationRow); + QList pack = m_data.mid(sourceStart, sourceEnd - sourceStart + 1); + m_data.remove(sourceStart, sourceEnd - sourceStart + 1); + while (!pack.isEmpty()) + m_data.insert(destinationRow, pack.takeLast()); + endMoveRows(); +} + +void TableHeaderLengthModel::checkModelReset() +{ + int availableItems = 0; + if (m_sourceModel) + availableItems = (orientation() == Qt::Horizontal) ? m_sourceModel->columnCount() + : m_sourceModel->rowCount(); + if (availableItems != rowCount()) + setupModel(); +} + +void TableHeaderLengthModel::setupModel() +{ + beginResetModel(); + m_data.clear(); + + QAbstractItemModel *source = m_sourceModel.get(); + if (!source) { + endResetModel(); + return; + } + + disconnect(source, 0, this, nullptr); + + connect(source, &QAbstractItemModel::modelReset, this, &TableHeaderLengthModel::checkModelReset); + + const Item defaultItem{true, m_defaultLength}; + if (orientation() == Qt::Horizontal) { + connect( + source, + &QAbstractItemModel::columnsInserted, + this, + &TableHeaderLengthModel::onSourceItemsInserted); + + connect( + source, + &QAbstractItemModel::columnsRemoved, + this, + &TableHeaderLengthModel::onSourceItemsRemoved); + + connect( + source, + &QAbstractItemModel::columnsMoved, + this, + &TableHeaderLengthModel::onSourceItemsMoved); + + m_data.insert(0, source->columnCount(), defaultItem); + } else { + connect( + source, + &QAbstractItemModel::rowsInserted, + this, + &TableHeaderLengthModel::onSourceItemsInserted); + + connect( + source, + &QAbstractItemModel::rowsRemoved, + this, + &TableHeaderLengthModel::onSourceItemsRemoved); + + connect( + source, + &QAbstractItemModel::rowsMoved, + this, + &TableHeaderLengthModel::onSourceItemsMoved); + m_data.insert(0, source->rowCount(), defaultItem); + } + endResetModel(); +} + +bool TableHeaderLengthModel::invalidSection(int section) const +{ + return (section < 0 || section >= m_data.length()); +} + +int TableHeaderLengthModel::defaultLength() const +{ + return m_defaultLength; +} + +void TableHeaderLengthModel::setDefaultLength(int value) +{ + if (m_defaultLength == value) + return; + m_defaultLength = value; + emit defaultLengthChanged(); +} + +int TableHeaderLengthModel::length(int section) const +{ + if (invalidSection(section)) + return 0; + return m_data.at(section).length; +} + +void TableHeaderLengthModel::setLength(int section, int len) +{ + QModelIndex idx = index(section, 0); + setData(idx, len, Roles::LengthRole); +} + +bool TableHeaderLengthModel::isVisible(int section) const +{ + if (invalidSection(section)) + return false; + + return m_data.at(section).visible; +} + +void TableHeaderLengthModel::setVisible(int section, bool visible) +{ + QModelIndex idx = index(section, 0); + setData(idx, !visible, Roles::HiddenRole); +} + +int TableHeaderLengthModel::minimumLength(int section) const +{ + if (invalidSection(section)) + return 0; + + // ToDo: handle it by data + if (orientation() == Qt::Horizontal) + return 70; + + return 20; +} diff --git a/src/plugins/effectcomposer/tableheaderlengthmodel.h b/src/plugins/effectcomposer/tableheaderlengthmodel.h new file mode 100644 index 00000000000..15796867849 --- /dev/null +++ b/src/plugins/effectcomposer/tableheaderlengthmodel.h @@ -0,0 +1,91 @@ +// Copyright (C) 2024 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 TableHeaderLengthModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY( + QAbstractItemModel *sourceModel + READ sourceModel + WRITE setSourceModel + NOTIFY sourceModelChanged) + + Q_PROPERTY( + Qt::Orientation orientation + READ orientation + WRITE setOrientation + NOTIFY orientationChanged) + + Q_PROPERTY( + int defaultLength + READ defaultLength + WRITE setDefaultLength + NOTIFY defaultLengthChanged + FINAL) + +public: + explicit TableHeaderLengthModel(QObject *parent = nullptr); + enum Roles { + LengthRole = Qt::UserRole + 1, + HiddenRole, + NameRole, + }; + + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + void setSourceModel(QAbstractItemModel *sourceModel); + QAbstractItemModel *sourceModel() const; + + void setOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + + int defaultLength() const; + void setDefaultLength(int value); + + Q_INVOKABLE int length(int section) const; + Q_INVOKABLE void setLength(int section, int len); + Q_INVOKABLE bool isVisible(int section) const; + Q_INVOKABLE void setVisible(int section, bool visible); + Q_INVOKABLE int minimumLength(int section) const; + +signals: + void sourceModelChanged(); + void orientationChanged(); + void defaultLengthChanged(); + void sectionVisibilityChanged(int); + +private slots: + void onSourceItemsInserted(const QModelIndex &parent, int first, int last); + void onSourceItemsRemoved(const QModelIndex &parent, int first, int last); + void onSourceItemsMoved( + const QModelIndex &sourceParent, + int sourceStart, + int sourceEnd, + const QModelIndex &destinationParent, + int destinationRow); + void checkModelReset(); + +private: + void setupModel(); + bool invalidSection(int section) const; + + struct Item + { + bool visible = true; + int length; + }; + + QPointer m_sourceModel; + Qt::Orientation m_orientation = Qt::Horizontal; + int m_defaultLength = 100; + QList m_data; +}; From e75d9001e5c16cb044ddb229bbb858e02d4028ef Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Sun, 17 Nov 2024 07:36:01 +0200 Subject: [PATCH 142/322] EffectComposer: Move live update button to Qml header Task-number: QDS-14123 Change-Id: I54d3bb7e916a5bf5f9b4e5b9db17692eed28aaaf Reviewed-by: Miikka Heikkinen --- .../CodeEditorHeader.qml | 16 ++++++- .../effectshaderscodeeditor.cpp | 47 +------------------ .../effectcomposer/effectshaderscodeeditor.h | 2 - 3 files changed, 16 insertions(+), 49 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 7a4b5a87b7c..065354bdc0e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -3,9 +3,14 @@ import QtQuick import QtQuick.Layouts +import StudioControls as StudioControls import StudioTheme as StudioTheme Rectangle { + id: root + + property var rootView: shaderEditor + color: StudioTheme.Values.themeToolbarBackground ColumnLayout { @@ -19,10 +24,19 @@ Rectangle { ColumnChooser { table: uniformsView.tableView - text: "Columns" + text: qsTr("Columns") style: StudioTheme.Values.viewBarControlStyle Layout.topMargin: StudioTheme.Values.marginTopBottom } + + StudioControls.CheckBox { + text: qsTr("Live Update") + actionIndicatorVisible: false + style: StudioTheme.Values.viewBarControlStyle + Layout.topMargin: StudioTheme.Values.marginTopBottom + checked: root.rootView ? root.rootView.liveUpdate : false + onToggled: root.rootView.liveUpdate = checked + } } CodeEditorUniformsView { diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index fce41f27c4a..35f11879fcd 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -14,8 +14,6 @@ #include -#include -#include #include #include @@ -29,22 +27,14 @@ #include #include #include -#include #include namespace { -using IconId = QmlDesigner::DesignerIcons::IconId; - inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate"; inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER[] = "QQuickWidgetEffectComposerCodeEditorHeader"; -QIcon toolbarIcon(IconId iconId) -{ - return QmlDesigner::DesignerActionManager::instance().toolbarIcon(iconId); -}; - QString propertyEditorResourcesPath() { #ifdef SHARE_QML_PATH @@ -222,7 +212,6 @@ void EffectShadersCodeEditor::setupUIComponents() tabWidget->addTab(m_vertexEditor, tr("Vertex Shader")); verticalLayout->setContentsMargins(0, 0, 0, 0); - verticalLayout->addWidget(createToolbar()); verticalLayout->addWidget(splitter); splitter->addWidget(m_headerWidget.get()); splitter->addWidget(tabWidget); @@ -275,41 +264,6 @@ void EffectShadersCodeEditor::readAndApplyLiveUpdateSettings() setLiveUpdate(liveUpdateStatus); } -QToolBar *EffectShadersCodeEditor::createToolbar() -{ - using QmlDesigner::Theme; - - QToolBar *toolbar = new QToolBar(this); - - toolbar->setFixedHeight(Theme::toolbarSize()); - toolbar->setFloatable(false); - toolbar->setContentsMargins(0, 0, 0, 0); - - toolbar->setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - - QAction *liveUpdateButton - = toolbar->addAction(toolbarIcon(IconId::LiveUpdateIcon), tr("Live Update")); - liveUpdateButton->setCheckable(true); - connect(liveUpdateButton, &QAction::toggled, this, &EffectShadersCodeEditor::setLiveUpdate); - - QAction *applyAction = toolbar->addAction(toolbarIcon(IconId::SyncIcon), tr("Apply")); - connect(applyAction, &QAction::triggered, this, &EffectShadersCodeEditor::rebakeRequested); - - auto syncLive = [liveUpdateButton, applyAction](bool liveState) { - liveUpdateButton->setChecked(liveState); - applyAction->setDisabled(liveState); - }; - - connect(this, &EffectShadersCodeEditor::liveUpdateChanged, this, syncLive); - syncLive(liveUpdate()); - - toolbar->addAction(liveUpdateButton); - toolbar->addAction(applyAction); - - return toolbar; -} - void EffectShadersCodeEditor::createHeader() { m_headerWidget = new StudioQuickWidget(this); @@ -320,6 +274,7 @@ void EffectShadersCodeEditor::createHeader() m_headerWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common"); m_headerWidget->setClearColor(QmlDesigner::Theme::getColor( QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_headerWidget->rootContext()->setContextProperty("shaderEditor", QVariant::fromValue(this)); } void EffectShadersCodeEditor::reloadQml() diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 2c2e136bd69..69ee9a19d38 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -8,7 +8,6 @@ #include QT_FORWARD_DECLARE_CLASS(QSettings) -QT_FORWARD_DECLARE_CLASS(QToolBar) class StudioQuickWidget; @@ -60,7 +59,6 @@ protected: private: void writeLiveUpdateSettings(); void readAndApplyLiveUpdateSettings(); - QToolBar *createToolbar(); void createHeader(); void reloadQml(); From 3c8c2eba130714c810364d0fc478fde07108d6c9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 19 Nov 2024 12:29:09 +0200 Subject: [PATCH 143/322] EffectComposer: Allow removing all properties of effects If the removed property is in use in the shader code, user is prompted to confirm the removal. Fixes: QDS-14100 Change-Id: I8a000f8935f1e09c9078b1fc9f1acbd30d12dfd0 Reviewed-by: Mahmoud Badri --- .../ConfirmPropertyRemoveForm.qml | 77 +++++++++++++++++++ .../EffectComposer.qml | 9 +++ .../EffectCompositionNode.qml | 46 ++++++++++- .../EffectCompositionNodeUniform.qml | 2 - .../effectcomposer/compositionnode.cpp | 16 ++++ src/plugins/effectcomposer/compositionnode.h | 1 + .../effectcomposer/effectcomposermodel.cpp | 7 ++ .../effectcomposer/effectcomposermodel.h | 1 + 8 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmPropertyRemoveForm.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmPropertyRemoveForm.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmPropertyRemoveForm.qml new file mode 100644 index 00000000000..c673d4fdebd --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmPropertyRemoveForm.qml @@ -0,0 +1,77 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioTheme as StudioTheme +import HelperWidgets as HelperWidgets + +Rectangle { + id: root + + width: 200 + height: 30 + titleLabel.height + buttonRow.height + border.width: 1 + border.color: StudioTheme.Values.themeControlOutline + color: StudioTheme.Values.themeSectionHeadBackground + + signal accepted() + signal canceled() + + Column { + id: column + anchors.fill: parent + anchors.margins: 10 + spacing: 10 + + Item { + id: titleLabel + + width: parent.width + height: 50 + Text { + anchors.centerIn: parent + color: StudioTheme.Values.themeTextColor + font.bold: true + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("The property is in use in the shader code.\nAre you sure you want to remove it?") + } + } + + Row { + id: buttonRow + + width: acceptButton.width + buttonRow.spacing + cancelButton.width + spacing: 10 + anchors.horizontalCenter: parent.horizontalCenter + height: 35 + + HelperWidgets.Button { + id: cancelButton + width: 80 + height: 30 + text: qsTr("Cancel") + padding: 4 + + onClicked: { + root.canceled() + root.visible = false + } + } + + HelperWidgets.Button { + id: acceptButton + width: 80 + height: 30 + text: qsTr("Remove") + padding: 4 + + onClicked: { + root.accepted() + root.visible = false + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 75c1aac037d..7cef76a760d 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -21,6 +21,7 @@ Item { property int moveToIdx: 0 property bool previewAnimationRunning: false property var expandStates: null + property real ensureVisibleY: -1 // Invoked after save changes is done property var onSaveChangesCallback: () => {} @@ -315,6 +316,12 @@ Item { scrollView.contentY = scrollView.contentItem.height - lastItemH nodesComboBox.nodeJustAdded = false } + + if (root.ensureVisibleY >= 0) { + if (root.ensureVisibleY > scrollView.contentY + scrollView.height) + scrollView.contentY = root.ensureVisibleY - scrollView.height + root.ensureVisibleY = -1 + } } Column { @@ -370,6 +377,8 @@ Item { expanded = wasExpanded dragAnimation.enabled = true } + + onEnsureVisible: visibleY => root.ensureVisibleY = visibleY } } // Repeater } // Column diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index 0c56893bd39..e26da34264e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -17,6 +17,16 @@ HelperWidgets.Section { property int modelIndex: 0 property int editedUniformIndex: -1 + signal ensureVisible(visibleY: real) + + function emitEnsure(item) + { + if (item.visible) { + let ensureY = item.mapToItem(root, Qt.point(0, 0)).y + item.height + root.y + 30 + root.ensureVisible(ensureY) + } + } + caption: nodeName category: "EffectComposer" @@ -76,13 +86,22 @@ HelperWidgets.Section { EffectCompositionNodeUniform { id: effectCompositionNodeUniform width: root.width - StudioTheme.Values.scrollBarThicknessHover - removable: uniformUserAdded editing: root.editedUniformIndex === index disableMoreMenu: root.editedUniformIndex >= 0 onReset: nodeUniformsModel.resetData(index) - onRemove: nodeUniformsModel.remove(index) + onRemove: { + if (root.backendModel.isNodeUniformInUse(root.modelIndex, index)) { + confirmRemoveForm.parent = effectCompositionNodeUniform.editPropertyFormParent + confirmRemoveForm.uniformIndex = index + confirmRemoveForm.visible = true + } else { + nodeUniformsModel.remove(index) + } + } onEdit: { + confirmRemoveForm.visible = false + confirmRemoveForm.parent = root addPropertyForm.parent = effectCompositionNodeUniform.editPropertyFormParent let dispNames = nodeUniformsModel.displayNames() let filteredDispNames = dispNames.filter(name => name !== uniformDisplayName); @@ -114,6 +133,8 @@ HelperWidgets.Section { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter onClicked: { + confirmRemoveForm.visible = false + confirmRemoveForm.parent = root root.editedUniformIndex = -1 addPropertyForm.parent = addProperty addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() @@ -128,6 +149,8 @@ HelperWidgets.Section { width: parent.width effectNodeName: nodeName + onHeightChanged: root.emitEnsure(addPropertyForm) + onVisibleChanged: root.emitEnsure(addPropertyForm) onAccepted: { root.backendModel.addOrUpdateNodeUniform(root.modelIndex, @@ -143,5 +166,24 @@ HelperWidgets.Section { } } } + + ConfirmPropertyRemoveForm { + id: confirmRemoveForm + + property int uniformIndex: -1 + + width: root.width - StudioTheme.Values.scrollBarThicknessHover - 8 + visible: false + + onHeightChanged: root.emitEnsure(confirmRemoveForm) + onVisibleChanged: root.emitEnsure(confirmRemoveForm) + + onAccepted: { + confirmRemoveForm.parent = root + nodeUniformsModel.remove(confirmRemoveForm.uniformIndex) + } + + onCanceled: confirmRemoveForm.parent = root + } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 92868eb19c5..7b0b0d65e0f 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -12,7 +12,6 @@ import EffectComposerBackend Item { id: root - property bool removable: false property bool editing: false property bool disableMoreMenu: false property alias editPropertyFormParent: editPropertyFormPlaceholder @@ -156,7 +155,6 @@ Item { StudioControls.MenuItem { text: qsTr("Remove") onTriggered: root.remove() - enabled: root.removable } } } diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 567d63e6b39..c597b8d98b3 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace EffectComposer { @@ -304,6 +306,20 @@ void CompositionNode::updateUniform(int index, const QVariantMap &data) m_uniformsModel.updateUniform(index, uniform); } +bool CompositionNode::isUniformInUse(int index) +{ + QTC_ASSERT(index >= 0 && index < m_uniforms.size(), return false); + + const QString name = m_uniforms[index]->name(); + QString pattern = QString("\\b%1\\b").arg(QRegularExpression::escape(name)); + QRegularExpression regex(pattern); + bool found = regex.match(m_fragmentCode).hasMatch(); + if (!found) + found = regex.match(m_vertexCode).hasMatch(); + + return found; +} + QString CompositionNode::name() const { return m_name; diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index 9b87f385d94..14ebcb88f3e 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -75,6 +75,7 @@ public: void closeCodeEditor(); void addUniform(const QVariantMap &data); void updateUniform(int index, const QVariantMap &data); + bool isUniformInUse(int index); signals: void uniformsModelChanged(); diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 16c62a69c0b..ea7dbe15e4c 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -195,6 +195,13 @@ void EffectComposerModel::removeNode(int idx) emit nodesChanged(); } +bool EffectComposerModel::isNodeUniformInUse(int nodeIndex, int uniformIndex) +{ + QTC_ASSERT(nodeIndex >= 0 && nodeIndex < m_nodes.size(), return false); + + return m_nodes[nodeIndex]->isUniformInUse(uniformIndex); +} + void EffectComposerModel::clear(bool clearName) { beginResetModel(); diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index b2fbe808bec..2edcefa9497 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -80,6 +80,7 @@ public: Q_INVOKABLE void moveNode(int fromIdx, int toIdx); Q_INVOKABLE void removeNode(int idx); + Q_INVOKABLE bool isNodeUniformInUse(int nodeIndex, int uniformIndex); Q_INVOKABLE void clear(bool clearName = false); Q_INVOKABLE void assignToSelected(); Q_INVOKABLE QString getUniqueEffectName() const; From f6593cac5828edb9b0fe759d665f4559603d985c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Sun, 17 Nov 2024 11:49:44 +0200 Subject: [PATCH 144/322] EffectComposer: Add copy button for the uniform id in editor Task-number: QDS-14124 Change-Id: If73f0ba40b672c1761c54765c8415fb498873b2a Reviewed-by: Shrief Gabr Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CodeEditorUniformsView.qml | 33 +++++++++++++++++++ .../effectcomposeruniformstablemodel.cpp | 4 +++ .../effectcomposeruniformstablemodel.h | 1 + .../effectshaderscodeeditor.cpp | 6 ++++ .../effectcomposer/effectshaderscodeeditor.h | 2 ++ 5 files changed, 46 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index 5d86c6d1ac7..f7319a3cd8c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -88,6 +88,38 @@ ColumnLayout { text: labelView.text enabled: labelView.truncated } + + Loader { + active: dataScope.canCopy + + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + + sourceComponent: MouseArea { + id: hoverArea + + width: 15 + hoverEnabled: true + enabled: true + + Row { + anchors.fill: parent + visible: hoverArea.containsMouse + + StudioControls.AbstractButton { + width: iconSize + height: iconSize + anchors.verticalCenter: parent.verticalCenter + buttonIcon: StudioTheme.Constants.copy_small + backgroundVisible: false + onClicked: rootView.copyText(dataScope.display) + } + + // ToDo: Add a button for placing the value to the editor + } + } + } } } } @@ -140,6 +172,7 @@ ColumnLayout { required property bool editing required property bool selected required property bool current + required property bool canCopy color: tableView.currentRow === row ? StudioTheme.Values.themeTableCellCurrent : StudioTheme.Values.themePanelBackground implicitWidth: StudioTheme.Values.cellWidth diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp index 5189a631dfe..16c9d489196 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp @@ -179,6 +179,7 @@ QHash EffectComposerUniformsTableModel::roleNames() const {Qt::DisplayRole, "display"}, {Role::ValueRole, "value"}, {Role::ValueTypeRole, "valueType"}, + {Role::CanCopyRole, "canCopy"}, }; } @@ -206,6 +207,9 @@ QVariant EffectComposerUniformsTableModel::data(const QModelIndex &index, int ro if (role == Role::ValueTypeRole) return mapToSource(index).valueTypeString(); + if (role == Role::CanCopyRole) + return mapToSource(index).role == UniformRole::NameRole; + return {}; } diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h index 68a11eaa168..52b7dd82932 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h @@ -30,6 +30,7 @@ public: enum Role { ValueRole = Qt::UserRole + 1, ValueTypeRole, + CanCopyRole, }; explicit EffectComposerUniformsTableModel( diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 35f11879fcd..3f1a48033ee 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -174,6 +175,11 @@ void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsModel *unif } } +void EffectShadersCodeEditor::copyText(const QString &text) +{ + qApp->clipboard()->setText(text); +} + EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() { static EffectCodeEditorFactory f; diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 69ee9a19d38..9de59010afb 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -41,6 +41,8 @@ public: bool isOpened() const; void setUniformsModel(EffectComposerUniformsModel *uniforms); + Q_INVOKABLE void copyText(const QString &text); + signals: void liveUpdateChanged(bool); void fragmentValueChanged(); From 3de21ec97c7378ebbadf26676bd43b8d139bfc02 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 19 Nov 2024 15:14:39 +0100 Subject: [PATCH 145/322] DVConnector: Cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4d1cd63e3d3c06debd2ba68c343da1e1cb504c0a Reviewed-by: Henning Gründl --- src/plugins/qmldesigner/components/designviewer/dvconnector.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index 23a460fe7bc..b5ca43deb26 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -112,7 +112,6 @@ DVConnector::DVConnector(QObject *parent) , m_isWebViewerVisible(false) , m_connectorStatus(ConnectorStatus::FetchingUserInfo) { - QLoggingCategory::setFilterRules("qtc.designer.deploymentPlugin.debug=true"); m_webEngineProfile.reset(new QWebEngineProfile("DesignViewer", this)); m_webEngineProfile->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); m_webEnginePage.reset(new CustomWebEnginePage(m_webEngineProfile.data(), this)); @@ -277,7 +276,6 @@ void DVConnector::uploadProject(const QString &projectId, const QString &filePat this, [this](qint64 bytesSent, qint64 bytesTotal) { emit projectUploadProgress(100.0 * (double) bytesSent / (double) bytesTotal); - ; }); evaluatorData.connectCallbacks(this); } From 59eec06cae45ed895a8fb62aa528af47b07c5d5b Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Thu, 14 Nov 2024 14:26:31 +0100 Subject: [PATCH 146/322] ResourceGenerator: Prevent crashing in case the qtversion is nullptr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4cac5ef2154523e9189dc115bc931e69baf4f0b0 Reviewed-by: Henning Gründl --- .../qmldesigner/components/componentcore/resourcegenerator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index 93f3d458e94..ffeefa6e8cb 100644 --- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -193,6 +193,8 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath) const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion( project->activeTarget()->kit()); + QTC_ASSERT(qtVersion, return false); + const FilePath rccBinary = qtVersion->rccFilePath(); Utils::Process rccProcess; From f2490f409367219cef105b709dbd1eef1c47235a Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Fri, 15 Nov 2024 12:02:52 +0200 Subject: [PATCH 147/322] Doc: Fix lists Fix lists to align with MS Style guide, mainly: - Punctation - Colon after intro text Change-Id: Ic4380be4505d68ebadb7d0d087dc4113bc1f2d9b Reviewed-by: Johanna Vanhatapio --- .../examples/doc/3DsceneTutorial.qdoc | 10 +-- doc/qtdesignstudio/examples/doc/loginui1.qdoc | 4 +- doc/qtdesignstudio/examples/doc/robotarm.qdoc | 2 +- .../src/components/qtquick-buttons.qdoc | 12 ++-- .../src/components/qtquick-controls.qdoc | 14 ++-- .../src/components/qtquick-positioning.qdoc | 5 +- .../components/qtquick-preset-components.qdoc | 4 +- .../src/components/qtquick-text.qdoc | 16 ++--- .../studio-designer-developer-workflow.qdoc | 19 ++--- .../qt-design-viewer-navigation.qdoc | 10 +-- .../src/overviews/qtquick-export.qdoc | 2 + .../overviews/qtquick-optimizing-designs.qdoc | 6 +- .../src/qtbridge/qtbridge-ps-using.qdoc | 2 +- .../src/qtbridge/qtbridge-xd-using.qdoc | 72 +++++++++---------- .../src/qtdesignstudio-importing-2d.qdoc | 3 +- .../src/qtdesignstudio-installation.qdoc | 4 ++ .../src/qtdesignstudio-javascript.qdoc | 2 +- .../src/qtdesignstudio-qt-ui-viewer.qdoc | 4 +- .../exporting-3d/exporting-from-qt3ds.qdoc | 13 ++-- .../qtdesignstudio-3d-materials.qdoc | 29 ++++---- .../qtdesignstudio-3d-repeater-3d.qdoc | 6 +- ...tdesignstudio-property-visual-effects.qdoc | 16 ++--- .../qtquick-connection-editor-properties.qdoc | 5 +- .../qtquick-connection-editor-signals.qdoc | 2 +- .../src/views/qtquick-states.qdoc | 4 +- .../src/views/qtquick-timeline.qdoc | 2 +- .../src/views/studio-material-editor.qdoc | 2 +- .../src/views/studio-qtinsight.qdoc | 6 +- 28 files changed, 140 insertions(+), 136 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc index b89689618e0..223bffe2d0d 100644 --- a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc @@ -45,7 +45,7 @@ \section1 Adding Materials to the 3D Models - First, use materials from the \uicontrol {Content Library} view on the ball bearing. + First, use materials from the \uicontrol {Content Library} view on the ball bearing: \list 1 \li In the \uicontrol 3D view, right-click the ball bearing and select @@ -74,6 +74,8 @@ Environmental lighting is a good way to create a realistic light for your scene. + To add environmental lighting: + \list 1 \li In the \uicontrol {Content Library} view, go to the \uicontrol Environments tab. \li Right-click the image \e BasicLights3_4k.hdr and select \uicontrol {Add Light Probe}. @@ -85,8 +87,8 @@ When you run the application, notice an improvement in the scene lighting. - Next, adjust the environmental light. As you will add a background image to the scene later, - you don't want to use the skybox. + As you will add a background image to the scene later, you need to disable the use of skybox: + \list 1 \li In the \uicontrol Navigator view, select \e sceneEnvironment. \li Go to the \uicontrol {Scene Environment} tab in \uicontrol Properties and set \uicontrol @@ -98,7 +100,7 @@ \section2 Adding a Background Image to the Scene - In the final step of this tutorial, you add a background image to your scene. + In the final step of this tutorial, you add a background image to your scene: \list 1 \li Go to the \uicontrol Textures tab in the \uicontrol {Content Library} view. diff --git a/doc/qtdesignstudio/examples/doc/loginui1.qdoc b/doc/qtdesignstudio/examples/doc/loginui1.qdoc index 6810f08742b..8fb7c472084 100644 --- a/doc/qtdesignstudio/examples/doc/loginui1.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui1.qdoc @@ -256,7 +256,7 @@ \section1 Adding Entry Fields to the UI - You will now add EntryField instances to the UI and modify their properties. + You will now add two entry fields to the UI and modify their properties: \list 1 \li Double-click \e Screen01.ui.qml in \uicontrol Projects @@ -339,7 +339,7 @@ \section1 Adding Push Buttons to the UI - You will now add PushButton instances to the UI and modify their properties. + You will now add two buttons to the UI and modify their properties: \list 1 \li Double-click \e Screen01.ui.qml in \uicontrol Projects diff --git a/doc/qtdesignstudio/examples/doc/robotarm.qdoc b/doc/qtdesignstudio/examples/doc/robotarm.qdoc index 6c6ae78b023..fa35f31a346 100644 --- a/doc/qtdesignstudio/examples/doc/robotarm.qdoc +++ b/doc/qtdesignstudio/examples/doc/robotarm.qdoc @@ -29,7 +29,7 @@ \section2 Creating the User Interface - First, the designer creates the user interface in \QDS. + First, the designer creates the user interface in \QDS: \list 1 \li Create a new 3D project. diff --git a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc index 071cf04554b..9a221408213 100644 --- a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc @@ -42,7 +42,7 @@ \li In the \uicontrol Properties view, modify the appearance of the rectangle: - \list a + \list \li In the \uicontrol Color field, select the button color. @@ -61,9 +61,9 @@ \uicontrol Navigator. \li In the \uicontrol Properties view, edit the properties of the - \uicontrol Text component + \uicontrol Text component: - \list a + \list \li In the \uicontrol Text field, enter \e Button. @@ -156,7 +156,7 @@ \li Drag-and-drop a \uicontrol Text component to the root component. \li Drag-and-drop a \uicontrol {Mouse Area} to the root component. \li Select a border image to edit the values of its properties: - \list a + \list 1 \li In the \uicontrol Id field, enter an ID for the border image. In this example, we use the ID \e inactiveButton. \li In the \uicontrol Source field, select the image file for @@ -170,7 +170,7 @@ \endlist \li Select the other border image to edit the values of its properties similarly: - \list a + \list 1 \li In the \uicontrol Id field, enter \e activeButton. \li In the \uicontrol Source field, select the image file for the button when it is clicked. For example, @@ -181,7 +181,7 @@ \endlist \li Select the text component to specify font size and color in \uicontrol Properties: - \list a + \list 1 \li In the \uicontrol Color field, use the \l{Picking Colors} {color picker} to select the font color, or enter a value in the field. diff --git a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc index 4999061e675..fea22dfeff9 100644 --- a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc @@ -238,7 +238,7 @@ \li Ensure that a sensible default option is checked. \li List radio button options vertically. \li Keep the list short. - \li In order to avoid confusion, do not put two groups of radio buttons + \li To avoid confusion, do not put two groups of radio buttons next to each other. \endlist @@ -360,9 +360,9 @@ Typical places for a busy indicator are: \list - \li In the corner of a \uicontrol {Tool Bar} - \li As an overlay on top of a \uicontrol Page - \li On the side of an \uicontrol {Item Delegate} + \li In the corner of a \uicontrol {Tool Bar}. + \li As an overlay on top of a \uicontrol {Page}. + \li On the side of an \uicontrol {Item Delegate}. \endlist \section2 Page Indicator @@ -410,10 +410,10 @@ Typical places for an indeterminate progress bar are: \list - \li At the bottom of a \uicontrol {Tool Bar} - \li Inline within the content of a \uicontrol Page + \li At the bottom of a \uicontrol {Tool Bar}. + \li Inline within the content of a \uicontrol {Page}. \li In an \uicontrol {Item Delegate} to show the progress - of a particular item + of a particular item. \endlist \section1 Selectors diff --git a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc index 98ea7d12264..001edb24947 100644 --- a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc @@ -307,7 +307,8 @@ to select its size in respect to its non-layout parent component. However, do not anchor the child components within layouts. - Follow the process to put components in the \uicontrol {Grid Layout}. + To put components in the \uicontrol {Grid Layout}: + \list 1 \li Select all the components and right-click on one of them. \li From the context menu, select \uicontrol Layout > \uicontrol {Grid Layout}. @@ -360,7 +361,6 @@ \note Alternatively, select the \uicontrol {Stack Layout} component, then go to the \uicontrol Properties view > \uicontrol {Stack Layout}, and update the \uicontrol {Current index}. The index starts from "0" (zero). - \endlist Follow the example below to understand how the \uicontrol {Stack Layout} works: @@ -404,7 +404,6 @@ \li Drag the slider to change the color in the rectangle. \image studio-stack-layout-example-output.webp "Stack Layout example output" - \endlist \section2 Organizing Components diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 10e33f973c2..979a0859281 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -44,7 +44,7 @@ can \l{Importing 3D Assets}{import} the assets you need and work with them to create scenes and states, as well as the transitions between them. - \list + \list \li \l {3D Views} \li \l {Node} \li \l {Group} @@ -64,7 +64,7 @@ \li \l {Repeater3D} \li \l {Loader3D} \li \l {Particles} - \endlist + \endlist When you import 3D scenes from files that you exported from 3D graphics tools, you also import the camera, light, model, and materials as 3D diff --git a/doc/qtdesignstudio/src/components/qtquick-text.qdoc b/doc/qtdesignstudio/src/components/qtquick-text.qdoc index 09635c142ba..c67dc40cbf4 100644 --- a/doc/qtdesignstudio/src/components/qtquick-text.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-text.qdoc @@ -41,14 +41,14 @@ In the rich text editor, you can: \list - \li Emphasize text - \li Create hyperlinks - \li Align text - \li Create bulleted and numbered lists - \li Specify text color - \li Select text font - \li Set font size - \li Create tables + \li Emphasize text. + \li Create hyperlinks. + \li Align text. + \li Create bulleted and numbered lists. + \li Specify text color. + \li Select text font. + \li Set font size. + \li Create tables. \endlist \image qtquick-rtf-editor.png "Text formatted as rich text in the editor" diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index d98b0499c1f..75fe3219430 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -36,9 +36,10 @@ Before you export a \QDS project for Qt Creator, install the following: \list - \li Qt Creator 13.0 or above - \li \QDS 4.5 or above - \li Git. Learn more about getting Git \l {https://wiki.qt.io/Git_Installation} {here} + \li Qt Creator 13.0 or above. + \li \QDS 4.5 or above. + \li Git. To learn more about installing Git, + see \l {https://wiki.qt.io/Git_Installation}. \endlist To export a \QDS project for Qt Creator: @@ -62,7 +63,7 @@ \section1 Opening the \QDS Project in Qt Creator - Open the \e {CMakeLists.txt} file in Qt Creator. To open: + Open the \e {CMakeLists.txt} file in Qt Creator: \list 1 \li In Qt Creator, select \uicontrol File > \uicontrol {Open File or Project}. @@ -171,18 +172,18 @@ \li \image studio-project-export-python-file.webp "The generated Python file in the Qt Design Studio project" \endtable \list 1 - \li If you don't have Python installed on your computer, install it. + \li Install Python. The latest version of Python is available \l {https://www.python.org/downloads/} {here}. - \li Next, follow the steps from this document to + \li Follow the steps from this document to \l {https://doc.qt.io/qtforpython-6/quickstart.html} {install PySide6}. You need this for working with Qt in Python. \note You need Python version between 3.8 and 3.13 to install PySide6. - \li After installing PySide6, install \QDS packages for PySide6. Stay + \li Install the \QDS packages for PySide6. Stay in the virtual environment that was accessed for installing PySide6. From there, execute the command in the command prompt. \code @@ -191,10 +192,10 @@ \li Go to your project folder in the command prompt. \code - cd path/to/your/project/folder + cd \endcode - \li Finally, run the command below in the command prompt to open the + \li Run the command below in the command prompt to open the \e {main.py} file from your project. \code python Python\main.py diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc index b1547c3ec73..79331f3ddfd 100644 --- a/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc +++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc @@ -26,7 +26,7 @@ \li \uicontrol {Target Version} to 6.2. \endlist \li In \uicontrol Navigator: - \list + \list 1 \li Select and delete \e Text. \li Select \e Rectangle and in \uicontrol Properties, set \uicontrol {Fill color} to #ffffff. @@ -46,8 +46,8 @@ \endlist To add the structure for the web application, - drag and drop the following components from \uicontrol Components - to \e rectangle in \uicontrol Navigator. + drag the following components from \uicontrol Components + to \e rectangle in \uicontrol Navigator: \list \li \uicontrol Rectangle \list @@ -181,7 +181,7 @@ code inside the \e Flickable component: \endcode To align the scrollbar to the right and bottom side of the window, set the height and width of the -main rectangle so that it adapts to the window size. +main rectangle so that it adapts to the window size: \list 1 \li In \uicontrol Navigator, select \e Rectangle. @@ -213,7 +213,7 @@ First, create an animation to use when scrolling between the different pages: \endlist Next, connect the buttons to the number animation to scroll the content -vertically to the correct place. +vertically to the correct place: \list 1 \li In \uicontrol Navigator, select \e rectangle and in \uicontrol Properties diff --git a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc index f083f85f8e4..e2a0e3cd9a1 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc @@ -40,6 +40,8 @@ \image qtquick-qml-export-dialog.png "Export Components dialog" + To configure the export: + \list 1 \li In the \uicontrol {Export path} field, specify the path where the metadata file and assets are exported. diff --git a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc index 937362450b4..c25b1396f6c 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc @@ -13,11 +13,11 @@ typically need to optimize the graphical assets used in the UI, such as images, effects, or 3D scenes. - How to optimize UIs for different target devices: + To optimize UIs for different target devices: \list - \li Minimize image size - \li Use transparency sparingly + \li Minimize image size. + \li Use transparency sparingly. \endlist For more useful information for application developers, see diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc index ed046b416fc..3ba29383c0c 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc @@ -280,7 +280,7 @@ specific functions in the script that are called by \QBPS with useful parameters. \section2 Overridable JSX Functions - You can define the following functions in the override JSX. + Define the following functions in the override JSX: \list \li preExport(document) This function is called before the document is exported. The parameter \a document is diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index 3a10de0147d..bace0d01338 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc @@ -39,7 +39,7 @@ \li Text \endlist - The following design elements might not be exported as expected. + The following design elements might not be exported as expected: \list \li Component states \li Component overrides @@ -65,19 +65,15 @@ \section2 Annotate Layers for Export With \QBXD, layers can be annotated to hint how each layer or group must be - exported. \uicontrol The {Home} panel displays and allows layer annotation for - export: + exported. The \uicontrol {Home} panel displays and allows layer annotation for + export. \image qt-bridge-xd-home.png \list 1 - \li \QBXD automatically proposes identifiers for all groups and layers - that you can change in the \uicontrol {ID} field. Use unique and - descriptive IDs to avoid duplicate IDs when the layer and the - respective artwork is imported into \QDS. Even though the importer - in \QDS is capable of fixing duplicate IDs, doing so will generate - warnings. It is recommend that you should manually check all the - IDs to make them unique and descriptive. + \li In the \uicontrol ID field, enter a unique and descriptive name. + Although the importer can resolve duplicate IDs, this generates warnings. + Always manually verify and adjust IDs to maintain their uniqueness and clarity. \li In the \uicontrol {Export As} field, select the export type for the group or layer: @@ -161,10 +157,9 @@ \QBXD assigns the following defaults to the layers: - By default: \list \li Artboards and XD Components are exported as \e components. - \li Component instances, Text layers and immediate children of an Artboard + \li Components, Text layers, and immediate children of an Artboard are exported as \e child. \li Any layer not falling under the aforementioned criteria is exported as \e merged. @@ -180,24 +175,26 @@ \section1 \QBXD Settings - Select \uicontrol Settings to change the export settings. - - \image qt-bridge-xd-menu.png - - \image qt-bridge-xd-settings.png - + To edit export settings: \list 1 - \li Select \uicontrol {Reset All} to remove all of the \QB data - stored in the document. Use \uicontrol {Edit} > \uicontrol {Undo} - to restore the data if you accidentally removed it. - \li You can export images into PNG, JPG or SVG format. In the section - \uicontrol {Export Formats}, select the image format to - export. - \li Depending on the image format selected for export, the - \uicontrol {Format Options} allows fine tuning the exported - image. - \list + \li Select \uicontrol Settings. + + \image qt-bridge-xd-menu.png + \image qt-bridge-xd-settings.png + \li You can now edit the following settings: + + \list + \li Select \uicontrol {Reset All} to remove all of the \QB data + stored in the document. Use \uicontrol {Edit} > \uicontrol {Undo} + to restore the data if you accidentally removed it. + \li You can export images into PNG, JPG or SVG format. In the section + \uicontrol {Export Formats}, select the image format to + export. + \li Depending on the image format selected for export, the + \uicontrol {Format Options} allows fine tuning the exported + image. + \list \li Select \uicontrol {Hi-DPI Assets} to generate Hi-DPI images alongside normal scale images. Being a vector format, this option is not available for SVG format. @@ -209,17 +206,16 @@ \section1 Suggestions and Tips - You can export assets using the default settings and make all the changes - later in \QDS. If you are familiar with the \l{QML Syntax Basics} - {QML syntax}, you can modify the settings to tailor the generated code to - a certain degree. For example, you can specify the component or - \l {Shapes}{Qt Quick Studio Component} to use for a component or - layer. If you have drawn an arc that you mean to animate, you can export it - as an \l Arc component to avoid having to replace the arc image with an Arc - component in \QDS. Or you could export a button as a Qt Quick Controls - \l Button component. - \list + \li You can export assets with the default settings and later adjust these in \QDS. + \li If you are familiar with the \l{QML Syntax Basics} {QML syntax}, + you can modify the settings to tailor the generated code to + a certain degree. For example, you can specify the component or + \l {Shapes}{Qt Quick Studio Component} to use for a component or + layer. If you have drawn an arc that you mean to animate, you can + export it as an \l Arc component to avoid having to replace the arc + image with an Arc component in \QDS. Or you could export a button as + a Qt Quick Controls \l Button component. \li Name the layers in exactly the same way as your IDs, to be able to find artwork later, especially as the export files can grow very large and complicated as they approach the level of a complete UI diff --git a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc index 3ce7c52cde2..341fe18624f 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc @@ -105,7 +105,8 @@ The QML item changes in the existing QML component are copied to the corresponding QML item in the new component. - The following rules are observed while merging QML components. + \QDS follows a set of rules while merging QML components: + \list \li While importing, a unique identifier (UUID) is added for each QML item in the component. The UUID is used to find the corresponding QML item in the existing diff --git a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc index 28a9d7cd091..fdf0f721b4f 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc @@ -21,6 +21,8 @@ Download \QOI from your Qt Account. If you don't have a Qt Account, create one \l {Qt Account}{here}. + To install \QDS with \QOI: + \list 1 \li Open \QOI. \li Select \uicontrol {\QDS}. @@ -36,6 +38,8 @@ \QMT is included in each Qt installation. If you already have Qt, use \QMT to install \QDS. + To install \QDS with \QMT: + \list 1 \li Open \QMT. \li Select \uicontrol {Add or remove components}. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc index 06f5975b8af..b1d1a7e8660 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc @@ -11,7 +11,7 @@ You can use JavaScript to simulate application logic that brings your UI to life. - You will need the following files: + You need the following files: \list \li Component file (.qml) that will specify the API of the UI diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc index c3c8af08c92..7e99f8d17dc 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc @@ -34,9 +34,7 @@ \section1 Connecting a \QDS ID with \QUV - Connect your \QDS ID to \QUV to access all your shared \QDS projects in \QUV. - - To do that: + Connect your \QDS ID to \QUV to access all your shared \QDS projects in \QUV: \list 1 \li Open \QDS on your computer. \li Open or create a project in \QDS. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc index 360a21183b0..97eebdd73ea 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc @@ -36,7 +36,7 @@ \section2 Custom Materials Custom materials (\e something.shader files in \Q3DS) are not imported - correctly, and have to be fixed manually. + correctly and have to be fixed manually: \list \li They are imported as \c {CustomMaterial { id: something; source: "something" }} that neither works nor does anything. @@ -59,7 +59,7 @@ \endlist \section2 Standard Materials - Some properties of standard materials may not be imported correctly. + Some properties of standard materials may not be imported correctly: \list \li The sensible value ranges of some properties may have changed between \Q3DS and \QDS and need to be redefined manually. @@ -79,15 +79,14 @@ \section2 Creating a New Project in \QDS + To create a new project in \QDS: + \list 1 - \li To create a new project in \QDS, select \uicontrol {File > New File or + \li Select \uicontrol {File > New File or Project}, or select \uicontrol {New Project} in the Welcome mode. \image exporting-from-qt3ds/01-welcome-screen.png "Welcome mode in Qt Design Studio" - \li Creating a new project in \QDS is aided by a wizard that contains - templates for creating different types of projects. Choose the - \uicontrol {Qt Quick 3D Application} template to get started with your - new 3D project. + \li Choose the \uicontrol {Qt Quick 3D Application} template. \image exporting-from-qt3ds/02-create-new-project.png "Create a new Project in Qt Design Studio" \li In the \uicontrol Name field, enter a name for the project. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc index fdbac629850..6b93ea4a200 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-materials.qdoc @@ -69,9 +69,7 @@ \section2 Reflection Use the properties under the \uicontrol Reflection tab to specify the - reflective qualities of the material. For more information on the various - material properties related to reflection, see \l {Using Highlights and - Reflections}. + reflective qualities of the material: \list \target tiling_metal @@ -89,24 +87,27 @@ reflections seen at grazing angles. \endlist + For more information on the various material properties related to + reflection, see \l {Using Highlights and Reflections}. + \section2 Roughness Use the \uicontrol Roughness properties to determine how light behaves when - it comes in contact with material. With zero roughness, light bounces off a - material, which makes it appear glossy. Increased roughness causes the light - reflected off the material to scatter, which results in a matte appearance. + it comes in contact with material: \list \li The \uicontrol {Map Offset} \uicontrol {Map Scale} and \uicontrol Texture specify the quality of roughness applied to the material. \li Use the numerical \uicontrol Roughness property to define how - glossy or matte the material appears. + glossy or matte the material appears. With zero roughness, light + bounces off a material, which makes it appear glossy. Increased + roughness causes the light reflected off the material to scatter, + which results in a matte appearance. \endlist \target emission_metal \section2 Emission Use the properties under the \uicontrol Emission tab to specify the - emissive qualities of the material. For more information on properties - related to emission, see \l {Self-Illuminating Materials}. + emissive qualities of the material: \list \li The \uicontrol Intensity property determines the quantity of light the surface of material emits. @@ -116,6 +117,9 @@ to set the mask offset for the emissive map. \endlist + For more information on properties related to emission, + see \l {Self-Illuminating Materials}. + \target bump_metal \section2 Bump Specify the properties under the \uicontrol Bump tab to simulate fine @@ -253,9 +257,7 @@ \section2 General - Plastic materials share some of the properties with glass materials. For - descriptions of \uicontrol Roughness and \uicontrol {Index of Refraction} - properties, see \l{general_glass} {general properties for glass materials}. + Plastic materials share some of the properties with glass materials: \list \li The \uicontrol {Texture scaling} property determines how fast a material is repeated on a surface. @@ -263,6 +265,9 @@ for glass materials. \endlist + For descriptions of \uicontrol Roughness and \uicontrol {Index of Refraction} + properties, see \l{general_glass} {general properties for glass materials}. + \section2 Random Gradient Mapping See \l {rgm_glass}{Random Gradient Mapping for Glass Materials}. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc index 56a2a803705..47e5a9e6775 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc @@ -78,7 +78,7 @@ \endlist Now, you have set up the \uicontrol Repeater3D component to use a numeric model that draws four instances of the same item. Next, you need to add the - item to draw. In this example we are using a \uicontrol Cube. + item to draw. In this example we are using a \uicontrol Cube: \list 1 \li From \uicontrol Components, drag a \uicontrol Cube to \e repeater3D in \uicontrol Navigator. @@ -167,7 +167,7 @@ Now, you have set up the \uicontrol Repeater3D component to use a \uicontrol ListModel to draw the items. Next, you need to add the - item to draw. In this example, you are using a \uicontrol Sphere. + item to draw. In this example, you are using a \uicontrol Sphere: \list 1 \li From \uicontrol Components, drag a \uicontrol Sphere to \e _3DRepeater @@ -187,7 +187,7 @@ \endlist Now, three spheres of different size are drawn but they are drawn in the same - position so you need to change the position to see all spheres. + position so you need to change the position to see all the spheres: \list 1 \li Select \e sphere in \uicontrol Navigator and in the \uicontrol Properties view, select diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc index 3530d4da645..ee3b500fa3c 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc @@ -26,7 +26,7 @@ \section1 Applying Layer Blur on a Component Use \uicontrol {Layer Blur} to make a component blurry. To apply \uicontrol {Layer Blur} - to a component, follow these steps: + to a component: \list 1 \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. \li Go to the \uicontrol {Properties} view > \uicontrol Effects @@ -45,7 +45,7 @@ \section1 Applying Background Blur on a Component Apply \uicontrol {Background Blur} to a component when you want to blur a selected - component behind it. There are a few essential conditions you should consider. + component behind it. Consider the following: \list \li Make the component partially transparent. With solid color, the background component is not visible. @@ -56,8 +56,8 @@ background component. All the other components ignore the blurring. \endlist - After fulfilling the above conditions, follow the next steps to apply the - \uicontrol {Background Blur} on a component. + After fulfilling the above conditions, apply the + \uicontrol {Background Blur} to a component: \list 1 \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. @@ -78,7 +78,7 @@ Shadows can either fall outside or inside the component. The shadow that falls outside is a drop shadow. To apply - a \uicontrol {Drop Shadow} to a component, follow the instructions below. + a \uicontrol {Drop Shadow} to a component: \list 1 \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. @@ -86,8 +86,7 @@ and select \uicontrol {Add Effects}. \endlist - This adds the default drop shadow to the component. To adjust this shadow, - follow these instructions. + This adds the default drop shadow to the component. To adjust this shadow: \list 1 \li Select the component and go to the \uicontrol Properties view > \uicontrol Effects. @@ -118,8 +117,7 @@ \section1 Applying Inner Shadow on a Component - \uicontrol {Inner shadow} works inside a component. To apply \uicontrol {Inner Shadow}, - do the following: + \uicontrol {Inner shadow} works inside a component. To apply \uicontrol {Inner Shadow}: \list 1 \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc index bc93841b4ef..18916ecf7a2 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc @@ -48,7 +48,7 @@ \section1 Binding a Property Value To bind the value of the property to that of another one or to data - accessible in the application. + accessible in the application: \list 1 \li In the \uicontrol Properties view, select @@ -62,8 +62,7 @@ \section1 Adding a Custom Property to a Component from the Connections View - You can add a custom property to a component from the \uicontrol {Connections} view. - Follow the process: + To add a custom property to a component from the \uicontrol {Connections} view: \list 1 \li Select the component you want to add a Custom property to in the diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc index 1c2f1b39d13..5a6d10e9adb 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-signals.qdoc @@ -102,7 +102,7 @@ \section2 Creating JavaScript Expressions - There are two ways to create JavaScript expressions for actions: + To create JavaScript expressions for actions, do one of the following: \list \li Follow the steps described above in Connect component \uicontrol Signal to diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index ab31c93f464..cf611255e69 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -139,8 +139,6 @@ To create views for an application by using states: - \image qmldesigner-screen-design.png "Designing views" - \list 1 \li In the base state, add all components you will need in the application (1). While you work on one view, you can click the @@ -160,6 +158,8 @@ select \uicontrol Default. \endlist + \image qmldesigner-screen-design.png "Designing views" + \section1 State Groups With state groups, you can change the state of certain components diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc index b599932ed03..c2a7710a055 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc @@ -212,7 +212,7 @@ You can copy the keyframes from the keyframe track for a component and paste them to the keyframe track of another component. - To copy all keyframes from one track to another one: + To copy all keyframes from one track to another: \list 1 \li Right-click the component ID and select \uicontrol {Copy All Keyframes} in the context menu. diff --git a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc index 44cea9504aa..d278114cebf 100644 --- a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc @@ -81,7 +81,7 @@ To copy material properties from one material to another: - \list + \list 1 \li In \uicontrol {Material Browser}, right-click the material that you want to copy properties from. \li Select \uicontrol {Copy properties} and then diff --git a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc index 6f420e1cb7e..ef77d6e3641 100644 --- a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc +++ b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc @@ -25,8 +25,8 @@ In \QDS, you can do the following with Qt Insight: \list - \li Turn on and off tracking - \li Set send cadence - \li Manage categories + \li Turn on and off tracking. + \li Set send cadence. + \li Manage categories. \endlist */ From d51e9104bd84fa08fc3e4d769fd7538b59fd2ba8 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 19 Nov 2024 09:42:20 +0200 Subject: [PATCH 148/322] QmlDesigner: Highlight possible targets for user materials in Navigator Fixes: QDS-13757 Change-Id: Ie594f395d8793f1d09bf15be562472f831e256ab Reviewed-by: Mahmoud Badri --- .../components/navigator/nameitemdelegate.cpp | 6 +++++- .../qmldesigner/components/navigator/navigatorview.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index 317ff794061..ac4d16fc62f 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -226,7 +226,11 @@ void NameItemDelegate::paint(QPainter *painter, }; bool validDrop = false; - if (dragType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { + + if (dragType == Constants::MIME_TYPE_BUNDLE_MATERIAL) { + Model *model = node.model(); + validDrop = metaInfo.isBasedOn(model->qtQuick3DModelMetaInfo()); + } else if (dragType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { validDrop = isValid3dTextureTarget(); } else if (dragType == Constants::MIME_TYPE_ASSET_IMAGE || dragType == Constants::MIME_TYPE_BUNDLE_TEXTURE) { diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index afb5ad6286f..c2b0e8f6364 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -285,6 +285,16 @@ void NavigatorView::dragStarted(QMimeData *mimeData) m_widget->setDragType(matNode.metaInfo().typeName()); #endif m_widget->update(); + } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { + QByteArray data = mimeData->data(Constants::MIME_TYPE_BUNDLE_ITEM); + QDataStream stream(data); + TypeName bundleItemType; + stream >> bundleItemType; + + if (bundleItemType.contains("UserMaterials")) { + m_widget->setDragType(Constants::MIME_TYPE_BUNDLE_MATERIAL); + m_widget->update(); + } } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) { m_widget->setDragType(Constants::MIME_TYPE_BUNDLE_TEXTURE); m_widget->update(); From 1ed505e70400902eeff3dc7f47ec6ef38d55cca4 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 19 Nov 2024 18:10:47 +0200 Subject: [PATCH 149/322] QmlDesigner: Change the camera view action to a radio menu Fixes: QDS-14171 Change-Id: If68b20ccc718dce530d0dd82be77d747a2b3836b Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/CMakeLists.txt | 1 - .../edit3d/cameraviewwidgetaction.cpp | 134 ------------------ .../edit3d/cameraviewwidgetaction.h | 65 --------- .../components/edit3d/edit3dactions.cpp | 88 ++++++++++-- .../components/edit3d/edit3dactions.h | 36 ++++- .../components/edit3d/edit3dview.cpp | 4 +- 6 files changed, 111 insertions(+), 217 deletions(-) delete mode 100644 src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.cpp delete mode 100644 src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 4ca9b40c99d..8ef483e2b43 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -236,7 +236,6 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/edit3d SOURCES - cameraviewwidgetaction.cpp cameraviewwidgetaction.h edit3dview.cpp edit3dview.h edit3dviewconfig.h edit3dwidget.cpp edit3dwidget.h diff --git a/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.cpp b/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.cpp deleted file mode 100644 index cd0342f222e..00000000000 --- a/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2024 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 "cameraviewwidgetaction.h" - -#include -#include - -namespace QmlDesigner { - -struct CameraActionsModel::DataItem -{ - QString name; - QString tooltip; - QString mode; -}; - -const QList CameraActionsModel::m_data{ - {tr("Hide Camera View"), tr("Never show the camera view."), "CameraOff"}, - {tr("Show Selected Camera View"), - tr("Show the selected camera in the camera view."), - "ShowSelectedCamera"}, - {tr("Always Show Camera View"), - tr("Show the last selected camera in the camera view."), - "AlwaysShowCamera"}, -}; - -CameraViewWidgetAction::CameraViewWidgetAction(QObject *parent) - : QWidgetAction(parent) -{ - setToolTip(CameraActionsModel::tr("Camera view settings")); - ComboBoxAction *defaultComboBox = new ComboBoxAction(); - CameraActionsModel *comboBoxModel = new CameraActionsModel(defaultComboBox); - - defaultComboBox->setModel(comboBoxModel); - setDefaultWidget(defaultComboBox); - - connect(defaultComboBox, &QComboBox::currentIndexChanged, this, [this] { - emit currentModeChanged(currentMode()); - }); - - connect(defaultComboBox, &ComboBoxAction::hovered, this, &CameraViewWidgetAction::onWidgetHovered); -} - -QString CameraViewWidgetAction::currentMode() const -{ - ComboBoxAction *defaultComboBox = qobject_cast(defaultWidget()); - QTC_ASSERT(defaultComboBox, return "CameraOff"); - - return defaultComboBox->currentData(CameraActionsModel::ModeRole).toString(); -} - -void CameraViewWidgetAction::setMode(const QString &mode) -{ - ComboBoxAction *defaultComboBox = qobject_cast(defaultWidget()); - QTC_ASSERT(defaultComboBox, return); - defaultComboBox->setCurrentIndex(CameraActionsModel::modeIndex(mode)); -} - -QWidget *CameraViewWidgetAction::createWidget(QWidget *parent) -{ - ComboBoxAction *defaultComboBox = qobject_cast(defaultWidget()); - QTC_ASSERT(defaultComboBox, return nullptr); - - ComboBoxAction *newComboBox = new ComboBoxAction(parent); - newComboBox->setModel(defaultComboBox->model()); - connect(defaultComboBox, &QComboBox::currentIndexChanged, newComboBox, &QComboBox::setCurrentIndex); - connect(newComboBox, &QComboBox::currentIndexChanged, defaultComboBox, &QComboBox::setCurrentIndex); - newComboBox->setCurrentIndex(defaultComboBox->currentIndex()); - - connect(newComboBox, &ComboBoxAction::hovered, this, &CameraViewWidgetAction::onWidgetHovered); - newComboBox->setProperty("_qdss_hoverFrame", true); - - return newComboBox; -} - -void CameraViewWidgetAction::onWidgetHovered() -{ - activate(Hover); -} - -CameraActionsModel::CameraActionsModel(QObject *parent) - : QAbstractListModel(parent) -{} - -CameraActionsModel::~CameraActionsModel() = default; - -int CameraActionsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_data.size(); -} - -QVariant CameraActionsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - const DataItem &data = m_data.at(index.row()); - switch (role) { - case Qt::DisplayRole: - return data.name; - case Qt::ToolTipRole: - return data.tooltip; - case ExtendedDataRoles::ModeRole: - return data.mode; - default: - return {}; - } -} - -int CameraActionsModel::modeIndex(const QString &mode) -{ - int idx = Utils::indexOf(m_data, Utils::equal(&DataItem::mode, mode)); - return std::max(0, idx); -} - -ComboBoxAction::ComboBoxAction(QWidget *parent) - : QComboBox(parent) -{ - setMouseTracking(true); -} - -void ComboBoxAction::enterEvent(QEnterEvent *event) -{ - QComboBox::enterEvent(event); - emit hovered(); -} - -void ComboBoxAction::moveEvent(QMoveEvent *event) -{ - QComboBox::moveEvent(event); - emit hovered(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.h b/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.h deleted file mode 100644 index f017f8e478e..00000000000 --- a/src/plugins/qmldesigner/components/edit3d/cameraviewwidgetaction.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2024 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 - -namespace QmlDesigner { - -class CameraViewWidgetAction : public QWidgetAction -{ - Q_OBJECT - -public: - explicit CameraViewWidgetAction(QObject *parent = nullptr); - QString currentMode() const; - void setMode(const QString &mode); - -protected: - QWidget *createWidget(QWidget *parent) override; - -private slots: - void onWidgetHovered(); - -signals: - void currentModeChanged(QString); -}; - -class CameraActionsModel : public QAbstractListModel -{ - Q_DECLARE_TR_FUNCTIONS(CameraActionsModel) - -public: - enum ExtendedDataRoles { ModeRole = Qt::UserRole + 1 }; - - explicit CameraActionsModel(QObject *parent); - virtual ~CameraActionsModel(); - - int rowCount(const QModelIndex &parent = {}) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - static int modeIndex(const QString &mode); - -private: - struct DataItem; - static const QList m_data; -}; - -class ComboBoxAction : public QComboBox -{ - Q_OBJECT - -public: - explicit ComboBoxAction(QWidget *parent = nullptr); - -protected: - void enterEvent(QEnterEvent *event) override; - void moveEvent(QMoveEvent *event) override; - -signals: - void hovered(); -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index 9bb1622cae8..839f71b0668 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -3,7 +3,6 @@ #include "edit3dactions.h" -#include "cameraviewwidgetaction.h" #include "edit3dview.h" #include "indicatoractionwidget.h" #include "qmldesignerconstants.h" @@ -14,6 +13,9 @@ #include #include +#include +#include + namespace QmlDesigner { Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, @@ -56,6 +58,47 @@ void Edit3DWidgetActionTemplate::actionTriggered([[maybe_unused]] bool b) m_action(m_selectionContext); } +Edit3DSingleSelectionAction::Edit3DSingleSelectionAction(const QString &description, + const QList
    @@ -170,14 +170,14 @@ @@ -185,28 +185,28 @@
  • Materials
  • @@ -215,25 +215,25 @@ @@ -249,13 +249,13 @@
  • Textures
    • -
    • UI Controls and Elements +
    • UI controls and elements
      • Shapes
      • Text
      • @@ -266,17 +266,17 @@ @@ -284,21 +284,21 @@ @@ -331,14 +331,15 @@ @@ -349,36 +350,36 @@

        Reference

          -
        • 3D Components +
        • 3D components
        • @@ -394,25 +395,25 @@ diff --git a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc index 223bffe2d0d..90a3f2aba29 100644 --- a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc @@ -1,18 +1,18 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page 3d-scene-tutorial.html \ingroup gstutorials - \title Setting up a 3D Scene + \title Setting up a 3D scene \sa {Content Library} \brief Illustrates how to set up a 3D scene with, for example, lights, models, and materials in \QDS. \image 3d-scene-tutorial.webp - The \e {Setting up a 3D Scene} tutorial illustrates how you can set up and improve a 3D + The \e {Setting up a 3D scene} tutorial illustrates how you can set up and improve a 3D scene with the following: \list \li 3D models @@ -30,10 +30,9 @@ Download the completed project from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/3Dscene%20tutorial/Completed/}{here}. - This tutorial requires that you know the basics of \QDS, see - \l{Getting Started}. + To learn the basics of \QDS required to complete this tutorial, see \l{Getting started}. - \section1 The Starting Project + \section1 The starting project The starting project consists of an animated 3D model of a ball bearing. Control the animations with a slider and a switch in the user interface. @@ -43,7 +42,7 @@ \include run-tutorial-project.qdocinc - \section1 Adding Materials to the 3D Models + \section1 Adding materials to the 3D models First, use materials from the \uicontrol {Content Library} view on the ball bearing: @@ -70,7 +69,7 @@ When you run the application or live preview, notice that you don't see much of the materials. The next step is to set up the environmental light. - \section1 Adding Environmental Lighting to the Scene + \section1 Adding environmental lighting to the scene Environmental lighting is a good way to create a realistic light for your scene. @@ -98,7 +97,7 @@ You also want to increase the brightness of the light a bit. Go to the \uicontrol {Image Based Lighting} tab in \uicontrol Properties and set \uicontrol Exposure to \e 10. - \section2 Adding a Background Image to the Scene + \section2 Adding a background image to the scene In the final step of this tutorial, you add a background image to your scene: diff --git a/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc b/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc index 3e80fe2c6e6..d474a14d7be 100644 --- a/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc +++ b/doc/qtdesignstudio/examples/doc/DesignEffect.qdoc @@ -5,12 +5,12 @@ \page design-effect-example.html \ingroup studioexamples - \title Design Effect - \brief Illustrates how to use the \QDS design effect. + \title Design Effects Demo + \brief Illustrates how to use the \QDS Design Effects. \image studio-design-effects-demo-gallery.webp - The \e{Design Effects Demo Gallery} example illustrates the use cases of the + The \e{Design Effects Demo} example illustrates various use cases of the design effects in \QDS. Learn more about design effects \l {https://doc.qt.io/qtdesignstudio/quick-design-effects.html}{here}. @@ -39,7 +39,7 @@ Watch this video tutorial to learn how to create these effects using \QDS design effects: \youtube h0p27_HScxc - \section1 Using Neumorphism Example + \section1 Using the Neumorphism example \image studio-neumorphism.webp @@ -57,7 +57,7 @@ accurately they translate into \QDS design effects. The animation gives a real-time animated Neumorphism design effect in \QDS. - \section1 Using Skeuomorphism Example + \section1 Using the Skeuomorphism example \image studio-skeuomorphism.webp @@ -79,7 +79,7 @@ these buttons to test different stages of the Skeuomorphism effect. - \section1 Using Glassmorphism Example + \section1 Using the Glassmorphism example \image studio-glassmorphism.webp @@ -97,7 +97,7 @@ part of the image hidden behind it would become blurred. \endlist - \section1 Using the Design Effect Demo Project + \section1 Using the Design Effect Demo project \image studio-design-effects-demo-project.webp @@ -114,7 +114,7 @@ To change the theme of the calculator between light and dark, select \uicontrol DARK. - \section2 Using Shadow or Blur Effects + \section2 Using shadow or blur effects To change the direction of the shadows on the calculator, drag \uicontrol {SHADOW DIR} up or down. @@ -127,8 +127,7 @@ To turn the design effects on or off in the calculator, select \uicontrol FX. - \section2 Using Music or Sound Effects - + \section2 Using music or sound effects To play sound or music in the calculator: \list \li To play music in a loop, select \uicontrol CHRD, \uicontrol TECH, or diff --git a/doc/qtdesignstudio/examples/doc/FireParticles.qdoc b/doc/qtdesignstudio/examples/doc/FireParticles.qdoc index d198fbdf965..c240df651b9 100644 --- a/doc/qtdesignstudio/examples/doc/FireParticles.qdoc +++ b/doc/qtdesignstudio/examples/doc/FireParticles.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,13 +6,14 @@ \ingroup gstutorials \sa Particles - \title Particle System: Fire Effect + \title Particle System: The fire effect + \brief Illustrates how to create a fire effect with the \QDS particle system. \image fire-particles.png - The \e{Fire Effect} tutorial illustrates how you can add a fire effect to + \e{The fire effect} tutorial illustrates how you can add a fire effect to your scene using the \QDS particle system. In this tutorial, you create a project from the beginning. You can download @@ -20,10 +21,9 @@ \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/fire-particles} {here}. - This tutorial requires that you know the basics of \QDS, see - \l{Getting Started}. + To learn the basics of \QDS required to complete this tutorial, see \l{Getting Started}. - \section1 Tutorial Assets + \section1 Tutorial assets You need the following assets to complete this tutorial: \list @@ -35,9 +35,9 @@ \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/fire-particles/FireParticles/content/images} {here}. - \section1 Creating a Fire Effect + \section1 Creating a fire effect - \section2 Creating a Project + \section2 Creating a project To create a new project: @@ -67,7 +67,7 @@ \uicontrol {Properties}, set \uicontrol {Fill Color} to #000000. \endlist - \section2 Adding a Particle System to Your Scene + \section2 Adding a Particle System to your scene To add a particle system, you first need to import the QtQuick3D.Particles3D module to your project: @@ -166,7 +166,7 @@ \image fire-particles-sprite-textures.png - \section2 Adjusting the Particle Emitter + \section2 Adjusting the particle emitter The next step is to adjust the particle emitter properties: @@ -218,7 +218,7 @@ \endlist - \section1 Running the Project + \section1 Running the project Now, the fire effect is ready. Before you run it, position the camera to show the effect from a suitable perspective: diff --git a/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc index 8dbc8bcfbff..d254ce69093 100644 --- a/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc +++ b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc @@ -5,12 +5,12 @@ \page fresnel-effect-example.html \ingroup studioexamples - \title Fresnel Example + \title The Fresnel example \brief Illustrates how to work with the fresnel effect. \image fresnel-example.webp - The \e{Fresnel} example illustrates how to add and adjust a fresnel effect on + The \e{Fresnel} example illustrates how to add and adjust a Fresnel effect on a 3D model. The fresnel effect affects how materials reflect light at different viewing angles. Imagine the @@ -19,12 +19,12 @@ \image fresnel-angle.webp - \section1 Running the Example + \section1 Running the example To run the example in \QDS, go to the \uicontrol Welcome screen and select the example from the \uicontrol Examples tab. - \section1 The 3D Scene + \section1 The 3D scene The example project consists of a basic 3D scene with the following components: @@ -34,7 +34,7 @@ \li An HDR image used to light the scene (image-based lighting). \endlist - \section1 The Material + \section1 The material The material on the 3D model in this example is a principled material with a clearcoat. @@ -47,9 +47,9 @@ You adjust clearcoat properties independently from the base material. - \section2 Fresnel Properties + \section2 The Fresnel properties - The following properties affect how the fresnel effect renders. These properties are + The following properties affect how the Fresnel effect renders. These properties are available both for the base material and the clearcoat layer. Adjusting the settings for the clearcoat has a bigger visual effect. @@ -59,24 +59,24 @@ \li Description \row \li Fresnel power - \li Increasing the fresnel power decreases the head-on reflections (steep viewing angle) + \li Increasing the Fresnel power decreases the head-on reflections (steep viewing angle) while maintaining the reflections seen from more shallow viewing angles. \row \li Enable scale and bias \li Takes the scale and bias properties into account. \row - \li Scale + \li Fresnel Scale \li Determines the rate of change in reflection intensity as the viewing angle varies. A large scale value results in a gentler transition between weak and strong reflections, while a smaller scale creates a more abrupt shift in reflection intensity. \row - \li Bias + \li Fresnel Bias \li Controls the offset for the fresnel power property and determines how quickly the reflection transitions from weak to strong as the viewing angle changes. A larger bias value shifts the transition point toward steeper angles. \endtable - \section3 Adjusting the Fresnel Settings + \section3 Adjusting the Fresnel settings To adjust the settings: diff --git a/doc/qtdesignstudio/examples/doc/Optimal3DScene.qdoc b/doc/qtdesignstudio/examples/doc/Optimal3DScene.qdoc index 34101aed178..275b3be64b2 100644 --- a/doc/qtdesignstudio/examples/doc/Optimal3DScene.qdoc +++ b/doc/qtdesignstudio/examples/doc/Optimal3DScene.qdoc @@ -10,6 +10,6 @@ \image studio-kitchen-combined.png The \e {Optimal 3D Scene} example contains the source files for the Optimal - 3D Scene described in \l {Creating Optimized 3D Scenes}. + 3D Scene described in \l {Creating optimized 3D scenes}. */ diff --git a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc index e7d4f318a0b..cf23bf9b7b9 100644 --- a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc +++ b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,13 +6,13 @@ \ingroup gstutorials \sa States, {Transitions}, {Working with States} - \title Animated State Transitions + \title Animated state transitions \brief Illustrates how to create animated state transitions. \image animated-state-transitions.jpg - The \e{Animated State Transitions} tutorial illustrates how you can animate - the transition between \l{Working with States}{states}. + The \e{Animated state transitions} tutorial illustrates how you can animate + the transition between \l{Working with states}{states}. The starting point of this tutorial is the Car Demo project, you can download it from @@ -24,15 +24,15 @@ {here}. This tutorial requires that you know the basics of \QDS, see - \l{Getting Started}. + \l{Getting started}. - \section1 Tutorial Assets + \section1 Tutorial assets All assets you need for this tutorial are included in the Car Demo project. \include run-tutorial-project.qdocinc - \section1 Creating States + \section1 Creating states First, you create the different states. In this tutorial, you create four different states with different views of the car in the scene: @@ -72,7 +72,7 @@ \image animated-state-transitions-states.png - \section1 Creating State Transitions + \section1 Creating state transitions With the states created, you need a way to move between the states in the UI. In this tutorial, you create buttons arranged in a column to do this. @@ -142,7 +142,7 @@ To preview, select \key Alt + \key{P}. - \section1 Animating State Transitions + \section1 Animating state transitions The final step of this tutorial is to create animations between the states: diff --git a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc index 66fb2009670..35a6901a899 100644 --- a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2024 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Design Studio documentation. @@ -28,20 +28,20 @@ \ingroup gstutorials \sa {Creating Timeline Animations} - \title Timeline Animation Tutorial + \title Timeline animation \brief Illustrates how to create timeline animations and bind them to properties in \QDS. \image animation-tutorial.gif - The \e{Timeline Animation} tutorial illustrates how to create timeline animations + The \e{Timeline animation} tutorial illustrates how to create timeline animations and bind them to properties in \QDS. First you create a keyframe animation which you control the running state of with a switch in the UI. Next, you create another keyframe animation where you use a slider in the UI to control the position of the playhead. The starting point of this tutorial is the Animation Tutorial project, - you can download it from + which you can download from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/animation-tutorial/AnimationTutorial/Start} {here}. @@ -49,17 +49,16 @@ \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/animation-tutorial/AnimationTutorial/Completed} {here}. - This tutorial requires that you know the basics of \QDS, see - \l{Getting Started}. + To learn the basics of \QDS required to complete this tutorial, see \l{Getting started}. \include run-tutorial-project.qdocinc - \section1 Creating a Timeline Animation + \section1 Creating a timeline animation First, you create an animation where the ball bearing continuously rotates around its Y axis. - \section2 Adding a Timeline and an Animation + \section2 Adding a timeline and an animation To add a timeline to your project: @@ -73,7 +72,7 @@ \list \li Set \uicontrol Duration to 7500. This sets the duration of the animation in milliseconds. - \li Select \uicontrol {Contiunous}. + \li Select \uicontrol {Continuous}. This sets the animation to start over again when it reaches the end. \endlist @@ -84,7 +83,7 @@ You can see the timeline in the \uicontrol Timeline and \uicontrol Navigator views. - \section2 Adding Keyframes + \section2 Adding keyframes Next, you add keyframes to animate the rotation of the ball bearing: @@ -109,7 +108,7 @@ timeline. \endlist - \section2 Controlling the Running State of the Animation + \section2 Controlling the running state of the animation There is a toggle switch in the UI of this project. To use this switch to control the running state of the animation: @@ -131,15 +130,15 @@ You can preview the animation and try the toggle switch in the live preview. To run the live preview, select \key Alt + \key{P}. - \section1 Creating a Timeline and Binding it to a Property + \section1 Creating a timeline and binding it to a property Next, you create the exploded view animation of the ball bearing. You don't want this animation to run automatically but instead you want to control it with a slider in the UI. - \section2 Adding a Timeline Inside a Component + \section2 Adding a timeline inside a component - You create this animation inside the ball bearing component, to do this: + To create this animation inside the ball bearing component: \list 1 \li In the \uicontrol Navigator view, select \e {ballBearing1}. @@ -156,7 +155,7 @@ \image animation-tutorial-timeline-2.png - \section2 Adding Keyframes + \section2 Adding keyframes Now, you add keyframes for the different parts of the ball bearing: @@ -190,7 +189,7 @@ You can preview the animation by dragging the playhead in the \uicontrol Timeline view. - \section2 Controlling the Animation with a Slider + \section2 Controlling the animation with a slider Now, you use the slider on the main screen to control the exploded view animation that you created. diff --git a/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc b/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc index 3d4da576061..def3b5185c5 100644 --- a/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc +++ b/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -21,7 +21,7 @@ the viewport and the timeline to animate the \l{glossary-transition} {transitions} between and on the screens. - \section1 Application Flow + \section1 Application flow On the startup screen, \e {AnimationFlowForm.ui.qml}, users can select from several types of coffee to fill the empty cup. The selection @@ -88,10 +88,10 @@ is triggered. It sets the application flow status to \c "reset", so that the application returns to the startup screen. - \section1 Using Timelines to Animate Transitions + \section1 Using timelines to animate transitions The Coffee Machine application screens for choosing coffee, empty cup, and - brewing each use custom components specified in separate \l{UI Files} + brewing each use custom components specified in separate \l{UI files} {UI files} (ui.qml). We use the \l Timeline view to animate the transitions between the screens @@ -120,9 +120,9 @@ new timeline appears in the view. For more information about using the timeline, see - \l {Creating Timeline Animations}. + \l {Creating timeline animations}. - \section1 Using States to Move Between Screens + \section1 Using states to move between screens We use the \l States view to determine the contents of the viewport. The animations are run when moving from one state to another. diff --git a/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc b/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc index fd61f4a1c31..5b5571cdd8f 100644 --- a/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc +++ b/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -27,7 +27,7 @@ viewport, and the timeline to animate the \l{glossary-transition} {transitions} between and on the screens. - \section1 Using Timelines to Animate Transitions + \section1 Using timelines to animate transitions We use the \l Timeline view to animate the transitions between the screens in \e {Screen01.ui.qml}. @@ -71,7 +71,7 @@ For more information about using the timeline, see \l {Creating Timeline Animations}. - \section1 Using States to Move Between Screens + \section1 Using states to move between screens We use the \l States view to determine the contents of the viewport. The animations are run in a particular state or when moving from one state diff --git a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc index eda2329886e..67e19302a37 100644 --- a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc +++ b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc @@ -5,12 +5,12 @@ \page effect-composer-example.html \ingroup studioexamples - \title Effect Composer Example + \title The Effect Composer example \brief Illustrates how to work with the Effect Composer effects. \image effect-composer-example.webp {The Effect Composer example project} - The \e {Effect Composer Example} illustrates how to work with the \uicontrol {Effect Composer} + The \e {Effect Composer} example illustrates how to work with the \uicontrol {Effect Composer} effects. Use the example project to experiment with effects and get familiar with \uicontrol {Effect Composer}. To learn more, see \l {Effect Composer}. @@ -22,12 +22,12 @@ The example project has a scalable design. When you resize the window to fit your screen, some components will adjust automatically. - \section1 Running the Example + \section1 Running the example To run the example in \QDS, go to the \uicontrol Welcome screen and select the example from the \uicontrol Examples tab. - \section1 The Effects + \section1 The effects The following table introduces the effects in this example. @@ -49,14 +49,14 @@ \li A weather effect, a combination of a rain effect and a thunder effect. \endtable - \section1 The Structure of the Example + \section1 The structure of the example In addition to the effects, the example project also includes the following components: \list \li A \l {3D} scene \list - \li \l {Importing 3D Assets}{A 3D model} + \li \l {Importing 3D assets}{A 3D model} \li \l {Lights}{A directional light} \li \l {Cameras}{A scene camera} \li \l {OrbitCameraController} @@ -64,8 +64,8 @@ \li \l {Text} components \li \l {Rectangle} components \li \l {Shapes#Border}{Border} components - \li \l {Creating Buttons}{Custom Buttons} - \li \l {Creating Custom Components}{Custom Sliders} + \li \l {Creating buttons}{Custom buttons} + \li \l {Creating custom components}{Custom sliders} \endlist The example project also uses \uicontrol States and \uicontrol Transitions. To learn more, see diff --git a/doc/qtdesignstudio/examples/doc/loginui1.qdoc b/doc/qtdesignstudio/examples/doc/loginui1.qdoc index 8fb7c472084..97eee4e4260 100644 --- a/doc/qtdesignstudio/examples/doc/loginui1.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui1.qdoc @@ -4,15 +4,14 @@ /*! \example Loginui1 \ingroup gstutorials - \nextpage {Log In UI - Positioning} + \nextpage {Login UI: Positioning} - \title Log In UI - Components + \title Login UI: Components \brief Illustrates how to use wizard templates to create a simple UI that contains a text label, images, and push buttons. \image loginui1.webp - - \e{Log In UI - Components} is the first tutorial in a series of tutorials + \e{Login UI: Components} is the first tutorial in a series of tutorials that describes how to use the \QDS wizard templates to create a project and a button UI control, and how to modify the files generated by the wizard templates to design the UI. @@ -20,7 +19,7 @@ You can download the completed project from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui1}{here}. - \section1 Creating the UI Project + \section1 Creating the UI project For the purposes of this tutorial, you will use the empty wizard template. Wizard templates are available also for creating UIs that are optimized for @@ -164,8 +163,8 @@ button controls available in \uicontrol Components > \uicontrol {Qt Quick Controls} to create the kind of push button that you want, you can create your button from scratch using default components. For - more information, see \l {Creating Buttons} and - \l {Creating Scalable Buttons and Borders}. + more information, see \l {Creating buttons} and + \l {Creating scalable buttons and borders}. To create a push button by using the wizard template: @@ -187,7 +186,7 @@ Next, you will change the appearance of the EntryField component by modifying its properties. - \section1 Styling the Button + \section1 Styling the button You can now modify the properties of the EntryField component to your liking. To make the changes apply to all the EntryField instances, you @@ -217,7 +216,7 @@ \li In \uicontrol Properties > \uicontrol Rectangle, set: \list \li \uicontrol {Fill color} to transparent light gray (\e #28e7e7e7). - You can also use the \l{Picking Colors}{color picker} to set the color. + You can also use the \l{Picking colors}{color picker} to set the color. \li \uicontrol {Border color} to white (\e #ffffff). \li \uicontrol Radius to \e 50 to give the button rounded corners. \endlist @@ -254,7 +253,7 @@ Next, you will add instances of the \e EntryField component to the \e Screen01 component and modify their properties. - \section1 Adding Entry Fields to the UI + \section1 Adding entry fields to the UI You will now add two entry fields to the UI and modify their properties: @@ -284,7 +283,7 @@ to save your changes. \endlist - \section1 Creating Another Button + \section1 Creating another button We want to center-align the text of two additional push buttons and use brighter colors for them, so we create a second button component as @@ -337,7 +336,7 @@ \image loginui1-button-styled.webp "Modified push button in the 2D view." - \section1 Adding Push Buttons to the UI + \section1 Adding push buttons to the UI You will now add two buttons to the UI and modify their properties: @@ -371,11 +370,11 @@ \image loginui1-ready.webp "The finished UI in the 2D view and live preview." - \section1 Learn More + \section1 Learn more The \e {Learn More} sections provide additional information about the tasks performed by the wizards and about other basic tasks and concepts. - \section2 Projects and Files + \section2 Projects and files \QDS creates a set of files and folders that you need to create a UI. The files are listed in the \l{File System} view. @@ -410,7 +409,7 @@ \e EventListSimulator.qml files are not used in this example, so you can ignore them for now. \endlist - \l{UI Files}{UI files} define a hierarchy of components with a + \l{UI files}{UI files} define a hierarchy of components with a highly-readable, structured layout. Every UI file consists of two parts: an imports section and an component declaration section. The components and functionality most common to UIs are provided in the \c QtQuick import. You @@ -448,7 +447,7 @@ descriptions of all components, see \l{All QML Types} in the Qt reference documentation. - \section3 Regtangle Properties + \section3 Regtangle properties The default \l {basic-rectangle}{Rectangle} component is used for drawing shapes with four sides and four corners. You can fill rectangles either with @@ -462,7 +461,7 @@ component. It is available in \uicontrol Components > \uicontrol {Qt Quick Studio Components}. - \section3 Text Properties + \section3 Text properties The \l Text component is used for adding static text to the UI, such as titles and labels. You can select the font to use and specify extensive @@ -472,7 +471,7 @@ If you want to create a label with a background, use the \l Label component from the \uicontrol {Qt Quick Controls} module instead of the text component. - \section3 Image Properties + \section3 Image properties The \l {Images}{Image} component is used for adding images to the UI in several supported formats, including bitmap formats such as PNG and JPEG and vector @@ -484,7 +483,7 @@ component, also available in \uicontrol Components > \uicontrol {Default Components} > \uicontrol Basic. - \section2 UI Controls + \section2 UI controls The \e {Custom Button} wizard template creates a button component based on the \l {Button} control in the \l {Qt Quick Controls} module. It @@ -510,11 +509,11 @@ For more information, see \l{The id Attribute}. - \section1 Next Steps + \section1 Next steps To learn how to add more UI controls and position them on the page using anchors and layouts so that the UI is scalable, see the next tutorial in - the series, \l {Log In UI - Positioning}. + the series, \l {Login UI: Positioning}. For a more advanced example of creating a menu button and using it to construct a button bar, see \l {Side Menu}. diff --git a/doc/qtdesignstudio/examples/doc/loginui2.qdoc b/doc/qtdesignstudio/examples/doc/loginui2.qdoc index 77ae6668e4e..baeae509ab1 100644 --- a/doc/qtdesignstudio/examples/doc/loginui2.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui2.qdoc @@ -4,16 +4,16 @@ /*! \example Loginui2 \ingroup gstutorials - \previouspage {Log In UI - Components} - \nextpage {Log In UI - States} + \previouspage {Login UI - Components} + \nextpage {Login UI: States} - \title Log In UI - Positioning + \title Login UI: Positioning \brief Illustrates how to position UI components on pages using anchors and positioners. - \image loginui2.webp "Log In UI" + \image loginui2.webp "Login UI" - \e{Log In UI - Positioning} is the second in a series of tutorials that build + \e{Login UI: Positioning} is the second in a series of tutorials that build on each other to illustrate how to use \QDS to create a simple UI with some basic UI components, such as pages, buttons, and entry fields. The second tutorial in the series describes how to position the UI components @@ -24,16 +24,16 @@ with different screen sizes, you will use anchors and positioners. The starting point for this tutorial is the completed - \l{Log In UI - Components} project. You can download the project + \l{Login UI: Components} project. You can download the project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui1}{here}. Additionally, you can download the completed project of this tutorial \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui2}{here}. - The \e {Learn More} sections provide additional information about the + The \e {Learn more} sections provide additional information about the task at hand. - \section1 Anchoring UI Components + \section1 Anchoring UI components First, you will \l {Setting Anchors and Margins}{anchor} the static page elements, background image (\e adventurePage), logo @@ -92,7 +92,7 @@ \uicontrol Target field, check that the component instance is located in the correct place in the component hierarchy in \uicontrol Navigator. For more information, - see \l{Arranging Components}. + see \l{Arranging components}. \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist @@ -102,10 +102,10 @@ \image loginui2-loginpage.webp "Login page in the 2D view and live preview" - \section1 Using Column Positioners + \section1 Using column positioners You will now position the entry fields and buttons in columns - to learn another method of \l{Using Positioners}{positioning components}. + to learn another method of \l{Using positioners}{positioning components}. Then, you will anchor the columns to the page to enable their positions to change when the screen size changes. @@ -171,7 +171,7 @@ \image loginui2-loginpage-ready.webp "Login page in the 2D view and live preview" - \section1 Learn More + \section1 Learn more \section2 Anchors In an anchor-based layout, each component instance can be thought of as having a set of invisible \e anchor lines: top, bottom, left, right, fill, @@ -183,7 +183,8 @@ component instance changes, the instances that are anchored to it will adjust automatically to maintain the anchoring. - For more information, see \l{Positioning with Anchors}. + For more information, see \l{Positioning with Anchors}{Positioning with anchors}. + \section2 Positioners For many use cases, the best positioner to use is a simple grid, row, or @@ -194,9 +195,9 @@ For more complicated UI designs, you can use components from the \l {Using Layouts}{Qt Quick Layouts module}. - \section1 Next Steps + \section1 Next steps To learn how to add a second page and move to it from the main page, see - the next tutorial in the series, \l {Log In UI - States}. + the next tutorial in the series, \l {Login UI: States}. */ diff --git a/doc/qtdesignstudio/examples/doc/loginui3.qdoc b/doc/qtdesignstudio/examples/doc/loginui3.qdoc index a6b978d3d67..d53f4f7afb5 100644 --- a/doc/qtdesignstudio/examples/doc/loginui3.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui3.qdoc @@ -5,14 +5,14 @@ \example Loginui3 \ingroup gstutorials - \title Log In UI - States + \title Login UI: States \brief Illustrates how to use states to create a second UI page. - \previouspage {Log In UI - Positioning} - \nextpage {Log In UI - Timeline} + \previouspage {Login UI: Positioning} + \nextpage {Login UI: Timeline} - \image loginui3.gif "Log In UI" + \image loginui3.gif "Login UI" - \e{Log In UI - States} is the third in a series of tutorials that build + \e{Login UI: States} is the third in a series of tutorials that build on each other to illustrate how to use \QDS to create a simple UI with some basic UI components, such as pages, buttons, and entry fields. The third tutorial in the series describes how to use \e states to add a @@ -25,7 +25,7 @@ when a user selects the \e {Create Account} button. The starting point for this tutorial is the completed - \l{Log In UI - Positioning} project. You can download the project + \l{Login UI: Positioning} project. You can download the project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui2}{here}. Additionally, you can download the completed project of this tutorial @@ -33,7 +33,7 @@ For more information, see \l{Working with states} and \l{Signal and Handler Event System}. - \section1 Adding UI Components + \section1 Adding UI components You will add another entry field for verifying the password that users enter to create an account. You are already familiar with the tasks in @@ -73,9 +73,9 @@ the login page and the \e {Create Account} button on the account creation page. - \section1 Using States to Simulate Page Changes + \section1 Using states to simulate page changes - You will now add \l{Working with States}{states} to the UI to show and hide UI + You will now add \l{Working with states}{states} to the UI to show and hide UI components in the \uicontrol {2D} view, depending on the current page: \list 1 @@ -114,7 +114,7 @@ \e {Create Account} button on the login page triggers a transition to the account creation page. - \section1 Connecting Buttons to States + \section1 Connecting buttons to states Components have predefined signals that are emitted when users interact with the UI. The \e PushButton component contains a \l{Mouse Area} component @@ -122,7 +122,7 @@ is clicked within the area. You will now use the \l {Connections} view to - \l{Connecting Components to Signals}{connect} the clicked signal of the + \l{Connecting components to signals}{connect} the clicked signal of the \e createAccount button to \e createAccount state: \list 1 @@ -154,9 +154,9 @@ \image loginui3.gif "Moving between login page and account creation page" - \section1 Next Steps + \section1 Next steps To learn how to use a timeline to animate the transition between the login and account creation pages, see the next tutorial in the series, - \l {Log In UI - Timeline}. + \l {Login UI: Timeline}. */ diff --git a/doc/qtdesignstudio/examples/doc/loginui4.qdoc b/doc/qtdesignstudio/examples/doc/loginui4.qdoc index 59c863d200d..cb4b63d123e 100644 --- a/doc/qtdesignstudio/examples/doc/loginui4.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui4.qdoc @@ -5,14 +5,14 @@ \example Loginui4 \ingroup gstutorials - \title Log In UI - Timeline + \title Login UI: Timeline \brief Illustrates how to use the timeline and states to animate UI components. - \previouspage {Log In UI - States} + \previouspage {Login UI: States} - \image loginui4.gif "Log In UI" + \image loginui4.gif "Login UI" - \e{Log In UI - Timeline} is the fourth in a series of tutorials that build + \e{Login UI: Timeline} is the fourth in a series of tutorials that build on each other to illustrate how to use \QDS to create a simple UI with some basic UI components, such as pages, buttons, and entry fields. The fourth part of the tutorial describes how to use the timeline and states @@ -20,25 +20,24 @@ to the preceding animation, where you can navigate to the account creation page by clicking the \e {Create Account} button. - In \l {Log In UI - States}, you learned how to use states to simulate page + In \l {Login UI: States}, you learned how to use states to simulate page changes in a UI and connections to provide user interaction with it. In this part, you will now learn another way of animating the UI by creating \l{Creating Timeline Animations}{timeline animations} that you bind to states. The starting point for this tutorial is the completed - \l{Log In UI - States} project. You can download the project + \l{Login UI: States} project. You can download the project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui3}{here}. Additionally, you can download the completed project of this tutorial \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui4}{here}. - For more information, see \l {Creating Timeline Animations}. - \section1 Animating UI Components + \section1 Animating UI components - In \l {Log In UI - States}, you changed the visibility property in different + In \l {Login UI: States}, you changed the visibility property in different states to simulate changing pages. To make sure that those changes won't interfere with the changes to the opacity property you will make next, you will first remove the states. @@ -54,7 +53,7 @@ To preview the changes that you make, select the \uicontrol {Live Preview} button or press \key {Alt+P}. - \section2 Replacing Columns with Anchors + \section2 Replacing columns with anchors First, prepare the \e Screen01 component for adding animation: @@ -69,7 +68,7 @@ rectangle. \li \inlineimage icons/navigator-arrowup.png to move \e username below \e tagLine in \uicontrol Navigator to preserve the - \l{Arranging Components}{component hierarchy}. + \l{Arranging components}{component hierarchy}. \endlist \li Repeat the previous step for \e password and \e repeatPassword. \li Select \e fields in \uicontrol Navigator and press \key Delete to @@ -117,9 +116,9 @@ \image loginui4-base-state.webp "UI with all the buttons and fields" - \section2 Adding a Timeline + \section2 Adding a timeline - You are now ready to add the \l{Creating Timeline Animations}{timeline}. + You are now ready to add the \l{Creating timeline animations}{timeline}. To add a timeline with settings for running the animation: @@ -141,7 +140,7 @@ Next, you will record the animation in \uicontrol Timeline. - \section2 Inserting Keyframes + \section2 Inserting keyframes To insert keyframes and record property changes in \uicontrol Timeline: @@ -186,7 +185,7 @@ You will now animate the top anchor margin of the \e {Repeat Password} field to make it appear to slide down from the \e Password field. - \section2 Animating Anchors + \section2 Animating anchors To animate the top anchor margin of the \e {Repeat Password} field: @@ -215,9 +214,9 @@ to save your changes. \endlist - \section2 Adding Easing Curves + \section2 Adding easing curves - You will now add an \l{Editing Easing Curves}{easing curve} to the anchor + You will now add an \l{Editing easing curves}{easing curve} to the anchor margin animation that will make the transition seem smoother: \list 1 @@ -245,9 +244,9 @@ Next, you will create states for the login and account creation pages and bind them to the animation settings. - \section1 Binding Animation to States + \section1 Binding animation to states - To bring back the \l{Working with States}{states} in the + To bring back the \l{Working with states}{states} in the \uicontrol States view and bind them to the animation settings in \uicontrol Timeline: @@ -279,7 +278,7 @@ \image loginui4.gif "Moving between login page and account creation page" - \section1 Next Steps + \section1 Next steps To continue learning about \QDS, see \l{Examples} and other \l{Tutorials}. */ diff --git a/doc/qtdesignstudio/examples/doc/materialbundle.qdoc b/doc/qtdesignstudio/examples/doc/materialbundle.qdoc index 4f808b14f25..e1a89148f79 100644 --- a/doc/qtdesignstudio/examples/doc/materialbundle.qdoc +++ b/doc/qtdesignstudio/examples/doc/materialbundle.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! diff --git a/doc/qtdesignstudio/examples/doc/multilanguage.qdoc b/doc/qtdesignstudio/examples/doc/multilanguage.qdoc index 8689960cbb8..e948682f15e 100644 --- a/doc/qtdesignstudio/examples/doc/multilanguage.qdoc +++ b/doc/qtdesignstudio/examples/doc/multilanguage.qdoc @@ -1,11 +1,11 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page multilanguage-tutorial.html \ingroup gstutorials - \title Adding Multi-language Support to Your Project + \title Adding multi-language support to your project \brief Illustrates how to add support for multiple languages to your project. @@ -13,7 +13,7 @@ The \e{Multi-language Support} tutorial illustrates how you can add support for multiple languages to your project. In this tutorial you - prepare a simple log in UI for multi-language support and import + prepare a simple login UI for multi-language support and import translations from a JSON file. You need to download the starting project for this tutorial from @@ -27,7 +27,7 @@ \include run-tutorial-project.qdocinc - \section1 JSON Translation File + \section1 The JSON translation file The JSON translation file you are using in this project has the following structure: @@ -104,7 +104,7 @@ } \endcode -\section1 Preparing Your Project +\section1 Preparing your project First, you need to prepare your project for translation: @@ -134,7 +134,7 @@ \endcode \endlist - \section1 Importing Translations + \section1 Importing translations Next, you need to import your JSON translation file: diff --git a/doc/qtdesignstudio/examples/doc/progressbar.qdoc b/doc/qtdesignstudio/examples/doc/progressbar.qdoc index 6ce9ff1562c..ab962ebbe0f 100644 --- a/doc/qtdesignstudio/examples/doc/progressbar.qdoc +++ b/doc/qtdesignstudio/examples/doc/progressbar.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -12,7 +12,7 @@ \e {Progress Bar} animates rectangles and numbers using timelines. - \section1 Creating the Progress Bar + \section1 Creating the progress bar First, we create an empty project, as described in \l {Creating Projects}. For the purposes of this example, we call the project \e progressbar. @@ -53,9 +53,9 @@ change for the animation. For more information about creating and positioning components, see - \l {Using Components} and \l {Scalable Layouts}. + \l {Using Components} and \l {Scalable layouts}. - \section1 Animating Progress Bar Elements + \section1 Animating the progress bar elements The text label will indicate the progress in numbers and changing color, while the indicator rectangle will indicate it by getting wider and @@ -65,7 +65,7 @@ For more information about using the timeline, see \l {Creating Timeline Animations}. - \section2 Adding Color Animation + \section2 Adding color animation First, we add a color animation to the \e root component in the \e Root.qml file. We select the \inlineimage icons/plus.png @@ -82,7 +82,7 @@ We then set the color at frame 0 to green (\e {#8de98d}) in \uicontrol Properties > \uicontrol Text > \uicontrol {Text Color}. - We can either \l{Picking Colors}{pick a color} from the color selector + We can either \l{Picking colors}{pick a color} from the color selector or use the \uicontrol {Set Binding} command in the \inlineimage icons/action-icon.png (\uicontrol Actions) menu to open the \uicontrol {Binding Editor}. @@ -96,7 +96,7 @@ We can drag the playhead along the timeline to see the color animation. - \section2 Animating the Indicator + \section2 Animating the indicator We select \e pb_front in \uicontrol Navigator and then select the record button again to animate the width of the indicator. At frame 0, we set the @@ -120,7 +120,7 @@ When we move the playhead, we can see that the color animation is also applied to the indicator. - \section1 Creating a Reusable Component + \section1 Creating a reusable component We want the progress bar to be reusable, so we'll move it to a separate component file. To make sure that the component will contain the timeline, @@ -135,14 +135,14 @@ \image progressbar-move-component.png "Move Component into Separate File dialog" - \section1 Exporting Properties + \section1 Exporting properties We now select the root component in \uicontrol Navigator, and then select \uicontrol {Go into Component} in the context menu to open \e Root.qml in the \uicontrol {2D} view. - We want to be able to use the keyframe value as the value of the text label, - so we will export it as a \l{Adding Property Aliases}{property alias}. We + We want to use the keyframe value as the value of the text label, + so we will export it as a \l{Adding property aliases}{property alias}. We select \e timeline in \uicontrol Navigator and then \uicontrol Properties > \uicontrol Timeline > \uicontrol {Current frame} > \uicontrol Actions > \uicontrol {Export Property as Alias}. @@ -166,7 +166,7 @@ When we move back to the top-level file, we can see that the number of the current keyframe (0) is displayed as the value of the text label. - \section1 Animating Progress Bar Instances + \section1 Animating the progress bar instances We want to experiment with different start and end values and easing curves, so we copy-paste the progress bar instance twice in the top-level file. We @@ -203,7 +203,7 @@ For more information about previewing UIs, see \l {Validating with Target Hardware}. - \section1 Specifying Easing Curves + \section1 Specifying easing curves We can add custom easing curves to every keyframe in the animations. First, we select the keyframe to add the easing curve to, and then select @@ -219,5 +219,5 @@ to \inlineimage icons/keyframe_manualbezier_active.png . - For more information, see \l{Editing Easing Curves}. + For more information, see \l{Editing easing curves}. */ diff --git a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc index 5cc040d4276..93c7174fd8c 100644 --- a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc +++ b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,15 +6,14 @@ \ingroup gstutorials \sa Particles - \title Particle System: Rain and Snow Effect - \brief Illustrates how to create a rain and snow effect with the \QDS + \title Particle System: The rain and the snow effects + \brief Illustrates how to create a rain and a snow effect with the \QDS particle system. \image snow-particles.png - The \e{Rain and Snow Effect} tutorial illustrates how you can add a rain and - a snow effect to your - scene using the \QDS particle system. + \e{The rain and the snow effects} tutorial illustrates how you can add a rain and + a snow effect to your scene using the \QDS particle system. You need to download the starting project for this tutorial from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/rain-snow-particles/Start}{here} @@ -24,9 +23,9 @@ to get started. \image rain-snow-tutorial-start.png - \section1 Creating a Rain Effect + \section1 Creating a rain effect - \section2 Adding a Particle System to Your Scene + \section2 Adding a Particle System to your scene To add a particle system, you first need to import the QtQuick3D.Particles3D module to your project: @@ -43,7 +42,8 @@ \image rain-snow-tutorial-particle-system.png - \section2 Adjusting the Behavior and Apperance of the Particle System + \section2 Adjusting the behavior and apperance of the Particle System + Next, you adjust the position, behavior, and apperance of the particle system to create a simple rain effect: @@ -93,7 +93,7 @@ \endlist \endlist - \section2 Adjusting the Size of the Emitting Area + \section2 Adjusting the size of the emitting area By default, the \uicontrol {Particle Emitter} emits particles from one point in the scene. In this scene you want to emit particles from a bigger @@ -121,7 +121,7 @@ Now, the rain effect is ready. Press \key Alt+P to see it in the live preview. - \section1 Creating a Snow Effect + \section1 Creating a snow effect To make it easy, you can duplicate the particle system you created for the rain effect and adjust the properties to create a snow effect. To do this, @@ -133,7 +133,7 @@ \image rain-snow-tutorial-states.png - \section2 Turning the Rain into Snow + \section2 Turning the rain into snow \list 1 \li With the new state that you just created selected in diff --git a/doc/qtdesignstudio/examples/doc/robotarm.qdoc b/doc/qtdesignstudio/examples/doc/robotarm.qdoc index fa35f31a346..00b721df020 100644 --- a/doc/qtdesignstudio/examples/doc/robotarm.qdoc +++ b/doc/qtdesignstudio/examples/doc/robotarm.qdoc @@ -1,11 +1,11 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page robotarm-example.html \ingroup gstutorials - \title Creating a C++ Backend for a \QDS Application + \title Creating a C++ backend for a \QDS application \brief Illustrates how to create an application in \QDS and add a backend in Qt Creator. @@ -23,11 +23,11 @@ To complete this tutorial, you need experience in using \QDS and Qt Creator as well as knowledge in C++ programming. - \section1 The Designer-Developer Workflow + \section1 The designer-developer workflow This section describes the high-level steps for creating the robot arm example. - \section2 Creating the User Interface + \section2 Creating the user interface First, the designer creates the user interface in \QDS: @@ -65,7 +65,7 @@ \endlist \endlist - \section2 Creating the C++ Backend + \section2 Creating the C++ backend With the frontend and user interface created, it is time to create the C++ backend. diff --git a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc index d1db82c8ee2..2061d021a83 100644 --- a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc +++ b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -16,13 +16,13 @@ when users hover the cursor over them or select them. Each button opens an image file. The side menu can be used to apply - \l {2D Effects}{graphical effects}, such as hue, saturation, + \l {2D effects}{graphical effects}, such as hue, saturation, and blur, to the images. \note Only a subset of effects is available if you select - \uicontrol {Qt 6} when \l{Creating Projects}{creating the project}. + \uicontrol {Qt 6} when \l{Creating projects}{creating the project}. - \section1 Creating Reusable Buttons + \section1 Creating reusable buttons We select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Controls} > @@ -31,7 +31,7 @@ The button can have the following states: checked, hover, pressed, and normal. We construct the button using different images for the button - background, frame, and front. We then add \l{Working with States}{states} in + background, frame, and front. We then add \l{Working with states}{states} in the \l States view for each of the button states. In each state, we turn the visibility of the appropriate images on or off in the button properties, to change the appearance of the button. @@ -77,7 +77,7 @@ We can now use CustomButton instances to create a menu bar. - \section1 Constructing a Menu Bar + \section1 Constructing a menu bar We construct the menu bar in the \e {MainFile.ui.qml} file using the \l {2D} view. The CustomButton component is listed in @@ -101,7 +101,7 @@ We can now select the \inlineimage icons/run_small.png "Run button" (\uicontrol Run) button to run the application and test our menu bar. - \section1 Creating a Side Menu + \section1 Creating a side menu We can now continue to create a side menu that slides open when users click the burger menu. We drag-and-drop a \l Text component from @@ -174,7 +174,7 @@ For more information about using the timeline, see \l {Creating Timeline Animations}. - \section1 Connecting the Burger Menu to Actions + \section1 Connecting the burger menu to actions In \e {SideMenu.qml}, we use connections to bind the action of clicking the burger menu to the signal handler for triggering the opening or @@ -195,7 +195,7 @@ For more information about Connecting Components to Signals, see \l {Connecting Components to Signals}. - \section1 Applying Effects + \section1 Applying effects We nest the effects in an effects stack and bind them to the \l {slider-control}{Slider} component instances. The effects apply @@ -203,7 +203,7 @@ currently open one. We use property bindings to connect the controls in the slider menu to - \l {2D Effects}{graphical effects}. To have access to the + \l {2D effects}{graphical effects}. To have access to the properties from all the slider component instances, we export them as aliases in \e SliderMenu.ui.qml. We select \uicontrol {Export Property as Alias} in the \uicontrol Settings menu of the \uicontrol Value property in @@ -217,9 +217,9 @@ We use an instance of the \l {Images}{Image} component as the last item in the stack to display images that we apply the effects to. We export the - image source property as an \l{Adding Property Aliases}{alias} to be able + image source property as an \l{Adding property aliases}{alias} to be able to switch the image inside the stack. For more information about the available graphical effects, see - \l {2D Effects}. + \l {2D effects}. */ diff --git a/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc b/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc index ed7066953a4..964ed09f85d 100644 --- a/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc +++ b/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,9 +6,9 @@ \ingroup studioexamples \brief Illustrates how to use a virtual keyboard in an application. - \title Simple Keyboard + \title Simple keyboard - \e {Simple Keyboard} provides a virtual keyboard for entering + \e {Simple keyboard} provides a virtual keyboard for entering text into several text fields. It uses functionality from the \l{Qt Virtual Keyboard} module. @@ -22,7 +22,7 @@ (\uicontrol Run) button to run the example on the desktop or a device. The keyboard is not available during preview. - \section1 Using a Virtual Keyboard + \section1 Using a virtual keyboard First, we create an empty project, as described in \l {Creating Projects}. For the purposes of this example, we call the project \e SimpleKeyboard. diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index 634ea666e06..b81213ebc67 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -26,7 +26,7 @@ Users select buttons to navigate between the screens. We use \l{glossary-connection}{connections} to determine which screen to open when users select a particular button and \l{glossary-state} - {states} to show the screens. We use the \l{Creating Timeline Animations} + {states} to show the screens. We use the \l{Creating timeline animations} {timeline} to create progress indicators for buttons and the \e Running screen. @@ -36,7 +36,7 @@ {\QMCU}, which does not support the \l Date component at the time of writing. - \section1 Creating an Application for MCUs + \section1 Creating an application for MCUs We use the \uicontrol {\QMCU Application} project template to create an application for MCUs, which support only a subset of the preset @@ -61,7 +61,7 @@ This way, only the components and properties supported on MCUs are visible in \l Components and \l Properties, and we won't accidentally add unsupported components to our UI or specify unsupported properties for - supported components. For more information, see \l{Creating Projects}. + supported components. For more information, see \l{Creating projects}. In addition, the wizard template creates a generic \c CMakeLists.txt file that we can use to configure and build our example application for running @@ -71,11 +71,11 @@ 1.6. You cannot run it on older versions. Also, at the time of writing, \QMCU only supports Qt 5. - \section1 Creating Screens + \section1 Creating screens For this example, we used an external tool to design the UI and then exported and imported our design into \QDS as assets and components - using \QB, as instructed in \l{Exporting from Design Tools}. While + using \QB, as instructed in \l{Exporting from design tools}. While exporting, we only picked components supported by \QMCU to use for our components. For the button components, we mostly use the \l {basic-image}{Image}, \l Text, and \l {Mouse Area} components. For the @@ -94,9 +94,9 @@ by selecting \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Files}. While designing the screens, you can move reusable components into separate files. For more information, - see \l{Using Components}. + see \l{Using components}. - \section1 Creating a Progress Indicator + \section1 Creating a progress indicator We create a reusable MultSegmentArc component, and use it in our \e Bigbutton and \e Smallbutton components to indicate the button @@ -163,7 +163,7 @@ \image washingmachineui-multsegmentarc.gif "Rotation animation in the 2D view" - \section1 Creating States + \section1 Creating states In our UI, we use connections and states to move between screens. First, we specify the application workflow in \e ApplicationFlow.qml. When the @@ -179,7 +179,7 @@ in the top-left corner of the root component. Then, we open the \uicontrol States view to create the \e start, - \e settings, \e presets, and \e running \l{Working with States}{states} for + \e settings, \e presets, and \e running \l{Working with states}{states} for displaying a particular screen by selecting \uicontrol {Create New State}. \image washingmachineui-states.png "States view" @@ -188,7 +188,7 @@ sometimes use \c when conditions to determine the state to apply, and sometimes switch states using signals and JavaScript expressions. - \section1 Connecting Buttons to State Changes + \section1 Connecting buttons to state changes In each file that defines a screen, we connect signals to the button components to change to a particular state when users select @@ -230,7 +230,7 @@ For more information, see \l {Connecting Components to Signals}. - \section1 Showing the Current Time + \section1 Showing the current time The \l Date component is not supported on \QMCU, and the \l{https://doc.qt.io/QtForMCUs/qtul-javascript-environment.html} diff --git a/doc/qtdesignstudio/examples/doc/webinardemo.qdoc b/doc/qtdesignstudio/examples/doc/webinardemo.qdoc index 56aedef21be..80e4cf0a0f2 100644 --- a/doc/qtdesignstudio/examples/doc/webinardemo.qdoc +++ b/doc/qtdesignstudio/examples/doc/webinardemo.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -23,10 +23,10 @@ design to \QDS as PNG images and custom components. Before we can begin, we must set up \QB as instructed in - \l{Exporting Designs from Adobe Photoshop}. + \l{Exporting designs from Adobe Photoshop}. We organize our design in Photoshop using artboards as instructed in - \l{Organizing Assets}. + \l{Organizing assets}. \QB automatically proposes identifiers for all groups and layers. The IDs will be used as filenames in \QDS. We want to change some of them in the @@ -47,7 +47,7 @@ properties for the components to export, so that we won't have to do that in \QDS. - \section2 Creating a Reference Image + \section2 Creating a reference image \image webinardemo-qb-layoutref.png "Settings for exporting stackLayout artboard" @@ -58,7 +58,7 @@ select \uicontrol Merged in the \uicontrol {Export As} field to merge them all into one image when this artboard is exported. - \section2 Creating the UI Main Page + \section2 Creating the UI main page We want to use the \e backgroundGradient artboard as the main page of the UI. Initially, it will contain a gradient image that we @@ -68,7 +68,7 @@ \uicontrol {Export As} field to export it as a PNG file that is referred to from the main page. - \section2 Using Stack Layout + \section2 Using stack layout \image webinardemo-qb-stacklayout.png "Settings for exporting stackLayout artboard" @@ -109,7 +109,7 @@ \image webinardemo-blureffect.png "FastBlurItem component in Design mode" - \section2 Creating a Menu with Reusable Buttons + \section2 Creating a menu with reusable buttons The \e miniMenu artboard contains menu buttons that we will use to toggle popup dialogs in the UI. We want to be able to reuse the buttons @@ -123,27 +123,27 @@ separate, so that we can switch the icons depending on the state of the button. We will implement this functionality in \QDS. - \section2 Preparing for Animation + \section2 Preparing for animation We want to animate the contents of the \e largePopup artboard in \QDS, so we export each group and layer as a child. - \section2 Exporting Our Design + \section2 Exporting our design When we have specified settings for all the artboards and the groups and layers in them, we select \uicontrol Export to copy the assets and metadata to the export path we specified. - \section1 Importing Our Design + \section1 Importing our design After using \QB in Adobe Photoshop to export our design, we import it into - a project that we create in \QDS, as instructed in \l{Importing Designs}. + a project that we create in \QDS, as instructed in \l{Importing designs}. If we need to make changes in Photoshop later and export our design again, \QDS will try to merge our changes during the import, so that none of the changes we mage in \QDS are lost. - \section1 Using Imported Components + \section1 Using imported components The \e {LayoutReference.ui.qml} file contains a reference image of the UI we will create. We use the imported components to create the @@ -162,7 +162,7 @@ We can now use states and the timeline to animate the components in the UI. - \section2 Animating Components + \section2 Animating components We open the \e {Largepopup.ui.qml} file in the \uicontrol Design mode to add animation to it. In the \l States view, we add the \e opening, \e open, diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index a13a2282c1e..7609ac4cb44 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -38,7 +38,7 @@ to define how their properties will animate when they change due to a state change. - The \l{Log In UI - States} example illustrates using states to create + The \l{Login UI: States} tutorial illustrates using states to create two UI screens and signals emitted by buttons to apply the states. The button components also switch states when they are pressed down. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc index d0ab93d9289..37f66ba6675 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc +++ b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only //! [creating studio components] @@ -54,7 +54,7 @@ \endlist For an example of using the \uicontrol Button template to create a button - and styling it, see \l{Creating a Push Button} in the \l{Log In UI - Components} + and styling it, see \l{Creating a Push Button} in the \l{Login UI: Components} tutorial. In addition, you can create starting points for different types of screens: diff --git a/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc index e1d3357b9bf..2fd95ac7665 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc @@ -8,20 +8,19 @@ \title Tutorials - \image loginui4.gif "Log In UI" + \image loginui4.gif "Login UI" - \section1 Video Tutorials + \section1 Video tutorials - When you run \QDS, the \uicontrol Tutorials tab in \uicontrol Welcome mode contains links - to video tutorials. To watch a tutorial on YouTube, - select it. + When you run \QDS, the \uicontrol Tutorials tab in the \uicontrol Welcome mode + contains links to video tutorials. Select a tutorial to watch it on YouTube. - \section1 Written Tutorials + \section1 Written tutorials You can follow a set of hands-on tutorials that illustrate how to use the features of \QDS. Even if you plan to export your designs from a design tool, it is useful to go through tutorials to learn to - use \QDS. In particular, \l {Log In UI - Components} + use \QDS. In particular, \l {Login UI: Components} describes the terms and concepts that you will run into when exporting designs with \QB. @@ -30,7 +29,7 @@ mode in particular: \l{User Interface} and \l{Design Views}. In addition to these tutorials, \QDS comes with examples that you can open - from the \uicontrol Examples and tabs in the \uicontrol Welcome mode. For more + from the \uicontrol Examples tab in the \uicontrol Welcome mode. For more information, see \l {Examples}. */ diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc index 85201a857e1..f3a30ca351c 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc @@ -342,8 +342,8 @@ \section1 Particle System Tutorials \list - \li \l{Particle System: Fire Effect} - \li \l{Particle System: Rain and Snow Effect} + \li \l{Particle System: The fire effect} + \li \l{Particle System: The rain and the snow effects} \endlist */ diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index cf611255e69..9770d78d3c1 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -31,7 +31,7 @@ by using a \l{Creating Custom Controls}{wizard template}. For more information about editing the states within the button component and hiding and showing buttons to create several screens, see - \l{Log In UI - Components} and \l{Log In UI - States}. + \l{Login UI: Components} and \l{Login UI: States}. To add motion to a screen, you can change the position of a component instance in the \uicontrol {2D} view and then add animation to the change diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc index 2e34d70dc4e..35c6739b8bc 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc @@ -50,7 +50,7 @@ \youtube V3Po15bNErw - To try it yourself, follow the \l{Log In UI - Timeline} tutorial. + To try it yourself, follow the \l{Login UI: Timeline} tutorial. For more information about creating timeline animations, see \l{Creating Timeline Animations}. From 359f039d169555d6e9971cf2e3556cd0ae0c5fd5 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 26 Nov 2024 14:18:16 +0100 Subject: [PATCH 188/322] DeviceShare: Use proper settings path and fix alias index issue Change-Id: I28b176892465ec19f6c58bc2271c29231e1f847d Reviewed-by: Thomas Hartmann --- .../devicesharing/devicemanager.cpp | 21 ++++++++----------- .../components/devicesharing/devicemanager.h | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp index 8ddc5f2079a..4d6bcc7241c 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.cpp @@ -15,10 +15,11 @@ namespace QmlDesigner::DeviceShare { -DeviceManager::DeviceManager(QObject *parent, const QString &settingsPath) +DeviceManager::DeviceManager(QObject *parent) : QObject(parent) - , m_settingsPath(settingsPath) { + QFileInfo fileInfo(Core::ICore::settings()->fileName()); + m_settingsPath = fileInfo.absolutePath() + "/device_manager.json"; readSettings(); if (m_uuid.isEmpty()) { m_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); @@ -229,19 +230,15 @@ void DeviceManager::setDeviceIP(const QString &deviceId, const QString &ip) QString DeviceManager::generateDeviceAlias() const { QStringList m_currentAliases; - - for (const auto &device : m_devices) { + for (const auto &device : m_devices) m_currentAliases.append(device->deviceSettings().alias()); - } - for (int i = 0; i < m_devices.size(); ++i) { - QString alias = QString("Device %1").arg(i); - if (!m_currentAliases.contains(alias)) { - return alias; - } - } + QString alias = "Device 0"; + int index = 0; + while (m_currentAliases.contains(alias)) + alias = QString("Device %1").arg(++index); - return QString("Device %1").arg(m_devices.size() + 1); + return alias; } bool DeviceManager::addDevice(const QString &ip) diff --git a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h index 8bb0b9de406..668f1c399c2 100644 --- a/src/plugins/qmldesigner/components/devicesharing/devicemanager.h +++ b/src/plugins/qmldesigner/components/devicesharing/devicemanager.h @@ -17,7 +17,7 @@ class DeviceManager : public QObject { Q_OBJECT public: - explicit DeviceManager(QObject *parent = nullptr, const QString &settingsPath = "settings.json"); + explicit DeviceManager(QObject *parent = nullptr); ~DeviceManager(); // Getters @@ -47,7 +47,7 @@ private: QList> m_udpSockets; // settings - const QString m_settingsPath; + QString m_settingsPath; QString m_uuid; QPointer m_widget; From 47564964365ea26242a99fd150d5f2f23036fced Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 27 Nov 2024 17:53:14 +0100 Subject: [PATCH 189/322] QmlDesigner: Fix Windows build If DESIGNSYSTEM_EXPORT is missing the symbols/functions will be duplicated which leads to a linking error. Exporting DSStore leads to stricter requirment for std::map DSThemeManager has to be copiable. Change-Id: I54bc953856acafde6eb66b906b49da6c7c77c666 Reviewed-by: Thomas Hartmann --- .../qmldesigner/libs/designsystem/dsconstants.h | 3 ++- src/plugins/qmldesigner/libs/designsystem/dsstore.h | 5 +---- .../qmldesigner/libs/designsystem/dsthememanager.cpp | 2 +- .../qmldesigner/libs/designsystem/dsthememanager.h | 10 ++++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsconstants.h b/src/plugins/qmldesigner/libs/designsystem/dsconstants.h index ab43e8ba839..95f6400535a 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsconstants.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsconstants.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 "designsystem_global.h" #include "nodeinstanceglobal.h" @@ -15,7 +16,7 @@ Q_NAMESPACE enum class GroupType { Colors, Flags, Numbers, Strings }; Q_ENUM_NS(GroupType) -class ThemeProperty +class DESIGNSYSTEM_EXPORT ThemeProperty { public: bool isValid() const { return !name.trimmed().isEmpty() && value.isValid(); } diff --git a/src/plugins/qmldesigner/libs/designsystem/dsstore.h b/src/plugins/qmldesigner/libs/designsystem/dsstore.h index 8c0cc9c2d7f..9708587821a 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsstore.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsstore.h @@ -9,12 +9,9 @@ #include namespace QmlDesigner { -class DSThemeManager; class ExternalDependenciesInterface; -using DSCollections = std::map; - -class DSStore +class DESIGNSYSTEM_EXPORT DSStore { Q_DECLARE_TR_FUNCTIONS(DSStore) diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp index b4faf0d5a4d..30e86ce95e8 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp +++ b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.cpp @@ -117,7 +117,7 @@ size_t DSThemeManager::themeCount() const size_t DSThemeManager::propertyCount() const { - using groupPair = std::pair>; + using groupPair = std::pair>; return std::accumulate(m_groups.cbegin(), m_groups.cend(), 0ull, [](size_t c, const groupPair &g) { return c + g.second->count(); }); diff --git a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h index cf1de885c42..1286aea72d3 100644 --- a/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h +++ b/src/plugins/qmldesigner/libs/designsystem/dsthememanager.h @@ -27,8 +27,8 @@ public: DSThemeManager(); ~DSThemeManager(); - DSThemeManager(const DSThemeManager&) = delete; - DSThemeManager& operator=(const DSThemeManager&) = delete; + DSThemeManager(const DSThemeManager &) = default; + DSThemeManager &operator=(const DSThemeManager &) = default; DSThemeManager(DSThemeManager&&) = default; DSThemeManager &operator=(DSThemeManager &&) = default; @@ -72,6 +72,8 @@ private: private: std::map m_themes; - std::map> m_groups; + std::map> m_groups; }; -} + +using DSCollections = std::map; +} // namespace QmlDesigner From f3815c815befa95b4b7150cee79d49cdc3d1b942 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 26 Nov 2024 14:15:10 +0200 Subject: [PATCH 190/322] EffectComposer: Show warning icon if uniform is not in use This gives visual feedback to user that the newly created properties need to also be used in shader code. Clicking on warning icon opens the node code editor. Fixes: QDS-14205 Change-Id: If7758cc2db2a47dcffb02a01d97e58231605d27d Reviewed-by: Mats Honkamaa Reviewed-by: Mahmoud Badri --- .../EffectCompositionNode.qml | 5 +-- .../EffectCompositionNodeUniform.qml | 24 +++++++++++--- .../effectcomposer/compositionnode.cpp | 32 ++++++++++++------- src/plugins/effectcomposer/compositionnode.h | 4 ++- .../effectcomposer/effectcomposermodel.cpp | 10 ++---- .../effectcomposer/effectcomposermodel.h | 1 - .../effectcomposeruniformsmodel.cpp | 5 ++- .../effectcomposeruniformsmodel.h | 3 +- src/plugins/effectcomposer/uniform.cpp | 13 ++++++++ src/plugins/effectcomposer/uniform.h | 6 ++++ 10 files changed, 75 insertions(+), 28 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index 6a437565a5b..d54fd26288e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -122,11 +122,11 @@ HelperWidgets.Section { width: root.width - StudioTheme.Values.scrollBarThicknessHover editing: root.editedUniformIndex === index disableMoreMenu: root.editedUniformIndex >= 0 - showMoreMenu: !isDependency + isDependencyNode: isDependency onReset: nodeUniformsModel.resetData(index) onRemove: { - if (root.backendModel.isNodeUniformInUse(root.modelIndex, index)) { + if (uniformIsInUse) { confirmRemoveForm.parent = effectCompositionNodeUniform.editPropertyFormParent confirmRemoveForm.uniformIndex = index confirmRemoveForm.visible = true @@ -149,6 +149,7 @@ HelperWidgets.Section { uniformName, uniformDescription, uniformDefaultValue, uniformMinValue, uniformMaxValue, uniformUserAdded) } + onOpenCodeEditor: root.backendModel.openCodeEditor(root.modelIndex) } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 2338869d397..69fcd1b8f65 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -14,7 +14,7 @@ Item { property bool editing: false property bool disableMoreMenu: false - property bool showMoreMenu: true + property bool isDependencyNode: true property alias editPropertyFormParent: editPropertyFormPlaceholder height: layout.implicitHeight + editPropertyFormPlaceholder.height + column.spacing @@ -23,6 +23,7 @@ Item { signal reset() signal remove() signal edit() + signal openCodeEditor() Component.onCompleted: { if (uniformType === "int") { @@ -102,12 +103,27 @@ Item { buttonSize: 24 icon: StudioTheme.Constants.reload_medium - iconSize: 16 + iconSize: StudioTheme.Values.mediumIconFontSize anchors.centerIn: parent - visible: mouseArea.containsMouse || iconButton.containsMouse + visible: !warningButton.visible && (mouseArea.containsMouse || iconButton.containsMouse) tooltip: qsTr("Reset value") + onClicked: root.reset() } + + HelperWidgets.IconButton { + id: warningButton + + buttonSize: 24 + icon: StudioTheme.Constants.warning_medium + iconSize: StudioTheme.Values.mediumIconFontSize + anchors.centerIn: parent + visible: !uniformIsInUse && !root.isDependencyNode + tooltip: qsTr("This property is not used in the shader code of the effect.") + iconColor: StudioTheme.Values.themeWarning + + onClicked: root.openCodeEditor() + } } Loader { @@ -130,7 +146,7 @@ Item { tooltip: root.disableMoreMenu ? qsTr("Additional actions disabled while editing existing property.") : qsTr("Access additional property actions.") enabled: !root.disableMoreMenu - visible: root.showMoreMenu + visible: !root.isDependencyNode onClicked: menuLoader.show() } diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 8ce4b337891..ca4482c356c 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -253,6 +253,7 @@ void CompositionNode::setFragmentCode(const QString &fragmentCode) return; m_fragmentCode = fragmentCode; + m_fragInUseCheckNeeded = true; emit fragmentCodeChanged(); requestRebakeIfLiveUpdateMode(); @@ -264,6 +265,7 @@ void CompositionNode::setVertexCode(const QString &vertexCode) return; m_vertexCode = vertexCode; + m_vertInUseCheckNeeded = true; emit vertexCodeChanged(); requestRebakeIfLiveUpdateMode(); @@ -302,18 +304,26 @@ void CompositionNode::updateUniform(int index, const QVariantMap &data) m_uniformsModel.updateUniform(index, uniform); } -bool CompositionNode::isUniformInUse(int index) const +void CompositionNode::updateAreUniformsInUse() { - QTC_ASSERT(index >= 0 && index < uniforms().size(), return false); - - const QString &name = uniforms().at(index)->name(); - QString pattern = QString("\\b%1\\b").arg(QRegularExpression::escape(name)); - QRegularExpression regex(pattern); - bool found = regex.match(m_fragmentCode).hasMatch(); - if (!found) - found = regex.match(m_vertexCode).hasMatch(); - - return found; + if (m_fragInUseCheckNeeded || m_vertInUseCheckNeeded) { + const QString matchTemplate("\\b%1\\b"); + const QList uniList = uniforms(); + for (int i = 0; i < uniList.size(); ++i) { + Uniform *u = uniList[i]; + QString pattern = matchTemplate.arg(QRegularExpression::escape(u->name())); + QRegularExpression regex(pattern); + bool found = false; + if (m_fragInUseCheckNeeded) + found = regex.match(m_fragmentCode).hasMatch(); + if (m_vertInUseCheckNeeded && !found) + found = regex.match(m_vertexCode).hasMatch(); + m_uniformsModel.setData(m_uniformsModel.index(i), found, + EffectComposerUniformsModel::IsInUse); + } + m_vertInUseCheckNeeded = false; + m_fragInUseCheckNeeded = false; + } } QString CompositionNode::name() const diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index 4004781b419..ec733639171 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -75,7 +75,7 @@ public: void closeCodeEditor(); void addUniform(const QVariantMap &data); void updateUniform(int index, const QVariantMap &data); - bool isUniformInUse(int index) const; + void updateAreUniformsInUse(); signals: void uniformsModelChanged(); @@ -103,6 +103,8 @@ private: bool m_isCustom = false; int m_refCount = 0; int m_extraMargin = 0; + bool m_vertInUseCheckNeeded = false; + bool m_fragInUseCheckNeeded = false; EffectComposerUniformsModel m_uniformsModel; Utils::UniqueObjectLatePtr m_shadersCodeEditor; diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index f2b0bedc082..cb992a60ac0 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -228,13 +228,6 @@ bool EffectComposerModel::changeNodeName(int nodeIndex, const QString &name) return newName == trimmedName; } -bool EffectComposerModel::isNodeUniformInUse(int nodeIndex, int uniformIndex) const -{ - QTC_ASSERT(nodeIndex >= 0 && nodeIndex < m_nodes.size(), return false); - - return m_nodes[nodeIndex]->isUniformInUse(uniformIndex); -} - void EffectComposerModel::clear(bool clearName) { beginResetModel(); @@ -2161,6 +2154,9 @@ void EffectComposerModel::bakeShaders() runQsb(qsbPath, outPaths, false); runQsb(qsbPrevPath, outPrevPaths, true); + + for (CompositionNode *node : std::as_const(m_nodes)) + node->updateAreUniformsInUse(); } bool EffectComposerModel::shadersUpToDate() const diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 70e55b4e888..f57c1ef599a 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -80,7 +80,6 @@ public: Q_INVOKABLE void moveNode(int fromIdx, int toIdx); Q_INVOKABLE void removeNode(int idx); - Q_INVOKABLE bool isNodeUniformInUse(int nodeIndex, int uniformIndex) const; Q_INVOKABLE bool changeNodeName(int nodeIndex, const QString& name); Q_INVOKABLE void clear(bool clearName = false); Q_INVOKABLE void assignToSelected(); diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index b8b2cdd80fb..f0f5d49079e 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -30,6 +30,7 @@ QHash EffectComposerUniformsModel::roleNames() const roles[ControlTypeRole] = "uniformControlType"; roles[UseCustomValueRole] = "uniformUseCustomValue"; roles[UserAdded] = "uniformUserAdded"; + roles[IsInUse] = "uniformIsInUse"; return roles; } @@ -55,7 +56,9 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria auto uniform = m_uniforms.at(index.row()); - if (uniform->type() == Uniform::Type::Sampler) { + if (role == IsInUse) { + uniform->setIsInUse(value.toBool()); + } else if (uniform->type() == Uniform::Type::Sampler) { QString updatedValue = value.toString(); int idx = value.toString().indexOf("file:"); diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index 62eaf2530de..5254d6485df 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -26,7 +26,8 @@ public: TypeRole, ControlTypeRole, UseCustomValueRole, - UserAdded + UserAdded, + IsInUse }; EffectComposerUniformsModel(QObject *parent = nullptr); diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index 3afd169a683..be25c1c5e47 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -158,6 +158,19 @@ bool Uniform::userAdded() const return m_userAdded; } +void Uniform::setIsInUse(bool inUse) +{ + if (m_isInUse != inUse) { + m_isInUse = inUse; + emit uniformIsInUseChanged(); + } +} + +bool Uniform::isInUse() const +{ + return m_isInUse; +} + QString Uniform::customValue() const { return m_customValue; diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index a316ee2a7b1..1a56d1e4272 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -30,6 +30,7 @@ class Uniform : public QObject Q_PROPERTY(QVariant uniformDefaultValue READ defaultValue NOTIFY uniformDefaultValueChanged) Q_PROPERTY(QVariant uniformUseCustomValue READ useCustomValue CONSTANT) Q_PROPERTY(bool uniformUserAdded READ userAdded CONSTANT) + Q_PROPERTY(bool uniformIsInUse READ isInUse NOTIFY uniformIsInUseChanged) public: enum class Type @@ -69,6 +70,9 @@ public: QString displayName() const; bool userAdded() const; + void setIsInUse(bool inUse); + bool isInUse() const; + QString customValue() const; void setCustomValue(const QString &newCustomValue); bool useCustomValue() const; @@ -87,6 +91,7 @@ signals: void uniformValueChanged(); void uniformBackendValueChanged(); void uniformDefaultValueChanged(); + void uniformIsInUseChanged(); private: QString mipmapPropertyName(const QString &name) const; @@ -113,6 +118,7 @@ private: bool m_useCustomValue = false; bool m_enabled = true; bool m_enableMipmap = false; + bool m_isInUse = false; QmlDesigner::PropertyEditorValue *m_backendValue = nullptr; bool operator==(const Uniform &rhs) const noexcept From 01b8a26fa54f9da8ed5eff4722ba6cf7a093ada1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 26 Nov 2024 16:55:07 +0200 Subject: [PATCH 191/322] EffectComposer: Move open code button Fixes: QDS-14218 Change-Id: I3f622ef649438475f85bd902d3b2547d64dffdde Reviewed-by: Mahmoud Badri --- .../AddPropertyForm.qml | 14 ++-- .../EffectCompositionNode.qml | 71 +++++++++---------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml index cecb0cef0e3..c53df6450bd 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/AddPropertyForm.qml @@ -725,15 +725,17 @@ Item { } Row { + id: buttonRow + width: acceptButton.width + root.horizontalSpacing + cancelButton.width spacing: root.horizontalSpacing - anchors.horizontalCenter: parent.horizontalCenter - height: 35 + x: (parent.width - buttonRow.width) / 2 + 6 + height: 30 HelperWidgets.Button { id: cancelButton - width: 130 - height: 35 + width: 100 + height: 30 text: qsTr("Cancel") padding: 4 @@ -745,8 +747,8 @@ Item { HelperWidgets.Button { id: acceptButton - width: 130 - height: 35 + width: 100 + height: 30 text: qsTr("Apply") padding: 4 enabled: !root.propNameError && !root.uniNameError diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index d54fd26288e..bc29a2f0d9c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -49,23 +49,6 @@ HelperWidgets.Section { nodeEnabled = root.eyeEnabled } - icons: HelperWidgets.IconButton { - id: codeButton - - icon: StudioTheme.Constants.codeEditor_medium - transparentBg: true - buttonSize: 21 - iconSize: StudioTheme.Values.smallIconFontSize - iconColor: root.codeEditorOpen - ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - iconScale: codeButton.containsMouse ? 1.2 : 1 - implicitWidth: width - tooltip: qsTr("Open code editor") - onClicked: root.backendModel.openCodeEditor(index) - visible: !isDependency - } - content: Label { text: root.caption color: root.labelColor @@ -158,28 +141,9 @@ HelperWidgets.Section { id: addProperty width: root.width - StudioTheme.Values.scrollBarThicknessHover height: addPropertyForm.visible && addPropertyForm.parent === addProperty - ? addPropertyForm.height : 50 + ? addPropertyForm.height : 0 visible: !isDependency - HelperWidgets.Button { - id: addPropertyButton - width: 130 - height: 35 - text: qsTr("Add Property") - visible: !addPropertyForm.visible - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - onClicked: { - confirmRemoveForm.visible = false - confirmRemoveForm.parent = root - root.editedUniformIndex = -1 - addPropertyForm.parent = addProperty - addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() - addPropertyForm.reservedUniNames = root.backendModel.uniformNames() - addPropertyForm.showForAdd() - } - } - AddPropertyForm { id: addPropertyForm visible: false @@ -301,8 +265,39 @@ HelperWidgets.Section { } } } - } + Row { + height: 40 + visible: !isDependency && !addPropertyForm.visible + anchors.horizontalCenter: parent.horizontalCenter + spacing: 10 + + HelperWidgets.Button { + width: 100 + height: 30 + text: qsTr("Add Property") + enabled: !addPropertyForm.visible + anchors.verticalCenter: parent.verticalCenter + + onClicked: { + confirmRemoveForm.visible = false + confirmRemoveForm.parent = root + root.editedUniformIndex = -1 + addPropertyForm.parent = addProperty + addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() + addPropertyForm.reservedUniNames = root.backendModel.uniformNames() + addPropertyForm.showForAdd() + } + } + + HelperWidgets.Button { + width: 100 + height: 30 + text: qsTr("Show Code") + anchors.verticalCenter: parent.verticalCenter + onClicked: root.backendModel.openCodeEditor(index) + } + } } From 0eab8c88b6472eaeb3b55c657dc7432304619e87 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Nov 2024 13:38:16 +0100 Subject: [PATCH 192/322] QmlDesigner.PropertyEditor: Remove workarounds and unnecessary code The workaround and optimizations do not seem to be required anymore. The property editor is now fast enough and no delay using a timer is required anymore. We also do not have to setup the Item pane upfront. Change-Id: Ieb64fcf5e37b74f1e755bc06674deede66a6f666 Reviewed-by: Mahmoud Badri --- .../propertyeditor/propertyeditorview.cpp | 75 ++----------------- .../propertyeditor/propertyeditorview.h | 5 -- 2 files changed, 5 insertions(+), 75 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index bba8439533e..71aa36d34f4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -82,8 +82,7 @@ PropertyEditorView::PropertyEditorView(AsynchronousImageCache &imageCache, , m_propertyComponentGenerator{QmlDesigner::PropertyEditorQmlBackend::propertyEditorResourcesPath(), model()} , m_locked(false) - , m_setupCompleted(false) - , m_singleShotTimer(new QTimer(this)) + { m_qmlDir = PropertyEditorQmlBackend::propertyEditorResourcesPath(); @@ -109,32 +108,6 @@ PropertyEditorView::~PropertyEditorView() qDeleteAll(m_qmlBackendHash); } -void PropertyEditorView::setupPane([[maybe_unused]] const TypeName &typeName) -{ -#ifndef QDS_USE_PROJECTSTORAGE - NodeMetaInfo metaInfo = model()->metaInfo(typeName); - - QUrl qmlFile = PropertyEditorQmlBackend::getQmlFileUrl("Qt/ItemPane", metaInfo); - QUrl qmlSpecificsFile; - - qmlSpecificsFile = PropertyEditorQmlBackend::getQmlFileUrl(typeName + "Specifics", metaInfo); - - PropertyEditorQmlBackend *qmlBackend = m_qmlBackendHash.value(qmlFile.toString()); - - if (!qmlBackend) { - qmlBackend = new PropertyEditorQmlBackend(this, m_imageCache); - - qmlBackend->initialSetup(typeName, qmlSpecificsFile, this); - qmlBackend->setSource(qmlFile); - - m_stackedWidget->addWidget(qmlBackend->widget()); - m_qmlBackendHash.insert(qmlFile.toString(), qmlBackend); - } else { - qmlBackend->initialSetup(typeName, qmlSpecificsFile, this); - } -#endif // QDS_USE_PROJECTSTORAGE -} - void PropertyEditorView::changeValue(const QString &name) { PropertyName propertyName = name.toUtf8(); @@ -441,30 +414,6 @@ void PropertyEditorView::updateSize() frame->resize(m_stackedWidget->size()); } -void PropertyEditorView::setupPanes() -{ - if (isAttached()) { - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - setupPane("QtQuick.Item"); - resetView(); - m_setupCompleted = true; - QApplication::restoreOverrideCursor(); - } -} - -void PropertyEditorView::delayedResetView() -{ - if (m_timerId) - killTimer(m_timerId); - m_timerId = startTimer(50); -} - -void PropertyEditorView::timerEvent(QTimerEvent *timerEvent) -{ - if (m_timerId == timerEvent->timerId()) - resetView(); -} - void PropertyEditorView::resetView() { if (model() == nullptr) @@ -780,20 +729,6 @@ void PropertyEditorView::modelAttached(Model *model) if (debug) qDebug() << Q_FUNC_INFO; - m_locked = true; - - if (!m_setupCompleted) { - QTimer::singleShot(50, this, [this] { - if (isAttached()) { - PropertyEditorView::setupPanes(); - /* workaround for QTBUG-75847 */ - reloadQml(); - } - }); - } - - m_locked = false; - resetView(); } @@ -982,7 +917,7 @@ void PropertyEditorView::select() if (m_qmlBackEndForCurrentType) m_qmlBackEndForCurrentType->emitSelectionToBeChanged(); - delayedResetView(); + resetView(); auto nodes = selectedModelNodes(); @@ -1026,7 +961,7 @@ void PropertyEditorView::currentStateChanged(const ModelNode &node) Q_ASSERT(newQmlModelState.isValid()); if (debug) qDebug() << Q_FUNC_INFO << newQmlModelState.name(); - delayedResetView(); + resetView(); } void PropertyEditorView::instancePropertyChanged(const QList > &propertyList) @@ -1065,13 +1000,13 @@ void PropertyEditorView::instancePropertyChanged(const QList Date: Thu, 28 Nov 2024 11:50:23 +0100 Subject: [PATCH 193/322] QmlDesigner: Fix deprication warnings Change-Id: I98ca6791317763a5a148f1fd3138f573600666ab Reviewed-by: Miikka Heikkinen --- .../qml2puppet/qml2puppet/editor3d/camerageometry.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp index f2eab9f5656..2c5987e0445 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/camerageometry.cpp @@ -158,10 +158,10 @@ void CameraGeometry::fillVertexData(QByteArray &vertexData, QByteArray &indexDat m = camera->projection.inverted(); } - const QVector3D farTopLeft = m * QVector3D(1.f, -1.f, 1.f); - const QVector3D farBottomRight = m * QVector3D(-1.f, 1.f, 1.f); - const QVector3D nearTopLeft = m * QVector3D(1.f, -1.f, -1.f); - const QVector3D nearBottomRight = m * QVector3D(-1.f, 1.f, -1.f); + const QVector3D farTopLeft = m.map(QVector3D(1.f, -1.f, 1.f)); + const QVector3D farBottomRight = m.map(QVector3D(-1.f, 1.f, 1.f)); + const QVector3D nearTopLeft = m.map(QVector3D(1.f, -1.f, -1.f)); + const QVector3D nearBottomRight = m.map(QVector3D(-1.f, 1.f, -1.f)); *dataPtr++ = nearTopLeft.x(); *dataPtr++ = nearBottomRight.y(); *dataPtr++ = nearTopLeft.z(); *dataPtr++ = nearTopLeft.x(); *dataPtr++ = nearTopLeft.y(); *dataPtr++ = nearTopLeft.z(); From 91f76efd40f905ad7abaa1043329cdf3f97a201c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 25 Nov 2024 18:41:22 +0200 Subject: [PATCH 194/322] EffectComposer: Use a single EffectsShaderCodeEditor for all nodes * Composition nodes use ShaderEditorData to share data with the editor * ShaderEditorData contains a table model for unions, shader documents and also a private instance of the editors. Task-number: QDS-14220 Change-Id: I63ca943d4abcbde273b6b3b64dffcf24f8f28f48 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../effectcomposer/compositionnode.cpp | 51 ++---- src/plugins/effectcomposer/compositionnode.h | 9 +- .../effectcomposer/effectcodeeditorwidget.cpp | 8 +- .../effectcomposer/effectcomposermodel.cpp | 125 ++++++------- .../effectcomposer/effectcomposermodel.h | 5 +- .../effectcomposeruniformsmodel.cpp | 5 + .../effectcomposeruniformsmodel.h | 1 + .../effectcomposeruniformstablemodel.cpp | 14 ++ .../effectcomposeruniformstablemodel.h | 2 + .../effectshaderscodeeditor.cpp | 172 +++++++++--------- .../effectcomposer/effectshaderscodeeditor.h | 47 +++-- 11 files changed, 226 insertions(+), 213 deletions(-) diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index ca4482c356c..0b51d9fd10d 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -55,7 +55,7 @@ CompositionNode::CompositionNode(const QString &effectName, const QString &qenPa CompositionNode::~CompositionNode() { - closeCodeEditor(); + EffectShadersCodeEditor::instance()->cleanFromData(m_shaderEditorData.get()); }; QString CompositionNode::fragmentCode() const @@ -175,40 +175,27 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c } } -void CompositionNode::ensureShadersCodeEditor() +void CompositionNode::ensureCodeEditorData() { - if (m_shadersCodeEditor) + using TextEditor::TextDocument; + if (m_shaderEditorData) return; - m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr(name()); - m_shadersCodeEditor->setUniformsModel(&m_uniformsModel); - m_shadersCodeEditor->setFragmentValue(fragmentCode()); - m_shadersCodeEditor->setVertexValue(vertexCode()); + m_shaderEditorData.reset(EffectShadersCodeEditor::instance() + ->createEditorData(fragmentCode(), vertexCode(), &m_uniformsModel)); - connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::vertexValueChanged, this, [this] { - setVertexCode(m_shadersCodeEditor->vertexValue()); + connect(m_shaderEditorData->fragmentDocument.get(), &TextDocument::contentsChanged, this, [this] { + setFragmentCode(m_shaderEditorData->fragmentDocument->plainText()); }); - connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::fragmentValueChanged, this, [this] { - setFragmentCode(m_shadersCodeEditor->fragmentValue()); + connect(m_shaderEditorData->vertexDocument.get(), &TextDocument::contentsChanged, this, [this] { + setVertexCode(m_shaderEditorData->vertexDocument->plainText()); }); - - connect( - m_shadersCodeEditor.get(), - &EffectShadersCodeEditor::rebakeRequested, - this, - &CompositionNode::rebakeRequested); - - connect( - m_shadersCodeEditor.get(), - &EffectShadersCodeEditor::openedChanged, - this, - &CompositionNode::codeEditorVisibilityChanged); } void CompositionNode::requestRebakeIfLiveUpdateMode() { - if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate()) + if (EffectShadersCodeEditor::instance()->liveUpdate()) emit rebakeRequested(); } @@ -273,18 +260,10 @@ void CompositionNode::setVertexCode(const QString &vertexCode) void CompositionNode::openCodeEditor() { - ensureShadersCodeEditor(); - - if (m_shadersCodeEditor->isVisible()) - return; - - m_shadersCodeEditor->showWidget(); -} - -void CompositionNode::closeCodeEditor() -{ - if (m_shadersCodeEditor) - m_shadersCodeEditor->close(); + auto editor = EffectShadersCodeEditor::instance(); + ensureCodeEditorData(); + editor->setupShader(m_shaderEditorData.get()); + editor->showWidget(); } void CompositionNode::addUniform(const QVariantMap &data) diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index ec733639171..096e959f224 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -7,12 +7,15 @@ #include +#include + #include #include namespace EffectComposer { class EffectShadersCodeEditor; +struct ShaderEditorData; class CompositionNode : public QObject { @@ -72,7 +75,6 @@ public: void setVertexCode(const QString &vertexCode); void openCodeEditor(); - void closeCodeEditor(); void addUniform(const QVariantMap &data); void updateUniform(int index, const QVariantMap &data); void updateAreUniformsInUse(); @@ -84,12 +86,11 @@ signals: void rebakeRequested(); void fragmentCodeChanged(); void vertexCodeChanged(); - void codeEditorVisibilityChanged(bool); void nameChanged(); private: void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); - void ensureShadersCodeEditor(); + void ensureCodeEditorData(); void requestRebakeIfLiveUpdateMode(); QString m_name; @@ -107,7 +108,7 @@ private: bool m_fragInUseCheckNeeded = false; EffectComposerUniformsModel m_uniformsModel; - Utils::UniqueObjectLatePtr m_shadersCodeEditor; + std::unique_ptr m_shaderEditorData; }; } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp index 0040dc8057f..ba5add34528 100644 --- a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp +++ b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp @@ -123,13 +123,7 @@ QStringList EffectCodeEditorWidget::getUniforms() const EffectDocument::EffectDocument() : QmlJSEditor::QmlJSEditorDocument(EFFECTEDITOR_CONTEXT_ID) , m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this)) -{ - auto *tmpFile = new Utils::TemporaryFile("EffectDoc.js"); - tmpFile->setParent(this); - tmpFile->open(); - tmpFile->close(); - setFilePath(Utils::FilePath::fromString(tmpFile->fileName())); -} +{} EffectDocument::~EffectDocument() { diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index cb992a60ac0..38a695046df 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -4,6 +4,7 @@ #include "effectcomposermodel.h" #include "compositionnode.h" +#include "effectcodeeditorwidget.h" #include "effectshaderscodeeditor.h" #include "effectutils.h" #include "propertyhandler.h" @@ -48,6 +49,7 @@ EffectComposerModel::EffectComposerModel(QObject *parent) m_rebakeTimer.setSingleShot(true); connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders); m_currentPreviewImage = defaultPreviewImages().first(); + connectCodeEditor(); } QHash EffectComposerModel::roleNames() const @@ -1142,6 +1144,46 @@ R"( return s; } +void EffectComposerModel::connectCodeEditor() +{ + EffectShadersCodeEditor *editor = EffectShadersCodeEditor::instance(); + connect(this, &QObject::destroyed, editor, &QObject::deleteLater); + + connect( + editor, + &EffectShadersCodeEditor::rebakeRequested, + this, + &EffectComposerModel::startRebakeTimer); + + connect(editor, &EffectShadersCodeEditor::openedChanged, this, [this](bool visible) { + if (!visible) + setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); + }); +} + +void EffectComposerModel::createCodeEditorData() +{ + using TextEditor::TextDocument; + if (m_shaderEditorData) + return; + + m_shaderEditorData.reset( + EffectShadersCodeEditor::instance() + ->createEditorData(m_rootFragmentShader, m_rootVertexShader, nullptr)); + + connect(m_shaderEditorData->fragmentDocument.get(), &TextDocument::contentsChanged, this, [this] { + setRootFragmentShader(m_shaderEditorData->fragmentDocument->plainText()); + setHasUnsavedChanges(true); + rebakeIfLiveUpdateMode(); + }); + + connect(m_shaderEditorData->vertexDocument.get(), &TextDocument::contentsChanged, this, [this] { + setRootVertexShader(m_shaderEditorData->vertexDocument->plainText()); + setHasUnsavedChanges(true); + rebakeIfLiveUpdateMode(); + }); +} + void EffectComposerModel::saveComposition(const QString &name) { resetEffectError(ErrorCommon); @@ -1222,68 +1264,21 @@ void EffectComposerModel::openCodeEditor(int idx) if (idx < 0 || idx >= m_nodes.size()) return; - if (m_codeEditorIndex == MAIN_CODE_EDITOR_INDEX && m_shadersCodeEditor) - m_shadersCodeEditor->close(); - else if (m_codeEditorIndex != idx && m_codeEditorIndex > -1 && m_codeEditorIndex < m_nodes.size()) - m_nodes.at(m_codeEditorIndex)->closeCodeEditor(); - CompositionNode *node = m_nodes.at(idx); node->openCodeEditor(); + + setCodeEditorIndex(idx); } void EffectComposerModel::openMainCodeEditor() { - if (!m_shadersCodeEditor) { - m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr( - currentComposition()); - m_shadersCodeEditor->setFragmentValue(m_rootFragmentShader); - m_shadersCodeEditor->setVertexValue(m_rootVertexShader); + createCodeEditorData(); - connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::vertexValueChanged, this, [this] { - setRootVertexShader(m_shadersCodeEditor->vertexValue()); - setHasUnsavedChanges(true); - rebakeIfLiveUpdateMode(); - }); + auto editor = EffectShadersCodeEditor::instance(); + editor->setupShader(m_shaderEditorData.get()); + editor->showWidget(); - connect( - m_shadersCodeEditor.get(), &EffectShadersCodeEditor::fragmentValueChanged, this, [this] { - setRootFragmentShader(m_shadersCodeEditor->fragmentValue()); - setHasUnsavedChanges(true); - rebakeIfLiveUpdateMode(); - }); - - connect( - m_shadersCodeEditor.get(), - &EffectShadersCodeEditor::rebakeRequested, - this, - &EffectComposerModel::startRebakeTimer); - - connect( - m_shadersCodeEditor.get(), - &EffectShadersCodeEditor::openedChanged, - this, - [this](bool visible) { - if (visible) { - setCodeEditorIndex(MAIN_CODE_EDITOR_INDEX); - } else { - // Invalidate codeEditorIndex only if the index is the same as the current index - // It means that if the current code editor index belongs to another node, we - // shouldn't declare it closed. - // This condition prevents marking the new code editor closed, in the case that - // the user opens another editor deliberately while the old editor is opened. - if (m_codeEditorIndex == MAIN_CODE_EDITOR_INDEX) - setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); - } - }); - } - - int oldIndex = m_codeEditorIndex; - - m_shadersCodeEditor->showWidget(); - - // Close the old editor - if (oldIndex > -1 && oldIndex < m_nodes.size()) - m_nodes.at(oldIndex)->closeCodeEditor(); + setCodeEditorIndex(MAIN_CODE_EDITOR_INDEX); } QVariant EffectComposerModel::valueLimit(const QString &type, bool max) const @@ -2362,20 +2357,6 @@ void EffectComposerModel::connectCompositionNode(CompositionNode *node) connect(node, &CompositionNode::rebakeRequested, this, &EffectComposerModel::startRebakeTimer); connect(node, &CompositionNode::fragmentCodeChanged, this, setUnsaved); connect(node, &CompositionNode::vertexCodeChanged, this, setUnsaved); - connect(node, &CompositionNode::codeEditorVisibilityChanged, this, [this, node](bool visible) { - int idx = m_nodes.indexOf(node); - if (idx < 0) - return; - if (visible) { - setCodeEditorIndex(idx); - } else { - // Invalidate codeEditorIndex only if the index is the same as current index - // It means that if current code editor index belongs to another node, we - // shouldn't declare it closed. - if (m_codeEditorIndex == idx) - setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); - } - }); } void EffectComposerModel::updateExtraMargin() @@ -2393,7 +2374,7 @@ void EffectComposerModel::startRebakeTimer() void EffectComposerModel::rebakeIfLiveUpdateMode() { - if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate()) + if (EffectShadersCodeEditor::instance()->liveUpdate()) startRebakeTimer(); } @@ -2446,7 +2427,11 @@ void EffectComposerModel::setCurrentComposition(const QString &newCurrentComposi m_currentComposition = newCurrentComposition; emit currentCompositionChanged(); - m_shadersCodeEditor.reset(); + auto editor = EffectShadersCodeEditor::instance(); + editor->close(); + editor->cleanFromData(m_shaderEditorData.get()); + + m_shaderEditorData.reset(); } QList EffectComposerModel::defaultPreviewImages() const diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index f57c1ef599a..89b3939a470 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -29,6 +29,7 @@ namespace EffectComposer { class CompositionNode; class EffectShadersCodeEditor; +struct ShaderEditorData; class Uniform; struct EffectError { @@ -218,6 +219,8 @@ private: QString stripFileFromURL(const QString &urlString) const; QString getQmlEffectString(); + void connectCodeEditor(); + void createCodeEditorData(); void updateCustomUniforms(); void initShaderDir(); void bakeShaders(); @@ -284,7 +287,7 @@ private: int m_extraMargin = 0; QString m_effectTypePrefix; Utils::FilePath m_compositionPath; - Utils::UniqueObjectLatePtr m_shadersCodeEditor; + std::unique_ptr m_shaderEditorData; QUrl m_currentPreviewImage; QList m_customPreviewImages; int m_currentBakeCounter = 0; diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index f0f5d49079e..a2bb086d9af 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -107,6 +107,11 @@ QStringList EffectComposerUniformsModel::displayNames() const return displayNames; } +QStringList EffectComposerUniformsModel::uniformNames() const +{ + return Utils::transform(m_uniforms, &Uniform::name); +} + void EffectComposerUniformsModel::resetModel() { beginResetModel(); diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index 5254d6485df..f97067434f1 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -39,6 +39,7 @@ public: Q_INVOKABLE bool resetData(int row); Q_INVOKABLE bool remove(int row); Q_INVOKABLE QStringList displayNames() const; + Q_INVOKABLE QStringList uniformNames() const; void resetModel(); diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp index 526e697adad..ae68b3c6c3a 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp @@ -117,6 +117,9 @@ EffectComposerUniformsTableModel::EffectComposerUniformsTableModel( : QAbstractTableModel(parent) , m_sourceModel(sourceModel) { + if (!sourceModel) + return; + connect( sourceModel, &QAbstractItemModel::modelAboutToBeReset, @@ -170,6 +173,9 @@ EffectComposerUniformsTableModel::EffectComposerUniformsTableModel( EffectComposerUniformsTableModel::SourceIndex EffectComposerUniformsTableModel::mapToSource( const QModelIndex &proxyIndex) const { + if (!m_sourceModel) + return {}; + return {m_sourceModel->index(proxyIndex.row(), 0), RoleColMap::colToRole(proxyIndex.column())}; } @@ -185,6 +191,9 @@ QHash EffectComposerUniformsTableModel::roleNames() const int EffectComposerUniformsTableModel::rowCount(const QModelIndex &) const { + if (!m_sourceModel) + return 0; + return m_sourceModel->rowCount(); } @@ -226,6 +235,11 @@ QVariant EffectComposerUniformsTableModel::headerData( return {}; } +EffectComposerUniformsModel *EffectComposerUniformsTableModel::sourceModel() const +{ + return m_sourceModel.get(); +} + void EffectComposerUniformsTableModel::onSourceRowsAboutToBeInserted( const QModelIndex &, int first, int last) { diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h index 52b7dd82932..5472818324f 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h @@ -47,6 +47,8 @@ public: QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + EffectComposerUniformsModel *sourceModel() const; + private: void onSourceRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); void onSourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 3f1a48033ee..08b9824b145 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -3,7 +3,6 @@ #include "effectshaderscodeeditor.h" -#include "effectcodeeditorwidget.h" #include "effectcomposeruniformsmodel.h" #include "effectcomposeruniformstablemodel.h" #include "effectcomposerwidget.h" @@ -52,34 +51,20 @@ namespace EffectComposer { EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget *parent) : QWidget(parent) , m_settings(new QSettings(qApp->organizationName(), qApp->applicationName(), this)) + , m_defaultTableModel(new EffectComposerUniformsTableModel(nullptr, this)) { setWindowFlag(Qt::Tool, true); setWindowFlag(Qt::WindowStaysOnTopHint); setWindowTitle(title); - m_fragmentEditor = createJSEditor(); - m_vertexEditor = createJSEditor(); - - connect( - m_fragmentEditor, - &QPlainTextEdit::textChanged, - this, - &EffectShadersCodeEditor::fragmentValueChanged); - connect( - m_vertexEditor, - &QPlainTextEdit::textChanged, - this, - &EffectShadersCodeEditor::vertexValueChanged); - setupUIComponents(); + setUniformsModel(nullptr); } EffectShadersCodeEditor::~EffectShadersCodeEditor() { if (isOpened()) close(); - m_fragmentEditor->deleteLater(); - m_vertexEditor->deleteLater(); } void EffectShadersCodeEditor::showWidget() @@ -97,34 +82,6 @@ void EffectShadersCodeEditor::showWidget(int x, int y) move(QPoint(x, y)); } -QString EffectShadersCodeEditor::fragmentValue() const -{ - if (!m_fragmentEditor) - return {}; - - return m_fragmentEditor->document()->toPlainText(); -} - -void EffectShadersCodeEditor::setFragmentValue(const QString &text) -{ - if (m_fragmentEditor) - m_fragmentEditor->setEditorTextWithIndentation(text); -} - -QString EffectShadersCodeEditor::vertexValue() const -{ - if (!m_vertexEditor) - return {}; - - return m_vertexEditor->document()->toPlainText(); -} - -void EffectShadersCodeEditor::setVertexValue(const QString &text) -{ - if (m_vertexEditor) - m_vertexEditor->setEditorTextWithIndentation(text); -} - bool EffectShadersCodeEditor::liveUpdate() const { return m_liveUpdate; @@ -149,30 +106,67 @@ bool EffectShadersCodeEditor::isOpened() const return m_opened; } -void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsModel *uniforms) +void EffectShadersCodeEditor::setupShader(ShaderEditorData *data) { - std::function uniformNames = [uniforms]() -> QStringList { - if (!uniforms) - return {}; - int count = uniforms->rowCount(); - QStringList result; - for (int i = 0; i < count; ++i) { - const QModelIndex mIndex = uniforms->index(i, 0); - result.append(mIndex.data(EffectComposerUniformsModel::NameRole).toString()); - } - return result; - }; - m_fragmentEditor->setUniformsCallback(uniformNames); - m_vertexEditor->setUniformsCallback(uniformNames); + if (m_currentEditorData == data) + return; - if (m_headerWidget && uniforms) { - m_uniformsTableModel - = Utils::makeUniqueObjectLatePtr(uniforms, this); - EffectComposerUniformsTableModel *uniformsTable = m_uniformsTableModel.get(); + while (m_tabWidget->count()) + m_tabWidget->removeTab(0); - m_headerWidget->rootContext() - ->setContextProperty("uniformsTableModel", QVariant::fromValue(uniformsTable)); + if (data) { + m_tabWidget->addTab(data->fragmentEditor.get(), tr("Fragment Shader")); + m_tabWidget->addTab(data->vertexEditor.get(), tr("Vertex Shader")); + + selectNonEmptyShader(data); + setUniformsModel(data->tableModel); + } else { + setUniformsModel(nullptr); } + + m_currentEditorData = data; +} + +void EffectShadersCodeEditor::cleanFromData(ShaderEditorData *data) +{ + if (m_currentEditorData == data) + setupShader(nullptr); +} + +ShaderEditorData *EffectShadersCodeEditor::createEditorData( + const QString &fragmentDocument, + const QString &vertexDocument, + EffectComposerUniformsModel *uniforms) +{ + ShaderEditorData *result = new ShaderEditorData; + result->fragmentEditor.reset(createJSEditor()); + result->vertexEditor.reset(createJSEditor()); + + result->fragmentEditor->setPlainText(fragmentDocument); + result->vertexEditor->setPlainText(vertexDocument); + + result->fragmentDocument = result->fragmentEditor->textDocumentPtr(); + result->vertexDocument = result->vertexEditor->textDocumentPtr(); + + if (uniforms) { + result->tableModel = new EffectComposerUniformsTableModel(uniforms, uniforms); + std::function uniformNames = + [uniformsTable = result->tableModel]() -> QStringList { + if (!uniformsTable) + return {}; + + auto uniformsModel = uniformsTable->sourceModel(); + if (!uniformsModel) + return {}; + + return uniformsModel->uniformNames(); + }; + + result->fragmentEditor->setUniformsCallback(uniformNames); + result->vertexEditor->setUniformsCallback(uniformNames); + } + + return result; } void EffectShadersCodeEditor::copyText(const QString &text) @@ -180,6 +174,13 @@ void EffectShadersCodeEditor::copyText(const QString &text) qApp->clipboard()->setText(text); } +EffectShadersCodeEditor *EffectShadersCodeEditor::instance() +{ + static EffectShadersCodeEditor *editorInstance = new EffectShadersCodeEditor( + tr("Shaders Code Editor")); + return editorInstance; +} + EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() { static EffectCodeEditorFactory f; @@ -208,35 +209,20 @@ void EffectShadersCodeEditor::setupUIComponents() { QVBoxLayout *verticalLayout = new QVBoxLayout(this); QSplitter *splitter = new QSplitter(this); - QTabWidget *tabWidget = new QTabWidget(this); + m_tabWidget = new QTabWidget(this); splitter->setOrientation(Qt::Vertical); createHeader(); - tabWidget->addTab(m_fragmentEditor, tr("Fragment Shader")); - tabWidget->addTab(m_vertexEditor, tr("Vertex Shader")); - verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(splitter); splitter->addWidget(m_headerWidget.get()); - splitter->addWidget(tabWidget); + splitter->addWidget(m_tabWidget); splitter->setCollapsible(0, false); splitter->setCollapsible(1, false); - connect(this, &EffectShadersCodeEditor::openedChanged, tabWidget, [this, tabWidget](bool opened) { - if (!opened) - return; - - QWidget *widgetToSelect = (m_vertexEditor->document()->isEmpty() - && !m_fragmentEditor->document()->isEmpty()) - ? m_fragmentEditor.get() - : m_vertexEditor.get(); - tabWidget->setCurrentWidget(widgetToSelect); - widgetToSelect->setFocus(); - }); - this->resize(660, 240); } @@ -290,4 +276,26 @@ void EffectShadersCodeEditor::reloadQml() m_headerWidget->setSource(QUrl::fromLocalFile(headerQmlPath)); } +void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsTableModel *uniformsTable) +{ + if (!uniformsTable) + uniformsTable = m_defaultTableModel; + + m_headerWidget->rootContext() + ->setContextProperty("uniformsTableModel", QVariant::fromValue(uniformsTable)); +} + +void EffectShadersCodeEditor::selectNonEmptyShader(ShaderEditorData *data) +{ + auto vertexDoc = data->vertexDocument->document(); + auto fragmentDoc = data->fragmentDocument->document(); + + QWidget *widgetToSelect = (vertexDoc->isEmpty() && !fragmentDoc->isEmpty()) + ? data->fragmentEditor.get() + : data->vertexEditor.get(); + + m_tabWidget->setCurrentWidget(widgetToSelect); + widgetToSelect->setFocus(); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 9de59010afb..52859ab309d 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -3,19 +3,37 @@ #pragma once +#include "effectcodeeditorwidget.h" + #include #include QT_FORWARD_DECLARE_CLASS(QSettings) +QT_FORWARD_DECLARE_CLASS(QTabWidget) class StudioQuickWidget; namespace EffectComposer { -class EffectCodeEditorWidget; class EffectComposerUniformsModel; class EffectComposerUniformsTableModel; +class EffectDocument; + +struct ShaderEditorData +{ + EffectComposerUniformsTableModel *tableModel = nullptr; + + TextEditor::TextDocumentPtr fragmentDocument; + TextEditor::TextDocumentPtr vertexDocument; + +private: + friend class EffectShadersCodeEditor; + Utils::UniqueObjectLatePtr fragmentEditor; + Utils::UniqueObjectLatePtr vertexEditor; + + ShaderEditorData() = default; +}; class EffectShadersCodeEditor : public QWidget { @@ -29,24 +47,25 @@ public: void showWidget(); void showWidget(int x, int y); - QString fragmentValue() const; - void setFragmentValue(const QString &text); - - QString vertexValue() const; - void setVertexValue(const QString &text); - bool liveUpdate() const; void setLiveUpdate(bool liveUpdate); bool isOpened() const; - void setUniformsModel(EffectComposerUniformsModel *uniforms); + + void setupShader(ShaderEditorData *data); + void cleanFromData(ShaderEditorData *data); + + ShaderEditorData *createEditorData( + const QString &fragmentDocument, + const QString &vertexDocument, + EffectComposerUniformsModel *uniforms); Q_INVOKABLE void copyText(const QString &text); + static EffectShadersCodeEditor *instance(); + signals: void liveUpdateChanged(bool); - void fragmentValueChanged(); - void vertexValueChanged(); void rebakeRequested(); void openedChanged(bool); @@ -63,12 +82,14 @@ private: void readAndApplyLiveUpdateSettings(); void createHeader(); void reloadQml(); + void setUniformsModel(EffectComposerUniformsTableModel *uniforms); + void selectNonEmptyShader(ShaderEditorData *data); QSettings *m_settings = nullptr; - QPointer m_fragmentEditor; - QPointer m_vertexEditor; QPointer m_headerWidget; - Utils::UniqueObjectLatePtr m_uniformsTableModel; + QPointer m_tabWidget; + QPointer m_defaultTableModel; + ShaderEditorData *m_currentEditorData = nullptr; bool m_liveUpdate = false; bool m_opened = false; From 479826158e5f850cec67086686262b910001a668 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 26 Nov 2024 18:35:47 +0200 Subject: [PATCH 195/322] EffectComposer: Add nodes combobox to the code editor Task-number: QDS-14138 Change-Id: I8a12f26d6b453128acfad67de08134c17e83b6fc Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CodeEditorHeader.qml | 23 ++- src/plugins/effectcomposer/CMakeLists.txt | 1 + .../effectcomposereditablenodesmodel.cpp | 160 ++++++++++++++++++ .../effectcomposereditablenodesmodel.h | 53 ++++++ .../effectcomposer/effectcomposermodel.cpp | 16 ++ .../effectcomposer/effectcomposermodel.h | 17 +- .../effectshaderscodeeditor.cpp | 16 +- .../effectcomposer/effectshaderscodeeditor.h | 7 +- 8 files changed, 279 insertions(+), 14 deletions(-) create mode 100644 src/plugins/effectcomposer/effectcomposereditablenodesmodel.cpp create mode 100644 src/plugins/effectcomposer/effectcomposereditablenodesmodel.h diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 065354bdc0e..6ebcf731204 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -17,6 +17,25 @@ Rectangle { anchors.fill: parent RowLayout { + Layout.topMargin: StudioTheme.Values.toolbarVerticalMargin + Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + + StudioControls.TopLevelComboBox { + id: nodesComboBox + style: StudioTheme.Values.toolbarStyle + Layout.preferredWidth: 400 + Layout.alignment: Qt.AlignVCenter + model: editableCompositionsModel + textRole: "display" + Binding on currentIndex { + value: editableCompositionsModel.selectedIndex + } + onActivated: (idx) => { + editableCompositionsModel.openCodeEditor(idx) + } + } + Item { // Spacer Layout.preferredHeight: 1 Layout.fillWidth: true @@ -26,16 +45,16 @@ Rectangle { table: uniformsView.tableView text: qsTr("Columns") style: StudioTheme.Values.viewBarControlStyle - Layout.topMargin: StudioTheme.Values.marginTopBottom + Layout.alignment: Qt.AlignVCenter } StudioControls.CheckBox { text: qsTr("Live Update") actionIndicatorVisible: false style: StudioTheme.Values.viewBarControlStyle - Layout.topMargin: StudioTheme.Values.marginTopBottom checked: root.rootView ? root.rootView.liveUpdate : false onToggled: root.rootView.liveUpdate = checked + Layout.alignment: Qt.AlignVCenter } } diff --git a/src/plugins/effectcomposer/CMakeLists.txt b/src/plugins/effectcomposer/CMakeLists.txt index 6719fcad1dd..eafc4b6d531 100644 --- a/src/plugins/effectcomposer/CMakeLists.txt +++ b/src/plugins/effectcomposer/CMakeLists.txt @@ -6,6 +6,7 @@ add_qtc_plugin(EffectComposer Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick QtCreator::Utils SOURCES + effectcomposereditablenodesmodel.cpp effectcomposereditablenodesmodel.h effectcodeeditorwidget.cpp effectcodeeditorwidget.h effectcomposerplugin.cpp effectcomposerwidget.cpp effectcomposerwidget.h diff --git a/src/plugins/effectcomposer/effectcomposereditablenodesmodel.cpp b/src/plugins/effectcomposer/effectcomposereditablenodesmodel.cpp new file mode 100644 index 00000000000..6f258d6ac8f --- /dev/null +++ b/src/plugins/effectcomposer/effectcomposereditablenodesmodel.cpp @@ -0,0 +1,160 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "effectcomposereditablenodesmodel.h" + +#include "effectcomposermodel.h" + +namespace EffectComposer { + +EffectComposerEditableNodesModel::EffectComposerEditableNodesModel(QObject *parent) + : QAbstractListModel(parent) +{} + +int EffectComposerEditableNodesModel::rowCount(const QModelIndex &) const +{ + return m_data.size(); +} + +QVariant EffectComposerEditableNodesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + if (role == Qt::DisplayRole) + return m_data.at(index.row()).nodeName; + + return {}; +} + +void EffectComposerEditableNodesModel::setSourceModel(EffectComposerModel *sourceModel) +{ + m_sourceModel = sourceModel; + if (sourceModel) { + connect( + sourceModel, + &QAbstractItemModel::modelReset, + this, + &EffectComposerEditableNodesModel::reload); + connect( + sourceModel, + &QAbstractItemModel::rowsInserted, + this, + &EffectComposerEditableNodesModel::reload); + connect( + sourceModel, + &QAbstractItemModel::rowsRemoved, + this, + &EffectComposerEditableNodesModel::reload); + connect( + sourceModel, + &QAbstractItemModel::rowsMoved, + this, + &EffectComposerEditableNodesModel::reload); + connect( + sourceModel, + &QAbstractItemModel::dataChanged, + this, + &EffectComposerEditableNodesModel::onSourceDataChanged); + connect( + sourceModel, + &EffectComposerModel::codeEditorIndexChanged, + this, + &EffectComposerEditableNodesModel::onCodeEditorIndexChanged); + } + reload(); +} + +QModelIndex EffectComposerEditableNodesModel::proxyIndex(int sourceIndex) const +{ + if (!m_sourceModel) + return {}; + + QModelIndex sourceIdx = m_sourceModel->index(sourceIndex, 0, {}); + if (!sourceIdx.isValid()) + return {}; + + return index(m_sourceToItemMap.value(sourceIndex, -1), 0, {}); +} + +void EffectComposerEditableNodesModel::openCodeEditor(int proxyIndex) +{ + if (!m_sourceModel) + return; + + if (proxyIndex < 0 || proxyIndex >= m_data.size()) + return; + + m_sourceModel->openCodeEditor(m_data.at(proxyIndex).sourceId); +} + +void EffectComposerEditableNodesModel::onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) +{ + using Roles = EffectComposerModel::Roles; + + if (!m_sourceModel) + return; + + const int startItem = topLeft.row(); + const int endItem = bottomRight.row(); + + if (roles.contains(Roles::Dependency)) { + reload(); + return; + } + + if (!roles.contains(Roles::NameRole)) + return; + + for (int i = startItem; i < endItem; ++i) { + QModelIndex sourceIdx = m_sourceModel->index(i); + QModelIndex proxyIdx = proxyIndex(i); + if (proxyIdx.isValid()) { + m_data[proxyIdx.row()].nodeName = sourceIdx.data(Roles::NameRole).toString(); + emit dataChanged(proxyIdx, proxyIdx, {Qt::DisplayRole}); + } + } +} + +void EffectComposerEditableNodesModel::onCodeEditorIndexChanged(int sourceIndex) +{ + int newSelectedIndex = m_sourceToItemMap.value(sourceIndex, -1); + if (m_selectedIndex != newSelectedIndex) { + m_selectedIndex = newSelectedIndex; + emit selectedIndexChanged(m_selectedIndex); + } +} + +void EffectComposerEditableNodesModel::reload() +{ + using Roles = EffectComposerModel::Roles; + beginResetModel(); + m_data.clear(); + m_sourceToItemMap.clear(); + + if (!m_sourceModel) { + endResetModel(); + return; + } + + const int mainIdx = m_sourceModel->mainCodeEditorIndex(); + + m_data.append(Item{tr("Main"), mainIdx}); + m_sourceToItemMap.insert(mainIdx, 0); + const int sourceSize = m_sourceModel->rowCount(); + for (int i = 0; i < sourceSize; ++i) { + QModelIndex idx = m_sourceModel->index(i, 0, {}); + bool isDependency = idx.data(Roles::Dependency).toBool(); + if (!isDependency) { + Item item; + item.nodeName = idx.data(Roles::NameRole).toString(); + item.sourceId = i; + m_data.append(item); + m_sourceToItemMap.insert(i, m_sourceToItemMap.size()); + } + } + endResetModel(); +} + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposereditablenodesmodel.h b/src/plugins/effectcomposer/effectcomposereditablenodesmodel.h new file mode 100644 index 00000000000..c5904cd5f4f --- /dev/null +++ b/src/plugins/effectcomposer/effectcomposereditablenodesmodel.h @@ -0,0 +1,53 @@ +// Copyright (C) 2024 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 EffectComposer { +class EffectComposerModel; + +class EffectComposerEditableNodesModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + +public: + explicit EffectComposerEditableNodesModel(QObject *parent = nullptr); + + struct Item + { + QString nodeName; + int sourceId; + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role) const override; + + void setSourceModel(EffectComposerModel *sourceModel); + QModelIndex proxyIndex(int sourceIndex) const; + + Q_INVOKABLE void openCodeEditor(int proxyIndex); + +signals: + void selectedIndexChanged(int); + +private slots: + void onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles); + void onCodeEditorIndexChanged(int sourceIndex); + void reload(); + +private: + QPointer m_sourceModel; + QList m_data; + QMap m_sourceToItemMap; + int m_selectedIndex = -1; +}; + +} // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 38a695046df..eab8b3ea5f1 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -163,6 +163,8 @@ void EffectComposerModel::moveNode(int fromIdx, int toIdx) if (fromIdx == toIdx) return; + int oldCodeEditorIdx = m_codeEditorIndex; + int toIdxAdjusted = fromIdx < toIdx ? toIdx + 1 : toIdx; // otherwise beginMoveRows() crashes beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted); m_nodes.move(fromIdx, toIdx); @@ -170,6 +172,18 @@ void EffectComposerModel::moveNode(int fromIdx, int toIdx) setHasUnsavedChanges(true); bakeShaders(); + + // Adjust codeEditorIndex after move + if (oldCodeEditorIdx > -1) { + int newCodeEditorIndex = oldCodeEditorIdx; + if (oldCodeEditorIdx == fromIdx) + newCodeEditorIndex = toIdx; + if (fromIdx < oldCodeEditorIdx && toIdx >= oldCodeEditorIdx) + --newCodeEditorIndex; + if (fromIdx > oldCodeEditorIdx && toIdx <= oldCodeEditorIdx) + ++newCodeEditorIndex; + setCodeEditorIndex(newCodeEditorIndex); + } } void EffectComposerModel::removeNode(int idx) @@ -1147,6 +1161,8 @@ R"( void EffectComposerModel::connectCodeEditor() { EffectShadersCodeEditor *editor = EffectShadersCodeEditor::instance(); + editor->setCompositionsModel(this); + connect(this, &QObject::destroyed, editor, &QObject::deleteLater); connect( diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 89b3939a470..5f8bf66a075 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -155,6 +155,14 @@ public: bool hasCustomNode() const; + enum Roles { + NameRole = Qt::UserRole + 1, + EnabledRole, + UniformsRole, + Dependency, + Custom, + }; + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -175,14 +183,6 @@ signals: void customPreviewImageCountChanged(); private: - enum Roles { - NameRole = Qt::UserRole + 1, - EnabledRole, - UniformsRole, - Dependency, - Custom - }; - enum ErrorTypes { ErrorCommon, ErrorQMLParsing, @@ -296,4 +296,3 @@ private: }; } // namespace EffectComposer - diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 08b9824b145..f18cd15c3c7 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -3,6 +3,8 @@ #include "effectshaderscodeeditor.h" +#include "effectcomposereditablenodesmodel.h" +#include "effectcomposermodel.h" #include "effectcomposeruniformsmodel.h" #include "effectcomposeruniformstablemodel.h" #include "effectcomposerwidget.h" @@ -52,6 +54,7 @@ EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget * : QWidget(parent) , m_settings(new QSettings(qApp->organizationName(), qApp->applicationName(), this)) , m_defaultTableModel(new EffectComposerUniformsTableModel(nullptr, this)) + , m_editableNodesModel(new EffectComposerEditableNodesModel(this)) { setWindowFlag(Qt::Tool, true); setWindowFlag(Qt::WindowStaysOnTopHint); @@ -59,18 +62,20 @@ EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget * setupUIComponents(); setUniformsModel(nullptr); + loadQml(); } EffectShadersCodeEditor::~EffectShadersCodeEditor() { if (isOpened()) close(); + + m_headerWidget->setSource({}); } void EffectShadersCodeEditor::showWidget() { readAndApplyLiveUpdateSettings(); - reloadQml(); show(); raise(); setOpened(true); @@ -106,6 +111,11 @@ bool EffectShadersCodeEditor::isOpened() const return m_opened; } +void EffectShadersCodeEditor::setCompositionsModel(EffectComposerModel *compositionsModel) +{ + m_editableNodesModel->setSourceModel(compositionsModel); +} + void EffectShadersCodeEditor::setupShader(ShaderEditorData *data) { if (m_currentEditorData == data) @@ -267,9 +277,11 @@ void EffectShadersCodeEditor::createHeader() m_headerWidget->setClearColor(QmlDesigner::Theme::getColor( QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); m_headerWidget->rootContext()->setContextProperty("shaderEditor", QVariant::fromValue(this)); + m_headerWidget->rootContext()->setContextProperty( + "editableCompositionsModel", QVariant::fromValue(m_editableNodesModel.get())); } -void EffectShadersCodeEditor::reloadQml() +void EffectShadersCodeEditor::loadQml() { const QString headerQmlPath = EffectComposerWidget::qmlSourcesPath() + "/CodeEditorHeader.qml"; QTC_ASSERT(QFileInfo::exists(headerQmlPath), return); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 52859ab309d..af7f1d472b7 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -16,8 +16,10 @@ class StudioQuickWidget; namespace EffectComposer { +class EffectComposerModel; class EffectComposerUniformsModel; class EffectComposerUniformsTableModel; +class EffectComposerEditableNodesModel; class EffectDocument; struct ShaderEditorData @@ -52,6 +54,8 @@ public: bool isOpened() const; + void setCompositionsModel(EffectComposerModel *compositionsModel); + void setupShader(ShaderEditorData *data); void cleanFromData(ShaderEditorData *data); @@ -81,7 +85,7 @@ private: void writeLiveUpdateSettings(); void readAndApplyLiveUpdateSettings(); void createHeader(); - void reloadQml(); + void loadQml(); void setUniformsModel(EffectComposerUniformsTableModel *uniforms); void selectNonEmptyShader(ShaderEditorData *data); @@ -89,6 +93,7 @@ private: QPointer m_headerWidget; QPointer m_tabWidget; QPointer m_defaultTableModel; + QPointer m_editableNodesModel; ShaderEditorData *m_currentEditorData = nullptr; bool m_liveUpdate = false; From 7ade402c98e7f3f626cef6511e9d13cf81e44784 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Nov 2024 11:51:11 +0100 Subject: [PATCH 196/322] QmlDesigner: Fix deprication warnings I looked at the implementation of the deprecated constructor and this seems to what it is doing. Change-Id: I98ca6791317763a56668f1fd3138f573667666ab Reviewed-by: Miikka Heikkinen --- .../qml2puppet/qml2puppet/editor3d/mousearea3d.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp index 51dc6416114..de6737c39db 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/mousearea3d.cpp @@ -775,7 +775,8 @@ void MouseArea3D::forcePressEvent(double x, double y) Qt::MouseButtons buttons; Qt::KeyboardModifiers mods; - QMouseEvent event(QEvent::MouseButtonPress, QPointF(x, y), Qt::LeftButton, buttons, mods); + auto pos = QPointF(x, y); + QMouseEvent event(QEvent::MouseButtonPress, pos, pos, Qt::LeftButton, buttons, mods); eventFilter(m_view3D, &event); } @@ -783,7 +784,8 @@ void QmlDesigner::Internal::MouseArea3D::forceMoveEvent(double x, double y) { Qt::MouseButtons buttons; Qt::KeyboardModifiers mods; - QMouseEvent event(QEvent::MouseMove, QPointF(x, y), Qt::LeftButton, buttons, mods); + auto pos = QPointF(x, y); + QMouseEvent event(QEvent::MouseMove, pos, pos, Qt::LeftButton, buttons, mods); eventFilter(m_view3D, &event); } @@ -791,7 +793,8 @@ void QmlDesigner::Internal::MouseArea3D::forceReleaseEvent(double x, double y) { Qt::MouseButtons buttons; Qt::KeyboardModifiers mods; - QMouseEvent event(QEvent::MouseButtonRelease, QPointF(x, y), Qt::LeftButton, buttons, mods); + auto pos = QPointF(x, y); + QMouseEvent event(QEvent::MouseButtonRelease, pos, pos, Qt::LeftButton, buttons, mods); eventFilter(m_view3D, &event); } @@ -835,9 +838,9 @@ static QPoint getPosFromMoveEvent(QEvent *event) { switch (event->type()) { case QEvent::MouseMove: - return static_cast(event)->pos(); + return static_cast(event)->position().toPoint(); case QEvent::HoverMove: - return static_cast(event)->pos(); + return static_cast(event)->position().toPoint(); default: break; } From 45d0753f0ba473c1d76848f31b697866fce33a13 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Nov 2024 11:51:01 +0100 Subject: [PATCH 197/322] QmlDesigner: Fix deprication warnings Change-Id: I98ca6791317763a5a148f1fd3138f573667666ab Reviewed-by: Miikka Heikkinen --- .../qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 19c50cfba0a..59036474709 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -759,7 +759,9 @@ void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, QVariant theState; // Convert JS arrays to QVariantLists for easier handling down the line // metaType().id() which only exist in Qt6 is the same as typeId() - if (state.typeId() != QMetaType::QString && state.canConvert(QMetaType::QVariantList)) + + if (state.typeId() != QMetaType::QString + && QMetaType::canConvert(state.metaType(), QMetaType(QMetaType::QVariantList))) theState = state.value(); else theState = state; @@ -1038,7 +1040,7 @@ void GeneralHelper::moveMultiSelection(bool commit) QMatrix4x4 m; if (it.key()->parentNode()) m = it.key()->parentNode()->sceneTransform(); - it.key()->setPosition(m.inverted() * newGlobalPos); + it.key()->setPosition(m.inverted().map(newGlobalPos)); } m_blockMultiSelectionNodePositioning = !commit; } @@ -1058,7 +1060,7 @@ void GeneralHelper::scaleMultiSelection(bool commit) QMatrix4x4 parentMat; if (it.key()->parentNode()) parentMat = it.key()->parentNode()->sceneTransform().inverted(); - it.key()->setPosition(parentMat * newGlobalPos); + it.key()->setPosition(parentMat.map(newGlobalPos)); QMatrix4x4 mat; mat.rotate(it.value().startSceneRot); @@ -1067,7 +1069,7 @@ void GeneralHelper::scaleMultiSelection(bool commit) QVector3D dimScale; float diffScaleDim = diffScale[dim]; dimScale[dim] = diffScaleDim; - dimScale = (mat.inverted() * dimScale).normalized() * diffScaleDim; + dimScale = (mat.inverted().map(dimScale).normalized()) * diffScaleDim; for (int i = 0; i < 3; ++i) dimScale[i] = qAbs(dimScale[i]); if (sceneScale[dim] < 1.0f) @@ -1096,7 +1098,7 @@ void GeneralHelper::rotateMultiSelection(bool commit) QMatrix4x4 parentMat; if (it.key()->parentNode()) parentMat = it.key()->parentNode()->sceneTransform().inverted(); - it.key()->setPosition(parentMat * newGlobalPos); + it.key()->setPosition(parentMat.map(newGlobalPos)); it.key()->setRotation(it.value().startRot); it.key()->rotate(rotAngle, rotAxis, QQuick3DNode::SceneSpace); } From f3d4f185905364802426b5a0c0e4a27907972919 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Nov 2024 18:02:03 +0100 Subject: [PATCH 198/322] QmlDesigner: Add extra call to setupContextProperties() This is required because we rely on the anchorBackend to change, when the selection is changed, because this is used in the gradient model. We need to cleanup how the selected model node is propergated through QML. Change-Id: I6bb59a1606c87203a166f4e0cd410560b4ecc666 Reviewed-by: Thomas Hartmann --- .../components/propertyeditor/propertyeditorqmlbackend.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index aa380883d59..b3b96412c86 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -523,6 +523,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // anchors m_backendAnchorBinding.setup(qmlObjectNode.modelNode()); + setupContextProperties(); contextObject()->setHasMultiSelection( !qmlObjectNode.view()->singleSelectedModelNode().isValid()); From 76a99f5a663b968c7b5039a358922bd975e2150a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 27 Nov 2024 16:30:22 +0200 Subject: [PATCH 199/322] EffectComposer: Add a button for inserting uniform id to code editor Task-number: QDS-14140 Change-Id: Ic4505a25ed46aee8e58fc1a9fd1af6e47c5172d2 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../CodeEditorHeader.qml | 6 +-- .../CodeEditorUniformsView.qml | 40 ++++++++++++++----- .../effectshaderscodeeditor.cpp | 24 +++++++++++ .../effectcomposer/effectshaderscodeeditor.h | 3 ++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 6ebcf731204..9a50c761299 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -9,7 +9,7 @@ import StudioTheme as StudioTheme Rectangle { id: root - property var rootView: shaderEditor + property var rootEditor: shaderEditor color: StudioTheme.Values.themeToolbarBackground @@ -52,8 +52,8 @@ Rectangle { text: qsTr("Live Update") actionIndicatorVisible: false style: StudioTheme.Values.viewBarControlStyle - checked: root.rootView ? root.rootView.liveUpdate : false - onToggled: root.rootView.liveUpdate = checked + checked: root.rootEditor ? root.rootEditor.liveUpdate : false + onToggled: root.rootEditor.liveUpdate = checked Layout.alignment: Qt.AlignVCenter } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index f7319a3cd8c..decb076094a 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -99,24 +99,28 @@ ColumnLayout { sourceComponent: MouseArea { id: hoverArea - width: 15 + width: buttonsRow.implicitWidth hoverEnabled: true enabled: true Row { + id: buttonsRow + anchors.fill: parent + spacing: StudioTheme.Values.controlGap visible: hoverArea.containsMouse - StudioControls.AbstractButton { - width: iconSize - height: iconSize - anchors.verticalCenter: parent.verticalCenter - buttonIcon: StudioTheme.Constants.copy_small - backgroundVisible: false - onClicked: rootView.copyText(dataScope.display) + CellButton { + buttonIcon: StudioTheme.Constants.assignTo_medium + onClicked: rootEditor.insertTextToCursorPosition(dataScope.display) + tooltip: qsTr("Insert into the editor cursor position.") } - // ToDo: Add a button for placing the value to the editor + CellButton { + buttonIcon: StudioTheme.Constants.copy_small + onClicked: rootEditor.copyText(dataScope.display) + tooltip: qsTr("Copy uniform name to clipboard.") + } } } } @@ -182,4 +186,22 @@ ColumnLayout { color: StudioTheme.Values.themeStateSeparator } } + + component CellButton: StudioControls.AbstractButton { + id: cellBtn + + property alias tooltip: cellBtnTooltip.text + + width: iconSize + height: iconSize + anchors.verticalCenter: parent.verticalCenter + buttonIcon: StudioTheme.Constants.assignTo_medium + backgroundVisible: false + + StudioControls.ToolTip { + id: cellBtnTooltip + + visible: cellBtn.hovered && text !== "" + } + } } diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index f18cd15c3c7..a229d9e6d25 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -184,6 +184,16 @@ void EffectShadersCodeEditor::copyText(const QString &text) qApp->clipboard()->setText(text); } +void EffectShadersCodeEditor::insertTextToCursorPosition(const QString &text) +{ + auto editor = currentEditor(); + if (!editor) + return; + + editor->textCursor().insertText(text); + editor->setFocus(); +} + EffectShadersCodeEditor *EffectShadersCodeEditor::instance() { static EffectShadersCodeEditor *editorInstance = new EffectShadersCodeEditor( @@ -310,4 +320,18 @@ void EffectShadersCodeEditor::selectNonEmptyShader(ShaderEditorData *data) widgetToSelect->setFocus(); } +EffectCodeEditorWidget *EffectShadersCodeEditor::currentEditor() const +{ + QWidget *currentTab = m_tabWidget->currentWidget(); + if (!m_currentEditorData || !currentTab) + return nullptr; + + if (currentTab == m_currentEditorData->fragmentEditor.get()) + return m_currentEditorData->fragmentEditor.get(); + if (currentTab == m_currentEditorData->vertexEditor.get()) + return m_currentEditorData->vertexEditor.get(); + + return nullptr; +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index af7f1d472b7..218ab58046b 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -65,6 +65,7 @@ public: EffectComposerUniformsModel *uniforms); Q_INVOKABLE void copyText(const QString &text); + Q_INVOKABLE void insertTextToCursorPosition(const QString &text); static EffectShadersCodeEditor *instance(); @@ -89,6 +90,8 @@ private: void setUniformsModel(EffectComposerUniformsTableModel *uniforms); void selectNonEmptyShader(ShaderEditorData *data); + EffectCodeEditorWidget *currentEditor() const; + QSettings *m_settings = nullptr; QPointer m_headerWidget; QPointer m_tabWidget; From 3bacff48ea070c617c515c22de03f7d57a991bb6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 27 Nov 2024 19:55:36 +0200 Subject: [PATCH 200/322] EffectComposer: Move code editor tabs to Qml side Task-number: QDS-14141 Change-Id: I8edb70f7773723e4f87cbed7ed0e766362917a7d Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../CodeEditorTabs.qml | 104 ++++++++++++++++++ .../effectshaderscodeeditor.cpp | 98 +++++++++++++++-- .../effectcomposer/effectshaderscodeeditor.h | 18 ++- 3 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml new file mode 100644 index 00000000000..897023a1715 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml @@ -0,0 +1,104 @@ +// Copyright (C) 2024 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 StudioTheme as StudioTheme + +Rectangle { + id: root + + property var rootEditor: shaderEditor + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + color: StudioTheme.Values.themeToolbarBackground + height: rowLayout.height + + RowLayout { + id: rowLayout + + width: parent.width + anchors.verticalCenter: parent.verticalCenter + spacing: StudioTheme.Values.controlGap + + TabButton { + text: qsTr("Fragment Shader") + tabId: "FRAGMENT" + } + + TabButton { + text: qsTr("Vertex Shader") + tabId: "VERTEX" + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + } + + component TabButton: Label { + id: tabButton + + required property string tabId + readonly property bool selected: rootEditor.selectedShader === tabId + + Layout.preferredHeight: 40 + Layout.preferredWidth: 120 + + font.pixelSize: StudioTheme.Values.mediumFont + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + padding: 10 + + color: { + if (!tabButton.enabled) + return root.style.text.disabled + + if (tabButton.selected) + return root.style.text.selectedText + + return root.style.text.idle + } + + background: Rectangle { + color: { + if (!tabButton.enabled) + return "transparent" + + if (tabItemMouseArea.containsMouse && tabButton.selected) + return root.style.interactionHover + + if (tabButton.selected) + return root.style.interaction + + if (tabItemMouseArea.containsMouse) + return root.style.background.hover + + return root.style.background.idle + } + + border.width: 1 + border.color: { + if (!tabButton.enabled) + return "transparent" + + if (tabButton.selected) + return root.style.border.interaction + + if (tabItemMouseArea.containsMouse) + return root.style.border.hover + + return root.style.border.idle + } + } + + MouseArea { + id: tabItemMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: rootEditor.selectedShader = tabButton.tabId + } + } +} diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index a229d9e6d25..b38c495a9a0 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include namespace { @@ -36,6 +36,11 @@ namespace { inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate"; inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER[] = "QQuickWidgetEffectComposerCodeEditorHeader"; +inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_TABS[] + = "QQuickWidgetEffectComposerCodeEditorTabs"; + +inline constexpr char EFFECTCOMPOSER_VERTEX_ID[] = "VERTEX"; +inline constexpr char EFFECTCOMPOSER_FRAGMENT_ID[] = "FRAGMENT"; QString propertyEditorResourcesPath() { @@ -71,6 +76,7 @@ EffectShadersCodeEditor::~EffectShadersCodeEditor() close(); m_headerWidget->setSource({}); + m_qmlTabWidget->setSource({}); } void EffectShadersCodeEditor::showWidget() @@ -121,12 +127,12 @@ void EffectShadersCodeEditor::setupShader(ShaderEditorData *data) if (m_currentEditorData == data) return; - while (m_tabWidget->count()) - m_tabWidget->removeTab(0); + auto oldEditorData = m_currentEditorData; + m_currentEditorData = data; if (data) { - m_tabWidget->addTab(data->fragmentEditor.get(), tr("Fragment Shader")); - m_tabWidget->addTab(data->vertexEditor.get(), tr("Vertex Shader")); + m_stackedWidget->addWidget(data->fragmentEditor.get()); + m_stackedWidget->addWidget(data->vertexEditor.get()); selectNonEmptyShader(data); setUniformsModel(data->tableModel); @@ -134,7 +140,10 @@ void EffectShadersCodeEditor::setupShader(ShaderEditorData *data) setUniformsModel(nullptr); } - m_currentEditorData = data; + if (oldEditorData) { + m_stackedWidget->removeWidget(oldEditorData->fragmentEditor.get()); + m_stackedWidget->removeWidget(oldEditorData->vertexEditor.get()); + } } void EffectShadersCodeEditor::cleanFromData(ShaderEditorData *data) @@ -143,6 +152,20 @@ void EffectShadersCodeEditor::cleanFromData(ShaderEditorData *data) setupShader(nullptr); } +void EffectShadersCodeEditor::selectShader(const QString &shaderName) +{ + using namespace Qt::StringLiterals; + if (!m_currentEditorData) + return; + EffectCodeEditorWidget *editor = nullptr; + if (shaderName == EFFECTCOMPOSER_FRAGMENT_ID) + editor = m_currentEditorData->fragmentEditor.get(); + else if (shaderName == EFFECTCOMPOSER_VERTEX_ID) + editor = m_currentEditorData->vertexEditor.get(); + + m_stackedWidget->setCurrentWidget(editor); +} + ShaderEditorData *EffectShadersCodeEditor::createEditorData( const QString &fragmentDocument, const QString &vertexDocument, @@ -229,20 +252,32 @@ void EffectShadersCodeEditor::setupUIComponents() { QVBoxLayout *verticalLayout = new QVBoxLayout(this); QSplitter *splitter = new QSplitter(this); - m_tabWidget = new QTabWidget(this); + QWidget *tabComplexWidget = new QWidget(this); + QVBoxLayout *tabsLayout = new QVBoxLayout(tabComplexWidget); + m_stackedWidget = new QStackedWidget(tabComplexWidget); splitter->setOrientation(Qt::Vertical); createHeader(); + createQmlTabs(); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(splitter); + tabsLayout->addWidget(m_qmlTabWidget); + tabsLayout->addWidget(m_stackedWidget); + splitter->addWidget(m_headerWidget.get()); - splitter->addWidget(m_tabWidget); + splitter->addWidget(tabComplexWidget); splitter->setCollapsible(0, false); splitter->setCollapsible(1, false); + connect( + m_stackedWidget.get(), + &QStackedWidget::currentChanged, + this, + &EffectShadersCodeEditor::onEditorWidgetChanged); + this->resize(660, 240); } @@ -291,11 +326,30 @@ void EffectShadersCodeEditor::createHeader() "editableCompositionsModel", QVariant::fromValue(m_editableNodesModel.get())); } +void EffectShadersCodeEditor::createQmlTabs() +{ + m_qmlTabWidget = new StudioQuickWidget(this); + m_qmlTabWidget->quickWidget()->setObjectName(OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_TABS); + m_qmlTabWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + QmlDesigner::Theme::setupTheme(m_qmlTabWidget->engine()); + m_qmlTabWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_qmlTabWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common"); + m_qmlTabWidget->setClearColor(QmlDesigner::Theme::getColor( + QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_qmlTabWidget->rootContext()->setContextProperty("shaderEditor", QVariant::fromValue(this)); + m_qmlTabWidget->setFixedHeight(43); +} + void EffectShadersCodeEditor::loadQml() { const QString headerQmlPath = EffectComposerWidget::qmlSourcesPath() + "/CodeEditorHeader.qml"; QTC_ASSERT(QFileInfo::exists(headerQmlPath), return); m_headerWidget->setSource(QUrl::fromLocalFile(headerQmlPath)); + + const QString editorTabsQmlPath = EffectComposerWidget::qmlSourcesPath() + + "/CodeEditorTabs.qml"; + QTC_ASSERT(QFileInfo::exists(editorTabsQmlPath), return); + m_qmlTabWidget->setSource(QUrl::fromLocalFile(editorTabsQmlPath)); } void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsTableModel *uniformsTable) @@ -316,13 +370,37 @@ void EffectShadersCodeEditor::selectNonEmptyShader(ShaderEditorData *data) ? data->fragmentEditor.get() : data->vertexEditor.get(); - m_tabWidget->setCurrentWidget(widgetToSelect); + m_stackedWidget->setCurrentWidget(widgetToSelect); widgetToSelect->setFocus(); } +void EffectShadersCodeEditor::setSelectedShaderName(const QString &shaderName) +{ + if (m_selectedShaderName == shaderName) + return; + m_selectedShaderName = shaderName; + emit selectedShaderChanged(m_selectedShaderName); +} + +void EffectShadersCodeEditor::onEditorWidgetChanged() +{ + QWidget *currentWidget = m_stackedWidget->currentWidget(); + if (!m_currentEditorData || !currentWidget) { + setSelectedShaderName({}); + return; + } + + if (currentWidget == m_currentEditorData->fragmentEditor.get()) + setSelectedShaderName(EFFECTCOMPOSER_FRAGMENT_ID); + else if (currentWidget == m_currentEditorData->vertexEditor.get()) + setSelectedShaderName(EFFECTCOMPOSER_VERTEX_ID); + else + setSelectedShaderName({}); +} + EffectCodeEditorWidget *EffectShadersCodeEditor::currentEditor() const { - QWidget *currentTab = m_tabWidget->currentWidget(); + QWidget *currentTab = m_stackedWidget->currentWidget(); if (!m_currentEditorData || !currentTab) return nullptr; diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index 218ab58046b..afb63bf77dd 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -10,7 +10,7 @@ #include QT_FORWARD_DECLARE_CLASS(QSettings) -QT_FORWARD_DECLARE_CLASS(QTabWidget) +QT_FORWARD_DECLARE_CLASS(QStackedWidget) class StudioQuickWidget; @@ -42,6 +42,12 @@ class EffectShadersCodeEditor : public QWidget Q_OBJECT Q_PROPERTY(bool liveUpdate READ liveUpdate WRITE setLiveUpdate NOTIFY liveUpdateChanged) + Q_PROPERTY( + QString selectedShader + MEMBER m_selectedShaderName + WRITE selectShader + NOTIFY selectedShaderChanged) + public: EffectShadersCodeEditor(const QString &title = tr("Untitled Editor"), QWidget *parent = nullptr); ~EffectShadersCodeEditor() override; @@ -59,6 +65,8 @@ public: void setupShader(ShaderEditorData *data); void cleanFromData(ShaderEditorData *data); + void selectShader(const QString &shaderName); + ShaderEditorData *createEditorData( const QString &fragmentDocument, const QString &vertexDocument, @@ -73,6 +81,7 @@ signals: void liveUpdateChanged(bool); void rebakeRequested(); void openedChanged(bool); + void selectedShaderChanged(const QString &); protected: using QWidget::show; @@ -86,21 +95,26 @@ private: void writeLiveUpdateSettings(); void readAndApplyLiveUpdateSettings(); void createHeader(); + void createQmlTabs(); void loadQml(); void setUniformsModel(EffectComposerUniformsTableModel *uniforms); void selectNonEmptyShader(ShaderEditorData *data); + void setSelectedShaderName(const QString &shaderName); + void onEditorWidgetChanged(); EffectCodeEditorWidget *currentEditor() const; QSettings *m_settings = nullptr; QPointer m_headerWidget; - QPointer m_tabWidget; + QPointer m_qmlTabWidget; + QPointer m_stackedWidget; QPointer m_defaultTableModel; QPointer m_editableNodesModel; ShaderEditorData *m_currentEditorData = nullptr; bool m_liveUpdate = false; bool m_opened = false; + QString m_selectedShaderName; }; } // namespace EffectComposer From 5f3a1d94758b938676dee9a6ceae4afa867ffb7e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 28 Nov 2024 11:35:01 +0200 Subject: [PATCH 201/322] EffectComposer: Add a footer to the code editor The footer contains: * Live update button * Close button * Apply button Fixes: QDS-14233 Change-Id: I25070356d5357141be5701ee701d938376bfc141 Reviewed-by: Miikka Heikkinen Reviewed-by: Shrief Gabr Reviewed-by: Mahmoud Badri --- .../CodeEditorFooter.qml | 59 +++++++++++++++++++ .../CodeEditorHeader.qml | 9 --- .../effectshaderscodeeditor.cpp | 25 ++++++++ .../effectcomposer/effectshaderscodeeditor.h | 2 + 4 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml new file mode 100644 index 00000000000..296af17a065 --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml @@ -0,0 +1,59 @@ +// Copyright (C) 2024 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 StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property var rootEditor: shaderEditor + + color: StudioTheme.Values.themeToolbarBackground + implicitHeight: rowLayout.height + + RowLayout { + id: rowLayout + + width: parent.width + anchors.verticalCenter: parent.verticalCenter + + spacing: StudioTheme.Values.controlGap + + StudioControls.CheckBox { + text: qsTr("Live Update") + actionIndicatorVisible: false + style: StudioTheme.Values.viewBarControlStyle + checked: root.rootEditor ? root.rootEditor.liveUpdate : false + onToggled: root.rootEditor.liveUpdate = checked + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin + } + + Item { // Spacer + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + + FooterButton { + buttonIcon: qsTr("Close") + onClicked: root.rootEditor.close() + } + + FooterButton { + buttonIcon: qsTr("Apply") + onClicked: root.rootEditor.rebakeRequested() + Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin + } + } + + component FooterButton: StudioControls.AbstractButton { + iconFontFamily: StudioTheme.Constants.font.family + style: StudioTheme.Values.viewBarControlStyle + checkable: false + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 100 + } +} diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 9a50c761299..508c174efb4 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -47,15 +47,6 @@ Rectangle { style: StudioTheme.Values.viewBarControlStyle Layout.alignment: Qt.AlignVCenter } - - StudioControls.CheckBox { - text: qsTr("Live Update") - actionIndicatorVisible: false - style: StudioTheme.Values.viewBarControlStyle - checked: root.rootEditor ? root.rootEditor.liveUpdate : false - onToggled: root.rootEditor.liveUpdate = checked - Layout.alignment: Qt.AlignVCenter - } } CodeEditorUniformsView { diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index b38c495a9a0..eff223a4ee9 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -38,6 +38,8 @@ inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER[] = "QQuickWidgetEffectComposerCodeEditorHeader"; inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_TABS[] = "QQuickWidgetEffectComposerCodeEditorTabs"; +inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_FOOTER[] + = "QQuickWidgetEffectComposerCodeEditorFooter"; inline constexpr char EFFECTCOMPOSER_VERTEX_ID[] = "VERTEX"; inline constexpr char EFFECTCOMPOSER_FRAGMENT_ID[] = "FRAGMENT"; @@ -77,6 +79,7 @@ EffectShadersCodeEditor::~EffectShadersCodeEditor() m_headerWidget->setSource({}); m_qmlTabWidget->setSource({}); + m_qmlFooterWidget->setSource({}); } void EffectShadersCodeEditor::showWidget() @@ -260,11 +263,15 @@ void EffectShadersCodeEditor::setupUIComponents() createHeader(); createQmlTabs(); + createQmlFooter(); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(splitter); + tabsLayout->setContentsMargins(0, 0, 0, 0); + tabsLayout->setSpacing(0); tabsLayout->addWidget(m_qmlTabWidget); tabsLayout->addWidget(m_stackedWidget); + tabsLayout->addWidget(m_qmlFooterWidget); splitter->addWidget(m_headerWidget.get()); splitter->addWidget(tabComplexWidget); @@ -340,6 +347,20 @@ void EffectShadersCodeEditor::createQmlTabs() m_qmlTabWidget->setFixedHeight(43); } +void EffectShadersCodeEditor::createQmlFooter() +{ + m_qmlFooterWidget = new StudioQuickWidget(this); + m_qmlFooterWidget->quickWidget()->setObjectName(OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_FOOTER); + m_qmlFooterWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + QmlDesigner::Theme::setupTheme(m_qmlFooterWidget->engine()); + m_qmlFooterWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_qmlFooterWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common"); + m_qmlFooterWidget->setClearColor(QmlDesigner::Theme::getColor( + QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_qmlFooterWidget->rootContext()->setContextProperty("shaderEditor", QVariant::fromValue(this)); + m_qmlFooterWidget->setFixedHeight(40); +} + void EffectShadersCodeEditor::loadQml() { const QString headerQmlPath = EffectComposerWidget::qmlSourcesPath() + "/CodeEditorHeader.qml"; @@ -350,6 +371,10 @@ void EffectShadersCodeEditor::loadQml() + "/CodeEditorTabs.qml"; QTC_ASSERT(QFileInfo::exists(editorTabsQmlPath), return); m_qmlTabWidget->setSource(QUrl::fromLocalFile(editorTabsQmlPath)); + + const QString footerQmlPath = EffectComposerWidget::qmlSourcesPath() + "/CodeEditorFooter.qml"; + QTC_ASSERT(QFileInfo::exists(footerQmlPath), return); + m_qmlFooterWidget->setSource(QUrl::fromLocalFile(footerQmlPath)); } void EffectShadersCodeEditor::setUniformsModel(EffectComposerUniformsTableModel *uniformsTable) diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index afb63bf77dd..cc5fea86d2b 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -96,6 +96,7 @@ private: void readAndApplyLiveUpdateSettings(); void createHeader(); void createQmlTabs(); + void createQmlFooter(); void loadQml(); void setUniformsModel(EffectComposerUniformsTableModel *uniforms); void selectNonEmptyShader(ShaderEditorData *data); @@ -107,6 +108,7 @@ private: QSettings *m_settings = nullptr; QPointer m_headerWidget; QPointer m_qmlTabWidget; + QPointer m_qmlFooterWidget; QPointer m_stackedWidget; QPointer m_defaultTableModel; QPointer m_editableNodesModel; From edb055fa78eb033a42387a5a88a048eea926b271 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 28 Nov 2024 15:04:36 +0200 Subject: [PATCH 202/322] EffectComposer: Highlight the editing section in the view The editing composition node which is being edited by the code editor is highlighted in the compositions view. This makes the editing section more clear. Task-number: QDS-14235 Change-Id: Ia8eb1ee9e9f524221b1faf1dab1be288f7c16b7c Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNode.qml | 217 ++++++++++-------- 1 file changed, 116 insertions(+), 101 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index bc29a2f0d9c..4a0cf78609b 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -93,81 +93,6 @@ HelperWidgets.Section { } } - Column { - spacing: 10 - - Repeater { - id: repeater - model: nodeUniformsModel - - EffectCompositionNodeUniform { - id: effectCompositionNodeUniform - width: root.width - StudioTheme.Values.scrollBarThicknessHover - editing: root.editedUniformIndex === index - disableMoreMenu: root.editedUniformIndex >= 0 - isDependencyNode: isDependency - - onReset: nodeUniformsModel.resetData(index) - onRemove: { - if (uniformIsInUse) { - confirmRemoveForm.parent = effectCompositionNodeUniform.editPropertyFormParent - confirmRemoveForm.uniformIndex = index - confirmRemoveForm.visible = true - } else { - nodeUniformsModel.remove(index) - } - } - onEdit: { - confirmRemoveForm.visible = false - confirmRemoveForm.parent = root - addPropertyForm.parent = effectCompositionNodeUniform.editPropertyFormParent - let dispNames = nodeUniformsModel.displayNames() - let filteredDispNames = dispNames.filter(name => name !== uniformDisplayName); - addPropertyForm.reservedDispNames = filteredDispNames - let uniNames = root.backendModel.uniformNames() - let filteredUniNames = uniNames.filter(name => name !== uniformName); - addPropertyForm.reservedUniNames = filteredUniNames - root.editedUniformIndex = index - addPropertyForm.showForEdit(uniformType, uniformControlType, uniformDisplayName, - uniformName, uniformDescription, uniformDefaultValue, - uniformMinValue, uniformMaxValue, uniformUserAdded) - } - onOpenCodeEditor: root.backendModel.openCodeEditor(root.modelIndex) - } - } - } - - Item { - id: addProperty - width: root.width - StudioTheme.Values.scrollBarThicknessHover - height: addPropertyForm.visible && addPropertyForm.parent === addProperty - ? addPropertyForm.height : 0 - visible: !isDependency - - AddPropertyForm { - id: addPropertyForm - visible: false - width: parent.width - - effectNodeName: nodeName - onHeightChanged: root.emitEnsure(addPropertyForm) - onVisibleChanged: root.emitEnsure(addPropertyForm) - - onAccepted: { - root.backendModel.addOrUpdateNodeUniform(root.modelIndex, - addPropertyForm.propertyData(), - root.editedUniformIndex) - addPropertyForm.parent = addProperty - root.editedUniformIndex = -1 - } - - onCanceled: { - addPropertyForm.parent = addProperty - root.editedUniformIndex = -1 - } - } - } - ConfirmPropertyRemoveForm { id: confirmRemoveForm @@ -268,35 +193,125 @@ HelperWidgets.Section { } Row { - height: 40 - visible: !isDependency && !addPropertyForm.visible - anchors.horizontalCenter: parent.horizontalCenter - spacing: 10 + width: parent.width + height: uniformsCol.height + spacing: 0 - HelperWidgets.Button { - width: 100 - height: 30 - text: qsTr("Add Property") - enabled: !addPropertyForm.visible - anchors.verticalCenter: parent.verticalCenter - - onClicked: { - confirmRemoveForm.visible = false - confirmRemoveForm.parent = root - root.editedUniformIndex = -1 - addPropertyForm.parent = addProperty - addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() - addPropertyForm.reservedUniNames = root.backendModel.uniformNames() - addPropertyForm.showForAdd() - } + Rectangle { + width: 4 + height: parent.height + color: codeEditorOpen ? StudioTheme.Values.themeInteraction : "transparent" } - HelperWidgets.Button { - width: 100 - height: 30 - text: qsTr("Show Code") - anchors.verticalCenter: parent.verticalCenter - onClicked: root.backendModel.openCodeEditor(index) + Column { + id: uniformsCol + + width: parent.width - 4 + spacing: 0 + + Repeater { + id: repeater + model: nodeUniformsModel + + EffectCompositionNodeUniform { + id: effectCompositionNodeUniform + width: root.width - StudioTheme.Values.scrollBarThicknessHover + editing: root.editedUniformIndex === index + disableMoreMenu: root.editedUniformIndex >= 0 + isDependencyNode: isDependency + + onReset: nodeUniformsModel.resetData(index) + onRemove: { + if (uniformIsInUse) { + confirmRemoveForm.parent = effectCompositionNodeUniform.editPropertyFormParent + confirmRemoveForm.uniformIndex = index + confirmRemoveForm.visible = true + } else { + nodeUniformsModel.remove(index) + } + } + onEdit: { + confirmRemoveForm.visible = false + confirmRemoveForm.parent = root + addPropertyForm.parent = effectCompositionNodeUniform.editPropertyFormParent + let dispNames = nodeUniformsModel.displayNames() + let filteredDispNames = dispNames.filter(name => name !== uniformDisplayName); + addPropertyForm.reservedDispNames = filteredDispNames + let uniNames = root.backendModel.uniformNames() + let filteredUniNames = uniNames.filter(name => name !== uniformName); + addPropertyForm.reservedUniNames = filteredUniNames + root.editedUniformIndex = index + addPropertyForm.showForEdit(uniformType, uniformControlType, uniformDisplayName, + uniformName, uniformDescription, uniformDefaultValue, + uniformMinValue, uniformMaxValue, uniformUserAdded) + } + onOpenCodeEditor: root.backendModel.openCodeEditor(root.modelIndex) + } + } + + Item { + id: addProperty + width: parent.width - StudioTheme.Values.scrollBarThicknessHover + height: addPropertyForm.visible && addPropertyForm.parent === addProperty + ? addPropertyForm.height : 0 + visible: !isDependency + + AddPropertyForm { + id: addPropertyForm + visible: false + width: parent.width + + effectNodeName: nodeName + onHeightChanged: root.emitEnsure(addPropertyForm) + onVisibleChanged: root.emitEnsure(addPropertyForm) + + onAccepted: { + root.backendModel.addOrUpdateNodeUniform(root.modelIndex, + addPropertyForm.propertyData(), + root.editedUniformIndex) + addPropertyForm.parent = addProperty + root.editedUniformIndex = -1 + } + + onCanceled: { + addPropertyForm.parent = addProperty + root.editedUniformIndex = -1 + } + } + } + + Row { + height: 40 + visible: !isDependency && !addPropertyForm.visible + anchors.horizontalCenter: parent.horizontalCenter + spacing: 10 + + HelperWidgets.Button { + width: 100 + height: 30 + text: qsTr("Add Property") + enabled: !addPropertyForm.visible + anchors.verticalCenter: parent.verticalCenter + + onClicked: { + confirmRemoveForm.visible = false + confirmRemoveForm.parent = root + root.editedUniformIndex = -1 + addPropertyForm.parent = addProperty + addPropertyForm.reservedDispNames = nodeUniformsModel.displayNames() + addPropertyForm.reservedUniNames = root.backendModel.uniformNames() + addPropertyForm.showForAdd() + } + } + + HelperWidgets.Button { + width: 100 + height: 30 + text: qsTr("Show Code") + anchors.verticalCenter: parent.verticalCenter + onClicked: root.backendModel.openCodeEditor(index) + } + } } } } From 0c7986ecc3c7241b38d75facfb31a8231dd9365d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Nov 2024 11:51:21 +0100 Subject: [PATCH 203/322] QmlDesigner: Fix deprication warnings Change-Id: I98ca679131776665a148f1fd3138f573667666ab Reviewed-by: Tim Jenssen --- .../qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp index b4d6520512f..a69de6324b9 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1189,7 +1189,7 @@ InformationChangedCommand NodeInstanceServer::createAllInformationChangedCommand static bool supportedVariantType(int type) { - return (type < int(QVariant::UserType) && type != QMetaType::QObjectStar + return (type < int(QMetaType::User) && type != QMetaType::QObjectStar && type != QMetaType::QModelIndex && type != QMetaType::VoidStar) || type == QMetaType::fromType().id(); } From 4560089e84ab4ee738a9a247dd4bce066e91bbde Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 28 Nov 2024 13:15:54 +0200 Subject: [PATCH 204/322] EffectComposer: Add advanced mode button In advanced mode, users can add/edit/remove properties and shader code of non-custom nodes. Fixes: QDS-14219 Change-Id: I9fe0b64da0201c47b83e31fbc5fd55d99c323316 Reviewed-by: Mahmoud Badri --- .../EffectComposerTopBar.qml | 36 +++++++++++++++---- .../EffectCompositionNode.qml | 3 +- .../EffectCompositionNodeUniform.qml | 6 ++-- .../effectcomposer/effectcomposermodel.h | 3 ++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index a484a6cde80..dc2437c0c84 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -82,6 +82,7 @@ Rectangle { enabled: root.backendModel ? root.backendModel.isEnabled && root.backendModel.currentComposition !== "" : false + visible: root.backendModel ? root.backendModel.advancedMode : false onClicked: root.backendModel.openMainCodeEditor() @@ -111,22 +112,43 @@ Rectangle { color: StudioTheme.Values.themeTextColor } - HelperWidgets.AbstractButton { - objectName: "btnEffectComposerHelp" - + Row { + spacing: 5 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 Composer: + HelperWidgets.AbstractButton { + objectName: "btnEffectComposerAdvancedMode" + + anchors.verticalCenter: parent.verticalCenter + + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.settings_medium + tooltip: qsTr("In advanced mode, you can manage effect properties and edit shader code for all effects.") + checkable: true + checked: root.backendModel ? root.backendModel.advancedMode : false + + onClicked: root.backendModel.advancedMode = !root.backendModel.advancedMode + } + + HelperWidgets.AbstractButton { + id: openHelpButton + + objectName: "btnEffectComposerHelp" + + anchors.verticalCenter: parent.verticalCenter + + style: StudioTheme.Values.viewBarButtonStyle + buttonIcon: StudioTheme.Constants.help + tooltip: qsTr("How to use Effect Composer: 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 assets library, if you wish to reuse the effect later") - onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qtquick-effect-composer-view.html") + onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qtquick-effect-composer-view.html") + } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index 4a0cf78609b..20612d52da1 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -219,6 +219,7 @@ HelperWidgets.Section { editing: root.editedUniformIndex === index disableMoreMenu: root.editedUniformIndex >= 0 isDependencyNode: isDependency + isCustomNode: isCustom onReset: nodeUniformsModel.resetData(index) onRemove: { @@ -282,7 +283,7 @@ HelperWidgets.Section { Row { height: 40 - visible: !isDependency && !addPropertyForm.visible + visible: (root.backendModel.advancedMode || isCustom) && !isDependency && !addPropertyForm.visible anchors.horizontalCenter: parent.horizontalCenter spacing: 10 diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 69fcd1b8f65..a692dda81a7 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -14,8 +14,10 @@ Item { property bool editing: false property bool disableMoreMenu: false - property bool isDependencyNode: true + property bool isDependencyNode: false + property bool isCustomNode: false property alias editPropertyFormParent: editPropertyFormPlaceholder + readonly property var backendModel: EffectComposerBackend.effectComposerModel height: layout.implicitHeight + editPropertyFormPlaceholder.height + column.spacing visible: !uniformUseCustomValue @@ -146,7 +148,7 @@ Item { tooltip: root.disableMoreMenu ? qsTr("Additional actions disabled while editing existing property.") : qsTr("Access additional property actions.") enabled: !root.disableMoreMenu - visible: !root.isDependencyNode + visible: (root.backendModel.advancedMode || root.isCustomNode) && !root.isDependencyNode onClicked: menuLoader.show() } diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 5f8bf66a075..b55762d22ed 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -61,6 +61,7 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(int customPreviewImageCount READ customPreviewImageCount NOTIFY customPreviewImageCountChanged) Q_PROPERTY(int mainCodeEditorIndex READ mainCodeEditorIndex CONSTANT) Q_PROPERTY(QString effectErrors READ effectErrors NOTIFY effectErrorsChanged) + Q_PROPERTY(bool advancedMode MEMBER m_advancedMode NOTIFY advancedModeChanged) public: EffectComposerModel(QObject *parent = nullptr); @@ -181,6 +182,7 @@ signals: void currentPreviewImageChanged(); void previewImagesChanged(); void customPreviewImageCountChanged(); + void advancedModeChanged(); private: enum ErrorTypes { @@ -291,6 +293,7 @@ private: QUrl m_currentPreviewImage; QList m_customPreviewImages; int m_currentBakeCounter = 0; + bool m_advancedMode = false; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; From 74dd9598aa95868397b4d5ec036acf7cb42dab68 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 29 Nov 2024 13:55:12 +0200 Subject: [PATCH 205/322] EffectComposer: Relax code change requirements for "in use" update In use update is now forced after property edit or add is finished. Also, a change to either fragment or vertex shader triggers checking in use state in both shaders to get the complete picture. Fixes: QDS-14240 Fixes: QDS-14239 Change-Id: Id2853440080a935a99b7945bcda20c7ade499aab Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian --- src/plugins/effectcomposer/compositionnode.cpp | 18 +++++++++--------- src/plugins/effectcomposer/compositionnode.h | 5 ++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 0b51d9fd10d..f460d64b041 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -240,7 +240,7 @@ void CompositionNode::setFragmentCode(const QString &fragmentCode) return; m_fragmentCode = fragmentCode; - m_fragInUseCheckNeeded = true; + m_InUseCheckNeeded = true; emit fragmentCodeChanged(); requestRebakeIfLiveUpdateMode(); @@ -252,7 +252,7 @@ void CompositionNode::setVertexCode(const QString &vertexCode) return; m_vertexCode = vertexCode; - m_vertInUseCheckNeeded = true; + m_InUseCheckNeeded = true; emit vertexCodeChanged(); requestRebakeIfLiveUpdateMode(); @@ -271,6 +271,7 @@ void CompositionNode::addUniform(const QVariantMap &data) const auto uniform = new Uniform({}, QJsonObject::fromVariantMap(data), {}); g_propertyData.insert(uniform->name(), uniform->value()); m_uniformsModel.addUniform(uniform); + updateAreUniformsInUse(true); } void CompositionNode::updateUniform(int index, const QVariantMap &data) @@ -281,11 +282,12 @@ void CompositionNode::updateUniform(int index, const QVariantMap &data) g_propertyData.insert(uniform->name(), uniform->value()); m_uniformsModel.updateUniform(index, uniform); + updateAreUniformsInUse(true); } -void CompositionNode::updateAreUniformsInUse() +void CompositionNode::updateAreUniformsInUse(bool force) { - if (m_fragInUseCheckNeeded || m_vertInUseCheckNeeded) { + if (force || m_InUseCheckNeeded) { const QString matchTemplate("\\b%1\\b"); const QList uniList = uniforms(); for (int i = 0; i < uniList.size(); ++i) { @@ -293,15 +295,13 @@ void CompositionNode::updateAreUniformsInUse() QString pattern = matchTemplate.arg(QRegularExpression::escape(u->name())); QRegularExpression regex(pattern); bool found = false; - if (m_fragInUseCheckNeeded) - found = regex.match(m_fragmentCode).hasMatch(); - if (m_vertInUseCheckNeeded && !found) + found = regex.match(m_fragmentCode).hasMatch(); + if (!found) found = regex.match(m_vertexCode).hasMatch(); m_uniformsModel.setData(m_uniformsModel.index(i), found, EffectComposerUniformsModel::IsInUse); } - m_vertInUseCheckNeeded = false; - m_fragInUseCheckNeeded = false; + m_InUseCheckNeeded = false; } } diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index 096e959f224..1c7999519ca 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -77,7 +77,7 @@ public: void openCodeEditor(); void addUniform(const QVariantMap &data); void updateUniform(int index, const QVariantMap &data); - void updateAreUniformsInUse(); + void updateAreUniformsInUse(bool force = false); signals: void uniformsModelChanged(); @@ -104,8 +104,7 @@ private: bool m_isCustom = false; int m_refCount = 0; int m_extraMargin = 0; - bool m_vertInUseCheckNeeded = false; - bool m_fragInUseCheckNeeded = false; + bool m_InUseCheckNeeded = false; EffectComposerUniformsModel m_uniformsModel; std::unique_ptr m_shaderEditorData; From ea855c888ddcccf008bc904d3c1f7996d691f9b1 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 29 Nov 2024 15:58:41 +0200 Subject: [PATCH 206/322] EffectComposer: Check property usage in customValues of other properties Properties may be used in customValues of other properties instead of being used in shaders, so need to check for those as well to find if property is in use. Fixes: QDS-14236 Change-Id: Ifc272d5fdc02596484dbb0b9d04a1b9e02d4ad7f Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/compositionnode.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index f460d64b041..eff9bc8fc97 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -290,6 +290,16 @@ void CompositionNode::updateAreUniformsInUse(bool force) if (force || m_InUseCheckNeeded) { const QString matchTemplate("\\b%1\\b"); const QList uniList = uniforms(); + + // Some of the uniforms may only be used by customValue properties + QString customValues; + for (const Uniform *u : uniList) { + if (!u->customValue().isEmpty()) { + customValues.append(u->customValue()); + customValues.append(' '); + } + } + for (int i = 0; i < uniList.size(); ++i) { Uniform *u = uniList[i]; QString pattern = matchTemplate.arg(QRegularExpression::escape(u->name())); @@ -298,6 +308,8 @@ void CompositionNode::updateAreUniformsInUse(bool force) found = regex.match(m_fragmentCode).hasMatch(); if (!found) found = regex.match(m_vertexCode).hasMatch(); + if (!found && !customValues.isEmpty()) + found = regex.match(customValues).hasMatch(); m_uniformsModel.setData(m_uniformsModel.index(i), found, EffectComposerUniformsModel::IsInUse); } From 7c01a532c68866314da5727172a687c8911f1bee Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Fri, 29 Nov 2024 12:11:19 +0100 Subject: [PATCH 207/322] DesignViewer: Make resource creation async MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ida366c3f6ec89b2d8b9e9cf09338f27af16447b2 Reviewed-by: Henning Gründl --- .../components/designviewer/dvconnector.cpp | 31 ++++++++++++------- .../components/designviewer/dvconnector.h | 12 ++++++- .../designviewer/resourcegeneratorproxy.cpp | 23 +++++++------- .../designviewer/resourcegeneratorproxy.h | 8 ++--- .../components/runmanager/runmanager.cpp | 4 +-- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index b5ca43deb26..a4f32d6cf10 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -3,9 +3,11 @@ #include "dvconnector.h" +#include #include #include #include + #include #include @@ -15,8 +17,6 @@ #include #include -#include "resourcegeneratorproxy.h" - namespace QmlDesigner::DesignViewer { Q_LOGGING_CATEGORY(deploymentPluginLog, "qtc.designer.deploymentPlugin", QtWarningMsg) @@ -145,6 +145,22 @@ DVConnector::DVConnector(QObject *parent) } }); + connect(&m_resourceGenerator, + &ResourceGeneratorProxy::resourceFileCreated, + this, + [this](const std::optional &resourcePath) { + emit projectIsUploading(); + QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName(); + uploadProject(projectName, resourcePath->toString()); + }); + + connect(&m_resourceGenerator, + &ResourceGeneratorProxy::errorOccurred, + [this](const QString &errorString) { + qCWarning(deploymentPluginLog) << "Error occurred while packing the project"; + emit projectPackingFailed(errorString); + }); + fetchUserInfo(); } @@ -190,16 +206,9 @@ void DVConnector::projectList() void DVConnector::uploadCurrentProject() { - ResourceGeneratorProxy resourceGenerator; QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName(); - QString resourcePath = resourceGenerator.createResourceFileSync(projectName); - - if (resourcePath.isEmpty()) { - qCWarning(deploymentPluginLog) << "Failed to create resource file"; - return; - } - - uploadProject(projectName, resourcePath); + m_resourceGenerator.createResourceFileAsync(projectName); + emit projectIsPacking(); } void DVConnector::uploadProject(const QString &projectId, const QString &filePath) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h index 9602b7212ff..5ffd0eebf9a 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.h +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -9,6 +9,8 @@ #include #include +#include "resourcegeneratorproxy.h" + namespace QmlDesigner::DesignViewer { class CustomWebEnginePage : public QWebEnginePage @@ -95,6 +97,9 @@ private: ConnectorStatus m_connectorStatus; QByteArray m_userInfo; + // other internals + ResourceGeneratorProxy m_resourceGenerator; + struct ReplyEvaluatorData { QNetworkReply *reply = nullptr; @@ -150,10 +155,15 @@ signals: void sharedProjectThumbnailDownloaded(); void sharedProjectThumbnailDownloadError(const int errorCode, const QString &message); - // UI integration - login/user related signals + // UI integration - login/user void userInfoReceived(const QByteArray &reply); void webViewerVisibleChanged(); + // UI integration - project packing/uploading + void projectIsPacking(); + void projectPackingFailed(const QString &errorString); + void projectIsUploading(); + // internal signals void connectorStatusUpdated(const ConnectorStatus status); }; diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp index bcd1d74d9cd..1e7235c2283 100644 --- a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -31,34 +32,34 @@ ResourceGeneratorProxy::~ResourceGeneratorProxy() } } -void ResourceGeneratorProxy::createResourceFileAsync() +void ResourceGeneratorProxy::createResourceFileAsync(const QString &projectName) { m_future = QtConcurrent::run([&]() { - const QString filePath = createResourceFileSync(); + const std::optional filePath = createResourceFileSync(projectName); - if (filePath.isEmpty()) { + if (filePath->isEmpty()) { emit errorOccurred("Failed to create resource file"); return; } - emit resourceFileCreated(filePath); + emit resourceFileCreated(filePath.value()); }); } -QString ResourceGeneratorProxy::createResourceFileSync(const QString &projectName) +std::optional ResourceGeneratorProxy::createResourceFileSync(const QString &projectName) { const auto project = ProjectExplorer::ProjectManager::startupProject(); - const Utils::FilePath tempFilePath = project->projectDirectory().pathAppended(projectName - + ".qmlrc"); + std::optional resourcePath = project->projectDirectory().pathAppended( + projectName + ".qmlrc"); - const bool retVal = ResourceGenerator::createQmlrcFile(tempFilePath); + const bool retVal = ResourceGenerator::createQmlrcFile(resourcePath.value()); - if (!retVal || tempFilePath.fileSize() == 0) { + if (!retVal || resourcePath->fileSize() == 0) { Core::MessageManager::writeDisrupting(tr("Failed to create resource file")); - return ""; + resourcePath.reset(); } - return tempFilePath.toString(); + return resourcePath; } } // namespace QmlDesigner::DesignViewer diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h index 0efe7ba51d7..1d83c477bde 100644 --- a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include @@ -14,15 +14,15 @@ class ResourceGeneratorProxy : public QObject Q_OBJECT public: ~ResourceGeneratorProxy(); - Q_INVOKABLE void createResourceFileAsync(); - Q_INVOKABLE QString createResourceFileSync(const QString &projectName = "share"); + Q_INVOKABLE void createResourceFileAsync(const QString &projectName = "share"); + Q_INVOKABLE std::optional createResourceFileSync(const QString &projectName = "share"); private: QFuture m_future; signals: void errorOccurred(const QString &error); - void resourceFileCreated(const QString &filename); + void resourceFileCreated(const Utils::FilePath &filePath); }; } // namespace QmlDesigner::DesignViewer diff --git a/src/plugins/qmldesigner/components/runmanager/runmanager.cpp b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp index 5947d63ec6a..ebc9ccfa35a 100644 --- a/src/plugins/qmldesigner/components/runmanager/runmanager.cpp +++ b/src/plugins/qmldesigner/components/runmanager/runmanager.cpp @@ -5,8 +5,8 @@ #include #include -#include +#include #include namespace QmlDesigner { @@ -332,7 +332,7 @@ bool AndroidTarget::enabled() const void AndroidTarget::run() const { auto qmlrcPath = DesignViewer::ResourceGeneratorProxy().createResourceFileSync(); - deviceManager()->sendProjectFile(m_deviceId, qmlrcPath); + deviceManager()->sendProjectFile(m_deviceId, qmlrcPath->toString()); } } // namespace QmlDesigner From 27c8a0673d1ba311d1b6a61ac8158f9cad2f7bb2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 29 Nov 2024 17:10:15 +0200 Subject: [PATCH 208/322] EffectComposer: Show scrollbar on show more error view Fixes: QDS-14238 Change-Id: I5f352b224ead4745100180c724b5e6270a68cea7 Reviewed-by: Mahmoud Badri --- .../effectComposerQmlSources/PreviewError.qml | 29 ++++++++----------- .../imports/HelperWidgets/ScrollView.qml | 3 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewError.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewError.qml index 29ce584c9d6..1df03250704 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewError.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewError.qml @@ -37,25 +37,20 @@ Rectangle { } } - MouseArea { - id: errorMouseArea + HelperWidgets.Button { + id: showLessButton - hoverEnabled: true - acceptedButtons: Qt.NoButton - anchors.fill: parent + width: 100 + height: 30 + text: qsTr("Show Less") + visible: root.showErrorDetails + opacity: scrollView.hovered || showLessButton.hovered ? 1 : 0.3 + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.bottomMargin: 8 + anchors.rightMargin: 8 + StudioTheme.Values.scrollBarThicknessHover - HelperWidgets.Button { - width: 100 - height: 30 - text: qsTr("Show Less") - visible: root.showErrorDetails - opacity: errorMouseArea.containsMouse ? 1 : 0.3 - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 4 - - onClicked: root.showErrorDetails = false - } + onClicked: root.showErrorDetails = false } Column { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml index 707f3e5669d..828aa7f1657 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ScrollView.qml @@ -15,7 +15,6 @@ Flickable { readonly property bool horizontalScrollBarVisible: horizontalScrollBar.scrollBarVisible readonly property bool bothVisible: flickable.verticalScrollBarVisible && flickable.horizontalScrollBarVisible - property bool hideVerticalScrollBar: false property bool hideHorizontalScrollBar: false @@ -24,6 +23,8 @@ Flickable { default property alias content: areaItem.children property bool adsFocus: false + property alias hovered: hoverHandler.hovered + // objectName is used by the dock widget to find this particular ScrollView // and set the ads focus on it. objectName: "__mainSrollView" From ec13696749683794dd1382fc09fc505a1a06d37a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 2 Dec 2024 10:17:31 +0200 Subject: [PATCH 209/322] EffectComposer: Tweak tab bar sizes * A left margin is applied to the tab bar (10 px) * Tab buttons have paddings instead of a fixed size Task-number: QDS-14244 Change-Id: Ibe4953b9447a1ba48c83cb0f9865068fb8cbec38 Reviewed-by: Miikka Heikkinen Reviewed-by: Shrief Gabr --- .../CodeEditorTabs.qml | 21 +++++++++++++------ .../imports/StudioTheme/Values.qml | 7 +++++++ .../effectshaderscodeeditor.cpp | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml index 897023a1715..f7f43085aff 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorTabs.qml @@ -18,9 +18,15 @@ Rectangle { RowLayout { id: rowLayout + property real leftMargin: StudioTheme.Values.tabBarHorizontalMargin + width: parent.width - anchors.verticalCenter: parent.verticalCenter - spacing: StudioTheme.Values.controlGap + anchors.bottom: parent.bottom + spacing: StudioTheme.Values.tabBarSpacing + + Item { // Empty item as a left margin + implicitWidth: rowLayout.leftMargin - rowLayout.spacing + } TabButton { text: qsTr("Fragment Shader") @@ -32,7 +38,7 @@ Rectangle { tabId: "VERTEX" } - Item { + Item { // Spacer Layout.fillWidth: true Layout.preferredHeight: 1 } @@ -44,13 +50,16 @@ Rectangle { required property string tabId readonly property bool selected: rootEditor.selectedShader === tabId - Layout.preferredHeight: 40 - Layout.preferredWidth: 120 + Layout.alignment: Qt.AlignBottom font.pixelSize: StudioTheme.Values.mediumFont verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - padding: 10 + + leftPadding: StudioTheme.Values.tabButtonHorizontalPadding + rightPadding: StudioTheme.Values.tabButtonHorizontalPadding + topPadding: StudioTheme.Values.tabButtonVerticalPadding + bottomPadding: StudioTheme.Values.tabButtonVerticalPadding color: { if (!tabButton.enabled) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 9d857513488..96f452c7019 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -257,6 +257,13 @@ QtObject { readonly property int cellWidth: 200 readonly property int cellHeight: 40 + // Tab + readonly property int tabBarHorizontalMargin: 10 + readonly property int tabBarSpacing: 2 + readonly property int tabButtonHorizontalPadding: 15 + readonly property int tabButtonVerticalPadding: 1 + + // Theme Colors property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index eff223a4ee9..484e16de3d5 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -344,7 +344,7 @@ void EffectShadersCodeEditor::createQmlTabs() m_qmlTabWidget->setClearColor(QmlDesigner::Theme::getColor( QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); m_qmlTabWidget->rootContext()->setContextProperty("shaderEditor", QVariant::fromValue(this)); - m_qmlTabWidget->setFixedHeight(43); + m_qmlTabWidget->setFixedHeight(37); } void EffectShadersCodeEditor::createQmlFooter() From 653b9ba215e0d8f37a37dfb9a4a5f4abe4517205 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 2 Dec 2024 12:18:43 +0200 Subject: [PATCH 210/322] EffectComposer: Enable mouse wheel scroll for the uniforms view Task-number: QDS-14246 Change-Id: I605a5ca5ee5e4763b897486183b46adbb2ca4e53 Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/CodeEditorUniformsView.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index decb076094a..774bf2db3f2 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -62,7 +62,7 @@ ColumnLayout { columnSpacing: -StudioTheme.Values.border rowSpacing: -StudioTheme.Values.border clip: true - interactive: false + interactive: true selectionMode: TableView.SingleSelection selectionBehavior: TableView.SelectRows selectionModel: ItemSelectionModel {} @@ -140,7 +140,7 @@ ColumnLayout { visible: !tableView.hideHorizontalScrollBar - show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + show: (hoverHandler.hovered || tableView.focus || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) && horizontalScrollBar.isNeeded otherInUse: verticalScrollBar.inUse @@ -158,8 +158,8 @@ ColumnLayout { visible: !tableView.hideVerticalScrollBar - show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus - || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + show: (hoverHandler.hovered || tableView.focus + || verticalScrollBar.inUse || verticalScrollBar.otherInUse) && verticalScrollBar.isNeeded otherInUse: horizontalScrollBar.inUse } From 7047ec9a22b9b905139c698a7d16c4c82c8be0a2 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Sat, 30 Nov 2024 16:09:52 +0200 Subject: [PATCH 211/322] EffectComposer: Tweak code editor geometry * Code editor is a non-modal tool * Saves the geometry on closing * Reads and applies geometry on opening * Remembers the splitter sizes. Fixes: QDS-14243 Change-Id: I5ee083080587453e917f2083e2d6a41a4eb65ee0 Reviewed-by: Miikka Heikkinen --- .../effectshaderscodeeditor.cpp | 84 ++++++++++++++++--- .../effectcomposer/effectshaderscodeeditor.h | 5 ++ 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 484e16de3d5..009e03e9936 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include #include @@ -34,6 +37,9 @@ namespace { inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate"; +inline constexpr char EFFECTCOMPOSER_SHADER_EDITOR_GEO_KEY[] = "EffectComposer/CodeEditor/Geometry"; +inline constexpr char EFFECTCOMPOSER_SHADER_EDITOR_SPLITTER_KEY[] + = "EffectComposer/CodeEditor/SplitterSizes"; inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_HEADER[] = "QQuickWidgetEffectComposerCodeEditorHeader"; inline constexpr char OBJECT_NAME_EFFECTCOMPOSER_SHADER_EDITOR_TABS[] @@ -53,6 +59,32 @@ QString propertyEditorResourcesPath() return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } +template +QByteArray serializeList(const QList &list) +{ + QJsonDocument doc; + QJsonArray jsonArray; + for (const T &value : list) + jsonArray.push_back(value); + doc.setArray(jsonArray); + return doc.toJson(); +} + +template +QList deserializeList(const QByteArray &serialData) +{ + const QJsonDocument doc = QJsonDocument::fromJson(serialData); + if (!doc.isArray()) + return {}; + + QList result; + const QJsonArray jsonArray = doc.array(); + for (const QJsonValue &val : jsonArray) + result.append(val.toVariant().value()); + + return result; +} + } // namespace namespace EffectComposer { @@ -64,7 +96,7 @@ EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget * , m_editableNodesModel(new EffectComposerEditableNodesModel(this)) { setWindowFlag(Qt::Tool, true); - setWindowFlag(Qt::WindowStaysOnTopHint); + setWindowModality(Qt::WindowModality::NonModal); setWindowTitle(title); setupUIComponents(); @@ -85,6 +117,7 @@ EffectShadersCodeEditor::~EffectShadersCodeEditor() void EffectShadersCodeEditor::showWidget() { readAndApplyLiveUpdateSettings(); + setParent(Core::ICore::dialogParent()); show(); raise(); setOpened(true); @@ -222,8 +255,8 @@ void EffectShadersCodeEditor::insertTextToCursorPosition(const QString &text) EffectShadersCodeEditor *EffectShadersCodeEditor::instance() { - static EffectShadersCodeEditor *editorInstance = new EffectShadersCodeEditor( - tr("Shaders Code Editor")); + static EffectShadersCodeEditor *editorInstance + = new EffectShadersCodeEditor(tr("Shaders Code Editor"), Core::ICore::dialogParent()); return editorInstance; } @@ -254,30 +287,30 @@ EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() void EffectShadersCodeEditor::setupUIComponents() { QVBoxLayout *verticalLayout = new QVBoxLayout(this); - QSplitter *splitter = new QSplitter(this); + m_splitter = new QSplitter(this); QWidget *tabComplexWidget = new QWidget(this); QVBoxLayout *tabsLayout = new QVBoxLayout(tabComplexWidget); m_stackedWidget = new QStackedWidget(tabComplexWidget); - splitter->setOrientation(Qt::Vertical); + m_splitter->setOrientation(Qt::Vertical); createHeader(); createQmlTabs(); createQmlFooter(); verticalLayout->setContentsMargins(0, 0, 0, 0); - verticalLayout->addWidget(splitter); + verticalLayout->addWidget(m_splitter); tabsLayout->setContentsMargins(0, 0, 0, 0); tabsLayout->setSpacing(0); tabsLayout->addWidget(m_qmlTabWidget); tabsLayout->addWidget(m_stackedWidget); tabsLayout->addWidget(m_qmlFooterWidget); - splitter->addWidget(m_headerWidget.get()); - splitter->addWidget(tabComplexWidget); + m_splitter->addWidget(m_headerWidget.get()); + m_splitter->addWidget(tabComplexWidget); - splitter->setCollapsible(0, false); - splitter->setCollapsible(1, false); + m_splitter->setCollapsible(0, false); + m_splitter->setCollapsible(1, false); connect( m_stackedWidget.get(), @@ -285,7 +318,8 @@ void EffectShadersCodeEditor::setupUIComponents() this, &EffectShadersCodeEditor::onEditorWidgetChanged); - this->resize(660, 240); + setMinimumSize(660, 240); + resize(900, 600); } void EffectShadersCodeEditor::setOpened(bool value) @@ -295,6 +329,7 @@ void EffectShadersCodeEditor::setOpened(bool value) m_opened = value; emit openedChanged(m_opened); + onOpenStateChanged(); } void EffectShadersCodeEditor::closeEvent(QCloseEvent *event) @@ -318,6 +353,25 @@ void EffectShadersCodeEditor::readAndApplyLiveUpdateSettings() setLiveUpdate(liveUpdateStatus); } +void EffectShadersCodeEditor::writeGeometrySettings() +{ + const QByteArray &splitterSizeData = ::serializeList(m_splitter->sizes()); + m_settings->setValue(EFFECTCOMPOSER_SHADER_EDITOR_GEO_KEY, saveGeometry()); + m_settings->setValue(EFFECTCOMPOSER_SHADER_EDITOR_SPLITTER_KEY, splitterSizeData); +} + +void EffectShadersCodeEditor::readAndApplyGeometrySettings() +{ + if (m_settings->contains(EFFECTCOMPOSER_SHADER_EDITOR_GEO_KEY)) + restoreGeometry(m_settings->value(EFFECTCOMPOSER_SHADER_EDITOR_GEO_KEY).toByteArray()); + + if (m_settings->contains(EFFECTCOMPOSER_SHADER_EDITOR_SPLITTER_KEY)) { + const QByteArray &splitterSizeData + = m_settings->value(EFFECTCOMPOSER_SHADER_EDITOR_SPLITTER_KEY).toByteArray(); + m_splitter->setSizes(::deserializeList(splitterSizeData)); + } +} + void EffectShadersCodeEditor::createHeader() { m_headerWidget = new StudioQuickWidget(this); @@ -423,6 +477,14 @@ void EffectShadersCodeEditor::onEditorWidgetChanged() setSelectedShaderName({}); } +void EffectShadersCodeEditor::onOpenStateChanged() +{ + if (isOpened()) + readAndApplyGeometrySettings(); + else + writeGeometrySettings(); +} + EffectCodeEditorWidget *EffectShadersCodeEditor::currentEditor() const { QWidget *currentTab = m_stackedWidget->currentWidget(); diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.h b/src/plugins/effectcomposer/effectshaderscodeeditor.h index cc5fea86d2b..b6c825ebc04 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.h +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.h @@ -11,6 +11,7 @@ QT_FORWARD_DECLARE_CLASS(QSettings) QT_FORWARD_DECLARE_CLASS(QStackedWidget) +QT_FORWARD_DECLARE_CLASS(QSplitter) class StudioQuickWidget; @@ -94,6 +95,8 @@ protected: private: void writeLiveUpdateSettings(); void readAndApplyLiveUpdateSettings(); + void writeGeometrySettings(); + void readAndApplyGeometrySettings(); void createHeader(); void createQmlTabs(); void createQmlFooter(); @@ -102,6 +105,7 @@ private: void selectNonEmptyShader(ShaderEditorData *data); void setSelectedShaderName(const QString &shaderName); void onEditorWidgetChanged(); + void onOpenStateChanged(); EffectCodeEditorWidget *currentEditor() const; @@ -110,6 +114,7 @@ private: QPointer m_qmlTabWidget; QPointer m_qmlFooterWidget; QPointer m_stackedWidget; + QPointer m_splitter; QPointer m_defaultTableModel; QPointer m_editableNodesModel; ShaderEditorData *m_currentEditorData = nullptr; From 785da8b434c6108e97e71fbaac66bf6eea70d243 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 9 Oct 2024 14:02:12 +0300 Subject: [PATCH 212/322] QmlDesigner: Track folders expanded/collapsed state in AssetsLibrary Fixes: QDS-13791 Change-Id: I6d30e6d1d79da99466f0fa7b762267701bc2d2e2 Reviewed-by: Miikka Heikkinen --- .../assetsLibraryQmlSources/AssetDelegate.qml | 15 ++++++ .../assetsLibraryQmlSources/AssetsView.qml | 36 ++++++------- .../assetslibrary/assetslibrarymodel.cpp | 50 ++++++++++++++++++- .../assetslibrary/assetslibrarymodel.h | 6 +++ .../assetslibrary/assetslibrarywidget.cpp | 16 ++++-- 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index 32485a61a1e..e49aba96ec8 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -48,6 +48,19 @@ TreeViewDelegate { root.depth -= root.assetsView.rootPathDepth root.initialDepth = root.depth } + + // expand/collapse folder based on its stored expanded state + if (root.__isDirectory) { + // if the folder expand state is not stored yet, stores it as true (expanded) + root.assetsModel.initializeExpandState(root.__itemPath) + + let expandState = assetsModel.folderExpandState(root.__itemPath) + + if (expandState) + root.assetsView.expand(root.__currentRow) + else + root.assetsView.collapse(root.__currentRow) + } } // workaround for a bug -- might be fixed by https://codereview.qt-project.org/c/qt/qtdeclarative/+/442721 @@ -295,6 +308,8 @@ TreeViewDelegate { } else { root.assetsView.expand(root.__currentRow) } + + assetsModel.saveExpandState(root.__itemPath, root.expanded) } function reloadImage() { diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index e11fab92f17..e404d2f3580 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -93,7 +93,6 @@ TreeView { function onDirectoryCreated(path) { root.__createdDirectories.push(path) - updateRowsTimer.restart() } @@ -118,15 +117,7 @@ TreeView { // updating rows for safety: the rows might have been created before the // directory (esp. the root path) has been loaded, so we must make sure all rows are // expanded -- otherwise, the tree may not become visible. - updateRowsTimer.restart() - - let idx = assetsModel.indexForPath(path) - let row = root.rowAtIndex(idx) - let column = root.columnAtIndex(idx) - - if (row >= root.rootPathRow && !root.isExpanded(row)) - root.expand(row) } function onRootPathChanged() @@ -180,9 +171,9 @@ TreeView { let index = assetsModel.indexForPath(dirPath) let row = root.rowAtIndex(index) - if (row > 0) + if (row > 0) { root.expand(row) - else if (row === -1 && assetsModel.indexIsValid(index)) { + } else if (row === -1 && assetsModel.indexIsValid(index)) { // It is possible that this directory, dirPath, was created inside of a parent // directory that was not yet expanded in the TreeView. This can happen with the // bridge plugin. In such a situation, we don't have a "row" for it yet, so we have @@ -194,6 +185,8 @@ TreeView { root.expand(row) }) } + + assetsModel.saveExpandState(dirPath, root.isExpanded(row)) } // we have no way to know beyond doubt here if updateRows() was called due @@ -215,10 +208,10 @@ TreeView { function __doExpandAll() { let expandedAny = false - for (let nRow = 0; nRow < root.rows; ++nRow) { - let index = root.__modelIndex(nRow) - if (assetsModel.isDirectory(index) && !root.isExpanded(nRow)) { - root.expand(nRow); + for (let r = 0; r < root.rows; ++r) { + let index = root.__modelIndex(r) + if (assetsModel.isDirectory(index) && !root.isExpanded(r)) { + root.expand(r) expandedAny = true } } @@ -352,15 +345,14 @@ TreeView { } Keys.onRightPressed: { - root.toggleDirectoryState("expand") + root.expandFolder(true) } Keys.onLeftPressed: { - root.toggleDirectoryState("collapse") + root.expandFolder(false) } - function toggleDirectoryState(action) { - + function expandFolder(expand) { let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) : root.__modelIndex(root.firstRow) @@ -369,9 +361,11 @@ TreeView { let row = root.rowAtIndex(index) - if (action === "expand") + assetsModel.saveExpandState(root.currentFilePath, expand) + + if (expand) root.expand(row) - else if (action === "collapse") + else root.collapse(row) } diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 19c7319d2c0..15d7a056c55 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -177,8 +177,18 @@ bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex) { auto idx = mapToSource(folderIndex); bool ok = m_sourceFsModel->remove(idx); - if (!ok) + + if (ok) { + Utils::FilePath parentPath = Utils::FilePath::fromString(filePath(folderIndex)); + const QStringList paths = s_folderExpandStateHash.keys(); + + for (const QString &path : paths) { + if (Utils::FilePath::fromString(path).isChildOf(parentPath)) + s_folderExpandStateHash.remove(path); + } + } else { qWarning() << __FUNCTION__ << " could not remove folder recursively: " << m_sourceFsModel->filePath(idx); + } return ok; } @@ -205,6 +215,44 @@ bool AssetsLibraryModel::isSameOrDescendantPath(const QUrl &source, const QStrin return srcPath == targetPath || targetPath.isChildOf(srcPath); } +bool AssetsLibraryModel::folderExpandState(const QString &path) const +{ + return s_folderExpandStateHash.value(path); +} + +void AssetsLibraryModel::initializeExpandState(const QString &path) +{ + if (!s_folderExpandStateHash.contains(path)) + saveExpandState(path, true); +} + +void AssetsLibraryModel::saveExpandState(const QString &path, bool expand) +{ + s_folderExpandStateHash.insert(path, expand); +} + +void AssetsLibraryModel::updateExpandPath(const Utils::FilePath &oldPath, const Utils::FilePath &newPath) +{ + // update parent folder expand state + bool value = s_folderExpandStateHash.take(oldPath.toFSPathString()); + saveExpandState(newPath.toFSPathString(), value); + + const QStringList paths = s_folderExpandStateHash.keys(); + + for (const QString &path : paths) { + Utils::FilePath childPath = Utils::FilePath::fromString(path); + + // update subfolders expand states + if (childPath.isChildOf(oldPath)) { + QString relativePath = Utils::FilePath::calcRelativePath(path, oldPath.toFSPathString()); + Utils::FilePath newChildPath = newPath.pathAppended(relativePath); + + value = s_folderExpandStateHash.take(path); + saveExpandState(newChildPath.toFSPathString(), value); + } + } +} + bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QString path = m_sourceFsModel->filePath(sourceParent); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 5b5525a01bf..10adc72425b 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -7,6 +7,7 @@ namespace Utils { class FileSystemWatcher; +class FilePath; } QT_FORWARD_DECLARE_CLASS(QFileSystemModel) @@ -51,6 +52,10 @@ public: Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const; Q_INVOKABLE bool allFilePathsAreComposedEffects(const QStringList &filePaths) const; Q_INVOKABLE bool isSameOrDescendantPath(const QUrl &source, const QString &target) const; + Q_INVOKABLE bool folderExpandState(const QString &path) const; + Q_INVOKABLE void initializeExpandState(const QString &path); + Q_INVOKABLE void saveExpandState(const QString &path, bool expand); + void updateExpandPath(const Utils::FilePath &oldPath, const Utils::FilePath &newPath); int columnCount(const QModelIndex &parent = QModelIndex()) const override { @@ -80,6 +85,7 @@ private: QFileSystemModel *m_sourceFsModel = nullptr; bool m_isEmpty = true; Utils::FileSystemWatcher *m_fileWatcher = nullptr; + inline static QHash s_folderExpandStateHash; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index e2df20ab8ce..ebcc325790c 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -427,11 +427,17 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList &urls, const QStrin } } - if (!src.renameFile(dest) && src.isDir()) { - QString message = tr("Failed to move folder \"%1\". " - "The folder might contain subfolders or one of its files is in use.") - .arg(src.fileName()); - Core::AsynchronousMessageBox::warning(tr("Folder move failure"), message); + bool isDir = src.isDir(); + + if (src.renameFile(dest)) { + if (isDir) + m_assetsModel->updateExpandPath(src, dest); + } else if (isDir) { + Core::AsynchronousMessageBox::warning( + tr("Folder move failure"), + tr("Failed to move folder \"%1\". The folder might contain subfolders or one of its files is in use.") + .arg(src.fileName()) + ); } } From 6d59679efbfb89c50cd12edff68c1e1f5a643614 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 2 Dec 2024 17:05:37 +0100 Subject: [PATCH 213/322] QmlDesigner: make WebEngine dependency optional Qt 6.7.3 Mingw does not have it, and our documentation engineer is using it. Task-number: QDS-14262 Change-Id: Ie571d6841a69314cf3b561a6497ccc2798238995 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 884ca3dedb8..604f1b3f785 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -35,7 +35,7 @@ add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") add_subdirectory(libs) -find_package(Qt6 REQUIRED COMPONENTS WebSockets WebEngineWidgets) +find_package(Qt6 QUIET COMPONENTS WebSockets WebEngineWidgets) add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview @@ -736,6 +736,7 @@ extend_qtc_plugin(QmlDesigner ) extend_qtc_plugin(QmlDesigner + CONDITION TARGET Qt::WebSockets SOURCES_PREFIX components/devicesharing DEPENDS QtCreator::QrCodeGenerator Qt::WebSockets @@ -755,6 +756,7 @@ extend_qtc_plugin(QmlDesigner ) extend_qtc_plugin(QmlDesigner + CONDITION TARGET Qt::WebEngineWidgets SOURCES_PREFIX components/designviewer DEPENDS Qt::WebEngineWidgets From 5ad2b7131f7940d2df149a2491f9c182ce5e1d24 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 30 Oct 2024 13:16:55 +0100 Subject: [PATCH 214/322] QmlPreview: workaround for not starting preview In case of absolute path do not try wrongly calculated candidates. Task-number: QDS-13292 Change-Id: I6edb61957200b10f9465ad2eaeefeee533ac2375 Reviewed-by: Tim Jenssen --- src/libs/utils/fileinprojectfinder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp index 3cef75e57c6..86121568b13 100644 --- a/src/libs/utils/fileinprojectfinder.cpp +++ b/src/libs/utils/fileinprojectfinder.cpp @@ -191,6 +191,11 @@ bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, File directoryHandler(node->children.keys(), origLength); qCDebug(finderLog) << "FileInProjectFinder: found virtual directory" << originalPath << "in mapped paths"; + } + } + + if (originalPath == m_projectDir) { + if (checkPath(originalPath, originalPath.toFSPathString().length(), fileHandler, directoryHandler)) { return true; } } From 02394c945740f5d47734c74905c1682377d72c31 Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Thu, 28 Nov 2024 16:33:22 +0100 Subject: [PATCH 215/322] FromEditorItem: Fix for the clipping issues Fixed the following issues that affect items that paint outside of bounding rectangles - No response to clipping changes - Inconsistent behavior when changing parent clipping. - Artifacts when changing clipping/visibility. Task-number: QDS-14044 Change-Id: Iaa93d7312fe10281b28899f1a7cbc1d95e2df35b Reviewed-by: Thomas Hartmann --- .../components/formeditor/formeditoritem.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 35f52c86eda..4d377142eec 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -84,6 +84,7 @@ void FormEditorItem::setup() setOpacity(qmlItemNode().instanceValue("opacity").toDouble()); } + setFlag(QGraphicsItem::ItemClipsToShape, qmlItemNode().instanceValue("clip").toBool()); setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode().instanceValue("clip").toBool()); if (NodeHints::fromModelNode(qmlItemNode()).forceClip()) @@ -105,7 +106,9 @@ void FormEditorItem::setup() QRectF FormEditorItem::boundingRect() const { - return m_boundingRect; + // Corner case: painting outside the bounding rectangle (boundingRec < paintedBoundingRect), which can be set in the Text items, for example. + // QGraphicsItem needs valid painting boundaries returned by boundingRect(). Returning a bounding rectangle that is too small will cause artefacts in the view. + return m_paintedBoundingRect; } QPainterPath FormEditorItem::shape() const @@ -205,9 +208,10 @@ void FormEditorItem::synchronizeOtherProperty(PropertyNameView propertyName) if (propertyName == "opacity") setOpacity(qmlItemNode().instanceValue("opacity").toDouble()); - if (propertyName == "clip") + if (propertyName == "clip") { + setFlag(QGraphicsItem::ItemClipsToShape, qmlItemNode().instanceValue("clip").toBool()); setFlag(QGraphicsItem::ItemClipsChildrenToShape, qmlItemNode().instanceValue("clip").toBool()); - + } if (NodeHints::fromModelNode(qmlItemNode()).forceClip()) setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); @@ -428,12 +432,6 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, if (isInStackedContainer) showPlaceHolder = qmlItemNode().instanceIsRenderPixmapNull() && isContentVisible(); - QRegion clipRegion = painter->clipRegion(); - if (clipRegion.contains(m_selectionBoundingRect.toRect().topLeft()) - && clipRegion.contains(m_selectionBoundingRect.toRect().bottomRight())) - painter->setClipRegion(boundingRect().toRect()); - painter->setClipping(true); - if (!hideCompletely && !parentHasEffect()) { if (showPlaceHolder) { if (scene()->showBoundingRects() && m_boundingRect.width() > 15 && m_boundingRect.height() > 15) @@ -455,7 +453,6 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, } } - painter->setClipping(false); if (!qmlItemNode().isRootModelNode()) paintBoundingRect(painter); From 6267af72f1298ca7de8c74f1f39e42708ba61025 Mon Sep 17 00:00:00 2001 From: Rafal Stawarski Date: Mon, 2 Dec 2024 09:34:10 +0100 Subject: [PATCH 216/322] qml2puppet: update pixmap when clipping changes This is a fix for a corner case in the form editor when an element is drawn out of it's bounding rectangle. Pixmap update was triggered only on visibility changes (not on clipping). Due to lack of information about pixmap size changes, item was stucking at the bounding rectangle when it became visible and did not react to clipping changes in the editor. Task-number: QDS-14044 Change-Id: If074ca6c1b11216c0be12a4fb44e4e79688d6b5c Reviewed-by: Thomas Hartmann --- .../instances/qt5informationnodeinstanceserver.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 24b797d64dc..890c1c084d2 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -39,6 +39,7 @@ #include "view3dactioncommand.h" #include "requestmodelnodepreviewimagecommand.h" #include "changeauxiliarycommand.h" +#include "pixmapchangedcommand.h" #include "../editor3d/boxgeometry.h" #include "../editor3d/camerageometry.h" @@ -2091,8 +2092,15 @@ void Qt5InformationNodeInstanceServer::collectItemChangesAndSendChangeCommands() nodeInstanceClient()->informationChanged( createAllInformationChangedCommand(QtHelpers::toList(informationChangedInstanceSet))); - if (!propertyChangedList.isEmpty()) + if (!propertyChangedList.isEmpty()) { nodeInstanceClient()->valuesChanged(createValuesChangedCommand(propertyChangedList)); + for (const auto &property : propertyChangedList) { + if (property.second.contains("clip")) { + nodeInstanceClient()->pixmapChanged( + createPixmapChangedCommand({property.first})); + } + } + } if (!m_parentChangedSet.isEmpty()) { sendChildrenChangedCommand(QtHelpers::toList(m_parentChangedSet)); From 3cbdff4839bb3d7d10231828154e5ce4ec055159 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Nov 2024 21:16:31 +0100 Subject: [PATCH 217/322] QmlDesigner: Signal handler support for new syntax This patch add support for the new syntax. It will continue to parse the old syntax but only write/create the new syntax. For the creation I still plan to add an option. The patch requires still some adjustments and tests. Change-Id: I89db2baf8f66a4e3daea9e55863dac318e682e38 Reviewed-by: Marco Bubke --- .../filemanager/addpropertyvisitor.cpp | 4 +++ .../filemanager/changepropertyvisitor.cpp | 28 +++++++++++++++++-- .../designercore/filemanager/qmlrefactoring.h | 3 +- .../filemanager/removepropertyvisitor.cpp | 6 +++- .../include/signalhandlerproperty.h | 3 ++ .../model/signalhandlerproperty.cpp | 23 +++++++++++++++ .../rewriter/modeltotextmerger.cpp | 9 ++++-- .../rewriter/qmltextgenerator.cpp | 5 +++- .../rewriter/texttomodelmerger.cpp | 15 +++++++++- 9 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/filemanager/addpropertyvisitor.cpp b/src/plugins/qmldesigner/libs/designercore/filemanager/addpropertyvisitor.cpp index 2807477b70a..2f17529669e 100644 --- a/src/plugins/qmldesigner/libs/designercore/filemanager/addpropertyvisitor.cpp +++ b/src/plugins/qmldesigner/libs/designercore/filemanager/addpropertyvisitor.cpp @@ -137,6 +137,10 @@ void AddPropertyVisitor::addInMembers(QmlJS::AST::UiObjectInitializer *initializ newPropertyTemplate = QStringLiteral("%1: %2"); break; + case QmlRefactoring::SignalHandler: + newPropertyTemplate = QStringLiteral("function %1() %2"); + break; + default: Q_ASSERT(!"unknown property type"); } diff --git a/src/plugins/qmldesigner/libs/designercore/filemanager/changepropertyvisitor.cpp b/src/plugins/qmldesigner/libs/designercore/filemanager/changepropertyvisitor.cpp index 7ad250613b4..69570c726e5 100644 --- a/src/plugins/qmldesigner/libs/designercore/filemanager/changepropertyvisitor.cpp +++ b/src/plugins/qmldesigner/libs/designercore/filemanager/changepropertyvisitor.cpp @@ -3,6 +3,8 @@ #include "changepropertyvisitor.h" +#include + #include using namespace QmlJS; @@ -83,6 +85,9 @@ void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer, case QmlRefactoring::ScriptBinding: replaceMemberValue(member, nextMemberOnSameLine(members)); break; + case QmlRefactoring::SignalHandler: + replaceMemberValue(member, nextMemberOnSameLine(members)); + break; default: Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType"); @@ -99,12 +104,19 @@ void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer, } } +QString ensureBraces(const QString &source) +{ + return SignalHandlerProperty::normalizedSourceWithBraces(source); +} + // FIXME: duplicate code in the QmlJS::Rewriter class, remove this void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, bool needsSemicolon) { QString replacement = m_value; int startOffset = -1; int endOffset = -1; + bool requiresBraces = false; + if (auto objectBinding = AST::cast(propertyMember)) { startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset; endOffset = objectBinding->initializer->rbraceToken.end(); @@ -114,7 +126,12 @@ void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, b } else if (auto arrayBinding = AST::cast(propertyMember)) { startOffset = arrayBinding->lbracketToken.offset; endOffset = arrayBinding->rbracketToken.end(); - } else if (auto publicMember = AST::cast(propertyMember)) { + } else if (auto sourceElement = AST::cast(propertyMember)) { + auto function = AST::cast(sourceElement->sourceElement); + startOffset = function->lbraceToken.offset; + endOffset = function->rbraceToken.end(); + requiresBraces = true; + } else if (auto publicMember = AST::cast(propertyMember)) { if (publicMember->type == AST::UiPublicMember::Signal) { startOffset = publicMember->firstSourceLocation().offset; if (publicMember->semicolonToken.isValid()) @@ -139,6 +156,9 @@ void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, b return; } + if (requiresBraces) + replacement = ensureBraces(replacement); + if (needsSemicolon) replacement += QChar::fromLatin1(';'); @@ -158,7 +178,11 @@ bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName, return propName == toString(arrayBinding->qualifiedId); else if (auto publicMember = AST::cast(member)) return propName == publicMember->name; - else + else if (auto uiSourceElement = AST::cast(member)) { + auto function = AST::cast(uiSourceElement->sourceElement); + + return function->name == propName; + } else return false; } diff --git a/src/plugins/qmldesigner/libs/designercore/filemanager/qmlrefactoring.h b/src/plugins/qmldesigner/libs/designercore/filemanager/qmlrefactoring.h index 54194ab1360..e1198e72c76 100644 --- a/src/plugins/qmldesigner/libs/designercore/filemanager/qmlrefactoring.h +++ b/src/plugins/qmldesigner/libs/designercore/filemanager/qmlrefactoring.h @@ -19,7 +19,8 @@ public: Invalid = -1, ArrayBinding = 1, ObjectBinding = 2, - ScriptBinding = 3 + ScriptBinding = 3, + SignalHandler = 4 }; public: diff --git a/src/plugins/qmldesigner/libs/designercore/filemanager/removepropertyvisitor.cpp b/src/plugins/qmldesigner/libs/designercore/filemanager/removepropertyvisitor.cpp index de803be0b31..b4af1522e5c 100644 --- a/src/plugins/qmldesigner/libs/designercore/filemanager/removepropertyvisitor.cpp +++ b/src/plugins/qmldesigner/libs/designercore/filemanager/removepropertyvisitor.cpp @@ -117,6 +117,10 @@ bool RemovePropertyVisitor::memberNameMatchesPropertyName(const QString &propert return toString(scriptBinding->qualifiedId) == propertyName; else if (auto arrayBinding = QmlJS::AST::cast(ast)) return toString(arrayBinding->qualifiedId) == propertyName; - else + else if (auto uiSourceElement = QmlJS::AST::cast(ast)) { + auto function = QmlJS::AST::cast( + uiSourceElement->sourceElement); + return function->name == propertyName; + } else return false; } diff --git a/src/plugins/qmldesigner/libs/designercore/include/signalhandlerproperty.h b/src/plugins/qmldesigner/libs/designercore/include/signalhandlerproperty.h index 1d43aa9e6bc..1a40e3a6d88 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/signalhandlerproperty.h +++ b/src/plugins/qmldesigner/libs/designercore/include/signalhandlerproperty.h @@ -17,6 +17,7 @@ class QMLDESIGNERCORE_EXPORT SignalHandlerProperty final : public AbstractProper public: void setSource(const QString &source); QString source() const; + QString sourceNormalizedWithBraces() const; SignalHandlerProperty(); SignalHandlerProperty(const SignalHandlerProperty &property, AbstractView *view); @@ -30,6 +31,8 @@ public: AbstractView *view) : AbstractProperty(propertyName, internalNode, model, view) {} + + static QString normalizedSourceWithBraces(const QString &source); }; class QMLDESIGNERCORE_EXPORT SignalDeclarationProperty final : public AbstractProperty diff --git a/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp index 28bceb42a45..4542875402c 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp @@ -6,6 +6,9 @@ #include "internalnode_p.h" #include "model.h" #include "model_p.h" + +#include + namespace QmlDesigner { SignalHandlerProperty::SignalHandlerProperty() = default; @@ -51,6 +54,11 @@ QString SignalHandlerProperty::source() const return QString(); } +QString SignalHandlerProperty::sourceNormalizedWithBraces() const +{ + return normalizedSourceWithBraces(source()); +} + PropertyName SignalHandlerProperty::prefixAdded(PropertyNameView propertyName) { QString nameAsString = QString::fromUtf8(propertyName); @@ -77,6 +85,21 @@ PropertyName SignalHandlerProperty::prefixRemoved(PropertyNameView propertyName) return nameAsString.toLatin1(); } +QString SignalHandlerProperty::normalizedSourceWithBraces(const QString &source) +{ + static const QRegularExpression reg("\\{(\\s*?.*?)*?\\}"); + + auto match = reg.match(source); + + if (match.hasMatch()) + return source; + + if (source.contains('\n')) + return "{\n" + source + "\n}"; + + return "{ " + source + " }"; +} + SignalDeclarationProperty::SignalDeclarationProperty() = default; SignalDeclarationProperty::SignalDeclarationProperty(const SignalDeclarationProperty &property, diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/modeltotextmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/modeltotextmerger.cpp index 7b2c61f5767..df681f4ac64 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/modeltotextmerger.cpp @@ -325,7 +325,7 @@ void ModelToTextMerger::schedule(RewriteAction *action) QmlRefactoring::PropertyType ModelToTextMerger::propertyType(const AbstractProperty &property, const QString &textValue) { - if (property.isBindingProperty() || property.isSignalHandlerProperty()) { + if (property.isBindingProperty()) { QString val = textValue.trimmed(); if (val.isEmpty()) return QmlRefactoring::ObjectBinding; @@ -334,13 +334,18 @@ QmlRefactoring::PropertyType ModelToTextMerger::propertyType(const AbstractPrope return QmlRefactoring::ObjectBinding; else return QmlRefactoring::ScriptBinding; + } else if (property.isSignalHandlerProperty()) { + QString val = textValue.trimmed(); + if (val.isEmpty()) + return QmlRefactoring::ObjectBinding; + return QmlRefactoring::SignalHandler; } else if (property.isNodeListProperty()) return QmlRefactoring::ArrayBinding; else if (property.isNodeProperty()) return QmlRefactoring::ObjectBinding; else if (property.isVariantProperty()) return QmlRefactoring::ScriptBinding; - else if (property.isSignalDeclarationProperty()) + else if (property.isSignalHandlerProperty()) return QmlRefactoring::ScriptBinding; Q_ASSERT(false); //Cannot convert property type diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/qmltextgenerator.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/qmltextgenerator.cpp index e332feba19a..d2b3157bbc3 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/qmltextgenerator.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/qmltextgenerator.cpp @@ -86,7 +86,7 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept if (property.isBindingProperty()) { return property.toBindingProperty().expression(); } else if (property.isSignalHandlerProperty()) { - return property.toSignalHandlerProperty().source(); + return property.toSignalHandlerProperty().sourceNormalizedWithBraces(); } else if (property.isSignalDeclarationProperty()) { return property.toSignalDeclarationProperty().signature(); } else if (property.isNodeProperty()) { @@ -267,6 +267,9 @@ QString QmlTextGenerator::propertyToQml(const AbstractProperty &property, int in } else if (property.isSignalDeclarationProperty()) { result = m_tabSettings.indentationString(0, indentDepth, 0) + "signal" + " " + QString::fromUtf8(property.name()) + " "_L1 + toQml(property, indentDepth); + } else if (property.isSignalHandlerProperty()) { + result = m_tabSettings.indentationString(0, indentDepth, 0) + "function" + " " + + QString::fromUtf8(property.name()) + "() "_L1 + toQml(property, indentDepth); } else { result = m_tabSettings.indentationString(0, indentDepth, 0) + QString::fromUtf8(property.name()) + ": "_L1 + toQml(property, indentDepth); diff --git a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp index 6ce2bf7c940..dcd25c8c6c6 100644 --- a/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/libs/designercore/rewriter/texttomodelmerger.cpp @@ -1178,7 +1178,20 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (!member) continue; - if (auto array = AST::cast(member)) { + if (auto source = AST::cast(member)) { + auto function = AST::cast(source->sourceElement); + + AbstractProperty modelProperty = modelNode.property(function->name.toUtf8()); + + QString astValue; + if (function->body) { + astValue = textAt(context->doc(), function->lbraceToken, function->rbraceToken); + astValue = astValue.trimmed(); + } + + syncSignalHandler(modelProperty, astValue, differenceHandler); + modelPropertyNames.remove(function->name.toUtf8()); + } else if (auto array = AST::cast(member)) { const QString astPropertyName = toString(array->qualifiedId); if (isPropertyChangesType(typeName) || isConnectionsType(typeName) || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { From 1cdd643ddfdb5c761b7e310dc2597aca6eb4b6b2 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 2 Dec 2024 12:09:37 +0200 Subject: [PATCH 218/322] QmlDesigner: Highlight possible targets for effects in Navigator Fixes: QDS-13757 Change-Id: I1cb157e7bae9d9db09f8cfe99865e06a1b50bb44 Reviewed-by: Miikka Heikkinen --- .../qmldesigner/components/navigator/nameitemdelegate.cpp | 5 +++-- .../qmldesigner/components/navigator/navigatorview.cpp | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index 1db4d3e6f82..ccd2aa4ce4c 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -228,8 +228,7 @@ void NameItemDelegate::paint(QPainter *painter, bool validDrop = false; if (dragType == Constants::MIME_TYPE_BUNDLE_MATERIAL) { - Model *model = node.model(); - validDrop = metaInfo.isBasedOn(model->qtQuick3DModelMetaInfo()); + validDrop = metaInfo.isBasedOn(node.model()->qtQuick3DModelMetaInfo()); } else if (dragType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { validDrop = isValid3dTextureTarget(); } else if (dragType == Constants::MIME_TYPE_ASSET_IMAGE @@ -237,6 +236,8 @@ void NameItemDelegate::paint(QPainter *painter, Model *model = node.model(); validDrop = isValid3dTextureTarget() || metaInfo.isBasedOn(model->qtQuickImageMetaInfo(), model->qtQuickBorderImageMetaInfo()); + } else if (dragType == Constants::MIME_TYPE_ASSET_EFFECT) { + validDrop = metaInfo.isBasedOn(node.model()->qtQuickItemMetaInfo()); } else { const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType); ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 130aa9eb5e2..5fa18097202 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -304,8 +304,7 @@ void NavigatorView::dragStarted(QMimeData *mimeData) auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetsPaths[0]); QString assetType = assetTypeAndData.first; if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { - // We use arbitrary type name because at this time we don't have effect composer - // specific type + m_widget->setDragType(Constants::MIME_TYPE_ASSET_EFFECT); m_widget->update(); } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { m_widget->setDragType(Constants::MIME_TYPE_ASSET_TEXTURE3D); From 67417d57cbf572af4e6b01bbbafa4b426e4e3415 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 3 Dec 2024 13:57:51 +0200 Subject: [PATCH 219/322] Doc: Remove all mentions of effect maker Fixes: QDS-14276 Change-Id: I953bf6abfe9c665379e0e5025362ce7a80e6e9d5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mats Honkamaa --- .../config/style/qt5-sidebar.html | 1 - .../src/overviews/qtquick-export.qdoc | 2 +- .../src/overviews/qtquick-motion-design.qdoc | 2 +- .../src/qtdesignstudio-sharing-assets.qdoc | 2 +- .../src/qtdesignstudio-toc.qdoc | 1 - ...signstudio-using-effect-maker-effects.qdoc | 44 ------------------- .../effects/qtdesignstudio-effects.qdoc | 3 -- 7 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index aea59d807ec..fd7435f1185 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -142,7 +142,6 @@
        • Effect Composer
        • Particles
        • Using the Content Library effects
        • -
        • Using the Qt Quick Effect Maker effects
      diff --git a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc index e2a0e3cd9a1..844d2b3ef4c 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc @@ -4,7 +4,7 @@ /*! \page creator-exporting-qml.html \previouspage studio-importing-3d.html - \nextpage qt-using-effect-maker-effects.html + \nextpage sharing-assets.html \title Exporting Components diff --git a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc index 80e0a844507..17ba0cb3650 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc @@ -3,7 +3,7 @@ /*! \page qtquick-motion-design.html - \previouspage qt-using-effect-maker-effects.html + \previouspage sharing-assets.html \nextpage quick-animation-overview.html \title Motion Design diff --git a/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc index a968c8aeac0..005660c63b9 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage qt-using-effect-maker-effects.html + \previouspage creator-exporting-qml.html \page sharing-assets.html \nextpage qtquick-motion-design.html \sa {Content Library} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index a9b187d409f..b983f8916ab 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -177,7 +177,6 @@ \li \l{Importing 3D Assets} \endlist \li \l{Exporting Components} - \li \l{Using Qt Quick Effect Maker Effects} \li \l{Sharing Assets} \endlist \endlist diff --git a/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc deleted file mode 100644 index 1e53c53ba0f..00000000000 --- a/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - - \page qt-using-effect-maker-effects.html - \nextpage qtquick-motion-design.html - \previouspage creator-exporting-qml.html - - \title Using Qt Quick Effect Maker Effects - - \QQEM is integrated into \QDS for creating shader effects. To create an - effect, you first create the effect file in \QDS, and then you edit it in \QQEM. - - For more information about \QQEM, see the - \l{https://doc.qt.io/qt-6/qtquickeffectmaker-index.html}{Qt Quick Effect Maker Manual}. - - \section1 Creating an Effect File - - To create an effect file in \QDS: - - \list 1 - \li Right-click in the \uicontrol Assets view and - select \uicontrol {New Effect}. - \QDS creates an effect file and opens it in \QQEM. - \image qt-quick-effect-maker.webp - \li Edit the effect. - \li In \QQEM, go to \uicontrol File > \uicontrol Save. - \li With the default settings, select \uicontrol OK. - \image effect-maker-export.png - \endlist - - Now, you can close \QQEM and return to \QDS and apply the - effect. - - \section1 Applying an Effect - - You can apply effects to components in \QDS. To do so, drag the effect - from the \uicontrol Assets view to the component in the \uicontrol 2D or - \uicontrol Navigator view. - - \image apply-effect-maker-effect.webp - -*/ diff --git a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc index bbe004dc98f..5a25832bbd9 100644 --- a/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc +++ b/doc/qtdesignstudio/src/working with/effects/qtdesignstudio-effects.qdoc @@ -45,9 +45,6 @@ \li \l {2D Effects} \li A set of ready-made 2D effect components. Includes customizable effects like \uicontrol {Brightness Control}, \uicontrol {Gamma Adjust}, and \uicontrol {Opacity Mask}. - \row - \li \l {Using Qt Quick Effect Maker Effects}{Qt Quick Effect Maker} - \li Import effects made with \QQEM to \QDS. \row \li \l {Creating Custom Effects}{Custom Effects in Qt 5} \li Use your own shader files and \QDS custom shader utilities to create From 70e83e7ce4f417b55b20cfbd1870356c5a90c19c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 3 Dec 2024 14:29:13 +0200 Subject: [PATCH 220/322] EffectComposer: Disable apply button in live update mode * Also some objectNames are added for tests Task-number: QDS-14277 Change-Id: Id7549ffda7d2d6b78e218d5bec76c9e4afaffc3f Reviewed-by: Shrief Gabr Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/CodeEditorFooter.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml index 296af17a065..028e9c7e298 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorFooter.qml @@ -23,6 +23,10 @@ Rectangle { spacing: StudioTheme.Values.controlGap StudioControls.CheckBox { + id: liveUpdateButton + + objectName: "BtnLiveUpdate" + text: qsTr("Live Update") actionIndicatorVisible: false style: StudioTheme.Values.viewBarControlStyle @@ -38,13 +42,16 @@ Rectangle { } FooterButton { + objectName: "BtnClose" buttonIcon: qsTr("Close") onClicked: root.rootEditor.close() } FooterButton { + objectName: "BtnApply" buttonIcon: qsTr("Apply") onClicked: root.rootEditor.rebakeRequested() + enabled: !liveUpdateButton.checked Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin } } From d512e8ed9bcc8cd1e3c39e968658a5ec325bae4a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 3 Dec 2024 13:37:06 +0200 Subject: [PATCH 221/322] EffectComposer: Tweak uniforms view table cells * Cell heights are decreased * Cell default widths are covering the content * Description column extends to the width of the view Task-number: QDS-14245 Change-Id: Ifc132e03b4cb14d3d0a4e9a8d5a7a4fc191ec7bd Reviewed-by: Miikka Heikkinen --- .../CodeEditorUniformsView.qml | 68 +++++++++++++++++-- .../effectcomposeruniformstablemodel.cpp | 17 +++-- .../effectcomposeruniformstablemodel.h | 1 + 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index 774bf2db3f2..2f4a01f927e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -9,13 +9,25 @@ import StudioTheme as StudioTheme import TableModules as TableModules ColumnLayout { + id: root property alias model: tableView.model readonly property alias tableView: tableView + property var headerImplicitWidths: [] spacing: -1 HoverHandler { id: hoverHandler } + CellFontMetrics { + id: headerFontMetrics + + font.bold: true + } + + CellFontMetrics { + id: cellFontMetrics + } + HorizontalHeaderView { id: horizontalHeader @@ -27,22 +39,36 @@ ColumnLayout { delegate: Rectangle { color: StudioTheme.Values.themePanelBackground - implicitWidth: StudioTheme.Values.cellWidth implicitHeight: StudioTheme.Values.cellHeight + implicitWidth: headerText.firstLineWidth + border { width: StudioTheme.Values.border color: StudioTheme.Values.themeStateSeparator } Text { + id: headerText + + readonly property real firstLineWidth: headerFontMetrics.getWidth(text) + + rightPadding + + leftPadding + + anchors.leftMargin + + anchors.rightMargin + + 2 + color: StudioTheme.Values.themeTextColor text: display anchors.fill: parent anchors.margins: 8 elide: Text.ElideRight - font.bold: true + font: headerFontMetrics.font verticalAlignment: Qt.AlignVCenter + + onFirstLineWidthChanged: { + root.headerImplicitWidths[column] = headerText.firstLineWidth + } } } } @@ -66,12 +92,30 @@ ColumnLayout { selectionMode: TableView.SingleSelection selectionBehavior: TableView.SelectRows selectionModel: ItemSelectionModel {} + delegate: Cell { id: dataScope + property real headerItemWidth: root.headerImplicitWidths[column] ?? 50 + + implicitWidth: Math.max(labelView.firstLineWidth, headerItemWidth) + + Binding on implicitWidth { + when: dataScope.isDescription && dataScope.isLastCol + value: root.width - dataScope.x + restoreMode: Binding.RestoreBinding + } + Text { id: labelView + readonly property real firstLineWidth: cellFontMetrics.getWidth(text) + + rightPadding + + leftPadding + + anchors.leftMargin + + anchors.rightMargin + + 2 + text: dataScope.display ?? "" visible: !dataScope.editing color: StudioTheme.Values.themeTextColor @@ -82,6 +126,8 @@ ColumnLayout { maximumLineCount: 1 verticalAlignment: Qt.AlignVCenter clip: true + rightPadding: cellButtonsLoader.width + font: cellFontMetrics.font StudioControls.ToolTipArea { anchors.fill: parent @@ -90,6 +136,7 @@ ColumnLayout { } Loader { + id: cellButtonsLoader active: dataScope.canCopy anchors.right: parent.right @@ -177,10 +224,13 @@ ColumnLayout { required property bool selected required property bool current required property bool canCopy + required property bool isDescription + + readonly property bool isLastCol: column === tableView.columns - 1 color: tableView.currentRow === row ? StudioTheme.Values.themeTableCellCurrent : StudioTheme.Values.themePanelBackground - implicitWidth: StudioTheme.Values.cellWidth - implicitHeight: StudioTheme.Values.cellHeight + implicitWidth: 120 + implicitHeight: 27 border { width: StudioTheme.Values.border color: StudioTheme.Values.themeStateSeparator @@ -204,4 +254,14 @@ ColumnLayout { visible: cellBtn.hovered && text !== "" } } + + component CellFontMetrics: FontMetrics { + id: textFont + + function getWidth(txt) { + let nIdx = txt.indexOf('\n') + let firstLineText = (nIdx > -1) ? txt.substr(0, nIdx) : txt + return Math.ceil(textFont.boundingRect(firstLineText).width) + } + } } diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp index ae68b3c6c3a..37ff6ee840f 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.cpp @@ -186,6 +186,7 @@ QHash EffectComposerUniformsTableModel::roleNames() const {Role::ValueRole, "value"}, {Role::ValueTypeRole, "valueType"}, {Role::CanCopyRole, "canCopy"}, + {Role::IsDescriptionRole, "isDescription"}, }; } @@ -207,19 +208,25 @@ QVariant EffectComposerUniformsTableModel::data(const QModelIndex &index, int ro if (!index.isValid()) return {}; - if (role == Role::ValueRole) + switch (role) { + case Role::ValueRole: return mapToSource(index).value(); - if (role == Qt::DisplayRole) + case Qt::DisplayRole: return mapToSource(index).display(); - if (role == Role::ValueTypeRole) + case Role::ValueTypeRole: return mapToSource(index).valueTypeString(); - if (role == Role::CanCopyRole) + case Role::CanCopyRole: return mapToSource(index).role == UniformRole::NameRole; - return {}; + case Role::IsDescriptionRole: + return mapToSource(index).role == UniformRole::DescriptionRole; + + default: + return {}; + } } QVariant EffectComposerUniformsTableModel::headerData( diff --git a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h index 5472818324f..defd88176d0 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformstablemodel.h @@ -30,6 +30,7 @@ public: enum Role { ValueRole = Qt::UserRole + 1, ValueTypeRole, + IsDescriptionRole, CanCopyRole, }; From ad8bafc2726dfec057fd63503603184b95f03d36 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 3 Dec 2024 14:52:26 +0200 Subject: [PATCH 222/322] EffectComposer: Remove old code editor button for the main effect Task-number: QDS-14278 Change-Id: Ibe2d0885d6d6a9ec1e4ea8a6f926d531089e5b2e Reviewed-by: Miikka Heikkinen --- .../EffectComposerTopBar.qml | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index dc2437c0c84..509bada3795 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -58,36 +58,6 @@ Rectangle { onClicked: root.saveAsClicked() } - HelperWidgets.AbstractButton { - id: openCodeEditorButton - objectName: "btnOpenCodeEditor" - - property bool codeEditorOpen: root.backendModel - && (root.backendModel.codeEditorIndex - === root.backendModel.mainCodeEditorIndex) - property color buttonIconColor: openCodeEditorButton.codeEditorOpen - ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - style: StudioTheme.ViewBarButtonStyle { - icon: StudioTheme.ControlStyle.IconColors { - idle: openCodeEditorButton.buttonIconColor - hover: openCodeEditorButton.buttonIconColor - interaction: openCodeEditorButton.buttonIconColor - disabled: StudioTheme.Values.themeToolbarIcon_blocked - } - } - - buttonIcon: StudioTheme.Constants.codeEditor_medium - tooltip: qsTr("Open code editor") - enabled: root.backendModel ? root.backendModel.isEnabled - && root.backendModel.currentComposition !== "" - : false - visible: root.backendModel ? root.backendModel.advancedMode : false - - onClicked: root.backendModel.openMainCodeEditor() - - } - HelperWidgets.AbstractButton { objectName: "btnAssignCompositionToItem" From 12ea0d20663a82326b88d50dd4159722ac32254c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 27 Nov 2024 19:03:22 +0100 Subject: [PATCH 223/322] QmlDesigner: Remove device manager TextField Remove unused TextField component in device manager. Change-Id: Ic5e7963eb0ad99ed1d99efc3b241b19e3d2f0437 Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- .../DeviceManagerControls/TextField.qml | 208 ------------------ .../imports/DeviceManagerControls/qmldir | 1 - 2 files changed, 209 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml deleted file mode 100644 index 38b5d5ee30e..00000000000 --- a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/TextField.qml +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) 2024 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 as StudioTheme - -T.TextField { - id: control - - property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle - - // This property is used to indicate the global hover state - property bool hover: (actionIndicator.hover || mouseArea.containsMouse || indicator.hover - || translationIndicator.hover) && control.enabled - property bool edit: control.activeFocus - - signal rejected - - horizontalAlignment: Qt.AlignLeft - verticalAlignment: Qt.AlignVCenter - - font.pixelSize: control.style.baseFontSize - - color: control.style.text.idle - selectionColor: control.style.text.selection - selectedTextColor: control.style.text.selectedText - placeholderTextColor: control.style.text.placeholder - - readOnly: false - selectByMouse: true - persistentSelection: contextMenu.visible || control.focus - - width: control.style.controlSize.width - height: control.style.controlSize.height - implicitHeight: control.style.controlSize.height - - leftPadding: control.style.inputHorizontalPadding + actionIndicator.width - rightPadding: control.style.inputHorizontalPadding + translationIndicator.width + indicator.width - - MouseArea { - id: mouseArea - anchors.fill: parent - enabled: true - hoverEnabled: true - propagateComposedEvents: true - acceptedButtons: Qt.NoButton - cursorShape: Qt.PointingHandCursor - } - - onPressed: function(event) { - if (event.button === Qt.RightButton) - contextMenu.popup(control) - } - - onActiveFocusChanged: { - // OtherFocusReason in this case means, if the TextField gets focus after the context menu - // was closed due to an menu item click. - if (control.activeFocus && control.focusReason !== Qt.OtherFocusReason) - control.preFocusText = control.text - - if (!control.activeFocus) - control.deselect() - } - - onEditChanged: { - if (control.edit) - contextMenu.close() - } - - onEditingFinished: control.focus = false - - Text { - id: placeholder - x: control.leftPadding - y: control.topPadding - width: control.width - (control.leftPadding + control.rightPadding) - height: control.height - (control.topPadding + control.bottomPadding) - - text: control.placeholderText - font: control.font - color: control.placeholderTextColor - verticalAlignment: control.verticalAlignment - visible: !control.length && !control.preeditText - && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) - elide: Text.ElideRight - renderType: control.renderType - } - - background: Rectangle { - id: textFieldBackground - color: control.style.background.idle - border.color: control.style.border.idle - border.width: control.style.borderWidth - x: actionIndicator.width - width: control.width - actionIndicator.width - height: control.height - } - - Indicator { - id: indicator - style: control.style - visible: false - x: control.width - translationIndicator.width - indicator.width - width: indicator.visible ? control.height : 0 - height: indicator.visible ? control.height : 0 - } - - TranslationIndicator { - id: translationIndicator - style: control.style - __parentControl: control - x: control.width - translationIndicator.width - width: translationIndicator.visible ? __translationIndicatorWidth : 0 - height: translationIndicator.visible ? __translationIndicatorHeight : 0 - } - - states: [ - State { - name: "default" - when: control.enabled && !control.hover && !control.edit && !contextMenu.visible - PropertyChanges { - target: textFieldBackground - color: control.style.background.idle - border.color: control.style.border.idle - } - PropertyChanges { - target: control - color: control.style.text.idle - placeholderTextColor: control.style.text.placeholder - } - PropertyChanges { - target: mouseArea - cursorShape: Qt.PointingHandCursor - } - }, - State { - name: "globalHover" - when: (actionIndicator.hover || translationIndicator.hover || indicator.hover) - && !control.edit && control.enabled && !contextMenu.visible - PropertyChanges { - target: textFieldBackground - color: control.style.background.globalHover - border.color: control.style.border.idle - } - PropertyChanges { - target: control - color: control.style.text.idle - placeholderTextColor: control.style.text.placeholder - } - }, - State { - name: "hover" - when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover - && !indicator.hover && !control.edit && control.enabled && !contextMenu.visible - PropertyChanges { - target: textFieldBackground - color: control.style.background.hover - border.color: control.style.border.hover - } - PropertyChanges { - target: control - color: control.style.text.hover - placeholderTextColor: control.style.text.placeholder - } - }, - State { - name: "edit" - when: control.edit || contextMenu.visible - PropertyChanges { - target: textFieldBackground - color: control.style.background.interaction - border.color: control.style.border.interaction - } - PropertyChanges { - target: control - color: control.style.text.idle - placeholderTextColor: control.style.text.placeholder - } - PropertyChanges { - target: mouseArea - cursorShape: Qt.IBeamCursor - } - }, - State { - name: "disable" - when: !control.enabled - PropertyChanges { - target: textFieldBackground - color: control.style.background.disabled - border.color: control.style.border.disabled - } - PropertyChanges { - target: control - color: control.style.text.disabled - placeholderTextColor: control.style.text.disabled - } - } - ] - - Keys.onEscapePressed: function(event) { - event.accepted = true - control.text = control.preFocusText - control.rejected() - control.focus = false - } -} diff --git a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir index de636d34458..052f4237d39 100644 --- a/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir +++ b/share/qtcreator/qmldesigner/devicemanager/imports/DeviceManagerControls/qmldir @@ -1,5 +1,4 @@ ButtonInput 1.0 ButtonInput.qml Dropdown 1.0 Dropdown.qml Switch 1.0 Switch.qml -TextField 1.0 TextField.qml ToolbarButton 1.0 ToolbarButton.qml From 0f4b3345340d5e2589e74d1f55b75cc67df63c7c Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Fri, 29 Nov 2024 11:54:41 +0200 Subject: [PATCH 224/322] Doc: Update telemetry plugin doc QDS is using Qt Insight as telemetry plugin. Change-Id: I0ec490bc14752150a43fd7673042450e4b9c07de Reviewed-by: Johanna Vanhatapio --- .../src/how-to/qtdesignstudio-usage-statistics.qdoc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc index 750d2d11c6b..e81be5df869 100644 --- a/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc +++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc @@ -37,15 +37,9 @@ \title Collecting Usage Statistics - The telemetry plugin uses the - \l{https://api.kde.org/frameworks/kuserfeedback/html/index.html} - {KUserFeedback} framework to collect the usage data. The library - has been designed from the user data privacy point of view and - \QC respects the same privacy rules. - - The data is transmitted to the backend storage using an encrypted - connection. The storage is located in the same Heroku backend as the - \QOI backend. Physically, data is stored in the Amazon cloud. + The telemetry plugin uses Qt Insight, an analytics solution for collecting usage data in + Qt applications. The Qt Insight Tracker library ensures compliance with GDPR regulations. + Data is securely transmitted to a centralized backend. \section1 Turning on Telemetry From dc8979a351b1f9c4b0be37a9db6be24bfb3c00a5 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 3 Dec 2024 17:49:44 +0200 Subject: [PATCH 225/322] EffectComposer: Decrease the spacing for nodes buttons The spacing for the section buttons `Show Code` and `Add Property` is decreased. Task-number: QDS-14288 Change-Id: I56aea32f18f34ddcc5485a60997114b31c283c11 Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/EffectCompositionNode.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index 20612d52da1..1dddffc7502 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -285,7 +285,7 @@ HelperWidgets.Section { height: 40 visible: (root.backendModel.advancedMode || isCustom) && !isDependency && !addPropertyForm.visible anchors.horizontalCenter: parent.horizontalCenter - spacing: 10 + spacing: 5 HelperWidgets.Button { width: 100 From 3e5b2cbaf84b7ccee27a3050260d369bedce2c08 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 3 Dec 2024 16:52:24 +0100 Subject: [PATCH 226/322] QmlProjectExporter: Use QApplication when generating main.cpp Fixes: QDS-14268 Change-Id: I7c8ec227a10f5b689ab72cdad84bfed671773d7e Reviewed-by: Thomas Hartmann --- .../qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp | 3 ++- .../qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp | 3 ++- .../qmlprojectexporter/templates/main_cpp_v1.tpl | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp index 55f0ee209f1..d728a22bf4f 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriter.cpp @@ -204,7 +204,8 @@ QString CMakeWriter::getEnvironmentVariable(const QString &key) const QString CMakeWriter::makeFindPackageBlock(const NodePtr &node, const QmlBuildSystem *buildSystem) const { QString head = "find_package(Qt" + buildSystem->versionQt(); - QString tail = " REQUIRED COMPONENTS Core Gui Qml Quick QuickTimeline ShaderTools"; + QString tail = " REQUIRED COMPONENTS Core Gui Widgets Qml Quick QuickTimeline ShaderTools"; + if (hasMesh(node) || hasQuick3dImport(buildSystem->mainUiFilePath())) tail.append(" Quick3D"); tail.append(")\n"); diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp index 74d078abbbe..7c81984d116 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/cmakewriterv1.cpp @@ -13,11 +13,12 @@ namespace QmlProjectExporter { const char TEMPLATE_SRC_CMAKELISTS[] = R"( target_sources(${CMAKE_PROJECT_NAME} PUBLIC -%2) +%1) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Qml))"; diff --git a/src/plugins/qmlprojectmanager/qmlprojectexporter/templates/main_cpp_v1.tpl b/src/plugins/qmlprojectmanager/qmlprojectexporter/templates/main_cpp_v1.tpl index 4d2a526d763..95236a5023c 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectexporter/templates/main_cpp_v1.tpl +++ b/src/plugins/qmlprojectmanager/qmlprojectexporter/templates/main_cpp_v1.tpl @@ -1,7 +1,7 @@ // Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include +#include #include #include "autogen/environment.h" @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { set_qt_environment(); - QGuiApplication app(argc, argv); + QApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(mainQmlFile); From abc0f704da3c99abaa925b1999d043a404b16a2e Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 28 Nov 2024 17:01:51 +0100 Subject: [PATCH 227/322] QmlDesigner: Update design viewer UX and UI * Add new icons * Add new popup window in toolbar to show design viewer related content * Fix MenuItemDelegate * Add ShareNotification to show upload progress and failure Task-number: QDS-12257 Change-Id: I921944732937758b97e511c8a243e211d379d8fc Reviewed-by: Thomas Hartmann --- .../imports/StudioTheme/InternalConstants.qml | 442 +++++++++--------- .../StudioTheme/TopToolbarButtonStyle.qml | 1 - .../imports/StudioTheme/icons.ttf | Bin 65872 -> 67228 bytes share/qtcreator/qmldesigner/toolbar/Main.qml | 306 +++++++++++- .../qmldesigner/toolbar/MenuItemDelegate.qml | 7 +- .../qmldesigner/toolbar/ShareNotification.qml | 203 ++++++++ .../components/componentcore/theme.h | 8 + .../components/designviewer/dvconnector.cpp | 10 +- .../components/designviewer/dvconnector.h | 8 +- .../designviewer/resourcegeneratorproxy.cpp | 2 +- .../components/toolbar/toolbarbackend.cpp | 11 + .../components/toolbar/toolbarbackend.h | 7 + 12 files changed, 754 insertions(+), 251 deletions(-) create mode 100644 share/qtcreator/qmldesigner/toolbar/ShareNotification.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index e606d48b30f..edbcd62304d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -165,223 +165,231 @@ QtObject { readonly property string fitSelected_small: "\u00B8" readonly property string fitSelection_medium: "\u00B9" readonly property string fitToView_medium: "\u00BA" - readonly property string flowAction: "\u00BB" - readonly property string flowTransition: "\u00BC" - readonly property string fontStyleBold: "\u00BD" - readonly property string fontStyleItalic: "\u00BE" - readonly property string fontStyleStrikethrough: "\u00BF" - readonly property string fontStyleUnderline: "\u00C0" - readonly property string forward_medium: "\u00C1" - readonly property string globalOrient_medium: "\u00C2" - readonly property string gradient: "\u00C3" - readonly property string gridView: "\u00C4" - readonly property string grid_medium: "\u00C5" - readonly property string group_small: "\u00C6" - readonly property string help: "\u00C7" - readonly property string home_large: "\u00C8" - readonly property string idAliasOff: "\u00C9" - readonly property string idAliasOn: "\u00CA" - readonly property string import_medium: "\u00CB" - readonly property string imported: "\u00CC" - readonly property string importedModels_small: "\u00CD" - readonly property string infinity: "\u00CE" - readonly property string invisible_medium: "\u00CF" - readonly property string invisible_small: "\u00D0" - readonly property string jumpToCode_medium: "\u00D1" - readonly property string jumpToCode_small: "\u00D2" - readonly property string keyframe: "\u00D3" - readonly property string languageList_medium: "\u00D4" - readonly property string layouts_small: "\u00D5" - readonly property string lights_small: "\u00D6" - readonly property string linear_medium: "\u00D7" - readonly property string linkTriangle: "\u00D8" - readonly property string linked: "\u00D9" - readonly property string listView: "\u00DA" - readonly property string listView_medium: "\u00DB" - readonly property string list_medium: "\u00DC" - readonly property string localOrient_medium: "\u00DD" - readonly property string lockOff: "\u00DE" - readonly property string lockOn: "\u00DF" - readonly property string loopPlayback_medium: "\u00E0" - readonly property string materialBrowser_medium: "\u00E1" - readonly property string materialPreviewEnvironment: "\u00E2" - readonly property string materialPreviewModel: "\u00E3" - readonly property string material_medium: "\u00E4" - readonly property string maxBar_small: "\u00E5" - readonly property string mergeCells: "\u00E6" - readonly property string merge_small: "\u00E7" - readonly property string minus: "\u00E8" - readonly property string mirror: "\u00E9" - readonly property string more_medium: "\u00EA" - readonly property string mouseArea_small: "\u00EB" - readonly property string moveDown_medium: "\u00EC" - readonly property string moveInwards_medium: "\u00ED" - readonly property string moveUp_medium: "\u00EE" - readonly property string moveUpwards_medium: "\u00EF" - readonly property string move_medium: "\u00F0" - readonly property string newMaterial: "\u00F1" - readonly property string nextFile_large: "\u00F2" - readonly property string normalBar_small: "\u00F3" - readonly property string openLink: "\u00F4" - readonly property string openMaterialBrowser: "\u00F5" - readonly property string orientation: "\u00F6" - readonly property string orthCam_medium: "\u00F7" - readonly property string orthCam_small: "\u00F8" - readonly property string paddingEdge: "\u00F9" - readonly property string paddingFrame: "\u00FA" - readonly property string particleAnimation_medium: "\u00FB" - readonly property string pasteStyle: "\u00FC" - readonly property string paste_small: "\u00FD" - readonly property string pause: "\u00FE" - readonly property string pause_medium: "\u00FF" - readonly property string perspectiveCam_medium: "\u0100" - readonly property string perspectiveCam_small: "\u0101" - readonly property string pin: "\u0102" - readonly property string plane_medium: "\u0103" - readonly property string plane_small: "\u0104" - readonly property string play: "\u0105" - readonly property string playFill_medium: "\u0106" - readonly property string playOutline_medium: "\u0107" - readonly property string plus: "\u0108" - readonly property string pointLight_small: "\u0109" - readonly property string positioners_small: "\u010A" - readonly property string previewEnv_medium: "\u010B" - readonly property string previousFile_large: "\u010C" - readonly property string promote: "\u010D" - readonly property string properties_medium: "\u010E" - readonly property string readOnly: "\u010F" - readonly property string recent_medium: "\u0110" - readonly property string recordFill_medium: "\u0111" - readonly property string recordOutline_medium: "\u0112" - readonly property string redo: "\u0113" - readonly property string reload_medium: "\u0114" - readonly property string remove_medium: "\u0115" - readonly property string remove_small: "\u0116" - readonly property string rename_small: "\u0117" - readonly property string replace_small: "\u0118" - readonly property string resetView_small: "\u0119" - readonly property string restartParticles_medium: "\u011A" - readonly property string reverseOrder_medium: "\u011B" - readonly property string roatate_medium: "\u011C" - readonly property string rotationFill: "\u011D" - readonly property string rotationOutline: "\u011E" - readonly property string runProjFill_large: "\u011F" - readonly property string runProjOutline_large: "\u0120" - readonly property string s_anchors: "\u0121" - readonly property string s_annotations: "\u0122" - readonly property string s_arrange: "\u0123" - readonly property string s_boundingBox: "\u0124" - readonly property string s_component: "\u0125" - readonly property string s_connections: "\u0126" - readonly property string s_edit: "\u0127" - readonly property string s_enterComponent: "\u0128" - readonly property string s_eventList: "\u0129" - readonly property string s_group: "\u012A" - readonly property string s_layouts: "\u012B" - readonly property string s_merging: "\u012C" - readonly property string s_mouseArea: "\u012D" - readonly property string s_positioners: "\u012E" - readonly property string s_selection: "\u012F" - readonly property string s_snapping: "\u0130" - readonly property string s_timeline: "\u0131" - readonly property string s_visibility: "\u0132" - readonly property string saveAs_medium: "\u0133" - readonly property string saveLogs_medium: "\u0134" - readonly property string save_medium: "\u0135" - readonly property string scale_medium: "\u0136" - readonly property string search: "\u0137" - readonly property string search_small: "\u0138" - readonly property string sectionToggle: "\u0139" - readonly property string selectFill_medium: "\u013A" - readonly property string selectOutline_medium: "\u013B" - readonly property string selectParent_small: "\u013C" - readonly property string selection_small: "\u013D" - readonly property string settings_medium: "\u013E" - readonly property string signal_small: "\u013F" - readonly property string snapping_conf_medium: "\u0140" - readonly property string snapping_medium: "\u0141" - readonly property string snapping_small: "\u0142" - readonly property string sortascending_medium: "\u0143" - readonly property string sortdescending_medium: "\u0144" - readonly property string sphere_medium: "\u0145" - readonly property string sphere_small: "\u0146" - readonly property string splitColumns: "\u0147" - readonly property string splitRows: "\u0148" - readonly property string splitScreen_medium: "\u0149" - readonly property string spotLight_small: "\u014A" - readonly property string stackedContainer_small: "\u014B" - readonly property string startNode: "\u014C" - readonly property string step_medium: "\u014D" - readonly property string stop_medium: "\u014E" - readonly property string tableView_medium: "\u014F" - readonly property string testIcon: "\u0150" - readonly property string textAlignBottom: "\u0151" - readonly property string textAlignCenter: "\u0152" - readonly property string textAlignJustified: "\u0153" - readonly property string textAlignLeft: "\u0154" - readonly property string textAlignMiddle: "\u0155" - readonly property string textAlignRight: "\u0156" - readonly property string textAlignTop: "\u0157" - readonly property string textBulletList: "\u0158" - readonly property string textFullJustification: "\u0159" - readonly property string textNumberedList: "\u015A" - readonly property string textures_medium: "\u015B" - readonly property string tickIcon: "\u015C" - readonly property string tickMark_small: "\u015D" - readonly property string timeline_small: "\u015E" - readonly property string toEndFrame_medium: "\u015F" - readonly property string toNextFrame_medium: "\u0160" - readonly property string toPrevFrame_medium: "\u0161" - readonly property string toStartFrame_medium: "\u0162" - readonly property string topToolbar_annotations: "\u0163" - readonly property string topToolbar_closeFile: "\u0164" - readonly property string topToolbar_designMode: "\u0165" - readonly property string topToolbar_enterComponent: "\u0166" - readonly property string topToolbar_home: "\u0167" - readonly property string topToolbar_makeComponent: "\u0168" - readonly property string topToolbar_navFile: "\u0169" - readonly property string topToolbar_runProject: "\u016A" - readonly property string translationCreateFiles: "\u016B" - readonly property string translationCreateReport: "\u016C" - readonly property string translationExport: "\u016D" - readonly property string translationImport: "\u016E" - readonly property string translationSelectLanguages: "\u016F" - readonly property string translationTest: "\u0170" - readonly property string transparent: "\u0171" - readonly property string trash_medium: "\u0172" - readonly property string triState: "\u0173" - readonly property string triangleArcA: "\u0174" - readonly property string triangleArcB: "\u0175" - readonly property string triangleCornerA: "\u0176" - readonly property string triangleCornerB: "\u0177" - readonly property string unLinked: "\u0178" - readonly property string undo: "\u0179" - readonly property string unify_medium: "\u017A" - readonly property string unpin: "\u017B" - readonly property string upDownIcon: "\u017C" - readonly property string upDownSquare2: "\u017D" - readonly property string updateAvailable_medium: "\u017E" - readonly property string updateContent_medium: "\u017F" - readonly property string visibilityOff: "\u0180" - readonly property string visibilityOn: "\u0181" - readonly property string visible_medium: "\u0182" - readonly property string visible_small: "\u0183" - readonly property string warning2_medium: "\u0184" - readonly property string warning_medium: "\u0185" - readonly property string wildcard: "\u0186" - readonly property string wizardsAutomotive: "\u0187" - readonly property string wizardsDesktop: "\u0188" - readonly property string wizardsGeneric: "\u0189" - readonly property string wizardsMcuEmpty: "\u018A" - readonly property string wizardsMcuGraph: "\u018B" - readonly property string wizardsMobile: "\u018C" - readonly property string wizardsUnknown: "\u018D" - readonly property string zoomAll: "\u018E" - readonly property string zoomIn: "\u018F" - readonly property string zoomIn_medium: "\u0190" - readonly property string zoomOut: "\u0191" - readonly property string zoomOut_medium: "\u0192" - readonly property string zoomSelection: "\u0193" + readonly property string flag_medium: "\u00BB" + readonly property string flowAction: "\u00BC" + readonly property string flowTransition: "\u00BD" + readonly property string fontStyleBold: "\u00BE" + readonly property string fontStyleItalic: "\u00BF" + readonly property string fontStyleStrikethrough: "\u00C0" + readonly property string fontStyleUnderline: "\u00C1" + readonly property string forward_medium: "\u00C2" + readonly property string globalOrient_medium: "\u00C3" + readonly property string gradient: "\u00C4" + readonly property string gridView: "\u00C5" + readonly property string grid_medium: "\u00C6" + readonly property string group_small: "\u00C7" + readonly property string help: "\u00C8" + readonly property string home_large: "\u00C9" + readonly property string idAliasOff: "\u00CA" + readonly property string idAliasOn: "\u00CB" + readonly property string import_medium: "\u00CC" + readonly property string imported: "\u00CD" + readonly property string importedModels_small: "\u00CE" + readonly property string infinity: "\u00CF" + readonly property string invisible_medium: "\u00D0" + readonly property string invisible_small: "\u00D1" + readonly property string jumpToCode_medium: "\u00D2" + readonly property string jumpToCode_small: "\u00D3" + readonly property string keyframe: "\u00D4" + readonly property string languageList_medium: "\u00D5" + readonly property string layouts_small: "\u00D6" + readonly property string lights_small: "\u00D7" + readonly property string linear_medium: "\u00D8" + readonly property string linkTriangle: "\u00D9" + readonly property string linked: "\u00DA" + readonly property string listView: "\u00DB" + readonly property string listView_medium: "\u00DC" + readonly property string list_medium: "\u00DD" + readonly property string localOrient_medium: "\u00DE" + readonly property string lockOff: "\u00DF" + readonly property string lockOn: "\u00E0" + readonly property string loopPlayback_medium: "\u00E1" + readonly property string materialBrowser_medium: "\u00E2" + readonly property string materialPreviewEnvironment: "\u00E3" + readonly property string materialPreviewModel: "\u00E4" + readonly property string material_medium: "\u00E5" + readonly property string maxBar_small: "\u00E6" + readonly property string mergeCells: "\u00E7" + readonly property string merge_small: "\u00E8" + readonly property string minus: "\u00E9" + readonly property string mirror: "\u00EA" + readonly property string more_medium: "\u00EB" + readonly property string mouseArea_small: "\u00EC" + readonly property string moveDown_medium: "\u00ED" + readonly property string moveInwards_medium: "\u00EE" + readonly property string moveUp_medium: "\u00EF" + readonly property string moveUpwards_medium: "\u00F0" + readonly property string move_medium: "\u00F1" + readonly property string newMaterial: "\u00F2" + readonly property string nextFile_large: "\u00F3" + readonly property string normalBar_small: "\u00F4" + readonly property string number_medium: "\u00F5" + readonly property string openLink: "\u00F6" + readonly property string openMaterialBrowser: "\u00F7" + readonly property string orientation: "\u00F8" + readonly property string orthCam_medium: "\u00F9" + readonly property string orthCam_small: "\u00FA" + readonly property string paddingEdge: "\u00FB" + readonly property string paddingFrame: "\u00FC" + readonly property string particleAnimation_medium: "\u00FD" + readonly property string pasteStyle: "\u00FE" + readonly property string paste_small: "\u00FF" + readonly property string pause: "\u0100" + readonly property string pause_medium: "\u0101" + readonly property string perspectiveCam_medium: "\u0102" + readonly property string perspectiveCam_small: "\u0103" + readonly property string pin: "\u0104" + readonly property string plane_medium: "\u0105" + readonly property string plane_small: "\u0106" + readonly property string play: "\u0107" + readonly property string playFill_medium: "\u0108" + readonly property string playOutline_medium: "\u0109" + readonly property string plus: "\u010A" + readonly property string pointLight_small: "\u010B" + readonly property string positioners_small: "\u010C" + readonly property string previewEnv_medium: "\u010D" + readonly property string previousFile_large: "\u010E" + readonly property string promote: "\u010F" + readonly property string properties_medium: "\u0110" + readonly property string readOnly: "\u0111" + readonly property string recent_medium: "\u0112" + readonly property string recordFill_medium: "\u0113" + readonly property string recordOutline_medium: "\u0114" + readonly property string redo: "\u0115" + readonly property string reload_medium: "\u0116" + readonly property string remove_medium: "\u0117" + readonly property string remove_small: "\u0118" + readonly property string rename_small: "\u0119" + readonly property string replace_small: "\u011A" + readonly property string resetView_small: "\u011B" + readonly property string restartParticles_medium: "\u011C" + readonly property string reverseOrder_medium: "\u011D" + readonly property string roatate_medium: "\u011E" + readonly property string rotationFill: "\u011F" + readonly property string rotationOutline: "\u0120" + readonly property string runProjFill_large: "\u0121" + readonly property string runProjOutline_large: "\u0122" + readonly property string s_anchors: "\u0123" + readonly property string s_annotations: "\u0124" + readonly property string s_arrange: "\u0125" + readonly property string s_boundingBox: "\u0126" + readonly property string s_component: "\u0127" + readonly property string s_connections: "\u0128" + readonly property string s_edit: "\u0129" + readonly property string s_enterComponent: "\u012A" + readonly property string s_eventList: "\u012B" + readonly property string s_group: "\u012C" + readonly property string s_layouts: "\u012D" + readonly property string s_merging: "\u012E" + readonly property string s_mouseArea: "\u012F" + readonly property string s_positioners: "\u0130" + readonly property string s_selection: "\u0131" + readonly property string s_snapping: "\u0132" + readonly property string s_timeline: "\u0133" + readonly property string s_visibility: "\u0134" + readonly property string saveAs_medium: "\u0135" + readonly property string saveLogs_medium: "\u0136" + readonly property string save_medium: "\u0137" + readonly property string scale_medium: "\u0138" + readonly property string search: "\u0139" + readonly property string search_small: "\u013A" + readonly property string sectionToggle: "\u013B" + readonly property string selectFill_medium: "\u013C" + readonly property string selectOutline_medium: "\u013D" + readonly property string selectParent_small: "\u013E" + readonly property string selection_small: "\u013F" + readonly property string settings_medium: "\u0140" + readonly property string share_large: "\u0141" + readonly property string signal_small: "\u0142" + readonly property string signin_medium: "\u0143" + readonly property string signout_medium: "\u0144" + readonly property string snapping_conf_medium: "\u0145" + readonly property string snapping_medium: "\u0146" + readonly property string snapping_small: "\u0147" + readonly property string sortascending_medium: "\u0148" + readonly property string sortdescending_medium: "\u0149" + readonly property string sphere_medium: "\u014A" + readonly property string sphere_small: "\u014B" + readonly property string splitColumns: "\u014C" + readonly property string splitRows: "\u014D" + readonly property string splitScreen_medium: "\u014E" + readonly property string spotLight_small: "\u014F" + readonly property string stackedContainer_small: "\u0150" + readonly property string startNode: "\u0151" + readonly property string step_medium: "\u0152" + readonly property string stop_medium: "\u0153" + readonly property string string_medium: "\u0154" + readonly property string tableView_medium: "\u0155" + readonly property string testIcon: "\u0156" + readonly property string textAlignBottom: "\u0157" + readonly property string textAlignCenter: "\u0158" + readonly property string textAlignJustified: "\u0159" + readonly property string textAlignLeft: "\u015A" + readonly property string textAlignMiddle: "\u015B" + readonly property string textAlignRight: "\u015C" + readonly property string textAlignTop: "\u015D" + readonly property string textBulletList: "\u015E" + readonly property string textFullJustification: "\u015F" + readonly property string textNumberedList: "\u0160" + readonly property string textures_medium: "\u0161" + readonly property string tickIcon: "\u0162" + readonly property string tickMark_small: "\u0163" + readonly property string timeline_small: "\u0164" + readonly property string toEndFrame_medium: "\u0165" + readonly property string toNextFrame_medium: "\u0166" + readonly property string toPrevFrame_medium: "\u0167" + readonly property string toStartFrame_medium: "\u0168" + readonly property string topToolbar_annotations: "\u0169" + readonly property string topToolbar_closeFile: "\u016A" + readonly property string topToolbar_designMode: "\u016B" + readonly property string topToolbar_enterComponent: "\u016C" + readonly property string topToolbar_home: "\u016D" + readonly property string topToolbar_makeComponent: "\u016E" + readonly property string topToolbar_navFile: "\u016F" + readonly property string topToolbar_runProject: "\u0170" + readonly property string translationCreateFiles: "\u0171" + readonly property string translationCreateReport: "\u0172" + readonly property string translationExport: "\u0173" + readonly property string translationImport: "\u0174" + readonly property string translationSelectLanguages: "\u0175" + readonly property string translationTest: "\u0176" + readonly property string transparent: "\u0177" + readonly property string trash_medium: "\u0178" + readonly property string triState: "\u0179" + readonly property string triangleArcA: "\u017A" + readonly property string triangleArcB: "\u017B" + readonly property string triangleCornerA: "\u017C" + readonly property string triangleCornerB: "\u017D" + readonly property string unLinked: "\u017E" + readonly property string undo: "\u017F" + readonly property string unify_medium: "\u0180" + readonly property string unpin: "\u0181" + readonly property string upDownIcon: "\u0182" + readonly property string upDownSquare2: "\u0183" + readonly property string updateAvailable_medium: "\u0184" + readonly property string updateContent_medium: "\u0185" + readonly property string upload_medium: "\u0186" + readonly property string user_medium: "\u0187" + readonly property string visibilityOff: "\u0188" + readonly property string visibilityOn: "\u0189" + readonly property string visible_medium: "\u018A" + readonly property string visible_small: "\u018B" + readonly property string warning2_medium: "\u018C" + readonly property string warning_medium: "\u018D" + readonly property string wildcard: "\u018E" + readonly property string wizardsAutomotive: "\u018F" + readonly property string wizardsDesktop: "\u0190" + readonly property string wizardsGeneric: "\u0191" + readonly property string wizardsMcuEmpty: "\u0192" + readonly property string wizardsMcuGraph: "\u0193" + readonly property string wizardsMobile: "\u0194" + readonly property string wizardsUnknown: "\u0195" + readonly property string zoomAll: "\u0196" + readonly property string zoomIn: "\u0197" + readonly property string zoomIn_medium: "\u0198" + readonly property string zoomOut: "\u0199" + readonly property string zoomOut_medium: "\u019A" + readonly property string zoomSelection: "\u019B" readonly property font iconFont: Qt.font({ family: controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml index 594dc033369..943707b3975 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml @@ -4,7 +4,6 @@ import QtQuick ControlStyle { - controlSize: Qt.size(Values.topLevelComboWidth, Values.topLevelComboHeight) borderWidth: Values.border baseIconFontSize: Values.topLevelComboIcon diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 485c4bbf4d7ab38c88c5316a8bd75ea9dcea04f2..b26e73aa2afade590098fd36f8db631d2ae9e83a 100644 GIT binary patch delta 2336 zcmcc6#4@LsrJjM2fq{XSp@D&!A;8To#CK&~v?Bu}TLS|FgN%Q$zER|YGXWdceTIbh#kExa53`sR9GT+bs+X5=X$EV`O0XQTwt#p5Nvx12^*v1_lO($NAI$ zLTIMxet%GEbf@SZ(Y>Xoq8FrBrFTW&KtIiZ&%nXpf}x&ap5Z#fUq%u} zvy9#tyBRMszGEU|Vq)^e)XKEa^p5E#GYhjB<_zWm<}1wKSQuGMv$U{WW%r+hTjjPQ|Xu?wP%seV6^adi!4vd=85oE;*Vw zdN?j|Qgd42^ubxpIl(#4xx;yt^9`3Mmw7I~Tvc4dT&K9nxYf8#b35kt&)ve^&pplk zfQN#IpT{0g9#1dND$gaJ$2|Xd<#{dgmhj%>!{X!O)8q5WH_W%g_ky2}Ux?ofzYqRS z{%!t80z3ld1l$V@3Tz9k-xqi$@I#PZP+8EW;B_GaA=^SZLgT{t!ji&vgsX*53;z&d z6p;|o7jYvpCh|<=tthRixTqP?OwkI_WzmOXcw!=A*2Vma4TxP6`y*~j+`o9Gc%S$Q z@rUBSBxoc=CCo{FdU}9#h zXe1`WrmU!BW}*g?F)|ZjQ&KZAGy3J1m*>a$1w{T8nv}u#C1cX0jDI{CPgRXoRgF`; z(mg%X8DHeh$;+FQ_wUYyNs}&2dSIfWVxm%%;pv&-$-uzKz_xiqvbSPA!~baJ7fgQ{ ztQqVX7{v4#?HQFpHdr$XD}rcaMRPkwIYxd)7;Ox4kv1bU)Me~^j37OV>}?sxh37eq4(nlXz@3ox571{i=DdV4{_r(GENLG(YT$LdTVd6j<~Rhdm0 z16=-XSjr~Gw@g@Cgwb7CTKF{|KbsieJt!qCE&Ok%h_vu#u;{YQe2mPD?wfzKh-%lf zFzEfaVBXBEz@Wikz+l1Pz`($$q|PcPYAC|aY6f<-lA5WBxhfmjf5t{);;L$WW4BWfZdGQ(_b z!|eV#J_FOtnzmtfc43TP%+5+k%1IwJ11YhCG8ARbNXvoE(X|7q`j-Ns7J)c+n>jZG zFxE5u|Nq~d`318ggE)gZ1A~g1i9I7UBGlDP%*~ACnfMvm7>&e41;GhJ&6W|IOw3FL zP1KYXl^}dZFF}r)e@VPTLcEOgYB)H!q&PQ-iEZFwa*P@aZYArwwR5F{z6C<@AGrhbAv%nCdL0uqA%Rx&a&@(2p@F#bKtBOt)T$oOxiptvA2Gf3Rnm=8F;({QtvNhZS0^AI|&`?rf;NP6GTU17di9!Cq1@i@FMFwsLWd;T@kWFBF z6_pq#C*BsVH#3qKJ3r0gyy12Z@a>|j=45M_{MP-S2+XA@O6F*CPgG!he+V-!_3QP*Qs zViSUv|Wc)S4n5Vva+&erHrPxX3e@aiz}oqv{hGA zTU%4NHMA~7MO~dysSKoG%Pf#Q!{+kqXZY0380Eng9|HpmgCt`Yl+DT@z<3PGW@C_K z;+~xKOjkXJVI!!%X5nODVK8D0gR)r}6d0F4*=!6(jMq2sd&a}aZpom_V8~!N`RsE` zPD@=wLtP^SBa_K}F9IgVzG#~K@Pz_n&1R;TF^nvXYZ)d@PI_g)!g!3~{p4w{Y}go| zGCpJA+kE4d5TgK70aGDU5mPZ!2~#Ok8B_V@SFfLOuyUv6B&KgZ`9+eMk2kM0Hz~C! zJ~uTbvov?}qwfMNX57UYiAAaLIf+H-sXWD*>3NAc@x{4`IXS!_PG%ldF&~JRUs?j? zPX7P(rGK%sb+6B}90sxr0 BhRy&0 delta 1011 zcmbQ!%W|QKrJjM2fq{XSp@D&!A;8To#COHi-wPQS8BG`%7-al|^^GDIq}ektFh(#i zFeD`BCKhOg>-IA+u&iKUUm4EH3!}pMjZykx_?%fq^4E zr!vhy_HZ5p1Lp?@hC2!wsfj5%jl#1T7#Qv_Fff>9WTe(7a{c_@%D})V!oa|wl95|d zp>&+zmVtqB2Ll7shn)Q6MEl2VuNWBESr{0YUgaiM6fj<4_|CwOWVNqcJipCX25#mTAeTP+ouLb% znKE=IG1@YgOtxc;Wt_Zu4r4Uq9*<4(S4>TqGzKwP4APwhW-TuCWA=^9}Eo)3k>%d-ZF|YT4&5*oMJrB_@41M zlWit{Oan~knQk%tWfo<2$Xv)g%Y2>r7mEmsUzRzR2dvVpCRp9Dwz3Yg&as|mz0dlM zjhKy#O_9wbTRB@d+ZNkfc5ZfUcCYOH?4#`S>K&LIj2xyotZ-y;Y;)pp3UF$3I^cB9 z>4h_wvxW06=YK9yE-fzGT)w#0xZZL7=BDNr=QhJ_o!c>Y1@{K`bsjPvIUcJ#Zh5kJ zs(HqEp7Ubzs_+)_PVrvi{l+KAXPeIpUjyGB-z~mB`~v(I`91Sj@n7NpC%`UXQ^2!8 zxj>!3`hdW`z-vK9!F<76LQ+C@gz|;fguV%L3+o8m7WO1uFMLykQiMT7L&TOyxk#JH zNs;%WG@?qPPDP7F=R}`~5sBFlD-~-On-RMv_FkMooJ(9;+>W?+@%IvZ64oUAOVmh= zOI(t~k>r-tlFX2-l{_PbDMc#9B*iZ!D`i2-y;L!i)SfhjG@G=5w5GHp>2B#;(m!P= zWaMS6%VfyBmlcuKmCcl0kbNb`CTCjCja;kTlH3D%0-O1`_D62^Oz~6P9Ni|Qz1eV+ z598vqTevnm?pKi6?0M%DpSlU7G&s31Ft9L4GPXe3tPBE-2cT>=21zE?$yTp*)iW42 zf>JpPCj$$E5n}+9&B~y_I1kEZV=!X8xVi5&4
      To be able to use the sharing service, you need to sign in with your Qt Account details.") - onClicked: backend.shareApplicationOnline() + checkable: true + checked: dvWindow.visible + checkedInverted: true + + onClicked: { + if (dvWindow.visible) { + dvWindow.close() + } else { + var originMapped = shareButton.mapToGlobal(0,0) + dvWindow.x = originMapped.x + shareButton.width - dvWindow.width + dvWindow.y = originMapped.y + shareButton.height + dvWindow.show() + dvWindow.requestActivate() + } + } + + Window { + id: dvWindow + + width: 300 + height: stackLayout.children[stackLayout.currentIndex].implicitHeight + + visible: false + flags: Qt.FramelessWindowHint | Qt.Tool | Qt.NoDropShadowWindowHint + modality: Qt.NonModal + transientParent: null + color: "transparent" + + onActiveFocusItemChanged: { + if (dvWindow.activeFocusItem === null && !dvWindow.active + && !shareButton.hover + && !backend.designViewerConnector.isWebViewerVisible) + dvWindow.close() + } + + onVisibleChanged: { + // if visible and logged in + // fetch user info + } + + onClosing: { + if (shareNotification.hasFinished()) + shareNotification.visible = false + } + + function formatBytes(bytes, decimals = 2) { + if (!+bytes) + return '0 Bytes' + + const k = 1024 + const dm = decimals < 0 ? 0 : decimals + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}` + } + + Connections { + target: backend.designViewerConnector + + function onUserInfoReceived(reply: var) { + let jsonReply = JSON.parse(reply) + + loggedInPage.email = jsonReply.email + loggedInPage.license = jsonReply.license + loggedInPage.storageUsed = jsonReply.storageUsed + loggedInPage.storageLimit = jsonReply.storageLimit + } + } + + StackLayout { + id: stackLayout + + property int internalMargin: 8 + + anchors.fill: parent + currentIndex: backend.designViewerConnector.connectorStatus + + // Fetching + Rectangle { + id: fetchingPage + color: StudioTheme.Values.themePopupBackground + Layout.fillWidth: true + implicitHeight: 200 + + BusyIndicator { + anchors.centerIn: parent + running: StackView.status === StackView.Active // TODO test + } + } + + // NotLoggedIn + Rectangle { + id: notLoggedInPage + color: StudioTheme.Values.themePopupBackground + Layout.fillWidth: true + implicitHeight: menuColumn.implicitHeight + + Column { + id: menuColumn + anchors.fill: parent + padding: StudioTheme.Values.border + + MenuItemDelegate { + width: parent.width + + myText: qsTr("Sign in") + myIcon: StudioTheme.Constants.signin_medium + + onClicked: backend.designViewerConnector.login() + } + } + } + + // LoggedIn + Rectangle { + id: loggedInPage + + property string email + property string license + property var storageUsed + property var storageLimit + + color: StudioTheme.Values.themePopupBackground + + Layout.fillWidth: true + implicitHeight: loggedInPageColumn.implicitHeight + + Column { + id: loggedInPageColumn + anchors.fill: parent + padding: StudioTheme.Values.border + spacing: 0 + + MenuItemDelegate { + id: shareMenuItem + width: parent.width + + myText: qsTr("Share") + myIcon: StudioTheme.Constants.upload_medium + + onClicked: backend.designViewerConnector.uploadCurrentProject() + } + + ShareNotification { + id: shareNotification + + Connections { + target: backend.designViewerConnector + + function onProjectUploadProgress(progress: var) { + shareNotification.setProgress(progress) + } + + function onProjectUploaded() { + shareNotification.type = ShareNotification.NotificationType.Success + shareNotification.setHelperText(qsTr("Upload succeeded.")) + + shareMenuItem.enabled = true + } + + function onProjectUploadError(errorCode: int, message: string) { + shareNotification.type = ShareNotification.NotificationType.Error + shareNotification.setHelperText(qsTr("Upload failed (" + errorCode + ").")) + + shareMenuItem.enabled = true + } + + function onProjectIsPacking() { + shareNotification.type = ShareNotification.NotificationType.Indeterminate + shareNotification.setText(qsTr("Packing")) + shareNotification.visible = true + + shareMenuItem.enabled = true + } + + function onProjectPackingFailed(errorString: string) { + shareNotification.type = ShareNotification.NotificationType.Error + shareNotification.setHelperText(qsTr("Packing failed.")) + } + + function onProjectIsUploading() { + shareNotification.type = ShareNotification.NotificationType.Normal + shareNotification.setText(qsTr("Uploading")) + shareNotification.visible = true + + shareMenuItem.enabled = false + } + } + } + + MenuItemDelegate { + width: parent.width + + myText: qsTr("Manage shared projects") + myIcon: StudioTheme.Constants.openLink + + onClicked: Qt.openUrlExternally("https://designviewer-staging.qt.io/") + } + + Rectangle { + width: parent.width + height: StudioTheme.Values.height * 2 + color: StudioTheme.Values.themePanelBackground + + Row { + anchors.fill: parent + spacing: 0 + + Label { + id: iconLabel + width: StudioTheme.Values.topLevelComboHeight + height: StudioTheme.Values.topLevelComboHeight + + anchors.verticalCenter: parent.verticalCenter + + color: StudioTheme.Values.themeTextColor + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.topLevelComboIcon + text: StudioTheme.Constants.user_medium + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + Column { + width: parent.width - parent.spacing - iconLabel.width - 8 // TODO 8 is the margin + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + Text { + color: StudioTheme.Values.themeTextColor + text: loggedInPage.email ?? "" + } + + RowLayout { + width: parent.width + + Text { + Layout.fillWidth: true + color: StudioTheme.Values.themeTextColor + text: loggedInPage.license ?? "" + } + + Text { + color: StudioTheme.Values.themeTextColor + text: `${dvWindow.formatBytes(loggedInPage.storageUsed)} / ${dvWindow.formatBytes(loggedInPage.storageLimit)}` + } + } + } + } + } + + MenuItemDelegate { + width: parent.width + + myText: qsTr("Sign out") + myIcon: StudioTheme.Constants.signout_medium + + onClicked: backend.designViewerConnector.logout() + } + } + } + } + } } ToolbarButton { @@ -328,8 +603,9 @@ Rectangle { id: window readonly property int padding: 6 + readonly property int morePopupWidth: Math.max(180, row.width) - width: row.width + window.padding * 2 + width: window.morePopupWidth + window.padding * 2 height: row.height + (backend.isLiteModeEnabled ? 0 : workspacesFlyout.height) + (backend.isLiteModeEnabled ? 2 : 3) * window.padding + (workspacesFlyout.popup.opened ? workspacesFlyout.popup.height : 0) @@ -411,18 +687,6 @@ Rectangle { onClicked: backend.setLockWorkspace(lockWorkspaceFlyout.checked) } - - ToolbarButton { - anchors.verticalCenter: parent.verticalCenter - style: StudioTheme.Values.primaryToolbarStyle - width: shareButton.width - iconFontFamily: StudioTheme.Constants.font.family - buttonIcon: qsTr("Share") - enabled: backend.isSharingEnabled - tooltip: shareButton.enabled ? qsTr("Share your project online.") : qsTr("Sharing your project online is disabled in the Community Version.") - - onClicked: backend.shareApplicationOnline() - } } StudioControls.ComboBox { @@ -430,7 +694,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter actionIndicatorVisible: false style: StudioTheme.Values.statusbarControlStyle - width: row.width + width: window.morePopupWidth maximumPopupHeight: 400 model: workspaceModel textRole: "displayName" diff --git a/share/qtcreator/qmldesigner/toolbar/MenuItemDelegate.qml b/share/qtcreator/qmldesigner/toolbar/MenuItemDelegate.qml index 674045bb3bc..1edc8711970 100644 --- a/share/qtcreator/qmldesigner/toolbar/MenuItemDelegate.qml +++ b/share/qtcreator/qmldesigner/toolbar/MenuItemDelegate.qml @@ -14,8 +14,8 @@ T.ItemDelegate { property alias myIcon: iconLabel.text property alias myText: textLabel.text - //width: root.menuWidth - 2 * window.padding - //height: root.style.controlSize.height// - 2 * root.style.borderWidth + implicitWidth: root.style.controlSize.width + implicitHeight: root.style.controlSize.height contentItem: Row { id: row @@ -40,7 +40,6 @@ T.ItemDelegate { font.pixelSize: root.style.baseIconFontSize verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: StudioTheme.Constants.playOutline_medium } T.Label { @@ -68,8 +67,6 @@ T.ItemDelegate { background: Rectangle { id: rootBackground - x: 0 - y: 0 width: root.width height: root.height opacity: enabled ? 1 : 0.3 diff --git a/share/qtcreator/qmldesigner/toolbar/ShareNotification.qml b/share/qtcreator/qmldesigner/toolbar/ShareNotification.qml new file mode 100644 index 00000000000..d8743e38fba --- /dev/null +++ b/share/qtcreator/qmldesigner/toolbar/ShareNotification.qml @@ -0,0 +1,203 @@ +// Copyright (C) 2024 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 QtQuick.Templates as T + +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Item { + id: root + + enum NotificationType { + Normal, + Indeterminate, + Success, + Error + } + + property int type: ShareNotification.NotificationType.Normal + + width: parent.width + height: StudioTheme.Values.height * 2 + visible: false + + function setText(value: string) { + label.text = value + } + + function setHelperText(value: string) { + helperText.text = value + } + + function setProgress(value: var) { + helperText.text = `${value.toFixed(0)} %` + progressBar.value = value / 100.0 + } + + function hasFinished() { + return root.type === ShareNotification.NotificationType.Success + || root.type === ShareNotification.NotificationType.Error + } + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 8 + spacing: 4 + + RowLayout { + width: parent.width + + Text { + id: label + Layout.fillWidth: true + color: StudioTheme.Values.themeTextColor + } + + Item { + implicitWidth: StudioTheme.Values.controlLabelWidth + implicitHeight: StudioTheme.Values.controlLabelWidth + + Label { + id: statusIcon + anchors.fill: parent + color: StudioTheme.Values.themeIconColor + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.myIconFontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + StudioControls.ToolTipArea { + anchors.fill: parent + text: qsTr("This can be your message.") + } + } + } + + T.ProgressBar { + id: progressBar + + property color color + + width: parent.width + height: 8 + + value: 0.5 + + background: Rectangle { + id: track + implicitWidth: 200 + implicitHeight: 6 + color: StudioTheme.Values.themeScrollBarTrack + } + + contentItem: Item { + implicitWidth: 200 + implicitHeight: 6 + clip: true + + // Progress indicator for determinate state. + Rectangle { + id: barIndicator + width: progressBar.visualPosition * parent.width + height: parent.height + color: progressBar.color + visible: !progressBar.indeterminate + } + + // Scrolling animation for indeterminate state. + Rectangle { + id: barIndicatorIndeterminate + width: parent.width * 0.5 + height: parent.height + color: progressBar.color + visible: progressBar.indeterminate + + XAnimator on x { + duration: 650 + from: -barIndicatorIndeterminate.width + to: progressBar.width + loops: Animation.Infinite + running: progressBar.indeterminate + } + } + } + } + + Text { + id: helperText + color: StudioTheme.Values.themeTextColor + } + } + + states: [ + State { + name: "normal" + when: root.type === ShareNotification.NotificationType.Normal + + PropertyChanges { + target: progressBar + color: StudioTheme.Values.themeInteraction + indeterminate: false + } + PropertyChanges { + target: statusIcon + visible: false + } + }, + State { + name: "error" + when: root.type === ShareNotification.NotificationType.Error + + PropertyChanges { + target: progressBar + color: StudioTheme.Values.themeRedLight + indeterminate: false + value: 1 + } + PropertyChanges { + target: statusIcon + visible: true + color: StudioTheme.Values.themeRedLight + text: StudioTheme.Constants.error_medium + } + }, + State { + name: "success" + when: root.type === ShareNotification.NotificationType.Success + + PropertyChanges { + target: progressBar + color: StudioTheme.Values.themeGreenLight + indeterminate: false + value: 1 + } + PropertyChanges { + target: statusIcon + visible: true + color: StudioTheme.Values.themeGreenLight + text: StudioTheme.Constants.apply_medium + } + }, + State { + name: "indeterminate" + when: root.type === ShareNotification.NotificationType.Indeterminate + + PropertyChanges { + target: progressBar + color: StudioTheme.Values.themeInteraction + indeterminate: true + } + PropertyChanges { + target: statusIcon + visible: false + } + } + ] +} diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 3afb0ae83c4..82ff73aa114 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -173,6 +173,7 @@ public: fitSelected_small, fitSelection_medium, fitToView_medium, + flag_medium, flowAction, flowTransition, fontStyleBold, @@ -230,6 +231,7 @@ public: newMaterial, nextFile_large, normalBar_small, + number_medium, openLink, openMaterialBrowser, orientation, @@ -305,7 +307,10 @@ public: selectParent_small, selection_small, settings_medium, + share_large, signal_small, + signin_medium, + signout_medium, snapping_conf_medium, snapping_medium, snapping_small, @@ -321,6 +326,7 @@ public: startNode, step_medium, stop_medium, + string_medium, tableView_medium, testIcon, textAlignBottom, @@ -370,6 +376,8 @@ public: upDownSquare2, updateAvailable_medium, updateContent_medium, + upload_medium, + user_medium, visibilityOff, visibilityOn, visible_medium, diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index a4f32d6cf10..610fb913359 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include @@ -115,10 +117,12 @@ DVConnector::DVConnector(QObject *parent) m_webEngineProfile.reset(new QWebEngineProfile("DesignViewer", this)); m_webEngineProfile->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); m_webEnginePage.reset(new CustomWebEnginePage(m_webEngineProfile.data(), this)); - m_webEngineView.reset(new QWebEngineView); + m_webEngineView.reset(new QWebEngineView(Core::ICore::instance()->dialogParent())); m_webEngineView->setPage(m_webEnginePage.data()); m_webEngineView->resize(1024, 750); + m_webEngineView->setWindowFlag(Qt::Dialog); m_webEngineView->installEventFilter(this); + m_webEngineView->hide(); m_networkCookieJar.reset( new CustomCookieJar(this, m_webEngineProfile->persistentStoragePath() + "/dv_cookies.txt")); @@ -284,7 +288,8 @@ void DVConnector::uploadProject(const QString &projectId, const QString &filePat &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 bytesTotal) { - emit projectUploadProgress(100.0 * (double) bytesSent / (double) bytesTotal); + if (bytesTotal != 0) + emit projectUploadProgress(100.0 * (double) bytesSent / (double) bytesTotal); }); evaluatorData.connectCallbacks(this); } @@ -592,6 +597,7 @@ void DVConnector::login() qCDebug(deploymentPluginLog) << "Logging in"; m_webEnginePage->load(QUrl(DVEndpoints::serviceUrl + DVEndpoints::login)); m_webEngineView->show(); + m_webEngineView->raise(); } void DVConnector::logout() diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h index 5ffd0eebf9a..52d81c05ec4 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.h +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -59,7 +59,7 @@ public: bool isWebViewerVisible() const; void projectList(); - void uploadCurrentProject(); + Q_INVOKABLE void uploadCurrentProject(); void uploadProject(const QString &projectId, const QString &filePath); void deleteProject(const QString &projectId); void downloadProject(const QString &projectId, const QString &filePath); @@ -78,9 +78,9 @@ public: void downloadSharedProject(const QString &projectId, const QString &filePath); void downloadSharedProjectThumbnail(const QString &projectId, const QString &filePath); - void login(); - void logout(); - void fetchUserInfo(); + Q_INVOKABLE void login(); + Q_INVOKABLE void logout(); + Q_INVOKABLE void fetchUserInfo(); private: // network diff --git a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp index 1e7235c2283..8f59d7f5891 100644 --- a/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp +++ b/src/plugins/qmldesigner/components/designviewer/resourcegeneratorproxy.cpp @@ -37,7 +37,7 @@ void ResourceGeneratorProxy::createResourceFileAsync(const QString &projectName) m_future = QtConcurrent::run([&]() { const std::optional filePath = createResourceFileSync(projectName); - if (filePath->isEmpty()) { + if (!filePath || filePath->isEmpty()) { emit errorOccurred("Failed to create resource file"); return; } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index d2901de4597..276747fbb72 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -510,6 +510,12 @@ void ToolBarBackend::registerDeclarativeType() "RunManager shouldn't be instantiated."); qmlRegisterUncreatableType( "ToolBar", 1, 0, "DeviceManagerModel", "DeviceManagerModel shouldn't be instantiated."); + + qmlRegisterUncreatableType("ToolBar", + 1, + 0, + "DVConnector", + "DVConnector shouldn't be instantiated."); } void ToolBarBackend::triggerModeChange() @@ -896,6 +902,11 @@ int ToolBarBackend::runManagerState() const return QmlDesignerPlugin::runManager().state(); } +DesignViewer::DVConnector *ToolBarBackend::designViewerConnector() +{ + return &m_designViewerConnector; +} + void ToolBarBackend::launchGlobalAnnotations() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index dc9a4c4de7a..71f83438806 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -6,6 +6,8 @@ #include #include +#include + namespace QmlDesigner { class ActionInterface; @@ -126,6 +128,8 @@ class ToolBarBackend : public QObject Q_PROPERTY(int runTargetIndex READ runTargetIndex NOTIFY runTargetIndexChanged) Q_PROPERTY(int runManagerState READ runManagerState NOTIFY runManagerStateChanged) + Q_PROPERTY(DesignViewer::DVConnector *designViewerConnector READ designViewerConnector CONSTANT) + public: ToolBarBackend(QObject *parent = nullptr); static void registerDeclarativeType(); @@ -186,6 +190,8 @@ public: int runTargetIndex() const; int runManagerState() const; + DesignViewer::DVConnector *designViewerConnector(); + static void launchGlobalAnnotations(); signals: @@ -218,6 +224,7 @@ private: QStringList m_openDocuments; QMetaObject::Connection m_kitConnection; + DesignViewer::DVConnector m_designViewerConnector; }; } // namespace QmlDesigner From 2f0739f089e8c2626db98970a48b907fc906c61b Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 3 Dec 2024 09:10:50 +0200 Subject: [PATCH 228/322] Doc: Update Fresnel example docs Fixes: QDS-14075 Change-Id: I55ad01b092f7a1eb3d35a6337ab8a17f40f20705 Reviewed-by: Johanna Vanhatapio --- .../config/style/qt5-sidebar.html | 2 +- .../examples/doc/FresnelExample.qdoc | 95 ++++++++++++++---- .../examples/doc/images/fresnel-example.webp | Bin 62494 -> 40396 bytes 3 files changed, 78 insertions(+), 19 deletions(-) diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index fd7435f1185..8616343e987 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -338,7 +338,7 @@
    • Side Menu
    • Simple keyboard
    • The Effect Composer example
    • -
    • The Fresnel example
    • +
    • Fresnel properties on materials
    • Washing Machine UI
    • Webinar Demo
    diff --git a/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc index d254ce69093..41f77e42480 100644 --- a/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc +++ b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc @@ -5,15 +5,17 @@ \page fresnel-effect-example.html \ingroup studioexamples - \title The Fresnel example - \brief Illustrates how to work with the fresnel effect. + \title Fresnel properties on materials + \brief Illustrates how to work with Fresnel. + \sa {Material Editor and Browser}, {Setting image-based lighting}, {Directional Light}, + {3D models}, {Content Library} \image fresnel-example.webp - The \e{Fresnel} example illustrates how to add and adjust a Fresnel effect on - a 3D model. + The \e {Fresnel properties on materials} example illustrates how to add Fresnel to a material + and use the material on a 3D model. - The fresnel effect affects how materials reflect light at different viewing angles. Imagine the + Fresnel affects how materials reflect light at different viewing angles. Imagine the water on a lake. If you look down at the water from straight above, you can see through the water, but if you look from a lower angle, the reflections are stronger. @@ -26,18 +28,58 @@ \section1 The 3D scene - The example project consists of a basic 3D scene with the following components: + The 3D scene of the example project consists of the following models: \list - \li A 3D model. - \li A directional light. - \li An HDR image used to light the scene (image-based lighting). + \li Monkey head (\e {monkeyModel}) + \li Sphere (\e {sphere}) + \li Plane (\e {mirror}) \endlist - \section1 The material + The 3D scene uses a directional light and an HDR image (image-based lighting) to light + the scene. - The material on the 3D model in this example is a principled material with a - clearcoat. + Use the example UI controls to manipulate the scene. + + \table + \header + \li Control + \li Description + \row + \li \uicontrol {Rotate background} + \li Rotates the background to see the models from different angles. + \row + \li \uicontrol {Move camera} or \uicontrol {Move model} + \li Switches between moving the monkey head model and the camera. + \row + \li \uicontrol {Reset camera} + \li Resets the camera. + \row + \li \uicontrol {Reset monkey} + \li Resets the monkey. + \endtable + + Navigate the scene with your keyboard using the \uicontrol {Camera controls} listed in + the example. + + \section1 The materials + + The following materials are used in this example. + + \table + \header + \li Material + \li Description + \row + \li Monkey Material + \li A yellow principled material with a clearcoat. + \row + \li Green Material + \li A green principled material with a clearcoat. + \row + \li Mirror + \li A ready-made material from \uicontrol {Content Library}. + \endtable \section2 Clearcoat @@ -49,7 +91,7 @@ \section2 The Fresnel properties - The following properties affect how the Fresnel effect renders. These properties are + The following properties affect how Fresnel renders. These properties are available both for the base material and the clearcoat layer. Adjusting the settings for the clearcoat has a bigger visual effect. @@ -58,7 +100,7 @@ \li Property \li Description \row - \li Fresnel power + \li Fresnel Power \li Increasing the Fresnel power decreases the head-on reflections (steep viewing angle) while maintaining the reflections seen from more shallow viewing angles. \row @@ -71,20 +113,37 @@ a smaller scale creates a more abrupt shift in reflection intensity. \row \li Fresnel Bias - \li Controls the offset for the fresnel power property and determines how quickly the + \li Controls the offset for the Fresnel power property, which determines how quickly the reflection transitions from weak to strong as the viewing angle changes. A larger bias value shifts the transition point toward steeper angles. \endtable - \section3 Adjusting the Fresnel settings + \section2 Adjusting the Fresnel properties - To adjust the settings: + To adjust the Fresnel properties, use the sliders in the example UI. + + To improve performance, select a lower reflection detail from the + \uicontrol {Reflection detail} dropdown menu. + + To reset the Fresnel properties, select \uicontrol {Reset fresnel}. + + \section1 Getting started with Fresnel + + To edit an existing material: \list 1 - \li In \uicontrol {Material Browser}, double-click \e {Monkey Material}. + \li In \uicontrol {Material Browser}, double-click a material. \li In \uicontrol {Material Editor}, find the properties under \uicontrol {Clearcoat} and \uicontrol {Fresnel} respectively. \endlist \note You see the changes live as you edit them in the \uicontrol 2D view. + + To create a new material with Fresnel: + + \list 1 + \li In \uicontrol {Material Editor}, select \inlineimage icons/plus.png. + \li Edit the properties under \uicontrol {Clearcoat} and \uicontrol {Fresnel} respectively. + \endlist + */ diff --git a/doc/qtdesignstudio/examples/doc/images/fresnel-example.webp b/doc/qtdesignstudio/examples/doc/images/fresnel-example.webp index 75436595aac72be2192584424d23ec32475b68a9..9233bdcc71e52bde59bce78087d4fe5a87d979f9 100644 GIT binary patch literal 40396 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 From 12139bf29a4a55b24a5a6e46a8f332186cb47e34 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 4 Dec 2024 12:51:58 +0100 Subject: [PATCH 229/322] QmlDesigner: Use right settings key for experimental features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We always should use the current version. Change-Id: Ibf4ee7427a9aa632ee796692cb59eb133b8f21a3 Reviewed-by: Henning Gründl --- .../qmldesignerbase/studio/studiosettingspage.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp index d2dd472f1ba..9922d797aa8 100644 --- a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp +++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp @@ -5,6 +5,8 @@ #include "../utils/designerpaths.h" +#include + #include #include @@ -31,8 +33,6 @@ namespace QmlDesigner { namespace { -const char experimentalFeatures[] = "QML/Designer/UseExperimentalFeatures44"; - bool hideBuildMenuSetting() { return Core::ICore::settings() @@ -61,7 +61,9 @@ bool hideToolsMenuSetting() bool showExperimentalFeatures() { - return Core::ICore::settings()->value(experimentalFeatures, false).toBool(); + return Core::ICore::settings() + ->value(QmlDesignerBasePlugin::experimentalFeaturesSettingsKey(), false) + .toBool(); } void setSettingIfDifferent(const Key &key, bool value, bool &dirty) @@ -191,7 +193,10 @@ void StudioSettingsPage::apply() m_toolsCheckBox->isChecked(), dirty); - setSettingIfDifferent(experimentalFeatures, m_experimentalCheckBox->isChecked(), dirty); + setSettingIfDifferent( + QmlDesignerBasePlugin::experimentalFeaturesSettingsKey(), + m_experimentalCheckBox->isChecked(), + dirty); if (dirty) { Core::ICore::askForRestart( From 1e9902d06e94187c49468bd4607544f01d486e55 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 4 Dec 2024 11:37:32 +0100 Subject: [PATCH 230/322] QmlDesigner: Add #1 iteration design system view Add the first iteration of the design system view which be default is hidden as an experimental feature. The current state is work in progress. A few feature are still missing from the front end as well as from the backend. Task-number: QDS-11856 Change-Id: Ia45c6964b4bea71608be696160dc9b55a8fd2d17 Reviewed-by: Vikas Pachdha --- .../qmldesigner/designsystem/Main.qml | 730 ++++++++++++++++++ .../imports/DesignSystemControls/MenuItem.qml | 68 ++ .../imports/DesignSystemControls/Switch.qml | 193 +++++ .../DesignSystemControls/TextField.qml | 190 +++++ .../imports/DesignSystemControls/qmldir | 3 + .../imports/StudioControls/IconTextButton.qml | 176 +++++ .../imports/StudioControls/qmldir | 1 + src/plugins/qmldesigner/CMakeLists.txt | 3 + .../designsystemview/collectionmodel.cpp | 15 +- .../designsystemview/collectionmodel.h | 1 + .../designsystemview/designsystemview.cpp | 50 ++ .../designsystemview/designsystemview.h | 42 + .../designsystemview/designsystemwidget.cpp | 123 +++ .../designsystemview/designsystemwidget.h | 48 ++ .../qmldesigner/qmldesignerconstants.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 6 + 16 files changed, 1648 insertions(+), 3 deletions(-) create mode 100644 share/qtcreator/qmldesigner/designsystem/Main.qml create mode 100644 share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/MenuItem.qml create mode 100644 share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/Switch.qml create mode 100644 share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/TextField.qml create mode 100644 share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/qmldir create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconTextButton.qml create mode 100644 src/plugins/qmldesigner/components/designsystemview/designsystemview.cpp create mode 100644 src/plugins/qmldesigner/components/designsystemview/designsystemview.h create mode 100644 src/plugins/qmldesigner/components/designsystemview/designsystemwidget.cpp create mode 100644 src/plugins/qmldesigner/components/designsystemview/designsystemwidget.h diff --git a/share/qtcreator/qmldesigner/designsystem/Main.qml b/share/qtcreator/qmldesigner/designsystem/Main.qml new file mode 100644 index 00000000000..7033eda1f6d --- /dev/null +++ b/share/qtcreator/qmldesigner/designsystem/Main.qml @@ -0,0 +1,730 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Templates as T +import Qt.labs.qmlmodels + +import QmlDesigner.DesignSystem +import DesignSystemBackend +import DesignSystemControls as DSC + +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property string currentCollectionName + + readonly property int cellWidth: 200 + readonly property int cellHeight: 40 + + readonly property color textColor: "#ffffff" + readonly property color iconColor: "#959595" + readonly property color backgroundColor: "#2c2c2c" + readonly property color borderColor: "#444444" + + readonly property int borderWidth: 1 + + readonly property int textSize: 18 + readonly property int iconSize: 16 + + readonly property int leftPadding: 14 + + width: 400 + height: 400 + color: StudioTheme.Values.themePanelBackground + + function loadModel(name) { + root.currentCollectionName = name + tableView.model = DesignSystemBackend.dsInterface.model(name) + } + + function groupString(gt) { + if (gt === GroupType.Strings) + return "string" + if (gt === GroupType.Numbers) + return "number" + if (gt === GroupType.Colors) + return "color" + if (gt === GroupType.Flags) + return "bool" + + return "unknow_group" + } + + Rectangle { + id: toolBar + + color: StudioTheme.Values.themeToolbarBackground + width: root.width + height: StudioTheme.Values.toolbarHeight + + Row { + height: StudioTheme.Values.toolbarHeight + leftPadding: 10 // TODO needs to into values + spacing: StudioTheme.Values.toolbarSpacing + + StudioControls.ComboBox { + id: collectionsComboBox + style: StudioTheme.Values.viewBarControlStyle + anchors.verticalCenter: parent.verticalCenter + actionIndicatorVisible: false + model: DesignSystemBackend.dsInterface.collections + onActivated: root.loadModel(collectionsComboBox.currentText) + } + + StudioControls.IconTextButton { + id: moreButton + anchors.verticalCenter: parent.verticalCenter + buttonIcon: StudioTheme.Constants.more_medium + checkable: true + checked: moreMenu.visible + + onClicked: moreMenu.popup(0, moreButton.height) + + StudioControls.Menu { + id: moreMenu + + StudioControls.MenuItem { + text: qsTr("Rename") + onTriggered: console.log(">>> Rename collection") + } + + StudioControls.MenuItem { + text: qsTr("Delete") + onTriggered: console.log(">>> Delete collection") + } + + StudioControls.MenuSeparator {} + + StudioControls.MenuItem { + text: qsTr("Create collection") + onTriggered: console.log(">>> Create collection") + } + } + } + + // TODO this is only for debugging purposes + Button { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("load") + onClicked: { + DesignSystemBackend.dsInterface.loadDesignSystem() + root.loadModel(DesignSystemBackend.dsInterface.collections[0]) + } + } + } + } + + component Cell: Rectangle { + required property string display + required property int row + required property int column + + required property bool editing + + required property bool isBinding + + color: root.backgroundColor + implicitWidth: root.cellWidth + implicitHeight: root.cellHeight + border { + width: root.borderWidth + color: root.borderColor + } + } + + component DataCell: Cell { + HoverHandler { id: cellHoverHandler } + + StudioControls.IconIndicator { + icon: isBinding ? StudioTheme.Constants.actionIconBinding + : StudioTheme.Constants.actionIcon + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 10 + visible: cellHoverHandler.hovered + + onClicked: { + tableView.closeEditor() + } + } + } + + DelegateChooser { + id: chooser + role: "group" + + DelegateChoice { + roleValue: GroupType.Strings + + DataCell { + id: stringDelegate + + Text { + anchors.fill: parent + leftPadding: root.leftPadding + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + color: StudioTheme.Values.themeTextColor + text: stringDelegate.display + visible: !stringDelegate.editing + } + + TableView.editDelegate: DSC.TextField { + id: stringEditDelegate + + anchors.fill: parent + leftPadding: root.leftPadding + + // Only apply more to right padding when hovered + //rightPadding + + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + + text: stringDelegate.display + Component.onCompleted: stringEditDelegate.selectAll() + + TableView.onCommit: { + console.log("onCommit", stringEditDelegate.text) + let index = TableView.view.index(stringDelegate.row, stringDelegate.column) + var prop = DesignSystemBackend.dsInterface.createThemeProperty("", + stringEditDelegate.text, + stringDelegate.isBinding) + TableView.view.model.setData(index, prop, Qt.EditRole) + } + } + + Component.onCompleted: console.log("DelegateChoice - string", stringDelegate.display) + } + } + + DelegateChoice { + roleValue: GroupType.Numbers + + DataCell { + id: numberDelegate + + Text { + anchors.fill: parent + leftPadding: root.leftPadding + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + + color: StudioTheme.Values.themeTextColor + text: numberDelegate.display + visible: !numberDelegate.editing + } + + TableView.editDelegate: SpinBox { + id: numberEditDelegate + + anchors.fill: parent + leftPadding: root.leftPadding + + value: parseInt(numberDelegate.display) + from: -1000 // TODO define min/max + to: 1000 + editable: true + + TableView.onCommit: { + let val = numberEditDelegate.valueFromText(numberEditDelegate.contentItem.text, + numberEditDelegate.locale) + console.log("onCommit", val) + let index = TableView.view.index(numberDelegate.row, numberDelegate.column) + var prop = DesignSystemBackend.dsInterface.createThemeProperty("", + val, + numberDelegate.isBinding) + TableView.view.model.setData(index, prop, Qt.EditRole) + } + } + + Component.onCompleted: console.log("DelegateChoice - number", display) + } + } + + DelegateChoice { + roleValue: GroupType.Flags + + DataCell { + id: flagDelegate + + DSC.Switch { + id: flagEditDelegate + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: root.leftPadding + + checked: flagDelegate.display === "true" + text: flagDelegate.display + + onToggled: { + console.log("onCommit", flagEditDelegate.checked) + let index = flagDelegate.TableView.view.index(flagDelegate.row, flagDelegate.column) + var prop = DesignSystemBackend.dsInterface.createThemeProperty("", + flagEditDelegate.checked, + flagEditDelegate.isBinding) + flagDelegate.TableView.view.model.setData(index, prop, Qt.EditRole) + } + } + + Component.onCompleted: console.log("DelegateChoice - bool", flagDelegate.display) + } + } + + DelegateChoice { + roleValue: GroupType.Colors + + DataCell { + id: colorDelegate + + Row { + anchors.fill: parent + leftPadding: root.leftPadding + spacing: 8 + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + + width: 20 + height: 20 + color: colorDelegate.display + border.color: "black" + border.width: 1 + + Image { + anchors.fill: parent + source: "qrc:/navigator/icon/checkers.png" + fillMode: Image.Tile + z: -1 + } + + MouseArea { + id: colorMouseArea + anchors.fill: parent + + onClicked: { + if (popupDialog.visibility) { + popupDialog.close() + } else { + popupDialog.ensureLoader() + popupDialog.show(colorDelegate) + + if (loader.status === Loader.Ready) + loader.item.originalColor = root.color + } + colorMouseArea.forceActiveFocus() + } + } + } + + Text { + height: parent.height + verticalAlignment: Qt.AlignVCenter + color: StudioTheme.Values.themeTextColor + text: colorDelegate.display + } + } + + Component.onCompleted: console.log("DelegateChoice - color", colorDelegate.display) + } + } + } + + StudioControls.PopupDialog { + id: popupDialog + + property QtObject loaderItem: loader.item + + keepOpen: loader.item?.eyeDropperActive ?? false + + width: 260 + + function ensureLoader() { + if (!loader.active) + loader.active = true + } + + Loader { + id: loader + + sourceComponent: StudioControls.ColorEditorPopup { + id: popup + width: popupDialog.contentWidth + visible: popupDialog.visible + parentWindow: popupDialog.window + + onActivateColor: function(color) { + console.log("set color", color) + //colorBackend.activateColor(color) + } + } + + Binding { + target: loader.item + property: "color" + value: root.color + when: loader.status === Loader.Ready + } + + onLoaded: { + loader.item.originalColor = root.color + popupDialog.titleBar = loader.item.titleBarContent + } + } + } + + GridLayout { + id: mainLayout + anchors.topMargin: toolBar.height + anchors.fill: parent + + columnSpacing: 0 + rowSpacing: 0 + + columns: 2 + rows: 2 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: 6 // TODO correct value + Layout.leftMargin: 6 // TODO correct value + + HoverHandler { id: hoverHandler } + + // top left cell + // TODO Can't use Cell as it contains required properties + Rectangle { + anchors.right: horizontalHeader.left + anchors.bottom: verticalHeader.top + anchors.rightMargin: -root.borderWidth + anchors.bottomMargin: -root.borderWidth + + color: root.backgroundColor + implicitWidth: verticalHeader.width + implicitHeight: horizontalHeader.height + border { + width: root.borderWidth + color: root.borderColor + } + + visible: tableView.model // TODO good enough? + z: 101 + + Row { // TODO might not be necessary + anchors.fill: parent + leftPadding: root.leftPadding + spacing: 8 + + Text { + height: parent.height + verticalAlignment: Qt.AlignVCenter + color: StudioTheme.Values.themeTextColor + text: "Name" + } + } + } + + HorizontalHeaderView { + id: horizontalHeader + + anchors.left: scrollView.left + anchors.top: parent.top + + syncView: tableView + clip: true + + z: 100 + + delegate: Cell { + id: horizontalHeaderDelegate + + property bool customEditing: false + + TapHandler { + onTapped: { + console.log("onTapped") + tableView.closeEditor() + horizontalHeaderDelegate.customEditing = true + horizontalHeaderTextField.forceActiveFocus() + } + } + + Text { + anchors.fill: parent + leftPadding: root.leftPadding + verticalAlignment: Qt.AlignVCenter + color: StudioTheme.Values.themeTextColor + text: horizontalHeaderDelegate.display + visible: !horizontalHeaderDelegate.customEditing + } + + DSC.TextField { + id: horizontalHeaderTextField + + anchors.fill: parent + leftPadding: root.leftPadding + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + text: horizontalHeaderDelegate.display + + visible: horizontalHeaderDelegate.customEditing + + //Component.onCompleted: stringEditDelegate.selectAll() + + onActiveFocusChanged: { + if (!horizontalHeaderTextField.activeFocus) + horizontalHeaderDelegate.customEditing = false + } + + onEditingFinished: { + console.log("onEditingFinished", horizontalHeaderTextField.text) + horizontalHeaderDelegate.TableView.view.model.setHeaderData(horizontalHeaderDelegate.column, + Qt.Horizontal, + horizontalHeaderTextField.text, + Qt.EditRole) + } + } + } + } + + VerticalHeaderView { + id: verticalHeader + + anchors.top: scrollView.top + anchors.left: parent.left + + syncView: tableView + clip: true + + z: 100 + + delegate: Cell { + id: verticalHeaderDelegate + + required property int group + + Row { + anchors.fill: parent + leftPadding: root.leftPadding + spacing: 8 + + Text { + id: icon + height: parent.height + verticalAlignment: Qt.AlignVCenter + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: root.iconSize + color: StudioTheme.Values.themeTextColor + + text: { + if (verticalHeaderDelegate.group === GroupType.Strings) + return StudioTheme.Constants.string_medium + + if (verticalHeaderDelegate.group === GroupType.Numbers) + return StudioTheme.Constants.number_medium + + if (verticalHeaderDelegate.group === GroupType.Flags) + return StudioTheme.Constants.flag_medium + + if (verticalHeaderDelegate.group === GroupType.Colors) + return StudioTheme.Constants.colorSelection_medium + + return StudioTheme.Constants.error_medium + } + } + + Text { + height: parent.height + verticalAlignment: Qt.AlignVCenter + color: StudioTheme.Values.themeTextColor + text: verticalHeaderDelegate.display + visible: !verticalHeaderDelegate.editing + } + } + } + } + + ScrollView { + id: scrollView + anchors.left: verticalHeader.right + anchors.top: horizontalHeader.bottom + anchors.right: parent.right + anchors.bottom: parent.bottom + + anchors.leftMargin: -root.borderWidth + anchors.topMargin: -root.borderWidth + anchors.rightMargin: -root.borderWidth + + contentItem: TableView { + id: tableView + + rowHeightProvider: function (col) { return root.cellHeight } + columnWidthProvider: function(column) { + let w = explicitColumnWidth(column) + if (w >= 0) + return w + + return implicitColumnWidth(column) + } + + onModelChanged: { + console.log("onModelChanged") + tableView.clearColumnWidths() + tableView.contentX = 0 + tableView.contentY = 0 + } + + resizableColumns: true + editTriggers: TableView.SingleTapped + columnSpacing: -root.borderWidth + rowSpacing: -root.borderWidth + clip: true + + delegate: chooser + } + + ScrollBar.horizontal: StudioControls.TransientScrollBar { + id: horizontalScrollBar + style: StudioTheme.Values.viewStyle + parent: tableView + x: 0 + y: tableView.height - horizontalScrollBar.height + width: tableView.availableWidth - (verticalScrollBar.isNeeded ? verticalScrollBar.thickness : 0) + orientation: Qt.Horizontal + + visible: !tableView.hideHorizontalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && horizontalScrollBar.isNeeded + otherInUse: verticalScrollBar.inUse + } + + ScrollBar.vertical: StudioControls.TransientScrollBar { + id: verticalScrollBar + style: StudioTheme.Values.viewStyle + parent: tableView + x: tableView.width - verticalScrollBar.width + y: 0 + height: tableView.availableHeight - (horizontalScrollBar.isNeeded ? horizontalScrollBar.thickness : 0) + orientation: Qt.Vertical + + visible: !tableView.hideVerticalScrollBar + + show: (hoverHandler.hovered || tableView.focus || tableView.adsFocus + || horizontalScrollBar.inUse || horizontalScrollBar.otherInUse) + && verticalScrollBar.isNeeded + otherInUse: horizontalScrollBar.inUse + } + } + } + + Item { + id: rightBar + width: StudioTheme.Values.toolbarHeight + Layout.fillHeight: true + + StudioControls.IconTextButton { + anchors.centerIn: parent + buttonIcon: StudioTheme.Constants.add_medium + text: qsTr("Create mode") + rotation: -90 + + onClicked: { + tableView.model.insertColumn(0) + } + } + } + + Item { + id: bottomBar + Layout.fillWidth: true + height: StudioTheme.Values.toolbarHeight + + StudioControls.IconTextButton { + id: createVariableButton + anchors.centerIn: parent + buttonIcon: StudioTheme.Constants.add_medium + text: qsTr("Create variable") + + checkable: true + checked: createVariableMenu.visible + + onClicked: { + if (createVariableMenu.opened) + createVariableMenu.close() + else + createVariableMenu.popup(0, -createVariableMenu.height) + } + + StudioControls.Menu { + id: createVariableMenu + + width: createVariableButton.width + + DSC.MenuItem { + text: qsTr("Color") + buttonIcon: StudioTheme.Constants.colorSelection_medium + onTriggered: { + console.log(">>> Add Color Property") + tableView.model.addProperty(GroupType.Colors, + "color_new", + "#800080", + false) + } + } + + DSC.MenuItem { + text: qsTr("Number") + buttonIcon: StudioTheme.Constants.number_medium + onTriggered: { + console.log(">>> Add Number Property") + tableView.model.addProperty(GroupType.Numbers, + "number_new", + 0, + false) + } + } + + DSC.MenuItem { + text: qsTr("String") + buttonIcon: StudioTheme.Constants.string_medium + onTriggered: { + console.log(">>> Add String Property") + tableView.model.addProperty(GroupType.Flags, + "string_new", + "String value", + false) + } + } + + DSC.MenuItem { + text: qsTr("Boolean") + buttonIcon: StudioTheme.Constants.flag_medium + onTriggered: { + console.log(">>> Add Boolean Property") + tableView.model.addProperty(GroupType.Flags, + "boolean_new", + true, + false) + } + } + } + } + } + + Item { + id: corner + width: StudioTheme.Values.toolbarHeight + height: StudioTheme.Values.toolbarHeight + } + } +} diff --git a/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/MenuItem.qml b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/MenuItem.qml new file mode 100644 index 00000000000..54e8d22fee9 --- /dev/null +++ b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/MenuItem.qml @@ -0,0 +1,68 @@ +// Copyright (C) 2024 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.MenuItem { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + property int labelSpacing: control.style.contextMenuLabelSpacing + + property alias buttonIcon: buttonIcon.text + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + padding: 0 + spacing: 0 + horizontalPadding: control.style.contextMenuHorizontalPadding + + indicator: Item { + implicitWidth: control.style.controlSize.height + implicitHeight: control.style.controlSize.height + + T.Label { + id: buttonIcon + anchors.fill: parent + font { + family: StudioTheme.Constants.iconFont.family + pixelSize: control.style.baseIconFontSize + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: control.enabled ? control.highlighted ? control.style.text.selectedText + : control.style.text.idle + : control.style.text.disabled + } + } + + contentItem: Text { + id: textLabel + leftPadding: control.indicator.width + text: control.text + font: control.font + color: control.enabled ? control.highlighted ? control.style.text.selectedText + : control.style.text.idle + : control.style.text.disabled + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitWidth: textLabel.implicitWidth + control.labelSpacing + + control.leftPadding + control.rightPadding + implicitHeight: control.style.controlSize.height + x: control.style.borderWidth + y: control.style.borderWidth + width: (control.menu?.width ?? 0) - (control.style.borderWidth * 2) + height: control.height - (control.style.borderWidth * 2) + color: control.highlighted ? control.style.interaction : "transparent" + } +} diff --git a/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/Switch.qml b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/Switch.qml new file mode 100644 index 00000000000..4ccdca4486c --- /dev/null +++ b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/Switch.qml @@ -0,0 +1,193 @@ +// Copyright (C) 2024 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.Switch { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + // This property is used to indicate the global hover state + property bool hover: control.hovered && control.enabled + property bool edit: false + + property alias labelVisible: label.visible + property alias labelColor: label.color + + property alias fontFamily: label.font.family + property alias fontPixelSize: label.font.pixelSize + + font.pixelSize: StudioTheme.Values.myFontSize + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + spacing: label.visible ? control.style.controlSpacing : 0 + hoverEnabled: true + activeFocusOnTab: false + + indicator: Rectangle { + id: switchBackground + x: 0 + y: 0 + z: 5 + implicitWidth: 40//control.style.squareControlSize.width * 2 + implicitHeight: 20//control.style.squareControlSize.height + radius: 10//control.style.squareControlSize.height * 0.5 + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + + Rectangle { + id: switchIndicator + + readonly property real gap: 2 + property real size: switchBackground.implicitHeight - switchIndicator.gap * 2 + + x: control.checked ? parent.width - width - switchIndicator.gap + : switchIndicator.gap + y: switchIndicator.gap + width: switchIndicator.size + height: switchIndicator.size + radius: switchIndicator.size * 0.5 + color: control.style.icon.idle + border.width: 0 + } + } + + contentItem: T.Label { + id: label + leftPadding: switchBackground.x + switchBackground.width + control.spacing + rightPadding: 0 + verticalAlignment: Text.AlignVCenter + text: control.text + font: control.font + color: control.style.text.idle + visible: control.text !== "" + } + + property bool __default: control.enabled && !control.hover && !control.pressed + property bool __globalHover: control.enabled && !control.pressed + property bool __hover: control.hover && !control.pressed + property bool __press: control.hover && control.pressed + + states: [ + State { + name: "default" + when: control.__default && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.idle + } + }, + State { + name: "globalHover" + when: control.__globalHover && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.globalHover + border.color: control.style.border.idle + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.__hover && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.hover + } + }, + State { + name: "press" + when: control.__press && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.interaction + border.color: control.style.border.interaction + } + PropertyChanges { + target: switchIndicator + color: control.style.interaction + } + }, + State { + name: "disable" + when: !control.enabled && !control.checked + PropertyChanges { + target: switchBackground + color: control.style.background.disabled + border.color: control.style.border.disabled + } + PropertyChanges { + target: switchIndicator + color: control.style.icon.disabled + } + PropertyChanges { + target: label + color: control.style.text.disabled + } + }, + + State { + name: "defaultChecked" + when: control.__default && control.checked + extend: "default" + PropertyChanges { + target: switchBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "globalHoverChecked" + when: control.__globalHover && control.checked + extend: "globalHover" + PropertyChanges { + target: switchBackground + color: control.style.interactionHover + border.color: control.style.interactionHover + } + }, + State { + name: "hoverChecked" + when: control.__hover && control.checked + extend: "hover" + PropertyChanges { + target: switchBackground + color: control.style.interactionHover + border.color: control.style.interactionHover + } + }, + State { + name: "pressChecked" + when: control.__press && control.checked + extend: "press" + }, + State { + name: "disableChecked" + when: !control.enabled && control.checked + extend: "disable" + } + ] +} diff --git a/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/TextField.qml b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/TextField.qml new file mode 100644 index 00000000000..35ec8b04c47 --- /dev/null +++ b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/TextField.qml @@ -0,0 +1,190 @@ +// Copyright (C) 2024 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 as StudioTheme + +T.TextField { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle + + // This property is used to indicate the global hover state + property bool hover: mouseArea.containsMouse && control.enabled + property bool edit: control.activeFocus + + property string preFocusText: "" + + signal rejected + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + font.pixelSize: control.style.baseFontSize + + color: control.style.text.idle + selectionColor: control.style.text.selection + selectedTextColor: control.style.text.selectedText + placeholderTextColor: control.style.text.placeholder + + readOnly: false + selectByMouse: true + persistentSelection: control.focus //|| contextMenu.visible + + width: control.style.controlSize.width + height: control.style.controlSize.height + implicitHeight: control.style.controlSize.height + + leftPadding: control.style.inputHorizontalPadding + rightPadding: control.style.inputHorizontalPadding + + MouseArea { + id: mouseArea + anchors.fill: parent + enabled: true + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } + + onPressed: function(event) { + //if (event.button === Qt.RightButton) + // contextMenu.popup(control) + } + + onActiveFocusChanged: { + // OtherFocusReason in this case means, if the TextField gets focus after the context menu + // was closed due to an menu item click. + // TODO focusReason needs to be ignored, otherwise will end up with empty TextField + if (control.activeFocus)// && control.focusReason !== Qt.OtherFocusReason) + control.preFocusText = control.text + + if (!control.activeFocus) + control.deselect() + } + + onEditChanged: { + //if (control.edit) + // contextMenu.close() + } + + //onEditingFinished: control.focus = false + + Text { + id: placeholder + x: control.leftPadding + y: control.topPadding + width: control.width - (control.leftPadding + control.rightPadding) + height: control.height - (control.topPadding + control.bottomPadding) + + text: control.placeholderText + font: control.font + color: control.placeholderTextColor + verticalAlignment: control.verticalAlignment + visible: !control.length && !control.preeditText + && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + renderType: control.renderType + } + + background: Rectangle { + id: textFieldBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + x: 0 + width: control.width + height: control.height + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hover && !control.edit// && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + PropertyChanges { + target: mouseArea + cursorShape: Qt.PointingHandCursor + } + }, + State { + name: "globalHover" + when: !control.edit && control.enabled// && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.globalHover + border.color: control.style.border.idle + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + }, + State { + name: "hover" + when: mouseArea.containsMouse && !control.edit && control.enabled// && !contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: control + color: control.style.text.hover + placeholderTextColor: control.style.text.placeholder + } + }, + State { + name: "edit" + when: control.edit// || contextMenu.visible + PropertyChanges { + target: textFieldBackground + color: control.style.background.interaction + border.color: control.style.border.interaction + } + PropertyChanges { + target: control + color: control.style.text.idle + placeholderTextColor: control.style.text.placeholder + } + PropertyChanges { + target: mouseArea + cursorShape: Qt.IBeamCursor + } + }, + State { + name: "disable" + when: !control.enabled + PropertyChanges { + target: textFieldBackground + color: control.style.background.disabled + border.color: control.style.border.disabled + } + PropertyChanges { + target: control + color: control.style.text.disabled + placeholderTextColor: control.style.text.disabled + } + } + ] + + Keys.onEscapePressed: function(event) { + event.accepted = true + control.text = control.preFocusText + control.rejected() + control.focus = false + } +} diff --git a/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/qmldir b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/qmldir new file mode 100644 index 00000000000..0f9345ed7d2 --- /dev/null +++ b/share/qtcreator/qmldesigner/designsystem/imports/DesignSystemControls/qmldir @@ -0,0 +1,3 @@ +MenuItem 1.0 MenuItem.qml +Switch 1.0 Switch.qml +TextField 1.0 TextField.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconTextButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconTextButton.qml new file mode 100644 index 00000000000..27bcb6fe048 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/IconTextButton.qml @@ -0,0 +1,176 @@ +// Copyright (C) 2024 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 HelperWidgets as HelperWidgets +import StudioTheme as StudioTheme + +T.AbstractButton { + id: control + + property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle + + property alias tooltip: toolTipArea.tooltip + property alias buttonIcon: buttonIcon.text + + implicitWidth: Math.max(control.style.squareControlSize.width, + implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + + implicitHeight: Math.max(control.style.squareControlSize.height, + implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + 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 + } + + contentItem: Row { + spacing: 0 + anchors.horizontalCenter: parent.horizontalCenter + + T.Label { + id: buttonIcon + width: control.style.squareControlSize.width + height: control.height + font { + family: StudioTheme.Constants.iconFont.family + pixelSize: control.style.baseIconFontSize + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + visible: buttonIcon.text !== "" + } + + T.Label { + id: buttonLabel + height: control.height + padding: 8 + leftPadding: buttonIcon.visible ? 0 : 8 + font.pixelSize: control.style.baseFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + + visible: control.text !== "" + } + } + + background: Rectangle { + id: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + border.width: control.style.borderWidth + radius: control.style.radius + } + + states: [ + State { + name: "default" + when: control.enabled && !control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.idle + border.color: control.style.border.idle + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.idle + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.idle + } + }, + State { + name: "hover" + when: control.enabled && control.hovered && !control.pressed && !control.checked + PropertyChanges { + target: controlBackground + color: control.style.background.hover + border.color: control.style.border.hover + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.hover + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.hover + } + }, + State { + 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: "press" + when: control.enabled && control.hovered && control.pressed + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + PropertyChanges { + target: buttonIcon + color: control.style.icon.interaction + } + PropertyChanges { + target: buttonLabel + color: control.style.icon.interaction + } + }, + State { + name: "check" + when: control.enabled && !control.pressed && control.checked + extend: "hoverCheck" + PropertyChanges { + target: controlBackground + color: control.style.interaction + border.color: control.style.interaction + } + }, + State { + name: "pressNotHover" + 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/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index 4266910554f..d187fcfa14e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -19,6 +19,7 @@ DialogButtonBox 1.0 DialogButtonBox.qml FilterComboBox 1.0 FilterComboBox.qml HueSlider 1.0 impl/HueSlider.qml IconIndicator 1.0 IconIndicator.qml +IconTextButton 1.0 IconTextButton.qml Indicator 1.0 Indicator.qml InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml ItemDelegate 1.0 ItemDelegate.qml diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 604f1b3f785..521cdcf8519 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -69,6 +69,7 @@ add_qtc_plugin(QmlDesigner ${CMAKE_CURRENT_LIST_DIR}/components/import3d ${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary ${CMAKE_CURRENT_LIST_DIR}/components/debugview + ${CMAKE_CURRENT_LIST_DIR}/components/designsystemview ${CMAKE_CURRENT_LIST_DIR}/components/edit3d ${CMAKE_CURRENT_LIST_DIR}/components/formeditor ${CMAKE_CURRENT_LIST_DIR}/components/integration @@ -237,6 +238,8 @@ extend_qtc_plugin(QmlDesigner SOURCES collectionmodel.h collectionmodel.cpp designsysteminterface.h designsysteminterface.cpp + designsystemview.cpp designsystemview.h + designsystemwidget.cpp designsystemwidget.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/designsystemview/collectionmodel.cpp b/src/plugins/qmldesigner/components/designsystemview/collectionmodel.cpp index df2908836c4..58077be2b05 100644 --- a/src/plugins/qmldesigner/components/designsystemview/collectionmodel.cpp +++ b/src/plugins/qmldesigner/components/designsystemview/collectionmodel.cpp @@ -67,14 +67,23 @@ QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, i if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return QString::fromLatin1(m_collection->themeName(findThemeId(section))); - if (orientation == Qt::Vertical && role == Qt::DisplayRole) { - if (auto propInfo = findPropertyName(section)) - return QString::fromLatin1(propInfo->second); + if (orientation == Qt::Vertical) { + if (auto propInfo = findPropertyName(section)) { + if (role == Qt::DisplayRole) + return QString::fromLatin1(propInfo->second); + if (role == static_cast(Roles::GroupRole)) + return QVariant::fromValue(propInfo->first); + } } return {}; } +Qt::ItemFlags CollectionModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsEditable | QAbstractItemModel::flags(index); +} + QHash CollectionModel::roleNames() const { auto roles = QAbstractItemModel::roleNames(); diff --git a/src/plugins/qmldesigner/components/designsystemview/collectionmodel.h b/src/plugins/qmldesigner/components/designsystemview/collectionmodel.h index 1eda71702d6..3b7a6d0a0ab 100644 --- a/src/plugins/qmldesigner/components/designsystemview/collectionmodel.h +++ b/src/plugins/qmldesigner/components/designsystemview/collectionmodel.h @@ -30,6 +30,7 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; QHash roleNames() const override; // Add Themes bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; diff --git a/src/plugins/qmldesigner/components/designsystemview/designsystemview.cpp b/src/plugins/qmldesigner/components/designsystemview/designsystemview.cpp new file mode 100644 index 00000000000..dfa5267c561 --- /dev/null +++ b/src/plugins/qmldesigner/components/designsystemview/designsystemview.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2024 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 "designsystemview.h" +#include "coreplugin/messagebox.h" +#include "designsystemwidget.h" +#include "dsstore.h" + +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +DesignSystemView::DesignSystemView(ExternalDependenciesInterface &externalDependencies, + ProjectStorageDependencies projectStorageDependencies) + : AbstractView(externalDependencies) + , m_externalDependencies(externalDependencies) + , m_dsStore(std::make_unique(m_externalDependencies, projectStorageDependencies)) + , m_dsInterface(m_dsStore.get()) +{} + +DesignSystemView::~DesignSystemView() {} + +WidgetInfo DesignSystemView::widgetInfo() +{ + if (!m_designSystemWidget) + m_designSystemWidget = new DesignSystemWidget(this, &m_dsInterface); + + return createWidgetInfo(m_designSystemWidget, + "DesignSystemView", + WidgetInfo::RightPane, + tr("Design System")); +} + +bool DesignSystemView::hasWidget() const +{ + return true; +} + +void DesignSystemView::loadDesignSystem() +{ + if (auto err = m_dsStore->load()) + qDebug() << *err; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/designsystemview/designsystemview.h b/src/plugins/qmldesigner/components/designsystemview/designsystemview.h new file mode 100644 index 00000000000..b3c44cfb873 --- /dev/null +++ b/src/plugins/qmldesigner/components/designsystemview/designsystemview.h @@ -0,0 +1,42 @@ +// Copyright (C) 2024 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 "designsysteminterface.h" +#include + +#include + +namespace QmlDesigner { + +class DSStore; +class ExternalDependenciesInterface; +class DesignSystemWidget; + +class DesignSystemView : public AbstractView +{ + Q_OBJECT + +public: + explicit DesignSystemView(ExternalDependenciesInterface &externalDependencies, + ProjectStorageDependencies projectStorageDependencies); + ~DesignSystemView() override; + + WidgetInfo widgetInfo() override; + bool hasWidget() const override; + +private: + void loadDesignSystem(); + QWidget *createViewWidget(); + +private: + QPointer m_designSystemWidget; + + QPointer m_dsWidget; + ExternalDependenciesInterface &m_externalDependencies; + std::unique_ptr m_dsStore; + DesignSystemInterface m_dsInterface; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.cpp b/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.cpp new file mode 100644 index 00000000000..7d698a8f94d --- /dev/null +++ b/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2024 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 "designsystemwidget.h" +#include "designsysteminterface.h" +#include "designsystemview.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +enum { + debug = false +}; + +namespace QmlDesigner { + +static 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(); +} + +DesignSystemWidget::DesignSystemWidget(DesignSystemView *view, DesignSystemInterface *interface) + : m_designSystemView(view) + , m_qmlSourceUpdateShortcut(nullptr) +{ + engine()->addImportPath(qmlSourcesPath()); + engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + engine()->addImportPath(qmlSourcesPath() + "/imports"); + + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F10), this); + connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &DesignSystemWidget::reloadQmlSource); + + quickWidget()->setObjectName(Constants::OBJECT_NAME_DESIGN_SYSTEM); + setResizeMode(QQuickWidget::SizeRootObjectToView); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto map = registerPropertyMap("DesignSystemBackend"); + map->setProperties({{"dsInterface", QVariant::fromValue(interface)}}); + + Theme::setupTheme(engine()); + + setWindowTitle(tr("Design System", "Title of Editor widget")); + setMinimumSize(QSize(195, 195)); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +DesignSystemWidget::~DesignSystemWidget() = default; + +QString DesignSystemWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/designsystem"; +#endif + return Core::ICore::resourcePath("qmldesigner/designsystem").toString(); +} + +void DesignSystemWidget::showEvent(QShowEvent *event) +{ + StudioQuickWidget::showEvent(event); + update(); + QMetaObject::invokeMethod(rootObject(), "showEvent"); +} + +void DesignSystemWidget::focusOutEvent(QFocusEvent *focusEvent) +{ + QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_DESIGNSYSTEM_TIME, + m_usageTimer.elapsed()); + StudioQuickWidget::focusOutEvent(focusEvent); +} + +void DesignSystemWidget::focusInEvent(QFocusEvent *focusEvent) +{ + m_usageTimer.restart(); + StudioQuickWidget::focusInEvent(focusEvent); +} + +void DesignSystemWidget::reloadQmlSource() +{ + QString statesListQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml"); + QTC_ASSERT(QFileInfo::exists(statesListQmlFilePath), return ); + setSource(QUrl::fromLocalFile(statesListQmlFilePath)); + + if (!rootObject()) { + QString errorString; + for (const QQmlError &error : errors()) + errorString += "\n" + error.toString(); + + Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), + tr("StatesEditorWidget: %1 cannot be created.%2") + .arg(qmlSourcesPath(), errorString)); + return; + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.h b/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.h new file mode 100644 index 00000000000..8741bad5476 --- /dev/null +++ b/src/plugins/qmldesigner/components/designsystemview/designsystemwidget.h @@ -0,0 +1,48 @@ +// Copyright (C) 2024 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 + +QT_BEGIN_NAMESPACE +class QShortcut; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class NodeInstanceView; + +class AbstractView; +class DesignSystemInterface; +class DesignSystemView; + +class DesignSystemWidget : public StudioQuickWidget +{ + Q_OBJECT + +public: + DesignSystemWidget(DesignSystemView *view, DesignSystemInterface *interface); + ~DesignSystemWidget() override; + + static QString qmlSourcesPath(); + +protected: + void showEvent(QShowEvent *) override; + void focusOutEvent(QFocusEvent *focusEvent) override; + void focusInEvent(QFocusEvent *focusEvent) override; + +private: + void reloadQmlSource(); + +private: + QPointer m_designSystemView; + QShortcut *m_qmlSourceUpdateShortcut; + QElapsedTimer m_usageTimer; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 0a8e6243af6..ed2a39db148 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -113,6 +113,7 @@ inline constexpr char EVENT_TIMELINE_TIME[] = "timeline"; inline constexpr char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor"; inline constexpr char EVENT_CURVEDITOR_TIME[] = "curveEditor"; inline constexpr char EVENT_STATESEDITOR_TIME[] = "statesEditor"; +inline constexpr char EVENT_DESIGNSYSTEM_TIME[] = "designSystem"; inline constexpr char EVENT_TEXTEDITOR_TIME[] = "textEditor"; inline constexpr char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor"; inline constexpr char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor"; @@ -163,6 +164,7 @@ inline constexpr char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary"; inline constexpr char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary"; inline constexpr char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator"; inline constexpr char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary"; +inline constexpr char OBJECT_NAME_DESIGN_SYSTEM[] = "QQuickWidgetDesignSystem"; inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectComposer"; inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index cbce7064430..d96b15e74b2 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -669,6 +670,11 @@ void QmlDesignerPlugin::enforceDelayedInitialize() std::make_unique(d->externalDependencies)); transitionEditorView->registerActions(); + if (QmlDesignerBasePlugin::experimentalFeaturesEnabled()) + d->viewManager.registerView( + std::make_unique(d->externalDependencies, + d->projectManager.projectStorageDependencies())); + d->viewManager.registerFormEditorTool(std::make_unique()); d->viewManager.registerFormEditorTool(std::make_unique()); d->viewManager.registerFormEditorTool(std::make_unique()); From 94e12f2a9e2a0e7f5016ddfcbb13cdb2ed97fe59 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 4 Dec 2024 15:55:03 +0100 Subject: [PATCH 231/322] QmlDesigner: Improve adding the code block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was failing with: if (something) { } Because there was already a code block, but another one is required. We also trimm the string. Task-number: QDS-14296 Change-Id: I055027eebe1e70cbaca8f2eaf1d305cc054e5f89 Reviewed-by: Henning Gründl --- .../designercore/model/signalhandlerproperty.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp index 4542875402c..c3f5f5df6cf 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/signalhandlerproperty.cpp @@ -87,17 +87,15 @@ PropertyName SignalHandlerProperty::prefixRemoved(PropertyNameView propertyName) QString SignalHandlerProperty::normalizedSourceWithBraces(const QString &source) { - static const QRegularExpression reg("\\{(\\s*?.*?)*?\\}"); + static const QRegularExpression reg("^\\{(\\s*?.*?)*?\\}$"); - auto match = reg.match(source); + const QString trimmed = source.trimmed(); + auto match = reg.match(trimmed); if (match.hasMatch()) - return source; + return trimmed; - if (source.contains('\n')) - return "{\n" + source + "\n}"; - - return "{ " + source + " }"; + return QString("{%2%1%2}").arg(trimmed).arg(trimmed.contains('\n') ? "\n" : " "); } SignalDeclarationProperty::SignalDeclarationProperty() = default; From 4654ca897024f97167bfa9047c936fc28218b4ff Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 4 Dec 2024 18:54:04 +0100 Subject: [PATCH 232/322] DesignViewer: Change the target URL depending on the CMake varible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie42e50893f77acafd403c67280974a38288c9283 Reviewed-by: Henning Gründl --- share/qtcreator/qmldesigner/toolbar/Main.qml | 2 +- src/plugins/qmldesigner/CMakeLists.txt | 8 ++++++++ .../qmldesigner/components/designviewer/dvconnector.cpp | 9 +++++++++ .../qmldesigner/components/designviewer/dvconnector.h | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 061b16a08b3..71941c97a9f 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -505,7 +505,7 @@ Rectangle { myText: qsTr("Manage shared projects") myIcon: StudioTheme.Constants.openLink - onClicked: Qt.openUrlExternally("https://designviewer-staging.qt.io/") + onClicked: Qt.openUrlExternally(backend.designViewerConnector.loginUrl()) } Rectangle { diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 521cdcf8519..1a12d03126f 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -33,6 +33,14 @@ env_with_default("QTC_ENABLE_METAINFO_TRACING" ENV_QTC_ENABLE_METAINFO_TRACING O option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAINFO_TRACING}) add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") +env_with_default("QDS_DESIGNVIEWER_USE_STAGING" ENV_QDS_DESIGNVIEWER_USE_STAGING OFF) +option(QDS_DESIGNVIEWER_USE_STAGING "Use staging API URL for Design Viewer" ${ENV_QDS_DESIGNVIEWER_USE_STAGING}) +add_feature_info("Use staging API URL for Design Viewer" ${QDS_DESIGNVIEWER_USE_STAGING} "") + +if(QDS_DESIGNVIEWER_USE_STAGING) + add_definitions(-DQDS_DESIGNVIEWER_USE_STAGING) +endif() + add_subdirectory(libs) find_package(Qt6 QUIET COMPONENTS WebSockets WebEngineWidgets) diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp index 610fb913359..23f99ebafe3 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.cpp @@ -24,7 +24,11 @@ Q_LOGGING_CATEGORY(deploymentPluginLog, "qtc.designer.deploymentPlugin", QtWarni namespace DVEndpoints { using namespace Qt::Literals; +#ifdef QDS_DESIGNVIEWER_USE_STAGING constexpr auto serviceUrl = "https://api-designviewer-staging.qt.io"_L1; +#else +constexpr auto serviceUrl = "https://api-designviewer.qt.io"_L1; +#endif constexpr auto project = "/api/v2/project"_L1; constexpr auto projectThumbnail = "/api/v2/project/image"_L1; constexpr auto share = "/api/v2/share"_L1; @@ -183,6 +187,11 @@ bool DVConnector::isWebViewerVisible() const return m_isWebViewerVisible; } +QString DVConnector::loginUrl() const +{ + return DVEndpoints::serviceUrl + DVEndpoints::login; +} + bool DVConnector::eventFilter(QObject *obj, QEvent *e) { if (obj == m_webEngineView.data()) { diff --git a/src/plugins/qmldesigner/components/designviewer/dvconnector.h b/src/plugins/qmldesigner/components/designviewer/dvconnector.h index 52d81c05ec4..69248c5d93d 100644 --- a/src/plugins/qmldesigner/components/designviewer/dvconnector.h +++ b/src/plugins/qmldesigner/components/designviewer/dvconnector.h @@ -57,6 +57,7 @@ public: ConnectorStatus connectorStatus() const; QByteArray userInfo() const; bool isWebViewerVisible() const; + Q_INVOKABLE QString loginUrl() const; void projectList(); Q_INVOKABLE void uploadCurrentProject(); From 7797f49cc5fd4c6f34bd1d2185702c286efe40ea Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 4 Dec 2024 09:25:49 +0200 Subject: [PATCH 233/322] EffectComposer: Enable line numbers for code editor Task-number: QDS-14295 Change-Id: I7daf5c8d7e2e2ff0633c0ba2d499f5ffa7b73246 Reviewed-by: Miikka Heikkinen --- src/plugins/effectcomposer/effectcodeeditorwidget.cpp | 6 +++++- src/plugins/effectcomposer/effectshaderscodeeditor.cpp | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp index ba5add34528..bb688aca788 100644 --- a/src/plugins/effectcomposer/effectcodeeditorwidget.cpp +++ b/src/plugins/effectcomposer/effectcodeeditorwidget.cpp @@ -174,7 +174,11 @@ void EffectCodeEditorFactory::decorateEditor(TextEditor::TextEditorWidget *edito [] { return new QmlJSEditor::QmlJSHighlighter(); }); editor->textDocument()->setIndenter(QmlJSEditor::createQmlJsIndenter( editor->textDocument()->document())); - editor->setAutoCompleter(new QmlJSEditor::AutoCompleter); + + editor->setLineNumbersVisible(true); + editor->setMarksVisible(false); + editor->setCodeFoldingSupported(false); + editor->setTabChangesFocus(true); } } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp index 009e03e9936..813bf6e747c 100644 --- a/src/plugins/effectcomposer/effectshaderscodeeditor.cpp +++ b/src/plugins/effectcomposer/effectshaderscodeeditor.cpp @@ -273,10 +273,7 @@ EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor() editor->editorWidget()); Q_ASSERT(editorWidget); - editorWidget->setLineNumbersVisible(false); - editorWidget->setMarksVisible(false); - editorWidget->setCodeFoldingSupported(false); - editorWidget->setTabChangesFocus(true); + f.decorateEditor(editorWidget); editorWidget->unregisterAutoCompletion(); editorWidget->setParent(this); editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); From edc0aae7ffe09473de006792ca441159337ce77c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 4 Dec 2024 11:59:32 +0200 Subject: [PATCH 234/322] EffectComposer: Open nearest code editor when working node is removed Code editor switches to the nearest node when the working node is removed. Working node is the node which is being edited by code editor. Fixes: QDS-14298 Change-Id: I26983ac3af71be25db0bc05cff1c17484ebac7eb Reviewed-by: Miikka Heikkinen --- .../effectcomposer/effectcomposermodel.cpp | 25 ++++++++++++++++++- .../effectcomposer/effectcomposermodel.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index eab8b3ea5f1..fb9826bc25d 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -193,7 +193,9 @@ void EffectComposerModel::removeNode(int idx) CompositionNode *node = m_nodes.takeAt(idx); // Invalidate codeEditorIndex only if the index is the same as current index - if (m_codeEditorIndex == idx) + // Then after the model reset, the nearest code editor should be opened. + const bool switchCodeEditorNode = m_codeEditorIndex == idx; + if (switchCodeEditorNode) setCodeEditorIndex(INVALID_CODE_EDITOR_INDEX); const QStringList reqNodes = node->requiredNodes(); @@ -208,6 +210,9 @@ void EffectComposerModel::removeNode(int idx) delete node; endResetModel(); + if (switchCodeEditorNode) + openNearestAvailableCodeEditor(idx); + if (m_nodes.isEmpty()) setIsEmpty(true); else @@ -1613,6 +1618,24 @@ void EffectComposerModel::saveResources(const QString &name) emit resourcesSaved(QString("%1.%2.%2").arg(m_effectTypePrefix, name).toUtf8(), effectPath); } +void EffectComposerModel::openNearestAvailableCodeEditor(int idx) +{ + int nearestIdx = idx; + + if (nearestIdx >= m_nodes.size()) + nearestIdx = m_nodes.size() - 1; + + while (nearestIdx >= 0) { + CompositionNode *node = m_nodes.at(nearestIdx); + if (!node->isDependency()) + return openCodeEditor(nearestIdx); + + --nearestIdx; + } + + openMainCodeEditor(); +} + // Get value in QML format that used for exports QString EffectComposerModel::valueAsString(const Uniform &uniform) { diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index b55762d22ed..5afc2c39c8f 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -227,6 +227,7 @@ private: void initShaderDir(); void bakeShaders(); void saveResources(const QString &name); + void openNearestAvailableCodeEditor(int idx); QString getQmlImagesString(bool localFiles, QString &outImageFixerStr); QString getQmlComponentString(bool localFiles); From f06cb25942cbad94b8bc59960a03ca4cce39ddc6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 4 Dec 2024 17:25:26 +0200 Subject: [PATCH 235/322] EffectComposer: Modify icon size of cell buttons for uniforms view Cell buttons of the uniforms view fit into the cell. A small left padding is also added. Task-number: QDS-14245 Change-Id: I9645ddbb8baee921dfe9fd1e859c2bd78abd5df4 Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/CodeEditorUniformsView.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml index 2f4a01f927e..016a9c0a3ed 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorUniformsView.qml @@ -156,6 +156,7 @@ ColumnLayout { anchors.fill: parent spacing: StudioTheme.Values.controlGap visible: hoverArea.containsMouse + leftPadding: 5 CellButton { buttonIcon: StudioTheme.Constants.assignTo_medium @@ -246,6 +247,7 @@ ColumnLayout { height: iconSize anchors.verticalCenter: parent.verticalCenter buttonIcon: StudioTheme.Constants.assignTo_medium + iconSize: StudioTheme.Values.miniIcon backgroundVisible: false StudioControls.ToolTip { From 736cb53cfdfc91baafbd858d1aac00d6fde6a15d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 4 Dec 2024 19:27:12 +0100 Subject: [PATCH 236/322] QmlDesigner: Do not allow id stateGroup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can become ambiguous, because we want to use stateGroup as a property for transitions. Task-number: QDS-14290 Change-Id: Ifc166e03b4cb14d3d0a466a8d5a7a4fc191ec666 Reviewed-by: Henning Gründl --- share/qtcreator/qmldesigner/stateseditor/Main.qml | 2 +- .../libs/designercore/designercoreutils/modelutils.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 9184c870ac7..ac3c11d9fe1 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -470,7 +470,7 @@ Rectangle { tooltip: StatesEditorBackend.statesEditorModel.isMCUs ? qsTr("State Groups are not supported with Qt for MCUs") : qsTr("Create State Group") - onClicked: StatesEditorBackend.statesEditorModel.addStateGroup("stateGroup") + onClicked: StatesEditorBackend.statesEditorModel.addStateGroup("newStateGroup") enabled: !StatesEditorBackend.statesEditorModel.isMCUs } diff --git a/src/plugins/qmldesigner/libs/designercore/designercoreutils/modelutils.cpp b/src/plugins/qmldesigner/libs/designercore/designercoreutils/modelutils.cpp index 5e13043b7e2..c22b9175c55 100644 --- a/src/plugins/qmldesigner/libs/designercore/designercoreutils/modelutils.cpp +++ b/src/plugins/qmldesigner/libs/designercore/designercoreutils/modelutils.cpp @@ -138,6 +138,7 @@ constexpr auto qmlDiscouragedIds = toSortedArray(u"action", u"sprite", u"spriteSequence", u"state", + u"stateGroup", u"text", u"texture", u"time", From fb30027683621624b39903abf6afa32f9b132304 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 3 Dec 2024 17:34:33 +0200 Subject: [PATCH 237/322] EffectComposer: Resize the nodes combobox based on the longest element ListModelWidthCalculator module is added to calculate the length of the longest element in the list Task-number: QDS-14286 Change-Id: I4a3a0fd05bcb03629a546d9287b68bb4ec6ed66c Reviewed-by: Miikka Heikkinen --- .../CodeEditorHeader.qml | 13 +- src/plugins/effectcomposer/CMakeLists.txt | 1 + .../effectcomposer/effectcomposerview.cpp | 2 + .../listmodelwidthcalculator.cpp | 226 ++++++++++++++++++ .../effectcomposer/listmodelwidthcalculator.h | 63 +++++ 5 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 src/plugins/effectcomposer/listmodelwidthcalculator.cpp create mode 100644 src/plugins/effectcomposer/listmodelwidthcalculator.h diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml index 508c174efb4..10f19b05c69 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/CodeEditorHeader.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Layouts import StudioControls as StudioControls import StudioTheme as StudioTheme +import ModelModules as ModelModules Rectangle { id: root @@ -24,7 +25,9 @@ Rectangle { StudioControls.TopLevelComboBox { id: nodesComboBox style: StudioTheme.Values.toolbarStyle - Layout.preferredWidth: 400 + Layout.preferredWidth: nodeNamesWidthCalculator.maxWidth + + nodesComboBox.indicator.width + + 20 Layout.alignment: Qt.AlignVCenter model: editableCompositionsModel textRole: "display" @@ -34,6 +37,14 @@ Rectangle { onActivated: (idx) => { editableCompositionsModel.openCodeEditor(idx) } + + ModelModules.ListModelWidthCalculator { + id: nodeNamesWidthCalculator + + model: nodesComboBox.model + font: nodesComboBox.font + textRole: nodesComboBox.textRole + } } Item { // Spacer diff --git a/src/plugins/effectcomposer/CMakeLists.txt b/src/plugins/effectcomposer/CMakeLists.txt index eafc4b6d531..45807777813 100644 --- a/src/plugins/effectcomposer/CMakeLists.txt +++ b/src/plugins/effectcomposer/CMakeLists.txt @@ -24,6 +24,7 @@ add_qtc_plugin(EffectComposer uniform.cpp uniform.h effectutils.cpp effectutils.h effectcomposercontextobject.cpp effectcomposercontextobject.h + listmodelwidthcalculator.cpp listmodelwidthcalculator.h shaderfeatures.cpp shaderfeatures.h syntaxhighlighterdata.cpp syntaxhighlighterdata.h propertyhandler.cpp propertyhandler.h diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index 437fdaf6ae8..f8252ae5e63 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -6,6 +6,7 @@ #include "effectcomposermodel.h" #include "effectcomposernodesmodel.h" #include "effectcomposerwidget.h" +#include "listmodelwidthcalculator.h" #include "studioquickwidget.h" #include "tableheaderlengthmodel.h" @@ -250,6 +251,7 @@ void EffectComposerView::dragEnded() void EffectComposer::EffectComposerView::registerDeclarativeTypes() { qmlRegisterType("TableModules", 1, 0, "TableHeaderLengthModel"); + qmlRegisterType("ModelModules", 1, 0, "ListModelWidthCalculator"); } } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/listmodelwidthcalculator.cpp b/src/plugins/effectcomposer/listmodelwidthcalculator.cpp new file mode 100644 index 00000000000..28697950397 --- /dev/null +++ b/src/plugins/effectcomposer/listmodelwidthcalculator.cpp @@ -0,0 +1,226 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "listmodelwidthcalculator.h" + +#include + +ListModelWidthCalculator::ListModelWidthCalculator(QObject *parent) + : QObject(parent) + , m_fontMetrics(qApp->font()) +{} + +void ListModelWidthCalculator::setModel(QAbstractItemModel *model) +{ + if (m_model == model) + return; + + clearConnections(); + + m_model = model; + + if (m_model) { + m_connections << connect( + m_model, + &QAbstractItemModel::rowsInserted, + this, + &ListModelWidthCalculator::onSourceItemsInserted); + + m_connections << connect( + m_model, + &QAbstractItemModel::rowsRemoved, + this, + &ListModelWidthCalculator::onSourceItemsRemoved); + + m_connections << connect( + m_model, + &QAbstractItemModel::dataChanged, + this, + &ListModelWidthCalculator::onSourceDataChanged); + + m_connections << connect( + m_model, &QAbstractItemModel::modelReset, this, &ListModelWidthCalculator::reset); + } + + emit modelChanged(); + + if (!updateRole()) + reset(); +} + +void ListModelWidthCalculator::setTextRole(const QString &role) +{ + if (m_textRole == role) + return; + m_textRole = role; + emit textRoleChanged(m_textRole); + updateRole(); +} + +void ListModelWidthCalculator::setFont(const QFont &font) +{ + if (m_font == font) + return; + + m_font = font; + emit fontChanged(); + reset(); +} + +QAbstractItemModel *ListModelWidthCalculator::model() const +{ + return m_model.get(); +} + +int ListModelWidthCalculator::maxWidth() const +{ + return m_maxWidth; +} + +void ListModelWidthCalculator::clearConnections() +{ + for (QMetaObject::Connection &connection : m_connections) + disconnect(connection); + + m_connections.clear(); +} + +void ListModelWidthCalculator::reset() +{ + int maxW = 0; + m_data.clear(); + if (m_model) { + const int rows = m_model->rowCount(); + m_data.reserve(rows); + + for (int row = 0; row < rows; ++row) { + const QModelIndex &modelIdx = m_model->index(row, 0); + const QString &text = modelIdx.data(m_role).toString(); + const int textWidth = widthOfText(text); + m_data.append(textWidth); + if (textWidth > maxW) + maxW = textWidth; + } + } + setMaxWidth(maxW); +} + +/*! + * \internal + * \brief ListModelWidthCalculator::updateRole + * \return True if role is updated. It means that reset() is called. + */ +bool ListModelWidthCalculator::updateRole() +{ + int newRole = -1; + if (m_model && !m_textRole.isEmpty()) + newRole = m_model->roleNames().key(m_textRole.toUtf8()); + + if (m_role != newRole) { + m_role = newRole; + reset(); + return true; + } + return false; +} + +void ListModelWidthCalculator::setMaxWidth(int maxWidth) +{ + if (m_maxWidth == maxWidth) + return; + + m_maxWidth = maxWidth; + emit maxWidthChanged(m_maxWidth); +} + +int ListModelWidthCalculator::widthOfText(const QString &str) const +{ + const int nIdx = str.indexOf('\n'); + const QString &firstLineText = (nIdx > -1) ? str.left(nIdx) : str; + return std::ceil(m_fontMetrics.boundingRect(firstLineText).width()); +} + +void ListModelWidthCalculator::onSourceItemsInserted(const QModelIndex &, int first, int last) +{ + if (!isValidRow(first)) { + reset(); + return; + } + + int maxW = maxWidth(); + m_data.reserve(m_data.size() + last - first + 1); + const QList &tail = m_data.mid(first, -1); + m_data.remove(first, -1); + for (int row = first; row <= last; ++row) { + const QModelIndex &modelIdx = m_model->index(row, 0); + const QString &text = modelIdx.data(m_role).toString(); + const int textWidth = widthOfText(text); + m_data.append(textWidth); + if (textWidth > maxW) + maxW = textWidth; + } + m_data.append(tail); + setMaxWidth(maxW); +} + +void ListModelWidthCalculator::onSourceItemsRemoved(const QModelIndex &parent, int first, int last) +{ + if (!isValidRow(first) || !isValidRow(last)) { + reset(); + return; + } + + int maxW = maxWidth(); + bool resetRequired = false; + + for (int row = first; row <= last; ++row) { + const int savedWidth = m_data.at(row); + if (savedWidth == maxW) { + resetRequired = true; + break; + } + } + + if (resetRequired) + reset(); + else + m_data.remove(first, last - first + 1); +} + +void ListModelWidthCalculator::onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) +{ + if (!roles.contains(m_role)) + return; + + const int first = topLeft.row(); + const int last = bottomRight.row(); + + if (!isValidRow(first) || !isValidRow(last)) { + reset(); + return; + } + + int maxW = maxWidth(); + + for (int row = first; row <= last; ++row) { + const QModelIndex &modelIdx = m_model->index(row, 0); + const QString &text = modelIdx.data(m_role).toString(); + const int textWidth = widthOfText(text); + const int savedWidth = m_data.at(row); + if (textWidth == savedWidth) + continue; + + if (textWidth > maxW) + maxW = textWidth; + + m_data[row] = textWidth; + } + + setMaxWidth(maxW); +} + +bool ListModelWidthCalculator::isValidRow(int row) +{ + return row > -1 && row < m_data.size(); +} diff --git a/src/plugins/effectcomposer/listmodelwidthcalculator.h b/src/plugins/effectcomposer/listmodelwidthcalculator.h new file mode 100644 index 00000000000..96e6d5d8722 --- /dev/null +++ b/src/plugins/effectcomposer/listmodelwidthcalculator.h @@ -0,0 +1,63 @@ +// Copyright (C) 2024 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 + +QT_FORWARD_DECLARE_CLASS(QModelIndex) + +class ListModelWidthCalculator : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QString textRole MEMBER m_textRole WRITE setTextRole NOTIFY textRoleChanged) + Q_PROPERTY(QFont font MEMBER m_font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(int maxWidth READ maxWidth NOTIFY maxWidthChanged) + +public: + explicit ListModelWidthCalculator(QObject *parent = nullptr); + + void setModel(QAbstractItemModel *model); + void setTextRole(const QString &role); + void setFont(const QFont &font); + + QAbstractItemModel *model() const; + int maxWidth() const; + +signals: + void modelChanged(); + void textRoleChanged(QString); + void fontChanged(); + void maxWidthChanged(int); + +private: + void clearConnections(); + void reset(); + bool updateRole(); + void setMaxWidth(int maxWidth); + + int widthOfText(const QString &str) const; + + void onSourceItemsInserted(const QModelIndex &parent, int first, int last); + void onSourceItemsRemoved(const QModelIndex &parent, int first, int last); + void onSourceDataChanged( + const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles); + + bool isValidRow(int row); + + QPointer m_model; + QList m_connections; + + QString m_textRole; + int m_role = -1; + QFont m_font; + QFontMetrics m_fontMetrics; + + int m_maxWidth = 0; + QList m_data; +}; From 6cea8545614ce5d94d7cfa59c9096ce046e56b37 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 5 Dec 2024 13:17:28 +0200 Subject: [PATCH 238/322] Doc: Fix broken links in side bar Fixes: QDS-14316 Change-Id: I8cacc14ec25c8e629a116feedb5f15b8f281bf26 Reviewed-by: Mats Honkamaa --- doc/qtdesignstudio/config/style/qt5-sidebar.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index 8616343e987..dc650be95f6 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -114,7 +114,6 @@
    • Data @@ -349,7 +349,7 @@

      Reference

        -
      • 2D components +
      • The 2D components
        • Overview
        • Animations
        • @@ -401,7 +401,7 @@
          • Help
              -
            • Concept and terms
            • +
            • Concepts and terms
            • Keyboard shortcuts
            • Getting help
            • Supported platforms
            • diff --git a/doc/qtdesignstudio/examples/doc/FireParticles.qdoc b/doc/qtdesignstudio/examples/doc/FireParticles.qdoc index c240df651b9..58a590b15a3 100644 --- a/doc/qtdesignstudio/examples/doc/FireParticles.qdoc +++ b/doc/qtdesignstudio/examples/doc/FireParticles.qdoc @@ -95,7 +95,7 @@ You now have the particle system in place. To preview it, select \key Alt + \key{P}. You can also preview the particle system in - the \uicontrol{3D} view by using the \l {Particle Editor} tools. + the \uicontrol{3D} view by using the \l {Particle editor} tools. \section2 Adding Sprites and Sprite Animations diff --git a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc index cf23bf9b7b9..91807b41277 100644 --- a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc +++ b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc @@ -4,7 +4,7 @@ /*! \page state-transition-animations.html \ingroup gstutorials - \sa States, {Transitions}, {Working with States} + \sa States, {Transitions}, {Working with states} \title Animated state transitions \brief Illustrates how to create animated state transitions. diff --git a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc index 35a6901a899..05cc7ffa19c 100644 --- a/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/animationTutorial.qdoc @@ -26,7 +26,7 @@ /*! \page animation-tutorial.html \ingroup gstutorials - \sa {Creating Timeline Animations} + \sa {Creating timeline animations} \title Timeline animation \brief Illustrates how to create timeline animations and bind them to diff --git a/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc b/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc index def3b5185c5..7c54ae1629f 100644 --- a/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc +++ b/doc/qtdesignstudio/examples/doc/coffeemachine.qdoc @@ -92,7 +92,7 @@ The Coffee Machine application screens for choosing coffee, empty cup, and brewing each use custom components specified in separate \l{UI files} - {UI files} (ui.qml). + (ui.qml). We use the \l Timeline view to animate the transitions between the screens during the application flow in \e {ApplicationFlowForm.ui.qml}. diff --git a/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc b/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc index 5b5571cdd8f..3cd79e902f5 100644 --- a/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc +++ b/doc/qtdesignstudio/examples/doc/ebikedesign.qdoc @@ -69,7 +69,7 @@ speedometer grow and shrink in size depending on its current position. For more information about using the timeline, see - \l {Creating Timeline Animations}. + \l {Creating timeline animations}. \section1 Using states to move between screens diff --git a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc index 67e19302a37..5f85955c8a4 100644 --- a/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc +++ b/doc/qtdesignstudio/examples/doc/effectComposerExample.qdoc @@ -5,7 +5,7 @@ \page effect-composer-example.html \ingroup studioexamples - \title The Effect Composer example + \title Effect Composer example \brief Illustrates how to work with the Effect Composer effects. \image effect-composer-example.webp {The Effect Composer example project} @@ -69,6 +69,6 @@ \endlist The example project also uses \uicontrol States and \uicontrol Transitions. To learn more, see - \l {Working with States} and \l {Transitions}. + \l {Working with states} and \l {Transitions}. */ diff --git a/doc/qtdesignstudio/examples/doc/loginui1.qdoc b/doc/qtdesignstudio/examples/doc/loginui1.qdoc index 97eee4e4260..af277fba96f 100644 --- a/doc/qtdesignstudio/examples/doc/loginui1.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui1.qdoc @@ -24,7 +24,7 @@ For the purposes of this tutorial, you will use the empty wizard template. Wizard templates are available also for creating UIs that are optimized for mobile platforms and for launcher applications. For more information about - the options you have, see \l {Creating Projects}. + the options you have, see \l {Creating projects}. To create a project: @@ -387,7 +387,7 @@ \li The \e {loginui1.qml} file defines the functionality of the UI. For the time being, it does not do anything. \li The \e {Screen01.ui.qml} file is a custom component created by - the wizard template. For more information, see \l {UI Files}. + the wizard template. For more information, see \l {UI files}. By default, this is the main file in the project, but you can change that in the .qmlproject file. While the custom component @@ -409,11 +409,11 @@ \e EventListSimulator.qml files are not used in this example, so you can ignore them for now. \endlist - \l{UI files}{UI files} define a hierarchy of components with a - highly-readable, structured layout. Every UI file consists of two parts: - an imports section and an component declaration section. The components and - functionality most common to UIs are provided in the \c QtQuick import. You - can view the code of a \e .ui.qml file in the \l{Code} view. + \l{UI files} define a hierarchy of components with a highly-readable, + structured layout. Every UI file consists of two parts: an imports section + and a component declaration section. The components and functionality most + common to UIs are provided in the \c QtQuick import. You can view the code + of a \e .ui.qml file in the \l{Code} view. \section2 Components diff --git a/doc/qtdesignstudio/examples/doc/loginui2.qdoc b/doc/qtdesignstudio/examples/doc/loginui2.qdoc index baeae509ab1..7696d91f52f 100644 --- a/doc/qtdesignstudio/examples/doc/loginui2.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui2.qdoc @@ -35,7 +35,7 @@ \section1 Anchoring UI components - First, you will \l {Setting Anchors and Margins}{anchor} the + First, you will \l {Setting anchors and margins}{anchor} the static page elements, background image (\e adventurePage), logo (\e qt_logo_green_128x128px), and tag line (\e tagLine), to the page. @@ -190,10 +190,10 @@ For many use cases, the best positioner to use is a simple grid, row, or column, and \QDS provides components that will position children in these formations in the most efficient manner possible. For more information - about using preset positioners, see \l {Using Positioners}. + about using preset positioners, see \l {Using positioners}. For more complicated UI designs, you can use components from the - \l {Using Layouts}{Qt Quick Layouts module}. + \l {Using layouts}{Qt Quick Layouts module}. \section1 Next steps diff --git a/doc/qtdesignstudio/examples/doc/loginui4.qdoc b/doc/qtdesignstudio/examples/doc/loginui4.qdoc index cb4b63d123e..5eb670b8d7a 100644 --- a/doc/qtdesignstudio/examples/doc/loginui4.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui4.qdoc @@ -23,7 +23,7 @@ In \l {Login UI: States}, you learned how to use states to simulate page changes in a UI and connections to provide user interaction with it. In this part, you will now learn another way of animating the UI by creating - \l{Creating Timeline Animations}{timeline animations} that you bind + \l{Creating timeline animations}{timeline animations} that you bind to states. The starting point for this tutorial is the completed @@ -33,7 +33,7 @@ Additionally, you can download the completed project of this tutorial \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui4}{here}. - For more information, see \l {Creating Timeline Animations}. + For more information, see \l {Creating timeline animations}. \section1 Animating UI components diff --git a/doc/qtdesignstudio/examples/doc/progressbar.qdoc b/doc/qtdesignstudio/examples/doc/progressbar.qdoc index ab962ebbe0f..a3be6899a04 100644 --- a/doc/qtdesignstudio/examples/doc/progressbar.qdoc +++ b/doc/qtdesignstudio/examples/doc/progressbar.qdoc @@ -14,7 +14,7 @@ \section1 Creating the progress bar - First, we create an empty project, as described in \l {Creating Projects}. + First, we create an empty project, as described in \l {Creating projects}. For the purposes of this example, we call the project \e progressbar. In this example, we use two overlapping instances of the preset @@ -63,7 +63,7 @@ in the \l Timeline view. For more information about using the timeline, see - \l {Creating Timeline Animations}. + \l {Creating timeline animations}. \section2 Adding color animation @@ -201,7 +201,7 @@ by specifying easing curves for them. For more information about previewing UIs, see - \l {Validating with Target Hardware}. + \l {Validating with target hardware}. \section1 Specifying easing curves diff --git a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc index 2061d021a83..469aa9a399a 100644 --- a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc +++ b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc @@ -172,7 +172,7 @@ dialog, we bind the states that don't have animations to fixed frames. For more information about using the timeline, see - \l {Creating Timeline Animations}. + \l {Creating timeline animations}. \section1 Connecting the burger menu to actions @@ -192,8 +192,8 @@ The side menu is fully visible and accepts input only in the \e open state. - For more information about Connecting Components to Signals, see - \l {Connecting Components to Signals}. + For more information about connecting components to signals, see + \l {Connecting components to signals}. \section1 Applying effects diff --git a/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc b/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc index 964ed09f85d..11d3e4322f0 100644 --- a/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc +++ b/doc/qtdesignstudio/examples/doc/simplekeyboard.qdoc @@ -24,7 +24,7 @@ \section1 Using a virtual keyboard - First, we create an empty project, as described in \l {Creating Projects}. + First, we create an empty project, as described in \l {Creating projects}. For the purposes of this example, we call the project \e SimpleKeyboard. We can use the default settings for other options, but we need to select the \uicontrol {Use Qt Virtual Keyboard} check box on the diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index b81213ebc67..c1e288ced63 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -228,7 +228,7 @@ We create similar connections between button components and signals in the other screens to apply other actions that move users to other screens. - For more information, see \l {Connecting Components to Signals}. + For more information, see \l {Connecting components to signals}. \section1 Showing the current time diff --git a/doc/qtdesignstudio/examples/doc/webinardemo.qdoc b/doc/qtdesignstudio/examples/doc/webinardemo.qdoc index 80e4cf0a0f2..31a1b04a8e9 100644 --- a/doc/qtdesignstudio/examples/doc/webinardemo.qdoc +++ b/doc/qtdesignstudio/examples/doc/webinardemo.qdoc @@ -178,5 +178,5 @@ \image webinardemo-timeline.png "Popup animations in the Timeline view" For more information about using the timeline, see - \l {Creating Timeline Animations}. + \l {Creating timeline animations}. */ diff --git a/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc index 22a71ec681d..29847e73004 100644 --- a/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc +++ b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc @@ -6,9 +6,9 @@ \page best-practices-glow.html \nextpage {Examples} - \title Creating Glow and Bloom Effects + \title Creating the glow and the bloom effects - In \QDS, you can add a glow and bloom effect to your 3D scene using the + In \QDS, you can add a glow and a bloom effect to your 3D scene using the \uicontrol ExtendedSceneEnvironment component (available in Qt 6.5 and later). With this effect, you can, for example, create glow around illuminated areas (such as material or skyboxes when using image-based lighting) or add ambient light. Using the glow effect is one way to make your @@ -16,12 +16,12 @@ \image glow-example.webp - \section1 Creating a Project with ExtendedSceneEnvironment + \section1 Creating a project with ExtendedSceneEnvironment To create a project with \uicontrol ExtendedSceneEnvironment as the default scene environment, use the \uicontrol {3D Extended} project preset. - For more information about creating projects, see \l{Creating Projects}. + For more information about creating projects, see \l{Creating projects}. \section1 Adding ExtendedSceneEnvironment to an Existing Project @@ -31,7 +31,7 @@ \image ext-scene-env-navigator.webp - \section1 Enabling the Glow Effect + \section1 Enabling the Glow effect To enable the glow effect, select \e SceneEnvironment in the \uicontrol Navigator view and then, in the \uicontrol Properties view, select \uicontrol Enabled in the @@ -42,7 +42,7 @@ \note When setting up or experimenting with the glow effect, use the \l {Blend Modes}{Replace} blend mode to see the effect more clearly. - \section1 The Flashlight Example Project + \section1 The Flashlight Example project The flashlight example used in this documentation is available from the \uicontrol Examples section of the \QDS \uicontrol Welcome page. @@ -54,7 +54,7 @@ \image glow-example-project.webp - \section1 Basic Properties + \section1 Basic properties Usually, the best way to achieve a realistic glow effect in your 3D scene is to adjust the \uicontrol {Strength}, \uicontrol {Intensity}, and \uicontrol {Bloom} @@ -131,7 +131,7 @@ \li \image glow_all_blur_levels.webp \endtable - \section2 Blend Modes + \section2 Blend modes The following blend modes are available: @@ -160,7 +160,7 @@ \li \image glow-replace-blend.webp \endtable - \section1 Improvement Properties + \section1 Improvement properties The \uicontrol{High Quality} and \uicontrol{Bicubical Upsampling} properties improve the quality of the glow blur by upsampling. Using these properties diff --git a/doc/qtdesignstudio/src/best-practices.qdoc b/doc/qtdesignstudio/src/best-practices.qdoc index e494ac74e92..345a055d3f2 100644 --- a/doc/qtdesignstudio/src/best-practices.qdoc +++ b/doc/qtdesignstudio/src/best-practices.qdoc @@ -6,32 +6,32 @@ \page best-practices.html \nextpage best-practices-glow.html - \title Best Practices + \title Best practices \section1 Graphics \list - \li \l {Creating Glow and Bloom Effects} + \li \l {Creating the glow and the bloom effects} \endlist \section1 Performance \list - \li \l {Optimizing Designs} - \li \l {QML Performance Considerations And Suggestions} - \li \l {Creating Optimized 3D Scenes} - \li \l {Using Components Economically} + \li \l {Optimizing designs} + \li \l {QML Performance Considerations And Suggestions}{QML performance considerations and suggestions} + \li \l {Creating optimized 3D scenes} + \li \l {Using components economically} \endlist \section1 Projects \list - \li \l {Converting Qt Design Studio Projects to Applications} + \li \l {Converting \QDS projects to applications} \endlist \section1 Workflow \list - \li \l {Designer-Developer Workflow} + \li \l {Designer-developer workflow} \endlist */ diff --git a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc index 11b0b7a75e6..3830dbb0f02 100644 --- a/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-animation-types.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -19,16 +19,16 @@ create instances of them. You can achieve similar results by using different animation techniques. - For more information, see \l{Introduction to Animation Techniques}. + For more information, see \l{Introduction to animation techniques}. - \section1 Applying Animation + \section1 Applying animations A property animation is applied when the value of a property changes. Color and number animations are property animation types for specific purposes. Specify settings for animations in \l Properties > \uicontrol {Animation Targets}. - \section2 Animating Color Changes + \section2 Animating color changes For example, you can apply animation to the value of the \uicontrol Color property of an instance of a \l Rectangle component to change its value @@ -49,7 +49,7 @@ color in the \uicontrol {To color} field. Specify the duration of the animation in the \uicontrol Duration field. - \section2 Animating Changes in Numerical Values + \section2 Animating changes in numerical values Similarly, to apply animation when a numerical value of a property changes, create an instance of the \uicontrol {Number Animation} component. @@ -72,7 +72,7 @@ For an example of using property animation to animate the scale and opacity of components, see the \l{Coffee Machine} example. - \section2 Setting Non-Animated Properties + \section2 Setting non-animated properties To immediately change a property value during an animation without animating the property change, create an instance @@ -92,7 +92,7 @@ \image qtquick-property-action.gif "Sequential property actions and number animation" - \section1 Playing Animations + \section1 Playing animations Specify settings for playing animations in the \uicontrol Animation group. @@ -104,7 +104,7 @@ You can connect the running property of an animation to a signal emitted by a component to play the animation when users click a button, for - example. For more information, see \l{Connecting Components to Signals}. + example. For more information, see \l{Connecting components to signals}. To run animations several times in a loop, set the number of times they should play in the \uicontrol Loops field. Set the value to -1 to have @@ -123,12 +123,12 @@ To pause animations, select the \inlineimage icons/pause-icon.png (\uicontrol Paused) check box. - To attach an \l{Editing Easing Curves}{easing curve} to + To attach an \l{Editing easing curves}{easing curve} to the animation, select the \inlineimage icons/curve_editor.png (\uicontrol {Easing Curve Editor}) button in the \uicontrol {Easing Curve} field. - \section2 Playing Groups of Animations + \section2 Playing groups of animations You can create several animations that can run in parallel or in sequence. To manage a group of animations that will play at the same time, create an @@ -155,7 +155,7 @@ check box and specify the duration of the pause in the \uicontrol Duration field. - \section1 Performance Considerations + \section1 Performance considerations \QDS enables you to use fluidity and dynamic transitions as well as visual effects to great effect in a UI. However, you need to take some care when @@ -176,7 +176,7 @@ component because script animations are run in the main thread and can therefore cause frames to be skipped if they take too long to complete. - \section1 Summary of Animation Components + \section1 Summary of the animation components The following table lists the components that you can use to animate component properties programmatically. They are available in diff --git a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc index 9a221408213..9e9c5df4ffe 100644 --- a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -12,7 +12,7 @@ \previouspage quick-components-creating.html \nextpage quick-scalable-image.html - \title Creating Buttons + \title Creating buttons To create a button component: @@ -20,7 +20,7 @@ \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Files} > \uicontrol {Qt Quick UI File} > - \uicontrol Choose to create a \l{UI Files}{UI file} called + \uicontrol Choose to create a \l{UI files}{UI file} called Button.ui.qml (for example). \note Components are listed in \uicontrol Components > @@ -95,7 +95,7 @@ To create a graphical button that scales beautifully without using vector graphics, use the \l {Border Image} component. For more - information, see \l{Creating Scalable Buttons and Borders}. + information, see \l{Creating scalable buttons and borders}. */ /*! @@ -103,13 +103,13 @@ \page quick-scalable-image.html \nextpage qtquick-properties.html - \title Creating Scalable Buttons and Borders + \title Creating scalable buttons and borders You can use the \l {Border Image} component to display an image, such as a PNG file, as a border and a background. Use two border images and suitable graphics to change the appearance of - a button when it is clicked. You can use use \l{Working with States}{states} + a button when it is clicked. You can use use \l{Working with states}{states} to determine which image is visible depending on whether the mouse button is pressed down. You could add more images and states to change the appearance of the button depending on other mouse events, @@ -124,20 +124,20 @@ \image qmldesigner-borderimage-type.png "Button component in the 2D and States views" - \section1 Creating the Button Component + \section1 Creating the Button component To create a button component, select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Files} > \uicontrol {Qt Quick UI File} > - \uicontrol Choose to create a \l{UI Files}{UI file} called Button.ui.qml + \uicontrol Choose to create a \l{UI files}{UI file} called Button.ui.qml (for example). \note Components are listed in \uicontrol Components > \uicontrol {My Components} only if the filename begins with a capital letter. - \section1 Constructing the Button Component + \section1 Constructing the Button component To construct the button component: @@ -198,7 +198,7 @@ \endlist \endlist - \section1 Using States to Change Component Property Values + \section1 Using States to change component property values \list 1 \li In the \l States view, select \inlineimage icons/plus.png @@ -244,7 +244,7 @@ and set the button text for each button instance, for example. For more information about positioning buttons on screens, see - \l{Scalable Layouts}. + \l{Scalable layouts}. \image qmldesigner-borderimage.png "Button preview as part of a screen" */ diff --git a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc index 7fb95a9880e..849e2da34cd 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc @@ -3,64 +3,64 @@ /*! //! [context-menu] - \section1 Context Menu + \section1 Context menu The following table summarizes the \uicontrol Navigator and - \uicontrol {2D} views context menu items and provides links + \uicontrol {2D} view context menu items and provides links to more information about them. \table \header - \li To Learn About - \li Go To + \li To learn about + \li Go to \row \li Arrange - \li \l{Arranging Components} + \li \l{Arranging components} \row \li Edit - \li \l{Showing and Hiding Components} + \li \l{Showing and hiding components} \row \li Anchors - \li \l{Setting Anchors and Margins} + \li \l{Setting anchors and margins} \row \li Group - \li \l{Organizing Components} + \li \l{Organizing components} \row \li Positioner - \li \l{Using Positioners} + \li \l{Using positioners} \row \li Layout - \li \l{Using Layouts} + \li \l{Using layouts} \row \li Stacked Container - \li \l{Lists and Other Data Models} + \li \l{Lists and other data models} \row \li Timeline \li \l{Creating a Timeline} \row \li Event List - \li \l{Simulating Events} + \li \l{Simulating events} \row \li Edit Color - \li \l{Editing Properties Inline} + \li \l{Editing properties inline} \row \li Edit Annotation - \li \l{Annotating Designs} + \li \l{Annotating designs} \row \li Merge File with Template - \li \l{Merging Files with Templates} + \li \l{Merging files with templates} \row \li Move Component Instances into Separate Files - \li \l{Turning Component Instances into Custom Components} + \li \l{Turning component instances into custom components} \row - \li Connecting Components to Signals - \li \l{Connecting Components to Signals in the Connection View} + \li Connecting components to signals + \li \l{Connecting components to signals in the Connection view} \row \li Go to Implementation - \li \l{Using UI Files} + \li \l{Using UI files} \row \li Edit Component - \li \l{Moving Within Components} + \li \l{Moving within components} \endtable //! [context-menu] */ diff --git a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc index 3fa33460569..f3b0a19f072 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,12 +6,12 @@ \previouspage studio-3d-loader-3d.html \nextpage quick-components-creating.html - \title Creating Component Instances + \title Creating component instances \QDS comes with \e {preset components} that you can use in your UI by creating instances of them. - \image qmldesigner-editing-components.webp "Creating Component Instances" + \image qmldesigner-editing-components.webp "Creating component instances" To create component instances and edit their properties: @@ -29,23 +29,23 @@ custom properties on the \uicontrol {Properties} tab in the \l {Connections} view. \image add-updated-local-custom-property.webp "Connections View Properties tab" - For more information, see \l{Specifying Custom Properties}. + For more information, see \l{Specifying custom properties}. \li To enable users to interact with the component instances, connect the instances to signals on the \uicontrol Connections tab in the \uicontrol {Connections} view. For example, you can specify what happens when a component instance is clicked. For more information, - see \l{Connecting Components to Signals}. + see \l{Connecting components to signals}. \image qmldesigner-connections.webp "Connections View Connections tab" \li To dynamically change the behavior of a component instance when another component instance changes, create bindings between them on the \uicontrol Bindings tab in the \uicontrol {Connections} view. - For more information, see \l{Adding Bindings Between Properties}. + For more information, see \l{Adding bindings between properties}. \image qmldesigner-bindings.webp "Connections view Bindings tab" \li Add states to apply sets of changes to the property values of one or several component instances in the \uicontrol States view. - For more information, see \l{Working with States}. + For more information, see \l{Working with states}. \li Animate the properties of component instances in the \uicontrol Timeline view. For more information, see - \l{Creating Timeline Animations}. + \l{Creating timeline animations}. \endlist */ diff --git a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc index 74c9eb142ce..5fe197502b1 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage quick-component-instances.html \nextpage quick-buttons.html - \title Creating Custom Components + \title Creating custom components You can either use project wizard templates to create custom components and controls or move component instances into separate files to turn them into @@ -20,7 +20,7 @@ by dragging-and-dropping them from \uicontrol Components to the \l {2D}, \l {3D}, or \l Navigator view. - \section1 Creating Components from Scratch + \section1 Creating components from scratch To use wizard templates to create custom components: @@ -37,7 +37,7 @@ \uicontrol Navigator or the \uicontrol {2D} view. \li Edit component properties in the \uicontrol Properties view. The available properties depend on the component type. You can - \l{Specifying Custom Properties}{add properties for + \l{Specifying custom properties}{add properties for components} on the \uicontrol {Properties} tab in the \uicontrol Connections view. \li To change the appearance and behavior of the component instances @@ -53,13 +53,13 @@ using instances of basic components: \list - \li The \l{2D} View - \li The \l{3D} View - \li \l{Creating Buttons} - \li \l{Creating Scalable Buttons and Borders} + \li The \l{2D} view + \li The \l{3D} view + \li \l{Creating buttons} + \li \l{Creating scalable buttons and borders} \endlist - \section1 Naming Conventions + \section1 Naming conventions Establish naming conventions to keep the components in your UI organized. Name your components accurately and give them suitable IDs. Particularly, @@ -78,7 +78,7 @@ \include qtdesignstudio-components.qdocinc creating studio components - \section1 Turning Component Instances into Custom Components + \section1 Turning component instances into custom components An alternative way of creating reusable components is to turn component instances into custom components by moving them into @@ -98,7 +98,7 @@ To open the new component file for editing the properties that you want to change for all instances of the component, right-click the component, and then select \uicontrol {Go into Component} in the context menu. For - additional ways of opening base components, see \l{Moving Within Components}. + additional ways of opening base components, see \l{Moving within components}. For an example of creating a reusable custom component, see \l{Progress Bar}. @@ -107,7 +107,7 @@ > \uicontrol {My Components}, and you can use instances of them to build more components. - \section1 Merging Files with Templates + \section1 Merging files with templates You can merge the current component file against an existing second component file using the second file in a way similar to using a CSS diff --git a/doc/qtdesignstudio/src/components/qtquick-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-components.qdoc index a0ab80b2570..7e1c0e7ef64 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components.qdoc @@ -6,7 +6,7 @@ \previouspage quick-uis.html \nextpage quick-preset-components.html - \title Using Components + \title Using components A \e component is a reusable building block for a UI. @@ -18,7 +18,7 @@ Some of the preset components represent simple shapes, text, or images, while others represent complex UI controls with full functionality, such as spin boxes or sliders. You can also add instances of preset - \l {3D Components}{3D components} to your UIs. + \l {3D components}{3D components} to your UIs. To build your own components, you can modify the \e properties of the component instances and combine them. @@ -27,8 +27,8 @@ \e ui.qml or \e .qml). For example, a Button component may be defined in \e Button.ui.qml. Typically, the visual appearance of a component is defined in a \e {UI file} (ui.qml). To create component files, - you can use \l{Creating Components from Scratch}{wizard templates}, or - \l{Turning Component Instances into Custom Components} + you can use \l{Creating components from scratch}{wizard templates}, or + \l{Turning component instances into custom components} {move component instances into separate component files}. Select \uicontrol Components to view the preset components that @@ -41,12 +41,12 @@ Read more about components: \list - \li \l {Preset Components} - \li \l {Creating Component Instances} - \li \l {Creating Custom Components} + \li \l {Preset components} + \li \l {Creating component instances} + \li \l {Creating custom components} \endlist - \section1 Using Components Economically + \section1 Using components economically It is important to understand the performance costs associated with using components. @@ -58,8 +58,8 @@ components that can be conveniently recombined to suit the needs of your UI. \li Use as few components as necessary. To minimize the number of - components, use \l{Adding Property Aliases}{alias properties} and - \l{Working with States}{states} to create the differences in your + components, use \l{Adding property aliases}{alias properties} and + \l{Working with states}{states} to create the differences in your component instances. We recommend reusing components instead of duplicating them, so the components do not need to be processed as completely new component types. This reduces loading @@ -69,8 +69,8 @@ a speedometer should have an \c int or \c real property for speed to which the UI is bound. \li Separate UI from the application logic. Designers should work with - the \l{UI Files}{UI files} (.ui.qml), while developers should work - on the corresponding implementation files (.qml) to define their + the \l{UI files} (.ui.qml), while developers should work on the + corresponding implementation files (.qml) to define their programmatic behaviors or JavaScript. This enables iteration from both the design and development side of the process without the risk of overwriting each other's work. diff --git a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc index fea22dfeff9..eb3129ae04c 100644 --- a/doc/qtdesignstudio/src/components/qtquick-controls.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-controls.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage quick-user-interaction-methods.html \nextpage quick-data-models.html - \title UI Controls + \title UI controls You can create instances of preset UI controls to inform users about the progress of the application or to gather input from users. They are @@ -23,7 +23,7 @@ \li \l {Selectors} \li \l {Tab Bar} \li \l {Tool Bar} - \li \l {Summary of UI Controls} + \li \l {Summary of the UI controls} \endlist You can specify values for the properties of component instances in the @@ -32,7 +32,7 @@ are only available for a particular control. The following sections describe the preset UI controls and their properties. - \section1 General Control Properties + \section1 General control properties You can set control properties in the \l Properties view. @@ -57,7 +57,7 @@ \l {Check Box}. Spacing is not enforced by the controls, so each style may interpret it differently, and some may ignore it altogether. - \section1 Button Controls + \section1 Button controls Qt Quick Controls offer a selection of button-like controls for specific use cases. The following sections contain guidelines for choosing the button @@ -109,7 +109,7 @@ Don't use a button to set state because a \l Switch is more suitable for that purpose. - \section3 Highlighted Buttons + \section3 Highlighted buttons Select the \uicontrol Highlight check box in the \uicontrol Button section to draw the users' attention towards a button. Highlighting a button has no @@ -121,7 +121,7 @@ \image qtquickcontrols2-button-highlighted.gif "Highlighted button" - \section3 Flat Buttons + \section3 Flat buttons A flat button typically does not draw a background unless it is pressed or checked. To create a flat button, select the \uicontrol Flat check box in @@ -131,14 +131,14 @@ \image qtquickcontrols2-button-flat.gif "Flat button" - \section3 Icon Buttons + \section3 Icon buttons To create a button that contains an icon, use the wizard template to \l{Creating Custom Controls}{create a custom button} and drag-and-drop the icon to the button background component. For an example of using the wizard template, see \l{Creating a Push Button}. - \section2 Delay Button + \section2 Delay button \image qtquickcontrols2-delaybutton.gif "Delay button" @@ -286,7 +286,7 @@ of the width or height of the button, and make the button's width and height identical. - \section2 Displaying Text and Icons + \section2 Displaying text and icons A button can contain text, an icon, or both. Specify the button text in the \uicontrol Text field in the \uicontrol {Button Content} section. The @@ -296,7 +296,7 @@ \image qtquick-designer-abstract-button-properties.png "Button Content properties" - \section2 Checking Buttons + \section2 Checking buttons A \e checkable button toggles between checked (on) and unchecked (off) when users click on it or press the space bar while the button has active @@ -316,10 +316,10 @@ If the buttons don't belong to the same parent, checking and unchecking buttons does not affect the other buttons in the group. - \section2 Button Signals + \section2 Button signals A button emits the \c clicked() signal when it is activated by users. - \l{Connecting Components to Signals}{Connect to this signal} to perform + \l{Connecting components to signals}{Connect to this signal} to perform the button's action. Buttons provide the following additional signals: \c canceled(), \c doubleClicked(), \c pressed(), \c released(), and \c pressAndHold() for long presses. @@ -525,7 +525,7 @@ A combo box is used to select a value from a static multiple-line drop-down list. Users cannot add new values, and only one option can be selected. - Combo box values are provided by a \l{Lists and Other Data Models} + Combo box values are provided by a \l{Lists and other data models} {data model}. The data model is usually a JavaScript array, a \l ListModel, or an integer, but other types of data models are also supported. @@ -621,7 +621,7 @@ position its contents, for instance by creating a \l RowLayout. If the toolbar contains only one item, it will resize to fit the implicit item size. This makes a toolbar particularly suitable for use together with - \l{Using Layouts}{layouts}. However, you can specify content width + \l{Using layouts}{layouts}. However, you can specify content width (\uicontrol W) and height (\uicontrol H) in the \uicontrol {Content size} field in the \uicontrol Pane section. @@ -630,7 +630,7 @@ be used in horizontal or vertical toolbars by setting the value of the \uicontrol Orientation field. - \section1 Styling Controls + \section1 Styling controls The preset UI controls can be \l {Styling Qt Quick Controls}{styled}. The \uicontrol {2D} view reads the preferred style from a @@ -647,7 +647,7 @@ For more information about how to customize a particular control, see \l{Customization Reference}. - \section1 Summary of UI Controls + \section1 Summary of the UI controls The following table lists preset UI controls with links to their developer documentation. They are available in \uicontrol Components > diff --git a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc index 092a5449536..80c479d0d90 100644 --- a/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-data-models.qdoc @@ -6,7 +6,7 @@ \previouspage quick-controls.html \nextpage quick-2d-effects.html - \title Lists and Other Data Models + \title Lists and other data models Applications typically need to handle and display data that is organized into list or grid views. Models, views, and delegates are used for @@ -28,7 +28,7 @@ For more information, see \l{Models and Views in Qt Quick}. - \section1 List and Grid Views + \section1 List Views and Grid Views Create instances of \uicontrol {List View} and \uicontrol {Grid View} components to organize other component instances in list or grid format. @@ -95,7 +95,7 @@ to realize that setting a cache will only postpone issues caused by slow-loading delegates, it is not a solution to this problem. - \section1 View Highlight + \section1 View highlight In the \uicontrol {List View Highlight} and \uicontrol {Grid View Highlight} sections, you can specify properties for highlighting items in views. @@ -130,7 +130,7 @@ \uicontrol {Resize duration}, and \uicontrol {Resize velocity} fields control the speed of the move and resize animations for the highlight. - \section1 Editing List Models + \section1 Editing list models When you add a \l{GridView}{Grid View}, \l{ListView}{List View}, or \l{PathView}{Path View}, the ListModel and the delegate component that @@ -165,7 +165,7 @@ \include qtquick-pathview-editor.qdocinc pathview \include qtquick-pathview-editor.qdocinc svgpath - \section1 Summary of Model Components + \section1 Summary of the model components The following table lists the components that you can use to add data models to UIs. The \e Location column indicates the location of the component in diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index bc5228bb118..277e410a46d 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -9,7 +9,7 @@ \title Images \target basic-image - The Image component is used for adding images to the UI in several supported + The \uicontrol Image component is used for adding images to the UI in several supported formats, including bitmap formats, such as PNG and JPEG, and vector graphics formats, such as SVG. To use any image files in your designs, you need to first add them to \l Assets: @@ -39,7 +39,7 @@ You can use the \l {Border Image} component to display an image, such as a PNG file, as a border and a background. For more information about using border images to create buttons, see - \l {Creating Scalable Buttons and Borders}. + \l {Creating scalable buttons and borders}. If you need to display animated images, such as GIFs, use the \l {Animated Image} component. @@ -47,7 +47,7 @@ \note Currently, the supported image formats include JPEG, JPG, PNG, SVG, HDR, KTX, BMP, TTF, TIFF, WEBP, and GIF. - \section1 Image Size + \section1 Image size \image qtquick-designer-image-properties.png "Image properties" @@ -77,7 +77,7 @@ Select the \uicontrol {Auto transform} check box if the image should automatically apply image transformation metadata, such as EXIF orientation. - \section1 Source Size + \section1 Source size The \uicontrol {Source size} property specifies the scaled width and height of the full-frame image. Unlike the value of the \uicontrol Size property, @@ -110,7 +110,7 @@ reloaded, potentially even from the network, if it is not in the disk cache. Select the \uicontrol Cache check box to cache the image. - \section1 Image Alignment + \section1 Image alignment You can align images horizontally and vertically in the \uicontrol {Alignment H} and \uicontrol {Alignment V} @@ -138,8 +138,9 @@ \section1 Border Image - The Border Image component extends the features of the Image component. - It is used to create borders out of images by scaling or tiling parts + The \uicontrol{Border Image} component extends the features of the + \uicontrol Image component. It is used to create borders out of images by + scaling or tiling parts of each image. A source image is broken into 9 regions that are scaled or tiled individually. The corner regions are not scaled at all, while the horizontal and vertical regions are scaled according to the values of the @@ -164,7 +165,7 @@ \section1 Animated Image - The Animated Image component extends the features of the Image component, + The \uicontrol {Animated Image} component extends the features of the Image component, providing a way to play animations stored as images containing a series of frames, such as those stored in GIF files. @@ -192,11 +193,11 @@ \section1 Iso Icon - \note The Iso Icon component is not available if you selected + \note The \uicontrol {Iso Icon} component is not available if you selected \uicontrol {Qt 6} when \l{Creating Projects}{creating the project}. - The Iso Icon component specifies an icon from an ISO 7000 icon library as a - \l [QtQuickExtras] {Picture} component. The icon to use for the type and + The \uicontrol {Iso Icon} component specifies an icon from an ISO 7000 icon + library as a \l [QtQuickExtras] {Picture} component. The icon to use for the type and its color can be specified. To select an icon in the \uicontrol {ISO Icon Browser} in \QDS, select @@ -205,12 +206,12 @@ \image studio-iso-icon.png - You can use the \l{Picking Colors}{color picker} in \l Properties to + You can use the \l{Picking colors}{color picker} in \l Properties to set the value of \uicontrol {Icon color}. \image iso-icon-browser.png - \section1 Summary of Images + \section1 Summary of the image components The following table lists the components that you can use to add images. The \e Location column contains the tab name where you can find the diff --git a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc index 001edb24947..9b0169febe2 100644 --- a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-properties.html \nextpage qtquick-annotations.html - \title Scalable Layouts + \title Scalable layouts The position of a \l{glossary-component}{component} in a UI is either absolute or relative to other components. The visual components exist at a @@ -25,30 +25,30 @@ \li Action \li Purpose \row - \li \l{Setting Bindings} + \li \l{Setting bindings} \li To connect the properties of components. \row - \li \l{Setting Anchors and Margins} + \li \l{Setting anchors and margins} \li To set the rules for attaching the component to other components. You can define the distance between the components and put margins on the components. \row - \li \l{Aligning and Distributing Components} + \li \l{Aligning and distributing components} \li To align the unanchored components left, right, vertical, horizontal, top, and bottom with respect to each other. \row - \li \l{Using Positioners} + \li \l{Using positioners} \li To arrange components in rows, columns, grids, or flows. \row - \li \l{Using Layouts} + \li \l{Using layouts} \li To place the components in columns, grids, rows and stacks in layouts. The layouts adept the nature of dynamic and resizable UI. \row - \li \l {Organizing Components} + \li \l {Organizing components} \li To keep the components or controls collected with frames, groups, group boxes, pages, and panes. \endtable - \section2 Setting Bindings + \section2 Setting bindings Using \e {Property Binding}, you can connect the properties of components. So, change in one can affect another. Once the binding is set, the property value is automatically @@ -72,7 +72,7 @@ \inlineimage icons/action-icon-binding.png. To remove bindings, select \uicontrol Actions > \uicontrol Reset. - See \l {Adding Bindings Between Properties} to learn how to set bindings using + See \l {Adding bindings between properties} to learn how to set bindings using the \uicontrol Bindings tab in the \l Connections view. \note For better performance, set anchors and margins for binding @@ -80,7 +80,7 @@ component, anchor the component to its sibling components on the left and the right. - \section2 Setting Anchors and Margins + \section2 Setting anchors and margins In an \l{Important Concepts In Qt Quick - Positioning#anchors} {anchor-based} layout, each component has a set of @@ -140,7 +140,7 @@ \uicontrol Margin. Margins work for anchors. They do not take any effect when using layouts or absolute positioning. - \section2 Aligning and Distributing Components + \section2 Aligning and distributing components For a group of components, select them to align and distribute them evenly. As the positions of the components are fixed, @@ -209,7 +209,7 @@ pixels, click \inlineimage icons/distribute-origin-none.png button. - \section2 Using Positioners + \section2 Using positioners Positioner components are containers that manage the positions of their child components. For many use cases, the best positioner to use is a simple @@ -231,7 +231,7 @@ \uicontrol Column, \uicontrol Grid or \uicontrol {Flow Positioner}. \endlist - \section3 Column Positioner + \section3 Column positioner A \uicontrol Column positions its child components along a single column. It is used as a convenient way to vertically position a series of @@ -246,7 +246,7 @@ content and the left, right, top, and bottom edges of components in the \l Padding section. - \section3 Row and Flow Positioners + \section3 Row and the Flow positioners A \uicontrol Row positions its child components along a single row. It is used as a convenient way to horizontally position a series of components @@ -255,7 +255,7 @@ The \uicontrol Flow component positions its child components like words on a page, wrapping them to create rows or columns of components. - \image qtquick-positioner-flow-properties.png "Flow properties" + \image qtquick-positioner-flow-properties.png "The Flow properties" For flow and row positioners, also set the direction of a flow to either left-to-right or top-to-bottom in the \uicontrol Flow field. @@ -269,7 +269,7 @@ the width of the row is explicitly set, the left anchor remains to the left of the row and the right anchor remains to the right of it. - \section3 Grid Positioner + \section3 Grid positioner A \uicontrol Grid creates a grid of cells that is large enough to hold all of its child components, and places these components in the cells from left @@ -292,7 +292,7 @@ To also mirror the horizontal alignment of components, select \uicontrol AlignRight in the \uicontrol {Alignment H} field. - \section2 Using Layouts + \section2 Using layouts Use the components available in \uicontrol Components > \uicontrol {Qt Quick Layouts} to arrange components in UIs. @@ -307,7 +307,7 @@ to select its size in respect to its non-layout parent component. However, do not anchor the child components within layouts. - To put components in the \uicontrol {Grid Layout}: + To put components in \uicontrol {Grid Layout}: \list 1 \li Select all the components and right-click on one of them. @@ -406,7 +406,7 @@ \image studio-stack-layout-example-output.webp "Stack Layout example output" \endlist - \section2 Organizing Components + \section2 Organizing components You can use the \uicontrol Frame and \uicontrol {Group Box} controls to draw frames around groups of controls. diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 979a0859281..e664418db37 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -6,7 +6,7 @@ \previouspage quick-components.html \nextpage quick-shapes.html - \title Preset Components + \title Preset components To use preset components, add the modules that contain them to your project by selecting \uicontrol Components > \inlineimage icons/plus.png @@ -19,7 +19,7 @@ and set its properties in the \l Properties view. For more information about creating your own components, see - \l{Creating Custom Components}. + \l{Creating custom components}. \section1 2D Components @@ -27,10 +27,10 @@ \li \l Shapes \li \l Text \li \l Images - \li \l {User Interaction Methods} - \li \l {UI Controls} - \li \l {Lists and Other Data Models} - \li \l {2D Effects} + \li \l {User interaction methods} + \li \l {UI controls} + \li \l {Lists and other data models} + \li \l {2D effects} \li \l {Design Effects} \li \l {Logic Helpers} \li \l Animations @@ -48,15 +48,15 @@ \li \l {3D Views} \li \l {Node} \li \l {Group} - \li \l {Instanced Rendering} - \li \l {Skeletal Animation} - \li \l {3D Models} - \li \l {Materials and Shaders} + \li \l {Instanced rendering} + \li \l {Skeletal animation} + \li \l {3D models} + \li \l {Materials and shaders} \li \l {Textures} - \li \l {3D Materials} - \li \l {3D Effects} - \li \l {Custom Shaders} - \li \l {Custom Effects and Materials in Qt 5} + \li \l {3D materials} + \li \l {3D effects} + \li \l {Custom shaders} + \li \l {Custom effects and materials in Qt 5} \li \l {Lights} \li \l {Cameras} \li \l {Scene Environments} diff --git a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc index b8c1bd4581a..a6143105100 100644 --- a/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-shapes.qdoc @@ -206,7 +206,7 @@ \uicontrol {Rotational axis} property determine whether the component is rotated around the x-axis or the y-axis. - \section1 Summary of Shapes + \section1 Summary of the shape components The following table lists the components that you can use to draw shapes. The \e Location column indicates the location of the component in diff --git a/doc/qtdesignstudio/src/components/qtquick-text.qdoc b/doc/qtdesignstudio/src/components/qtquick-text.qdoc index c67dc40cbf4..3dc606e0aec 100644 --- a/doc/qtdesignstudio/src/components/qtquick-text.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-text.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -29,7 +29,7 @@ They differ from the basic components in that a common style is applied to them and that you can specify placeholder text for them. - \section1 Using Rich Text + \section1 Using rich text You can use rich text in the \uicontrol Text and \uicontrol {Text Input} components. To open the rich text editor, select the \inlineimage icons/edit.png @@ -53,7 +53,7 @@ \image qtquick-rtf-editor.png "Text formatted as rich text in the editor" - \section1 Marking Strings for Translation + \section1 Marking strings for translation To support translators, mark the strings that should be translated. In \uicontrol Properties > \uicontrol Character > \uicontrol Text, select @@ -77,7 +77,7 @@ For more information, see \l {Mark Strings for Translation}. - \section1 Character Properties + \section1 Character properties You can set font properties in the \uicontrol Character section in \uicontrol Properties. For each string that you enter in the @@ -123,7 +123,7 @@ to set the spacing proportionally to the line (as a multiplier). For example, set to 2 for double spacing. - \section1 Text Alignment + \section1 Text alignment You can align text components horizontally and vertically. By default, text is vertically aligned to the top. Horizontal alignment follows the natural @@ -136,11 +136,11 @@ For a single line of text, the size of the text is the area of the text. In this common case, all alignments are equivalent. To center a text in - its parent, use \l{Setting Anchors and Margins}{anchoring} or bind the + its parent, use \l{Setting anchors and margins}{anchoring} or bind the width of the text component to that of the parent. For more information, see - \l{Setting Bindings}. + \l{Setting bindings}. - \section1 Text and Style Colors + \section1 Text and style colors You can set the color of the text itself and a secondary color used by text styles. @@ -156,7 +156,7 @@ For more information about selecting colors, see \l{Picking Colors}. You can only set solid colors for text components. - \section1 Advanced Text Properties + \section1 Advanced text properties The height and width of a text component are determined automatically depending on the values of the properties you set, to accommodate the length of the @@ -178,7 +178,7 @@ has a minimum bound specified by the \uicontrol {Min size} field and maximum bound specified by the \uicontrol Size field. - \section3 Wrapping and Eliding Text + \section3 Wrapping and eliding text In the \uicontrol {Wrap mode} field, you can wrap the text to the text component's width. The text will only wrap if you set an explicit width for @@ -202,7 +202,7 @@ Multi-length strings are ordered from longest to shortest, separated by the Unicode \e {String Terminator} character \c U009C. - \section3 Formatting Text + \section3 Formatting text Text can be either in plain text or rich text format, depending on the value you set in the \uicontrol Format field. If you select @@ -211,7 +211,7 @@ described on the \l {Supported HTML Subset}. Note that plain text offers better performance than rich text. - \section3 Rendering Text + \section3 Rendering text In the \uicontrol {Render type} field, you can override the default rendering type for a text component. Select \uicontrol NativeRendering if @@ -238,7 +238,7 @@ \note This property only describes a preference, as the full range of hinting levels are not supported on all of Qt's supported platforms. - \section1 Advanced Font Properties + \section1 Advanced font properties You can specify additional properties for fonts in the \uicontrol {Font Extras} section. @@ -279,7 +279,7 @@ \image qtquick-designer-text-input-properties.png "Text input field properties" - \section2 Entering Passwords + \section2 Entering passwords You can set properties for \uicontrol {Text Input} components that make them suitable for entering passwords. @@ -299,7 +299,7 @@ as users enter them. The mask character is displayed in the \uicontrol {Password character} field. - \section2 Entering Text + \section2 Entering text You can specify how users can enter text into text edit or input fields. @@ -318,7 +318,7 @@ To prevent users from changing the text, select the \uicontrol {Read only} check box. - \section2 Selecting Text + \section2 Selecting text In the \uicontrol {Selection mode} field, you can specify whether individual characters or whole words are selected when selecting text @@ -350,7 +350,7 @@ If the \uicontrol {Persistent selection} check box is selected, a text edit or input keeps its selection when active focus moves to another component. - \section2 Tabs and Margins + \section2 Tabs and margins You can specify additional properties for formatting a block of text in a \uicontrol {Text Edit} component. @@ -376,7 +376,7 @@ text does not have enough vertical or horizontal space in which to be rendered, it will appear clipped. - \section1 Placeholder Text + \section1 Placeholder text For \uicontrol {Text Field} and \uicontrol {Text Area} controls, you can specify text to display in a field before users enter text into it. @@ -388,7 +388,7 @@ Select the \uicontrol Hover check box to enable the text field to accept hover events. - \section1 Summary of Text Components + \section1 Summary of the text components The following table lists the components that you can use to add text to UIs. The \e Location column contains the tab name where you can find the diff --git a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc index 1fec407e956..eaac3ad3aa5 100644 --- a/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-user-interaction-methods.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage quick-images.html \nextpage quick-controls.html - \title User Interaction Methods + \title User interaction methods You can create instances of preset basic components to add interaction methods to UIs, such as performing actions by using a pointing device or @@ -14,7 +14,7 @@ vertically. They are availabe in \uicontrol Components > \uicontrol {Default Components} > \uicontrol Basic. - In addition, you can create instances of preset \l{UI Controls} to inform + In addition, you can create instances of preset \l{UI controls} to inform users about the progress of the application or to gather input from users. The following basic components are available for user interaction: @@ -23,7 +23,7 @@ \li \l {Mouse Area} \li \l {Focus Scope} \li \l {Flickable} - \li \l {Summary of Basic Interaction Methods} + \li \l {Summary of the basic interaction methods} \endlist You can specify values for the properties of component instances in the @@ -37,14 +37,14 @@ a defined area. A mouse area receives events within a defined area. One quick way to define - this area is to \l{Setting Anchors and Margins}{anchor} the mouse area to + this area is to \l{Setting anchors and margins}{anchor} the mouse area to its parent's area. If the parent is a \l {basic-rectangle}{Rectangle} (or any component that is derived from an \l {basic-item}{Item}), the mouse area will fill the area defined by the parent's dimensions. Alternatively, you can define an area smaller or larger than the parent. Several controls, such as \l {Button}{buttons}, contain a mouse area. - A mouse area emits \l{Connecting Components to Signals}{signals} in response + A mouse area emits \l{Connecting components to signals}{signals} in response to different mouse events: \list @@ -59,7 +59,7 @@ \li \c released() \endlist - \section2 Mouse Area Properties + \section2 Mouse Area properties A \uicontrol {Mouse Area} is an invisible component that is typically used in conjunction with a visible component in order to provide mouse handling @@ -125,7 +125,7 @@ propagating down this visual hierarchy until a \uicontrol {Mouse Area} accepts the event. - \section2 Advanced Mouse Area Properties + \section2 Advanced Mouse Area properties You can place a \uicontrol {Mouse Area} instance within a component that filters child mouse events, such as \uicontrol Flickable. However, the @@ -140,7 +140,7 @@ For more information, see the developer documentation for the \l {MouseArea} {Mouse Area} component. - \section2 Drag Properties + \section2 Drag properties You can specify properties for dragging components in the \uicontrol Drag section. Select the component to drag in the \uicontrol Target field. @@ -190,8 +190,8 @@ The \uicontrol {Focus Scope} component is not a visual component and therefore the properties of its children need to be exposed to the parent - component of the focus scope. \l{Using Layouts}{Layouts} and - \l{Using Positioners}{positioners} will use these visual and styling + component of the focus scope. \l{Using layouts}{Layouts} and + \l{Using positioners}{positioners} will use these visual and styling properties to create the layout. For more information, see \l {Keyboard Focus in Qt Quick}. @@ -202,7 +202,7 @@ and flicked, causing the view onto the child components to scroll. This behavior forms the basis of components that are designed to show large numbers of child components, such as \uicontrol {List View} and - \uicontrol {Grid View}. For more information, see \l{List and Grid Views}. + \uicontrol {Grid View}. For more information, see \l{List Views and Grid Views}. In traditional user interfaces, views can be scrolled using standard controls, such as scroll bars and arrow buttons. In some situations, it @@ -269,7 +269,7 @@ provides a smoother experience (no jump) at the cost of losing some of the drag distance at the beginning. - \section2 Flickable Geometry + \section2 Flickable Geometry properties The \uicontrol {Content size} field specifies the dimensions of the surface controlled by a flickable. Typically, set the values of the @@ -287,7 +287,7 @@ may have an arbitrary origin due to delegate size variation, or component insertion or removal outside the visible region. - \section1 Summary of Basic Interaction Methods + \section1 Summary of the basic interaction methods The following table lists the components that you can use to add basic interaction methods to UIs with links to their developer documentation. diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 75fe3219430..9868764d24f 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -6,20 +6,20 @@ \previouspage studio-implementing-applications.html \nextpage studio-debugging.html - \title Designer-Developer Workflow + \title Designer-developer workflow \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see \l{Customizing the Menu Bar}. \QDS enables designers and developers to work together on common - projects to develop applications. Designers use the \l{Design Views}{views} - in the \uicontrol Design mode to modify \l{UI Files}{UI files} (\e .ui.qml), + projects to develop applications. Designers use the \l{Design views}{views} + in the \uicontrol Design mode to modify \l{UI files} (\e .ui.qml), whereas developers use Qt Creator to work on the Qt Quick (\e .qml) and other files that are needed to implement the application logic and to prepare the application for production. - \QDS \l{Creating Projects}{projects} come with boilerplate code for a + \QDS \l{Creating projects}{projects} come with boilerplate code for a working Qt 6 application that you can build and run in Qt Creator using CMake. Therefore, you can open, build, and run the projects with Qt Creator. @@ -32,7 +32,7 @@ \e {main.py}. Use this file to start the development in Python for the UI made with \QDS. - \section1 Exporting a \QDS Project for C++ Development + \section1 Exporting a \QDS project for C++ development Before you export a \QDS project for Qt Creator, install the following: \list @@ -44,7 +44,7 @@ To export a \QDS project for Qt Creator: \list 1 - \li \l {Creating a Project} {Create} or open your \QDS project with \QDS 4.5 or above. + \li \l {Creating a project} {Create} or open your \QDS project with \QDS 4.5 or above. \note If you are creating a new project in \QDS, select the \uicontrol {Target Qt Version} that is not higher than the Qt version @@ -61,7 +61,7 @@ \image studio-project-export-cmake.webp "Exporting Qt Design Studio project to CMake" \endlist - \section1 Opening the \QDS Project in Qt Creator + \section1 Opening the \QDS project in Qt Creator Open the \e {CMakeLists.txt} file in Qt Creator: @@ -138,7 +138,7 @@ \enddetails - \section1 Exporting a \QDS Project for Python Development + \section1 Exporting a \QDS project for Python development \list 1 \li \l {Creating a Project} {Create} a project with \QDS 4.5 or above. @@ -160,7 +160,7 @@ \image studio-project-export-python.webp "Exporting Qt Design Studio project to Python" \endlist - \section1 Opening the \QDS Project with Python + \section1 Opening the \QDS project with Python After your project have the Python folder and the \e {main.py} file available, you can start setting up your Python environment for developing @@ -206,7 +206,7 @@ functionalities to the project. Go to \l {Qt for Python} to learn more about developing Qt projects using Python. - \section1 Converting Qt Design Studio Projects to Applications + \section1 Converting \QDS projects to applications \include qtquick-converting-ui-projects-to-applications.qdocinc {converting-ui-projects-to-applications} {\QDS} diff --git a/doc/qtdesignstudio/src/developers/studio-jump-to-the-code.qdoc b/doc/qtdesignstudio/src/developers/studio-jump-to-the-code.qdoc index 8eaedf0a7e2..f76544606e3 100644 --- a/doc/qtdesignstudio/src/developers/studio-jump-to-the-code.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-jump-to-the-code.qdoc @@ -13,10 +13,10 @@ You can jump to the code from: \list - \li \uicontrol {Navigator} view - \li \uicontrol {2D} view - \li \uicontrol {States} view - \li \uicontrol {Connections} view + \li The \uicontrol {Navigator} view + \li The \uicontrol {2D} view + \li The \uicontrol {States} view + \li The \uicontrol {Connections} view \endlist \section1 Jump to the Code from the Navigator View @@ -28,7 +28,7 @@ \image jump-to-the-code-from-navigator-view.webp - \section1 Jump to the Code from the 2D View + \section1 Jump to the Code from the 2D view \list 1 \li Right-click on a component in the \uicontrol {2D} view. @@ -41,7 +41,7 @@ or in the \uicontrol {2D} view and press \key {F4}. That takes you to the code location in the \uicontrol {Code} view. - \section1 Jump to the Code from the States View + \section1 Jump to the Code from the States view \list 1 \li Locate the state you want to check in the \uicontrol {States} view. @@ -52,7 +52,7 @@ \image jump-to-the-code-from-state-view.webp - \section1 Jump to the Code from the Connections View + \section1 Jump to the Code from the Connections view \list 1 \li Select a connection in the \uicontrol {Connections} view. diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc index 9424731fb62..66748251533 100644 --- a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc +++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc @@ -6,7 +6,7 @@ \previouspage studio-live-preview.html \nextpage creator-live-preview-devices.html - \title Previewing on Desktop + \title Previewing on desktop To preview the currently active QML file on the desktop: @@ -30,7 +30,7 @@ To preview the application in a different zoom level, right-click the \uicontrol {Live Preview} button and select the zoom level. - \section1 Overriding the Preview Tool + \section1 Overriding the preview tool By default, the QML runtime is used for previewing. @@ -44,6 +44,6 @@ the preview tool executable is located. \endlist - To preview the design on a device, see \l {Previewing on Devices}. + To preview the design on a device, see \l {Previewing on devices}. */ diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc index e81be5df869..ba4b5cc5449 100644 --- a/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc +++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc @@ -6,14 +6,14 @@ \previouspage creator-quick-ui-forms.html \nextpage studio-collecting-usage-statistics.html - \title Managing Data Collection + \title Managing data collection See below for more information about the collected data: \list - \li \l {Collecting Usage Statistics} - \li \l {Collecting User Feedback} - \li \l {Reporting Crashes} + \li \l {Collecting usage statistics} + \li \l {Collecting user feedback} + \li \l {Reporting crashes} \endlist \section1 Principles of Data Collection @@ -27,7 +27,7 @@ \l{https://www.qt.io/terms-conditions/privacy-and-security} {Qt Appendix for Privacy and Security}. - \sa {Collecting Usage Statistics} + \sa {Collecting usage statistics} */ /*! @@ -35,7 +35,7 @@ \previouspage studio-telemetry.html \nextpage studio-user-feedback.html - \title Collecting Usage Statistics + \title Collecting usage statistics The telemetry plugin uses Qt Insight, an analytics solution for collecting usage data in Qt applications. The Qt Insight Tracker library ensures compliance with GDPR regulations. @@ -49,5 +49,5 @@ \image studio-preferences-telemetry-usage-statistics.webp {Usage Statistics} - \sa {Managing Data Collection} + \sa {Managing data collection} */ 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 aba73cf69eb..25395bc0910 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page studio-compatibility-with-mcu-sdks.html \nextpage studio-features-on-mcu-projects.html - \title \QDS Version Compatibility with \QMCU SDKs + \title \QDS version compatibility with \QMCU SDKs The following table lists the \QDS versions you can use to develop applications with particular \QMCU SDK versions. 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 624edffe6b5..b191f5f56e7 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-projects-for-mcus.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page studio-projects-for-mcus.html \nextpage studio-creating-uis-for-mcus.html - \title Creating Projects for MCUs + \title Creating projects for MCUs Use the \uicontrol {\QMCU} preset in the \QDS wizard to set up a new \QMCU project. When you create a project with the wizard, all the necessary files @@ -18,9 +18,9 @@ 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}. + projects, see \l {\QDS features on MCU projects}. - \section1 Creating an MCU Project + \section1 Creating an MCU project To create an MCU project: @@ -50,7 +50,7 @@ \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}. + 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 @@ -79,7 +79,7 @@ \sa {Using Custom Presets} - \section1 Adding Files to MCU Projects + \section1 Adding files to MCU projects You can use wizard templates to add individual files to projects. @@ -99,7 +99,7 @@ \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: + 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. @@ -110,7 +110,7 @@ \row \li Qt Quick Views \li Generates a List View. For more information, see - \l{List and Grid Views}. + \l{List Views and Grid Views}. \row \li Qt Quick UI Form \li Creates a UI file along with a matching QML file for 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 9b580802a00..4032394c387 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc @@ -1,4 +1,4 @@ -/ Copyright (C) 2023 The Qt Company Ltd. +/ Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -14,20 +14,20 @@ design from the design tools, and import your 2D 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}. + \l {Asset creation with other tools}. Once your UI design assets are in \QDS, use it to \l {Wireframing} {wireframe} your MCU application, to visualize its structure. To modify the look and feel of your UI further, utilize the preset UI components available in \QDS. - \section1 Using MCU Components + \section1 Using MCU components With \QDS, you can use subsets of components to create UIs for devices that are powered by microcontroller units (MCU). The subset of supported components depends on the \QMCU version that you use for development. - To develop for MCUs, \l{Creating Projects for MCUs}{create an MCU project}. + 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 @@ -35,7 +35,7 @@ \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 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 @@ -48,5 +48,5 @@ \li \l {Designing a UI for Renesas RH850-D1M1A} \endlist - \sa {Specifying Component Properties}, {Qt Design Studio/MCU FAQ} + \sa {Specifying component properties}, {Qt Design Studio/MCU FAQ} */ diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc index edcfd5fd172..00f8b6f9ae2 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-developing-applications-for-mcus.qdoc @@ -1,4 +1,4 @@ -/ Copyright (C) 2023 The Qt Company Ltd. +/ Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage studio-creating-uis-for-mcus.html \nextpage studio-connecting-mcus-with-creator.html - \title Developing Applications for MCUs + \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} @@ -16,7 +16,7 @@ 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). For more information, see - \l {Implementing Applications}. + \l {Implementing applications}. \image qds-mcu-target-deployment.png 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 558ef9f4aa0..7d1730a8294 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-features-on-mcu-projects.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page studio-features-on-mcu-projects.html \nextpage studio-projects-for-mcus.html - \title \QDS Features on MCU Projects + \title \QDS features on MCU projects The table below provides a summary of how the key \QDS features are supported for developing MCU projects. diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc index 82a7b20c852..ba67a04ae36 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-mcu-framework.qdoc @@ -1,4 +1,4 @@ -/ Copyright (C) 2023 The Qt Company Ltd. +/ Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage studio-on-mcus.html \nextpage studio-compatibility-with-mcu-sdks.html - \title \QMCU Framework + \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 diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc index 68e7ac0bb39..baf71ae627d 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-on-mcus.qdoc @@ -20,16 +20,16 @@ \endtable \list - \li \l {\QMCU Framework} + \li \l {\QMCU framework} Provides an overview of the \QMCU framework. \li \l {\QDS Version Compatibility with \QMCU SDKs} Lists how the \QDS versions match with particular \QMCU SDKs. - \li \l {\QDS Features on MCU Projects} + \li \l {\QDS features on MCU projects} Specifies how the \QDS features are supported for developing MCU projects. - \li \l {Creating Projects for MCUs} + \li \l {Creating projects for MCUs} Describes how to use the \QDS wizard and \uicontrol {\QMCU} preset to set up a new \QMCU project. diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc index 79331f3ddfd..72c780187cb 100644 --- a/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc +++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -8,13 +8,13 @@ \nextpage qt-ui-viewer.html - \title Creating a Single Page Navigation Web Application + \title Creating a single page navigation web application This example explains how you can create a single page navigation web application suitable to run in Qt Design Viewer. In this project, you create the structure and navigation for the web application. - \section1 Setting up the Project + \section1 Setting up the project To set up the project: \list 1 @@ -33,7 +33,7 @@ \endlist \endlist - \section1 Adding Components + \section1 Adding components Next, add the needed components to create the structure for your web application. @@ -70,7 +70,7 @@ \image web-navigation-components.png - \section1 Creating the Pages + \section1 Creating the pages Next, create the separate pages for your web application. In this example, you create pages for \e Home, \e {About Us}, and \e {Contact Us}. @@ -132,7 +132,7 @@ You can see the pages you created under \uicontrol {My Components} in the \image web-navigation-page-components.png -\section1 Organizing the Pages +\section1 Organizing the pages To organize the pages vertically: @@ -194,7 +194,7 @@ main rectangle so that it adapts to the window size: \c {Window.height}. \endlist -\section1 Creating the Navigation +\section1 Creating the navigation The final step is to create the navigation for the web page. To do this, use the buttons that you created earlier. diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc index 88dbe9e00a1..e3f4c9f3926 100644 --- a/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc +++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage creator-live-preview-devices.html \nextpage design-viewer-single-page-navigation.html - \title Sharing Applications Online + \title Sharing applications online \image qt-design-viewer.webp @@ -24,7 +24,7 @@ The loaded applications remain locally in your browser. No data is uploaded into the cloud. - \section1 Sharing your Application Online + \section1 Sharing your application online To share your \QDS application online: @@ -45,7 +45,7 @@ \image share-online-manage.webp - \section1 Best Practices + \section1 Best practices - \l {Creating a Single Page Navigation Web Application} + \l {Creating a single page navigation web application} */ diff --git a/doc/qtdesignstudio/src/overviews/qtdesignstudio-simulation-overview.qdoc b/doc/qtdesignstudio/src/overviews/qtdesignstudio-simulation-overview.qdoc index aea873fd9c4..8b12650e230 100644 --- a/doc/qtdesignstudio/src/overviews/qtdesignstudio-simulation-overview.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtdesignstudio-simulation-overview.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-creating-ui-logic.html \nextpage qtquick-placeholder-data.html - \title Simulating Complex Experiences + \title Simulating complex experiences \QDS enables you to connect UIs to different forms of data from various sources, such as QML-based data models, JavaScript files, and backend @@ -15,23 +15,23 @@ \list - \li \l{Loading Placeholder Data} + \li \l{Loading placeholder data} You can create QML files that contain placeholder data, so that you can test grid, list, or path views, even though you don't have access to real data. - \li \l{Simulating Application Logic} + \li \l{Simulating application logic} You can use JavaScript to generate mock data for your UI. - \li \l{Simulating Dynamic Systems} + \li \l{Simulating dynamic systems} Use the Simulink connector to connect a Simulink Simulation Model to your UI. Simulink is a MATLAB-based graphical programming environment for modeling, simulating, and analyzing multi-domain dynamic systems. - \li \l{Using QML Modules with Plugins} + \li \l{Using QML modules with plugins} You can load C++ plugins for QML to simulate data. \endlist diff --git a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc index 23de84abcaa..7e09578307f 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-animation-overview.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-motion-design.html \nextpage studio-timeline.html - \title Introduction to Animation Techniques + \title Introduction to animation techniques \image timeline-rotation-animation.gif "Timeline animation of rotation and opacity" @@ -19,7 +19,7 @@ \li Data-driven UI logic animations \endlist - \section1 Common Motion Design Techniques + \section1 Common motion design techniques The following table summarizes common motion design techniques for 2D and 3D and their typical use cases. @@ -33,7 +33,7 @@ \li Linear interpolation through intermediate values at specified keyframes instead of immediately changing to the target value. \row - \li \l{Editing Easing Curves}{Easing curves} attached to keyframes + \li \l{Editing easing curves}{Easing curves} attached to keyframes \li Nonlinear interpolation between keyframes to make components appear to pick up speed, slow down, or bounce back at the end of the animation. @@ -44,7 +44,7 @@ simultaneously. \endtable - \section2 Timeline and Keyframe Based Animation + \section2 Timeline and keyframe based animation Timeline animation is based on \e keyframes. In \QDS, keyframes determine the value of the property of a \l{glossary_component}{component} at a certain @@ -57,7 +57,7 @@ In the middle of the animation, the y property has the value of 50 since keyframes are interpolated linearly by default. - \section2 Easing Curves + \section2 Easing curves Sometimes you don't want linear movement but would rather like the rectangle to move faster at the beginning and slower at the end of the animation. To @@ -67,7 +67,7 @@ easing curves can make components appear to pick up speed, slow down, or bounce back at the end of the animation. - \section2 Animation Curves + \section2 Animation curves While easing curves work well for most simple UI animations, more complex 3D animations require several keyframes, so it becomes necessary to visualize @@ -78,13 +78,13 @@ simultaneously so that you can see the animation for the x position and the animation for the y position side-by-side. - \section1 State-to-State Animations + \section1 State-to-state animations To navigate between UI states, use transitions between different states of the UI using a transition timeline that is based on keyframes. You can apply easing curves to the keyframes. - \section2 Transitions Between States + \section2 Transitions between states UIs are designed to present different UI configurations in different scenarios, or to modify their appearances in response to user @@ -118,10 +118,10 @@ In \l {Transitions}, you can set the start frame, end frame, and duration for the transition of each property. You can also set - an \l{Editing Easing Curves}{easing curve} for each animation and + an \l{Editing easing curves}{easing curve} for each animation and the maximum duration of the whole transition. - \section1 Data-Driven UI Logic Animations + \section1 Data-driven UI logic animations The following table summarizes techniques used for animating the UI logic by using real or mock data from a backend. @@ -139,7 +139,7 @@ transitions. \endtable - \section2 Data-Driven Timeline Animation + \section2 Data-driven timeline animation You can connect property values to data backends to drive timeline animation. You can fetch data from various sources, such as data models, @@ -156,7 +156,7 @@ For more information, see \l{Simulating Complex Experiences}. - \section2 Programmatic Animation + \section2 Programmatic animation You can control property animation programmatically. Property animations are created by binding \uicontrol Animation components to property diff --git a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc index ee21695251c..1b89784d9f7 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-positioning.html \nextpage qtquick-prototyping.html - \title Annotating Designs + \title Annotating designs You can submit your designs to review or further development as QML files. You can annotate your designs to provide reviewers or developers with diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index 7609ac4cb44..b77fabc1ce9 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-prototyping.html \nextpage studio-simulation-overview.html - \title Creating UI Logic + \title Creating UI logic Turn your wireframe into an interactive prototype by adding UI logic that enables your components to apply actions or react to mock data from backend @@ -23,7 +23,7 @@ components by emitting a signal that indicates a change in the value. To reference a property of a component from another component, you can - create \l{Adding Property Aliases}{property aliases} that hold a reference + create \l{Adding property aliases}{property aliases} that hold a reference to another property. Unlike an ordinary property definition, which allocates a new, unique storage space for the property, a property alias connects the newly declared property (called the @@ -32,7 +32,7 @@ exported as a public property of the relevant component. For example, a speedometer should have a property for speed to which the UI is bound. - You can declare various \l{Working with States}{UI states} that describe how + You can declare various \l{Working with states}{UI states} that describe how property values change from a base state. States can be a useful way of organizing your UI logic. You can associate transitions with components to define how their properties will animate when they change due to a @@ -50,7 +50,7 @@ component types. This reduces the loading and compilation time as well as the package size of the final application. - The preset \l{UI Controls}{UI controls} have default properties and states + The preset \l{UI controls} have default properties and states that you can modify. If you need additional properties, you can turn instances of the controls into custom components and specify new properties for them. @@ -81,34 +81,34 @@ \li Go to \row \li Responding to application events - \li \l{Connecting Components to Signals} + \li \l{Connecting components to signals} \row \li Formatting connections - \li \l{Actions and Conditions} + \li \l{Actions and conditions} \row \li Dynamically changing the behavior of a component - \li \l{Adding Bindings Between Properties} + \li \l{Adding bindings between properties} \row \li Formatting property bindings - \li \l{Setting Bindings} + \li \l{Setting bindings} \row \li Referencing a property of a component from another component - \li \l{Adding Property Aliases} + \li \l{Adding property aliases} \row \li Referencing a state from within a specific component - \li \l{Working with States} + \li \l{Working with states} \row \li Switching to a state when a particular property changes - \li \l{Applying States} + \li \l{Applying states} \row \li Using preset UI controls that have default properties and states - \li \l{UI Controls} + \li \l{UI controls} \row \li Creating conditional conditions \li \l{Logic Helpers} \row \li Adding custom properties for a particular component type - \li \l{Specifying Custom Properties} + \li \l{Specifying custom properties} \omit \row \li Adding properties for controlling states diff --git a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc index 844d2b3ef4c..bd01ba56d7b 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc @@ -6,14 +6,14 @@ \previouspage studio-importing-3d.html \nextpage sharing-assets.html - \title Exporting Components + \title Exporting components \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see \l{Customizing the Menu Bar}. - \l{glossary-component}{Components} contained in \l{UI Files} - {UI files} (.ui.qml) can be exported to JSON metadata format and PNG assets. + \l{glossary-component}{Components} contained in \l{UI files} (.ui.qml) + can be exported to JSON metadata format and PNG assets. To export the UI files from the current project, select \uicontrol Build > \uicontrol {Export Components}. @@ -33,7 +33,7 @@ components generate assets as PNG files. \endlist - \section1 Configuring QML Export + \section1 Configuring the QML export You can configure the export in the \uicontrol {Export Components} dialog, which lists the UI files (.ui.qml) of the current project. diff --git a/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc index d2f6745df5e..64a22439c76 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-fonts.qdoc @@ -6,7 +6,7 @@ \previouspage studio-importing-2d.html \nextpage studio-importing-3d.html - \title Using Custom Fonts + \title Using custom fonts For your UI to use custom fonts when you preview it on a device, you have to import the fonts to the project folder. \QDS deploys them to diff --git a/doc/qtdesignstudio/src/overviews/qtquick-live-preview.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-live-preview.qdoc index d286c41c7a1..eabfe99bc6b 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-live-preview.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-live-preview.qdoc @@ -6,7 +6,7 @@ \previouspage quick-states.html \nextpage studio-live-preview-desktop.html - \title Validating with Target Hardware + \title Validating with target hardware Preview a UI file or the entire UI on the desktop, as well as on embedded Linux devices to instantly view the changes you make to the UI. On Android @@ -20,18 +20,18 @@ don't have \QC. \list - \li \l{Previewing on Desktop} + \li \l{Previewing on desktop} Preview individual QML files or the whole UI. - \li \l{Previewing on Devices} + \li \l{Previewing on devices} Connect your devices to your computer. - \li \l{Sharing Applications Online} + \li \l{Sharing applications online} Share applications online and view them in a web browser. - \li \l{Viewing Applications on Android} + \li \l{Viewing applications on Android} Preview \QDS projects with \QUV on an Android device. \endlist diff --git a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc index 17ba0cb3650..611dd4b1969 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-motion-design.qdoc @@ -19,29 +19,29 @@ \endtable \list - \li \l {Introduction to Animation Techniques} + \li \l {Introduction to animation techniques} Learn more about which animation techniques are supported by \QDS and the use cases they are most suitable for. - \li \l {Creating Timeline Animations} + \li \l {Creating timeline animations} You can use a timeline and keyframe based editor in the \l Timeline view to animate the properties of UI components. Animating properties enables their values to move through intermediate values at specified keyframes instead of immediately changing to the target value. - \li \l{Editing Easing Curves} + \li \l{Editing easing curves} Specify easing curves for nonlinear interpolation between keyframes in timeline animations, as well as between original and new property values in property animations and between transitions. - \li \l {Production Quality} + \li \l {Production quality} After the wireframing and prototyping phases, you can use previewing and profiling tools to fine-tune your UI for production. - \li \l{Optimizing Designs} + \li \l{Optimizing designs} You can test your UIs on the target devices to make sure you get the best performance out of your animations. To solve performance diff --git a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc index c25b1396f6c..f00018846bf 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage qtquick-production-quality-animation.html \nextpage studio-optimized-3d-scenes.html - \title Optimizing Designs + \title Optimizing designs You can test your UIs on the target devices to make sure you get the best performance out of your animations. To solve performance problems, you @@ -24,9 +24,9 @@ \l {QML Performance Considerations And Suggestions}. For more information about optimizing 3D scenes, see - \l{Creating Optimized 3D Scenes}. + \l{Creating optimized 3D scenes}. - \section1 Minimizing Image Size + \section1 Minimizing image size Images are a vital part of any user interface. Unfortunately, they are also a big source of problems due to the time it takes to load them, the amount @@ -38,7 +38,7 @@ For more information about how to use images efficiently in your UI, see \l{Images}. - \section1 Avoid Transparency + \section1 Avoiding transparency Opaque content is generally a lot faster to draw than transparent because the latter needs blending and the renderer can potentially optimize opaque diff --git a/doc/qtdesignstudio/src/overviews/qtquick-placeholder-data.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-placeholder-data.qdoc index 033b0a135d8..58273d298cb 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-placeholder-data.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-placeholder-data.qdoc @@ -6,7 +6,7 @@ \previouspage studio-simulation-overview.html \nextpage studio-javascript.html - \title Loading Placeholder Data + \title Loading placeholder data \QDS supports views, models, and delegates, so that when you add a Grid View, List View, or Path View component, the ListModel and @@ -18,7 +18,7 @@ or in other component files. A typical example is a component that uses the properties of its parent, such as \c parent.width. - \section1 Using Dummy Models + \section1 Using dummy models If you open a file in the \l {2D} view that references a C++ model, you see nothing in it. If the data in the model is fetched from the @@ -64,7 +64,7 @@ } \endqml - \section1 Creating Dummy Context + \section1 Creating dummy context The following example presents a common pattern: diff --git a/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc index 068a7c140bc..23e6cc68a70 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc @@ -6,7 +6,7 @@ \previouspage qtquick-editing-easing-curves.html \nextpage qtquick-optimizing-designs.html - \title Production Quality + \title Production quality After the wireframing and prototyping phases, you can use previewing and profiling tools to fine-tune your UI for production. @@ -19,7 +19,7 @@ and stuttering. \endlist - \section1 FPS Refresh Rate + \section1 FPS refresh rate As a general rule, animators strive to allow the rendering engine to achieve a consistent 60 frames-per-second (FPS) refresh rate. 60 FPS @@ -43,11 +43,11 @@ \endlist For more information about previewing UIs on devices, see - \l{Validating with Target Hardware}. + \l{Validating with target hardware}. - \section1 Profiling UI Code + \section1 Profiling UI code - You can use \l{Profiling QML Applications}{QML Profiler} that is integrated + You can use \l{Profiling QML applications}{QML Profiler} that is integrated into \QDS to find causes for typical performance problems in your UI. For example, your UI might be slow, unresponsive, or stuttering. Typically, such problems are caused by executing too much JavaScript in too few frames. All @@ -65,5 +65,5 @@ to optimize code without profiling is likely to result in very minor rather than significant performance improvements. - For more information, see \l{Profiling QML Applications}. + For more information, see \l{Profiling QML applications}. */ diff --git a/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc index af936f1507f..ea9cc8da1e2 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-prototyping.qdoc @@ -24,13 +24,13 @@ \list - \li \l {Creating UI Logic} + \li \l {Creating UI logic} You can turn your wireframe into an interactive prototype by adding the logic that enables your components to apply actions or react to mock data from backend systems to simulate complex experiences. - \li \l{Simulating Complex Experiences} + \li \l{Simulating complex experiences} You can connect UIs to different forms of data from various sources, such as QML-based data models, JavaScript files, and @@ -38,20 +38,20 @@ You can also connect your UI to Simulink to load live data from a Simulink simulation. - \li \l {Dynamic Behaviors} + \li \l {Dynamic behaviors} You can create connections between components to enable them to communicate with each other. The connections can be triggered by changes in component property values or in UI states. - \li \l {Validating with Target Hardware} + \li \l {Validating with target hardware} You can use the live preview feature to preview a UI file or the entire UI on the desktop, as well as on Android and embedded Linux devices. The changes you make to the UI are instantly visible to you in the preview. - \li \l {Asset Creation with Other Tools} + \li \l {Asset creation with other tools} Describes how to export designs containing 2D and 3D assets into files that you can import to projects in \QDS, how to import them, diff --git a/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc index 74063602795..7273f55ce50 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-uis.qdoc @@ -38,7 +38,7 @@ the developer documentation by pressing \key F1. \list - \li \l {Using Components} + \li \l {Using components} \QDS comes with \e {preset components} that you can use in wireframes and prototypes by creating instances of them. @@ -46,7 +46,7 @@ of the component instances and combine them. You can import designs and assets from other tools as components. - \li \l {Specifying Component Properties} + \li \l {Specifying component properties} You can specify values for the properties of a component to change its appearance and behavior. All components have a set of predefined @@ -55,7 +55,7 @@ component. You can specify values for properties of component instances in the \l Properties view. - \li \l {Scalable Layouts} + \li \l {Scalable layouts} The position of a component in a UI can be either absolute or relative to other components. While manual positioning @@ -63,7 +63,7 @@ methods, such as anchors, layouts, positioners, and property bindings, for dynamic UIs. - \li \l {Annotating Designs} + \li \l {Annotating designs} You can annotate your designs to provide reviewers or developers with additional information about them. diff --git a/doc/qtdesignstudio/src/overviews/studio-crashpad.qdoc b/doc/qtdesignstudio/src/overviews/studio-crashpad.qdoc index 22803b36f81..da703f7ec59 100644 --- a/doc/qtdesignstudio/src/overviews/studio-crashpad.qdoc +++ b/doc/qtdesignstudio/src/overviews/studio-crashpad.qdoc @@ -6,7 +6,7 @@ \previouspage studio-user-feedback.html \nextpage studio-packaging.html - \title Reporting Crashes + \title Reporting crashes You can enable \QDS to report crashes automatically. \QDS uses Google Crashpad to collect crashes and report them to the Sentry backend storage diff --git a/doc/qtdesignstudio/src/overviews/studio-user-feedback.qdoc b/doc/qtdesignstudio/src/overviews/studio-user-feedback.qdoc index 5feddcd2c97..f0546ae49c2 100644 --- a/doc/qtdesignstudio/src/overviews/studio-user-feedback.qdoc +++ b/doc/qtdesignstudio/src/overviews/studio-user-feedback.qdoc @@ -6,7 +6,7 @@ \previouspage collecting-usage-statistics.html \nextpage studio-crashpad.html - \title Collecting User Feedback + \title Collecting user feedback A pop-up survey asking for your feedback will appear for some of the features after you have been using them for some time. You will be asked to to rate diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ai.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ai.qdoc index e94d8700bc6..3cf03e85c9d 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ai.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ai.qdoc @@ -6,7 +6,7 @@ \page qtbridge-ai.html \nextpage psqtbridge.html - \title Exporting Designs from Adobe Illustrator + \title Exporting designs from Adobe Illustrator Even though \QDS does not provide a specific export bridge for Adobe Illustrator, you can design UIs in it and export your designs to \QDS in @@ -20,9 +20,9 @@ assets into formats supported by \QDS, such as PNG and JPEG. \endlist - \section1 Placing Illustrator Content into Photoshop + \section1 Placing the Illustrator content into Photoshop - You can place Illustrator content into Photoshop in several ways. If you + You can place the Illustrator content into Photoshop in several ways. If you select \uicontrol File > \uicontrol {Place linked} in Illustrator, the content updates automatically. However, the whole file content is placed on a single Photoshop layer, which means that you cannot use \QBPS to @@ -39,9 +39,9 @@ smart objects, see the Illustrator and Photoshop documentation. For more information about exporting designs from Photoshop, see - \l{Exporting Designs from Adobe Photoshop}. + \l{Exporting designs from Adobe Photoshop}. - \section1 Exporting Assets for Screens + \section1 Exporting assets for screens Sometimes it is easier to just export layers and artboards from Illustrator and to create scalable layouts and UI flows in \QDS. You can export assets diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc index 798ca9551c6..f93808d2619 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc @@ -8,7 +8,7 @@ \page figmaqtbridge.html \nextpage qtbridge-figma-setup.html - \title Exporting Designs from Figma + \title Exporting designs from Figma You can use \QBF to export designs from Figma to a \e {.qtbridge} archive that you can \l{Importing 2D Assets}{import} to projects in \QDS. @@ -19,7 +19,7 @@ \list - \li \l{Setting Up Qt Bridge for Figma} + \li \l{Setting up Qt Bridge for Figma} You must install Figma and the \QBF export tool before you can use the tool to export designs. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc index 00e9586df25..cb635e2ad73 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc @@ -6,7 +6,7 @@ \page qtbridge-figma-setup.html \nextpage qtbridge-figma-using.html - \title Setting Up \QBF + \title Setting up \QBF \note \QBF is included in the \l{https://www.qt.io/pricing}{\QDS Enterprise license}. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc index 29b161562db..f0cc8d8bab9 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page qtbridge-figma-template.html \nextpage exporting-3d-assets.html - \title Using Figma Quick Control Template Components in \QDS + \title Using the Figma Quick Control Template components in \QDS You can design with the template components created by \QDS in Figma and import them to \QDS with \QBF. These template components are structured @@ -14,7 +14,7 @@ \QBF, they generate functional QML components for \QDS. So, you can edit components both in \QDS and Figma. - \section1 Using Figma Template Components + \section1 Using the Figma Template components You should have these prerequisites available: \list @@ -27,11 +27,11 @@ for that. \endlist - \section2 Creating Figma Design with Template Components + \section2 Creating a Figma design with the Template components \list 1 \li Sign in to Figma. - \li Go to the Template provided by \QDS team + \li Go to the Template provided by the \QDS team \externallink {https://www.figma.com/community/file/1185200043286168239}{here}. \li Select \uicontrol {Get a copy} and then your account to have a copy on your Figma workspace. @@ -59,7 +59,7 @@ Toggle them active to use in a project. \endlist - \section2 Importing the Figma Design to \QDS with \QBF + \section2 Importing the Figma design to \QDS with \QBF \list 1 \li In Figma, do one of the following: diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index ba978680a96..154cf7627b9 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -8,7 +8,7 @@ \title Using \QBF - \section1 Organizing Designs + \section1 Organizing designs To get the best results during export and import, follow these guidelines when working with Figma: @@ -46,9 +46,9 @@ To use the fonts that you use in Figma also in \QDS, you need to add them to \QDS as assets. \QDS deploys them to devices when you preview the - UI. For more information, see \l{Using Custom Fonts}. + UI. For more information, see \l{Using custom fonts}. - \section2 Items You Can Export + \section2 Items you can export You can export the following parts of your design using \QBF: \list @@ -65,27 +65,27 @@ \li Groups \endlist - \section2 Using Frames + \section2 Using frames Frames are exported as components of the \l Rectangle type by default. However, if you have applied effects to the frames that \QBF cannot handle, such as gradient fill colors or a mixed radius, the frames are exported as images. - \section2 Using Variables + \section2 Using variables With \QDS 4.6, variables are exported as a QML module named \e DesignSystem. A QML singleton is created for each collection. Modes of the collection are exported as theme objects. A collection has a \e currentTheme property, updating the property changes the active theme. - + When a variable is bound to a property of a layer, the \e DesignSystem module import is added to the generated code and the property binding is created accordingly. - + To export variables, select \uicontrol {Export Variables} from \l{Settings}. \note Remote variables are not supported. Setting the active mode on the page or layer does not affect the generated code. - - \section2 Using Variants + + \section2 Using variants Figma variants are exported as a component with states. All variants inside a \e component-set are merged together and the differences across the variants are translated into states. @@ -103,7 +103,7 @@ encouraged to create the variant differences. \endlist - \section1 Exporting Designs + \section1 Exporting designs \image qt-figma-bridge.png "Qt Bridge for Figma" @@ -118,7 +118,7 @@ \QBF exports everything into a .qtbridge archive. You can import the archive into a project in \QDS, as described in \l{Importing 2D Assets}. - \section1 Export Settings + \section1 Export settings You can specify export settings in the \uicontrol Home tab and in the \uicontrol Settings tab. @@ -213,7 +213,7 @@ } \endcode \note The code must have a scope of a component (such as Item, MouseArea, - Connections) with a valid syntax for \l {UI Files}. + Connections) with a valid syntax for \l {UI files}. \note Add respective imports for your snippet in \uicontrol {Imports}. \row \li \uicontrol Alias diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc index 7c62a994f8a..8d4fcc3c702 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-overview.qdoc @@ -6,10 +6,10 @@ \page qtbridge-overview.html \nextpage qtbridge-ai.html - \title Exporting from Design Tools + \title Exporting from design tools When working with 2D assets, you can use \QB to export them from design - tools into a metadata format that you can then \l{Importing Designs}{import} + tools into a metadata format that you can then \l{Importing designs}{import} into \QDS. When working with 3D assets, you can use the export functions provided by @@ -19,7 +19,7 @@ For best results when importing assets, follow the guidelines for creating and exporting them. - \section1 2D Assets + \section1 2D assets You can use \QOI to install \QB if you have a \QDS enterprise license. @@ -32,20 +32,20 @@ \li \inlineimage sketch-logo.png \li \inlineimage figma-logo.png \row - \li \l{Exporting Designs from Adobe Illustrator}{Adobe Illustrator} - \li \l{Exporting Designs from Adobe Photoshop}{Adobe Photoshop} - \li \l{Exporting Designs from Adobe XD}{Adobe XD} - \li \l{Exporting Designs from Sketch}{Sketch} - \li \l{Exporting Designs from Figma}{Figma} + \li \l{Exporting designs from Adobe Illustrator}{Adobe Illustrator} + \li \l{Exporting designs from Adobe Photoshop}{Adobe Photoshop} + \li \l{Exporting designs from Adobe XD}{Adobe XD} + \li \l{Exporting designs from Sketch}{Sketch} + \li \l{Exporting designs from Figma}{Figma} \endtable - \section1 3D Assets + \section1 3D assets You can import files you created using 3D graphics applications and stored in several widely-used formats, such as .blend, .dae, .fbx, .glb, .gltf, .obj, .uia, or .uip. - For an overview, see \l{Exporting 3D Assets}. + For an overview, see \l{Exporting 3D assets}. \table \row diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-overview.qdoc index 11747b6dab2..80c41467290 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-overview.qdoc @@ -8,7 +8,7 @@ \page psqtbridge.html \nextpage qtbridge-ps-setup.html - \title Exporting Designs from Adobe Photoshop + \title Exporting designs from Adobe Photoshop You can use \QBPS to export designs from Adobe Photoshop to \e {.metadata} format that you can \l{Importing 2D Assets}{import} to projects in \QDS. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc index 864e12c9e47..3b27cc9f0c1 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-setup.qdoc @@ -6,7 +6,7 @@ \page qtbridge-ps-setup.html \nextpage qtbridge-ps-using.html - \title Setting Up \QBPS + \title Setting up \QBPS \QBPS is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. @@ -67,7 +67,7 @@ \note On \macos \QBPS fails to load when Adobe Photoshop runs natively on an ARM processor (Apple silicon). For more information, see \l {Running \QBPS on Apple Silicon}. - \section1 Enabling Remote Connections + \section1 Enabling remote connections To set up \QBPS: @@ -94,7 +94,7 @@ \note - \section1 Running \QBPS on Apple Silicon + \section1 Running \QBPS on Apple silicon If you are using \macos on an ARM processor (Apple silicon), \QBPS may not be listed in Adobe Photoshop under \uicontrol Window > \uicontrol {Extensions (Legacy)}. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc index 3ba29383c0c..f6d78347464 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-ps-using.qdoc @@ -8,7 +8,7 @@ \title Using \QBPS - \section1 Organizing Assets + \section1 Organizing assets To get the best results when you use \QBPS to export designs from Adobe Photoshop for importing them to \QDS, follow these guidelines when working @@ -26,13 +26,13 @@ To use the fonts that you use in Photoshop also in \QDS, you need to load them to \QDS. \QDS deploys them to devices when you preview the UI. For more - information, see \l{Using Custom Fonts}. + information, see \l{Using custom fonts}. \note You can export only files that are saved in the Photoshop file format, such as \e {.psd} and \e {.psb}. For all other document formats, \QDS displays the following error message: \e {Document is not supported.} - \section2 Items You Can Export + \section2 Items you can export You can export the following parts of your design using \QBPS: \list @@ -48,14 +48,14 @@ \li Frames \endlist - \section2 Using Artboards + \section2 Using artboards The relationships between the groups and layers on an artboard are preserved when you export designs from Adobe Photoshop and import them into \QDS. When you use \QBPS to export your designs, you will determine how you want each group or layer exported: as a \e component or \e child. A component - will be imported as a single \l {UI Files}{UI file} that can contain other + will be imported as a single \l {UI files}{UI file} that can contain other assets. A child will be imported as a single image file that you can use within UI files. @@ -88,7 +88,7 @@ \QB imports the control as a custom component that you can program in \QDS. - \section1 Exporting Assets + \section1 Exporting assets Each artboard is exported automatically as a component, which means that it will be imported as a separate file that contains all the artwork on the @@ -135,7 +135,7 @@ \image qt-bridge.png - \section2 Specifying Settings for Exporting Assets + \section2 Specifying settings for exporting assets To export your design using \QBPS: @@ -212,7 +212,7 @@ \li Select \uicontrol {Cascade properties} to apply the current set of properties to all the children of the selected layer. \li In the \uicontrol Annotations field, specify annotation for the - component. See \l {Annotating Designs}. + component. See \l {Annotating designs}. \li Select \uicontrol Export to copy your assets to the export path you specified. \li When the exporting is done, select \uicontrol OK. @@ -222,7 +222,7 @@ might take a little while depending on the complexity of your project. You can now create a project in \QDS and import the assets to it, as - described in \l {Creating Projects} and \l{Importing Designs}. + described in \l {Creating projects} and \l{Importing designs}. \note Exporting your design using \QBPS can be slow for documents with large number @@ -240,7 +240,7 @@ \image qt-bridge-qml-id-settings.png - \section1 Cloning Documents + \section1 Cloning documents \QBPS enables creating a clone of the current document. The clone workflow allows the user to filter out certain kind of layers and groups. In the \QBPS @@ -248,7 +248,7 @@ \image qt-bridge-clone.png - \section2 Clone Options + \section2 Clone options The following exclusion options can be selected to exclude certain kind of layers and groups in the cloned document: \list @@ -261,7 +261,7 @@ the other selected exclusion options. \endlist - \section1 Sanitizing Documents + \section1 Sanitizing documents \QBPS enables removing all \QBPS related metadata from the active document. In the \QBPS \uicontrol Settings dialog, select @@ -279,7 +279,7 @@ You can change the default behavior of \QBPS with the help of a JSX script. One can write specific functions in the script that are called by \QBPS with useful parameters. - \section2 Overridable JSX Functions + \section2 Overridable JSX functions Define the following functions in the override JSX: \list \li preExport(document) @@ -306,10 +306,10 @@ In the \QBPS \uicontrol Settings dialog, select \uicontrol {Override JSX Script} to set the override JSX script. - \section1 Importing Metadata & Assets + \section1 Importing metadata and assets \QBPS can import metadata generated from other tools and generate a Photoshop document. A - conventional workflow would be to generate metadata and assets by \l {Exporting Components} {exporting} + conventional workflow would be to generate metadata and assets by \l {Exporting components} {exporting} a QML project from \QDS and use \QBPS to generate a Photoshop document. Imported text and the assets are organized into Artboards, layers, and groups. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-overview.qdoc index 42369e70584..12156d6733b 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-overview.qdoc @@ -1,14 +1,14 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only -// Note: The \page value is hard-coded as a link in Qt Bridge for Sketch. +// Note: The \page value is hard-coded as a link in \QBSK. /*! \previouspage qtbridge-xd-using.html \page sketchqtbridge.html \nextpage qtbridge-sketch-setup.html - \title Exporting Designs from Sketch + \title Exporting designs from Sketch You can use \QBSK to export designs from Sketch to \e {.metadata} format that you can \l{Importing 2D Assets}{import} to projects in \QDS. @@ -19,12 +19,12 @@ \list - \li \l{Setting Up Qt Bridge for Sketch} + \li \l{Setting up \QBSK} You must install Sketch and the \QBSK export tool before you can use the tool to export designs. - \li \l{Using Qt Bridge for Sketch} + \li \l{Using \QBSK} To get the best results when you use \QBSK to export designs from Sketch, you should follow the guidelines for working with Sketch and diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc index 9fbccf11714..2abc617918e 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-setup.qdoc @@ -6,7 +6,7 @@ \page qtbridge-sketch-setup.html \nextpage qtbridge-sketch-using.html - \title Setting Up \QBSK + \title Setting up \QBSK \QBSK is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc index 06f23a29967..d052bf3138c 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-sketch-using.qdoc @@ -8,7 +8,7 @@ \title Using \QBSK - \section1 Organizing Assets + \section1 Organizing assets To get the best results when you use \QBSK to export designs from Sketch to \QDS, follow these guidelines when working with Sketch: @@ -48,7 +48,7 @@ To use the fonts that you use in Sketch also in \QDS, you need to import them to \QDS as assets. \QDS deploys them to devices when you preview the - UI. For more information, see \l{Using Custom Fonts}. + UI. For more information, see \l{Using custom fonts}. For more information, see the \QBSK tutorials that are also accessible from the \uicontrol Tutorials tab of the Welcome mode: @@ -60,7 +60,7 @@ {Sketch Bridge Tutorial Part 2} \endlist - \section2 Items You Can Export + \section2 Items you can export You can export the following parts of your design using \QBSK: \list @@ -79,7 +79,7 @@ \li Libraries \endlist - \section2 Using Artboards + \section2 Using artboards The relationships between artboards and layers are preserved when you export designs from Sketch and import them into \QDS. @@ -104,7 +104,7 @@ If you want to use the assets on an artboard in \QDS as they are in Sketch, you can import the artboard without generating code for it. - \section2 Using Layers and Groups + \section2 Using layers and groups When you use \QBSK to export your designs, you will determine how you want each layer or group to be exported: as \e merged or as \e child. Each @@ -130,7 +130,7 @@ recommend that you do not change the IDs after the first time you export the assets, to avoid problems. - \section1 Exporting Assets + \section1 Exporting assets By default, assets are exported as follows: @@ -162,7 +162,7 @@ \image qt-sketch-bridge.png - \section2 Specifying Settings for Exporting Assets + \section2 Specifying settings for exporting assets To export your design using \QBSK: @@ -234,7 +234,7 @@ \li Select the \uicontrol Visible check box to determine the visibility of the layer. \li In the \uicontrol Annotations field, specify annotation for the - component. See \l {Annotating Designs}. + component. See \l {Annotating designs}. \li Select the \uicontrol Settings tab to specify the export path and asset format. \li Select \uicontrol Export to export the document into a .qtbridge archive. @@ -246,9 +246,9 @@ your project. You can now create a project in \QDS and import the .qtbridge archive to it, as - described in \l {Creating Projects} and \l{Importing Designs}. + described in \l {Creating projects} and \l{Importing designs}. - \section1 Specifying Export Path and Asset Format + \section1 Specifying export path and asset format You can export assets into JPG, PNG, or SVG format. To specify export path and asset format, select \uicontrol Settings. @@ -286,7 +286,7 @@ \note Using invalid characters or reservered file names for the page name will result into imports errors as page name is used for the directory name to organize the UI files in \QDS . - \section1 Exporting Library Symbols + \section1 Exporting library symbols \QBSK can handle symbols used from a local library. Before you use \QBSK to export a document that contains remote symbols, you must prepare the Sketch document of the local library with diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-tutorial-links.qdocinc b/doc/qtdesignstudio/src/qtbridge/qtbridge-tutorial-links.qdocinc index 194c8ddf9fa..ef690111b89 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-tutorial-links.qdocinc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-tutorial-links.qdocinc @@ -3,7 +3,7 @@ //! [qtsketchbridge tutorials] - \section2 \QBSK Tutorials + \section2 \QBSK tutorials For more information, read the tutorials about using \QBSK that are also accessible from the \uicontrol Tutorials tab of the Welcome mode: diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-overview.qdoc index 36913c3d4ba..13442251676 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-overview.qdoc @@ -8,7 +8,7 @@ \page xdqtbridge.html \nextpage qtbridge-xd-setup.html - \title Exporting Designs from Adobe XD + \title Exporting designs from Adobe XD \note This is a \e {Technical Preview} release of the \QBXD. Some design elements might not be exported and imported into \QDS as expected. @@ -22,7 +22,7 @@ \list - \li \l{Setting Up Qt Bridge for Adobe XD} + \li \l{Setting up Qt Bridge for Adobe XD} You must install and set up the \QBXD export plugin before you can use it to export designs. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc index 392a3c1f7a3..a20bfddd969 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-setup.qdoc @@ -6,7 +6,7 @@ \page qtbridge-xd-setup.html \nextpage qtbridge-xd-using.html - \title Setting Up \QBXD + \title Setting up \QBXD \QBXD is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc index e08dba9f562..0bc76b1eca5 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-xd-using.qdoc @@ -8,7 +8,7 @@ \title Using \QBXD - \section1 Organizing Assets + \section1 Organizing assets To get the best results when you use \QBXD to export designs from Adobe XD to \QDS, follow these guidelines when working with Adobe XD: @@ -26,10 +26,10 @@ To use the fonts that you use in Adobe XD also in \QDS, you need to import them to \QDS as assets. \QDS deploys them to devices when you preview the - UI. For more information, see \l{Using Custom Fonts}. + UI. For more information, see \l{Using custom fonts}. - \section2 Supported Design Elements + \section2 Supported design elements You can export the following parts of your design using \QBXD: \list @@ -51,7 +51,7 @@ for XD Components is limited. This might change in the future and \QBXD might extend the XD Component support. - \section2 Using Artboards + \section2 Using artboards The hierarchical relationships between artboards and layers are preserved when you export designs from Adobe XD and import them into \QDS. The @@ -62,7 +62,7 @@ be imported as a separate file that contains the artwork and text on the artboard. - \section2 Annotate Layers for Export + \section2 Annotate layers for export With \QBXD, layers can be annotated to hint how each layer or group must be exported. The \uicontrol {Home} panel displays and allows layer annotation for @@ -135,7 +135,7 @@ } \endcode \note The code must have a scope of a component (such as Item, MouseArea, Connections) - with a valid syntax for \l {UI Files}. + 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 @@ -153,7 +153,7 @@ \uicontrol {Render Text} is selected. - \section2 Export Defaults + \section2 Export defaults \QBXD assigns the following defaults to the layers: @@ -171,9 +171,9 @@ might take a little while depending on the complexity of your project. You can now create a project in \QDS and import the assets to it, as - described in \l {Creating Projects} and \l{Importing Designs}. + described in \l {Creating projects} and \l{Importing designs}. - \section1 \QBXD Settings + \section1 \QBXD settings To edit export settings: @@ -205,7 +205,7 @@ \endlist \endlist - \section1 Suggestions and Tips + \section1 Suggestions and tips \list \li You can export assets with the default settings and later adjust these in \QDS. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc index fbc362f6c8c..f9b2f7c1f3c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page studio-advanced.html \nextpage creator-quick-ui-forms.html - \title Advanced Designer Topics + \title Advanced designer topics \table \row @@ -18,12 +18,12 @@ \list - \li \l{UI Files} + \li \l{UI files} Some of the wizard templates create projects that contain UI files. You should always edit UI files in the \l {2D} and \l Properties view, to avoid breaking the code. - \li \l{Managing Data Collection} + \li \l{Managing data collection} You can enable \QDS to report crashes automatically. If you enable the telemetry plugin, you can turn on the pseudonymous user @@ -31,7 +31,7 @@ and transmitted to the backend storage. You can also modify settings for collecting user feedback. - \li \l{Packaging Applications} + \li \l{Packaging applications} When you are ready to deliver your application to users or upload it to app stores, you can use \QDS to create suitable packages that diff --git a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc index 37f66ba6675..07dfde82cb3 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc +++ b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc @@ -3,7 +3,7 @@ //! [creating studio components] - \section1 Creating Custom Controls + \section1 Creating custom controls You can use project wizard templates to create stylable UI controls based on the components in the Qt Quick Controls module: @@ -28,7 +28,7 @@ \endtable You can edit the properties of the controls in all the preset - \l{Working with States}{states} to apply your own style to them. + \l{Working with states}{states} to apply your own style to them. \note For buttons and check boxes, you can disable the misbehaving hover effects by selecting \l Properties > \uicontrol Control, and then disabling @@ -49,7 +49,7 @@ \li Edit component properties in the \l Properties view. The available properties depend on the component type. You can - \l{Specifying Custom Properties}{add properties for components} on + \l{Specifying custom properties}{add properties for components} on the \uicontrol Properties tab in the {Connections} view. \endlist diff --git a/doc/qtdesignstudio/src/qtdesignstudio-debugging.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-debugging.qdoc index 09cd1d0f584..3abc8cb6471 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-debugging.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-debugging.qdoc @@ -6,21 +6,21 @@ \page studio-debugging.html \nextpage creator-debugging-qml.html - \title Debugging and Profiling + \title Debugging and profiling \list - \li \l{Debugging Qt Quick Projects} + \li \l{Debugging Qt Quick projects} You can use the integrated debugger to debug JavaScript functions, to execute JavaScript expressions to get information about the state of the UI, and to inspect QML properties and JavaScript variables and change them temporarily at runtime. - \li \l{Debugging a Qt Quick Application} + \li \l{Debugging a Qt Quick Application}{Debugging a Qt Quick application} This section uses the \l{QML Advanced Tutorial}{Same Game} example application to illustrate how to debug Qt Quick applications in the \uicontrol Debug mode. - \li \l{Profiling QML Applications} + \li \l{Profiling QML applications} You can use QML Profiler to find causes for typical performance problems in your UIs, such as slowness, freezing, and stuttering. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc index 3f6f190d218..51482e37d47 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc @@ -6,7 +6,7 @@ \page studio-developer-topics.html \nextpage studio-finding-the-qt-runtime-version.html - \title Developer Topics + \title Developer topics \table \row @@ -17,7 +17,7 @@ \endtable \list - \li \l{Finding the Qt Runtime Version} + \li \l{Finding the Qt runtime version} \QDS runs projects using a specific version of Qt. Identify which version of Qt your \QDS uses. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-examples.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-examples.qdoc index 5827d89d4e5..a0fcd6b05dc 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-examples.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-examples.qdoc @@ -26,6 +26,6 @@ on the \uicontrol Welcome page. Select the icon to download the latest version of the example project. - \section1 Example Documentation + \section1 Example documentation */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc index a02bf70b77c..bc5ccdc12ab 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc @@ -6,7 +6,7 @@ \page studio-exporting-and-importing.html \nextpage qtbridge-overview.html - \title Asset Creation with Other Tools + \title Asset creation with other tools Typically, you as a designer would like to use specialized UI design tools, such as Adobe Photoshop, Sketch, Figma, Blender, or Maya, and then send the @@ -26,14 +26,14 @@ \list 1 \li Export your design from a design tool into a metadata format supported by \QDS. - \li \l{Creating Projects}{Create a project} in \QDS and import the + \li \l{Creating projects}{Create a project} in \QDS and import the metadata file to it. \li Edit the imported components and create more components in the \l {2D} and \l {3D} view. \li Animate your design in \l {Transitions} or \l Timeline and \l {Curves}. \li Create interactions in \l States and \l {Connections}. - \li \l{Validating with Target Hardware}{Preview} your design in + \li \l{Validating with target hardware}{Preview} your design in real time, on the desktop or on a mobile or an embedded device. \endlist @@ -43,11 +43,11 @@ \youtube pEETxSxYazg \list - \li \l {Exporting from Design Tools} + \li \l {Exporting from design tools} Export designs containing 2D and 3D assets into a metadata format that you can import to projects in \QDS. - \li \l{Importing Designs} + \li \l{Importing designs} Import assets that you exported from design tools to a \QDS project and edit them in the \uicontrol Design mode to create a UI. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc index 97901839b64..8f0a655a6aa 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc @@ -6,16 +6,16 @@ \page studio-finding-the-qt-runtime-version.html \nextpage creator-vcs-git.html - \title Finding the Qt Runtime Version + \title Finding the Qt runtime version \include qtdesignstudio-qt-runtime-version.qdocinc qt-runtime-version - Find Qt runtime versions for previous \QDS releases in the table below. + Find the Qt runtime versions for previous \QDS releases in the table below. \table \header \li \QDS - \li Qt runtime version + \li The Qt runtime version \row \li 4.0 \li 6.4.1 and 5.15.5 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc index a975dfe7fc6..75cfb78045a 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc @@ -6,7 +6,7 @@ \page studio-getting-started.html \nextpage studio-installation.html - \title Getting Started + \title Getting started \table \row @@ -29,10 +29,10 @@ Follow a set of hands-on tutorials that illustrate how to use the features of \QDS. - \li \l {User Interface} + \li \l {User interface} Describes the parts and basic features of \QDS. - \li \l {Creating Projects} + \li \l {Creating projects} To wireframe or prototype an application, you need a project. Setting up a new project in \QDS is aided by a wizard that @@ -43,10 +43,10 @@ You can use wizards to create projects for the desktop or embedded Linux and Android devices. In addition, you can add individual QML files, components, and JavaScript files to your projects. - \li \l {Use Cases} + \li \l {Use cases} Describes the main use cases of \QDS at a general level. - \li \l{Concepts and Terms} + \li \l{Concepts and terms} Describes main \QDS concepts and terms. \li \l{Examples} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index 36fbc8c0155..247c97a9db0 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -22,7 +22,7 @@ and index functions to find particular topics in the helps, or request context-sensitive help by pressing \key F1 in the Design mode. - \li \l{Supported Platforms} + \li \l{Supported platforms} You can install and run \QDS on several operating systems to design UIs for multiple desktop, embedded, and mobile device platforms. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-implementing-applications.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-implementing-applications.qdoc index d19ede682f7..67d507f54ef 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-implementing-applications.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-implementing-applications.qdoc @@ -6,7 +6,7 @@ \page studio-implementing-applications.html \nextpage studio-designer-developer-workflow.html - \title Implementing Applications + \title Implementing applications \table \row @@ -23,9 +23,9 @@ \list - \li \l{Designer-Developer Workflow} + \li \l{Designer-developer workflow} - In \QDS projects, you work on \l{UI Files}{UI files} (.ui.qml), + In \QDS projects, you work on \l{UI files} (.ui.qml), while developers work on the .qml and C++ source files in Qt Creator to create an application that you can build and run on target hardware. @@ -35,7 +35,7 @@ understands the QML language as code, not just as plain text, so it can offer useful features, such as semantic highlighting, checking code syntax, code completion, and refactoring actions. - \li \l{Debugging and Profiling} + \li \l{Debugging and profiling} \QDS comes with a JavaScript debugger. In the \uicontrol Debug mode, you can inspect the state of your UI while debugging. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc index 341fe18624f..736f13fea82 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc @@ -28,7 +28,7 @@ will only appear in \uicontrol Assets, and you can then drag-and-drop them to a suitable view. - \section1 Importing Designs From Other Design Tools + \section1 Importing designs from other design tools \image studio-imported-assets.webp "UI imported into Qt Design Studio" @@ -44,7 +44,7 @@ The following instructions use an empty project as an example. For more information about the options you have, see - \l {Creating Projects}. + \l {Creating projects}. To import the exported assets to \QDS projects: @@ -100,7 +100,7 @@ \uicontrol {Asset Import} dialog while importing, fix the issues in design tool and export the assets again. - \section2 Merging QML Files + \section2 Merging QML files When you re-import a QML component, the changes done in \QDS are preserved. The QML item changes in the existing QML component are copied to the corresponding QML item in the new component. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-importing-designs.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-importing-designs.qdoc index 6e6cea54a78..26f5878f867 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-importing-designs.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-importing-designs.qdoc @@ -6,18 +6,18 @@ \page studio-importing-designs.html \nextpage studio-importing-2d.html - \title Importing Designs + \title Importing designs You can import 2D and 3D assets to \QDS projects. You need to use \QB for exporting the 2D assets from design tools, whereas you can directly import 3D assets saved in widely-used 3D graphics formats. \list - \li \l{Importing 2D Assets} + \li \l{Importing 2D assets} You can import 2D assets that you exported with \QB to a \QDS project as image and QML files and edit them in the Design mode. - \li \l{Importing 3D Assets} + \li \l{Importing 3D assets} You can import 3D assets that you created using 3D graphics applications and stored in one of the supported file formats. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc index fdf0f721b4f..27de04f2bf7 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc @@ -14,7 +14,7 @@ get \QDS using the Qt Edu license. \QDS is available on several operating systems. For more information, see - \l{Supported Platforms}. + \l{Supported platforms}. \section1 Using \QOI @@ -56,5 +56,6 @@ \section1 Using Qt Edu license Students and teachers can get a licensing package for learning purposes. - To apply for the educational license and install \QDS, see \l {Qt Edu for Designers}. + To apply for the educational license and install \QDS, see + \l {Qt Edu for Designers}{Qt Edu for designers}. */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc index b1d1a7e8660..ee768ecf8d8 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-javascript.qdoc @@ -6,7 +6,7 @@ \page studio-javascript.html \nextpage studio-simulink.html - \title Simulating Application Logic + \title Simulating application logic You can use JavaScript to simulate application logic that brings your UI to life. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc index 4158a7b545d..e2e68aa21eb 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \previouspage studio-crashpad.html \nextpage studio-developer-topics.html - \title Packaging Applications + \title Packaging applications \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see @@ -16,7 +16,7 @@ app stores, you can use \QDS to create suitable packages that contain all the necessary files, such as fonts, images, components, and modules. - \section1 Adding Resources to Packages + \section1 Adding resources to packages \QDS supports the \e{Qt Resource System} (QRC), which is a platform-independent mechanism for storing files in the application's @@ -42,7 +42,7 @@ resource collection file, you can recreate the resource collection file to remove them also from there. - \section1 Embedding Resources into Applications + \section1 Embedding resources into applications Alternatively, you can embed the resources into your application by selecting \uicontrol File > \uicontrol {Export Project} > diff --git a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc index 501e30adc77..6d7c3e3c6dc 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc @@ -6,12 +6,12 @@ \page studio-platforms.html \nextpage technical-support.html - \title Supported Platforms + \title Supported platforms You can install and run \QDS on several operating systems to create applications for multiple desktop, embedded, and mobile device platforms. - \section1 Host Platforms + \section1 Host platforms \QDS is available in binary packages for the following operating systems: @@ -31,7 +31,7 @@ minimum hardware and software: Intel Core i7 with 8GB of RAM and integrated Intel Graphics. - \section1 Design Tools + \section1 Design tools \QB is available for the following design tools: diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc index 7f6a8b67ca2..f35212ab7fe 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,7 +6,7 @@ \page studio-projects-managing.html \nextpage studio-projects.html - \title Managing Projects + \title Managing projects One of the major advantages of \QDS is that it allows a team of designers and developers to share a project across different development platforms @@ -21,7 +21,7 @@ project source files and configuration files. Do not store generated files. - \li \l {Converting Qt Design Studio Projects to Applications} + \li \l {Converting \QDS projects to applications} \QDS projects are useful for creating UIs. To use them for application development in Qt Creator, you have to convert them diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index 54215eb3661..323ba12125b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -6,7 +6,7 @@ \previouspage studio-keyboard-shortcuts.html \nextpage studio-use-cases.html - \title Creating Projects + \title Creating projects One of the major advantages of \QDS is that it allows a team of designers and developers to share a project across different development platforms @@ -16,7 +16,7 @@ \list \li Group files together. - \li Include \l{UI Files}{UI files} (.ui.qml), component files (.qml), + \li Include \l{UI files} (.ui.qml), component files (.qml), and assets files. \li Specify settings for previewing UIs. \endlist @@ -56,7 +56,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. For more information, see \l {Creating Projects for MCUs}. + on MCU boards. For more information, see \l {Creating projects for MCUs}. \row \li {1,3} Mobile \li Scroll @@ -86,13 +86,13 @@ To test how well your designs work, you can preview the UIs on the desktop, embedded Linux devices, or Android devices. For more - information, see \l{Validating with Target Hardware}. + information, see \l{Validating with target hardware}. You can export designs from other design tools and import them to projects. - For more information, see \l{Exporting from Design Tools} and - \l{Importing Designs From Other Design Tools}. + For more information, see \l{Exporting from design tools} and + \l{Importing designs from other design tools}. - \section1 Creating a Project + \section1 Creating a project To create a project: @@ -170,7 +170,7 @@ \row \li Screen01.ui.qml \li This file defines a custom component that you can edit in the \uicontrol {2D} view. - For more information, see \l {UI Files}. + For more information, see \l {UI files}. The project wizard generates this as the first scene. \row \li qtquickcontrols2.conf @@ -216,7 +216,7 @@ \uicontrol Assets > \inlineimage icons/plus.png . - \section1 Using Custom Presets + \section1 Using custom presets You can save project settings as custom presets. All saved custom presets are available on the \uicontrol Custom tab in the @@ -231,7 +231,7 @@ preset. \endlist - \section1 Adding Files to Projects + \section1 Adding files to projects You can use wizard templates to add individual files to projects. @@ -250,7 +250,7 @@ \li {1,4} Qt Quick Files \li Qt Quick File \li Generates a component with one of the following default components - or \l{Using Positioners}{positioners} as the root component: + 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. @@ -261,7 +261,7 @@ \row \li Qt Quick Views \li Generates a Grid View or a List View. For more information, see - \l{List and Grid Views}. + \l{List Views and Grid Views}. \row \li Qt Quick UI Form \li Creates a UI file along with a matching QML file for @@ -304,6 +304,6 @@ \li Generates files that you can use to write the application logic. This is useful for testing the application before the developers implement the application logic in C++, for example. For more - information, see \l {Simulating Application Logic}. + information, see \l {Simulating application logic}. \endtable */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc index 7e99f8d17dc..cc345cc5b60 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-ui-viewer.qdoc @@ -7,7 +7,7 @@ \page qt-ui-viewer.html \nextpage studio-exporting-and-importing.html - \title Viewing Applications on Android + \title Viewing applications on Android View \QDS projects that you have shared online in \QUV. Bring all the projects from a \QDS ID to \QUV, or establish a direct @@ -51,7 +51,7 @@ \note Only one \QDS ID can be connected to the \QUV at a time. - \section1 Running a \QDS Project in \QUV + \section1 Running a \QDS project in \QUV Once the projects are in \QUV: \list 1 @@ -74,7 +74,7 @@ ID connection. \endlist - \section1 Creating a Network Connection between \QDS and \QUV + \section1 Creating a network connection between \QDS and \QUV To create a direct connection with a \QDS project without using a \QDS ID, first, get your Android device's \e {Local IP} from \QUV. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc index 005660c63b9..52ab0eb0015 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-sharing-assets.qdoc @@ -7,7 +7,7 @@ \nextpage qtquick-motion-design.html \sa {Content Library} - \title Sharing Assets + \title Sharing assets With \QDS Bundle files (\e{.qdsbundle}), you can import and export 3D components (such as cameras, lights, and models) and materials easily. @@ -21,7 +21,7 @@ \uicontrol {Export Material}. \endlist - \section1 Importing Bundles + \section1 Importing bundles To import a 3D component or material bundle, do one of the following: \list \li In the \uicontrol {3D}, \uicontrol {2D}, or \uicontrol {Navigator} view, right-click diff --git a/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc index 6bad9919d54..21bb5eada68 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc @@ -6,7 +6,7 @@ \page studio-simulink.html \nextpage creator-qml-modules-with-plugins.html - \title Simulating Dynamic Systems + \title Simulating dynamic systems Use the Simulink connector to connect simulation to your UI. Simulink is a MATLAB-based graphical programming environment for modeling, simulating, @@ -55,7 +55,7 @@ {Qt/QML Send} blocks. \endlist - \section2 Address and Port + \section2 Address and port An \uicontrol Address block delivers the IP address of a server to the \uicontrol {Simulink-Qt Client} block as a typical IP address string. @@ -107,7 +107,7 @@ Signal or \uicontrol Property value. \endlist - \section2 Specifying Property Names in Simulink + \section2 Specifying property names in Simulink Double-click the \uicontrol {Qt/SML Send} or \uicontrol {Qt/QML Receive} block in Simulink to specify a property name. A pop-up for \uicontrol @@ -118,9 +118,9 @@ \image simulink-qt-send-example-property.png "Example property of the Qt Send block" - \section1 Integrating the Simulink Model to \QDS + \section1 Integrating the Simulink model to \QDS - \section2 Importing the Simulink Connector + \section2 Importing the Simulink connector To integrate the Simulink model into \QDS, you first need to add the Simulink connector module to your project. In the \uicontrol Components @@ -143,13 +143,13 @@ Navigator to add the properties on the \uicontrol Properties tab in the \l Connections view. - See \l {Specifying Custom Properties} for a detailed description of how + See \l {Specifying custom properties} for a detailed description of how to add a custom property. The name of the property and the data type need to match those of the send or receive property of the Simulink model. \image studio-connection-view-properties.png "The Properties tab in the Connections view" - \section2 Creating Bindings + \section2 Creating bindings Next, you need to bind the value of the property you just created to the desired properties of UI components. @@ -166,7 +166,7 @@ (\uicontrol Actions) menu next to a property, and then select \uicontrol {Set Binding}. In the \uicontrol {Binding Editor}, select the text field and type in \c {.}, for example - \c rectangle.speedProp. For more information, see \l {Setting Bindings}. + \c rectangle.speedProp. For more information, see \l {Setting bindings}. \image studio-binding-editor.png "The Binding Editor window" diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index 00464f7447a..d87919ced3e 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -6,7 +6,7 @@ \previouspage studio-use-cases.html \nextpage best-practices.html - \title Concepts and Terms + \title Concepts and terms This topic describes main \QDS concepts and terms: @@ -65,8 +65,8 @@ Read more about bindings: \list - \li \l{Adding Bindings Between Properties} - \li \l{Setting Bindings} + \li \l{Adding bindings between properties} + \li \l{Setting bindings} \endlist \section1 Component @@ -83,10 +83,10 @@ Some of the \l {Component Types}{preset components} represent simple shapes, text, or images, while others represent complex UI controls with full functionality, such as spin boxes or sliders. You can also add instances of - preset \l {3D Components}{3D components} to your UIs. You can find all + preset \l {3D components}{3D components} to your UIs. You can find all the preset components in \l Components. - To build \l {Creating Component Instances}{your own components}, you can + To build \l {Creating component instances}{your own components}, you can modify the \l{glossary-property}{properties} of the component instances and combine them. @@ -94,16 +94,16 @@ \e ui.qml or \e .qml). For example, a Button component may be defined in Button.ui.qml. Typically, the visual appearance of a component is defined in a \e {UI file}. To create component files, you can use - \l{Creating Components from Scratch}{wizard templates}, or - \l{Turning Component Instances into Custom Components} + \l{Creating components from scratch}{wizard templates}, or + \l{Turning component instances into custom components} {move component instances into separate component files}. Read more about components: \list - \li \l {Preset Components} - \li \l {Creating Component Instances} - \li \l {Creating Custom Components} + \li \l {Preset components} + \li \l {Creating component instances} + \li \l {Creating custom components} \endlist \section1 Connection @@ -121,7 +121,7 @@ \list \li \l{Connections} - \li \l{Working with Connections} + \li \l{Working with connections} \endlist \section1 Device @@ -132,7 +132,7 @@ Read more about devices: \list - \li \l{Previewing on Devices} + \li \l{Previewing on devices} \endlist \section1 Mode @@ -175,7 +175,7 @@ Read more about projects: \list - \li \l{Creating Projects} + \li \l{Creating projects} \endlist \section1 Property @@ -193,10 +193,10 @@ \list \li \l{Properties} - \li \l{Preset Components} - \li \l{Specifying Component Properties} - \li \l{Adding Bindings Between Properties} - \li \l{Specifying Custom Properties} + \li \l{Preset components} + \li \l{Specifying component properties} + \li \l{Adding bindings between properties} + \li \l{Specifying custom properties} \endlist \section1 Signal @@ -221,7 +221,7 @@ Read more about signals: \list - \li \l{Connecting Components to Signals} + \li \l{Connecting components to signals} \li \l{Mouse Area} \endlist @@ -248,7 +248,7 @@ \list \li \l{States} - \li \l{Working with States} + \li \l{Working with states} \endlist \section1 Transition @@ -264,7 +264,7 @@ \list \li \l{Transitions} - \li \l{Animating Transitions Between States} + \li \l{Animating transitions between states} \endlist \omit diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 78b20747459..5c001add704 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -10,9 +10,9 @@ \list \li \l{Installing \QDS} \li \l{Tutorials} - \li \l{User Interface} + \li \l{User interface} \list - \li \l{Design Views} + \li \l{Design views} \list \li \l{2D} \li \l{3D} @@ -40,44 +40,44 @@ \li \l{Transitions} \li \l{Translations} \endlist - \li \l{Managing Workspaces} + \li \l{Managing workspaces} \li \l{Manage sessions} - \li \l{Keyboard Shortcuts} + \li \l{Keyboard shortcuts} \endlist - \li \l{Creating Projects} - \li \l{Use Cases} - \li \l{Concepts and Terms} - \li \l{Best Practices} + \li \l{Creating projects} + \li \l{Use cases} + \li \l{Concepts and terms} + \li \l{Best practices} \li \l{Examples} \endlist \li \l{Wireframing} \list - \li \l {Using Components} + \li \l {Using components} \list - \li \l{Preset Components} + \li \l{Preset components} \list \li \l{Shapes} \li \l{Text} \li \l{Images} - \li \l{User Interaction Methods} - \li \l{UI Controls} - \li \l{Lists and Other Data Models} - \li \l{2D Effects} + \li \l{User interaction methods} + \li \l{UI controls} + \li \l{Lists and other data models} + \li \l{2D effects} \li \l{Design Effects} - \li \l{Logic Helpers} + \li \l{Logic helpers} \li \l{Animations} \li \l{3D Views} \li \l{Node} \li \l{Group} - \li \l{Instanced Rendering} - \li \l{Skeletal Animation} - \li \l{3D Models} - \li \l{Materials and Shaders} + \li \l{Instanced rendering} + \li \l{Skeletal animation} + \li \l{3D models} + \li \l{Materials and shaders} \li \l{Textures} - \li \l{3D Materials} - \li \l{3D Effects} - \li \l{Custom Shaders} - \li \l{Custom Effects and Materials in Qt 5} + \li \l{3D materials} + \li \l{3D effects} + \li \l{Custom shaders} + \li \l{Custom effects and materials in Qt 5} \li \l{Lights} \li \l{Cameras} \li \l{Scene Environments} @@ -87,157 +87,158 @@ \li \l{Particles} \list \li \l {Particle System} - \li \l {Logical Particles} - \li \l {Particle Emitters} - \li \l {Particle Affectors} - \li \l {Particle Directions} + \li \l {Logical particles} + \li \l {Particle emitters} + \li \l {Particle affectors} + \li \l {Particle directions} \endlist \endlist - \li \l {Creating Component Instances} - \li \l {Creating Custom Components} + \li \l {Creating component instances} + \li \l {Creating custom components} \list - \li \l{Creating Buttons} - \li \l{Creating Scalable Buttons and Borders} + \li \l{Creating buttons} + \li \l{Creating scalable buttons and borders} \endlist \endlist - \li \l{Specifying Component Properties} - \li \l{Scalable Layouts} - \li \l{Annotating Designs} + \li \l{Specifying component properties} + \li \l{Scalable layouts} + \li \l{Annotating designs} \endlist \li \l{Prototyping} \list - \li \l{Creating UI Logic} + \li \l{Creating UI logic} \li \l{Effects} - \li \l{Simulating Complex Experiences} + \li \l{Simulating complex experiences} \list - \li \l{Loading Placeholder Data} - \li \l{Simulating Application Logic} - \li \l{Simulating Dynamic Systems} - \li \l{Using QML Modules with Plugins} + \li \l{Loading placeholder data} + \li \l{Simulating application logic} + \li \l{Simulating dynamic systems} + \li \l{Using QML modules with plugins} \endlist - \li \l{Dynamic Behaviors} + \li \l{Dynamic behaviors} \list - \li \l{Working with Connections} + \li \l{Working with connections} \list - \li\l{Connecting Components to Signals} - \li\l{Adding Bindings Between Properties} - \li\l{Specifying Custom Properties} + \li\l{Connecting components to signals} + \li\l{Adding bindings between properties} + \li\l{Specifying custom properties} \endlist - \li \l{Working with States} + \li \l{Working with states} \endlist - \li \l{Validating with Target Hardware} + \li \l{Validating with target hardware} \list - \li \l{Previewing on Desktop} - \li \l{Previewing on Devices} - \li \l{Sharing Applications Online} + \li \l{Previewing on desktop} + \li \l{Previewing on devices} + \li \l{Sharing applications online} \list - \li \l{Creating a Single Page Navigation Web Application} + \li \l{Creating a single page navigation web application} \endlist - \li \l {Viewing Applications on Android} + \li \l {Viewing applications on Android} \endlist - \li \l {Asset Creation with Other Tools} + \li \l {Asset creation with other tools} \list - \li \l{Exporting from Design Tools} + \li \l{Exporting from design tools} \list - \li \l{Exporting Designs from Adobe Illustrator} - \li \l{Exporting Designs from Adobe Photoshop} + \li \l{Exporting designs from Adobe Illustrator} + \li \l{Exporting designs from Adobe Photoshop} \list - \li \l{Setting Up Qt Bridge for Adobe Photoshop} + \li \l{Setting up Qt Bridge for Adobe Photoshop} \li \l{Using Qt Bridge for Adobe Photoshop} \endlist - \li \l{Exporting Designs from Adobe XD} + \li \l{Exporting designs from Adobe XD} \list - \li \l{Setting Up Qt Bridge for Adobe XD} + \li \l{Setting up Qt Bridge for Adobe XD} \li \l{Using Qt Bridge for Adobe XD} \endlist - \li \l{Exporting Designs from Sketch} + \li \l{Exporting designs from Sketch} \list - \li \l{Setting Up Qt Bridge for Sketch} - \li \l{Using Qt Bridge for Sketch} + \li \l{Setting up \QBSK} + \li \l{Using \QBSK} \endlist - \li \l{Exporting Designs from Figma} + \li \l{Exporting designs from Figma} \list - \li \l{Setting Up Qt Bridge for Figma} + \li \l{Setting up Qt Bridge for Figma} \li \l{Using Qt Bridge for Figma} - \li \l{Using Figma Quick Control Template Components in Qt Design Studio} + \li \l{Using the Figma Quick Control Template components in \QDS} \endlist \endlist - \li \l {Exporting 3D Assets} + \li \l {Exporting 3D assets} \list \li \l{Exporting from Blender} \li \l{Exporting from Maya} \li \l{Exporting from Qt 3D Studio} \endlist - \li \l{Importing Designs} + \li \l{Importing designs} \list - \li \l{Importing 2D Assets} + \li \l{Importing 2D assets} \list - \li \l{Using Custom Fonts} + \li \l{Using custom fonts} \endlist - \li \l{Importing 3D Assets} + \li \l{Importing 3D assets} \endlist - \li \l{Exporting Components} - \li \l{Sharing Assets} + \li \l{Exporting components} + \li \l{Using the Qt Quick Effect Maker effects} + \li \l{Sharing assets} \endlist \endlist \li \l{Motion Design} \list - \li \l{Introduction to Animation Techniques} - \li \l{Creating Timeline Animations} - \li \l{Editing Easing Curves} - \li \l{Production Quality} - \li \l{Optimizing Designs} + \li \l{Introduction to animation techniques} + \li \l{Creating timeline animations} + \li \l{Editing easing curves} + \li \l{Production quality} + \li \l{Optimizing designs} \list - \li \l{Creating Optimized 3D Scenes} + \li \l{Creating optimized 3D scenes} \endlist \endlist - \li \l{Implementing Applications} + \li \l{Implementing applications} \list - \li \l{Designer-Developer Workflow} - \li \l{Debugging and Profiling} + \li \l{Designer-developer workflow} + \li \l{Debugging and profiling} \list - \li \l{Debugging Qt Quick Projects} + \li \l{Debugging Qt Quick projects} \list - \li \l{Viewing Call Stack Trace} - \li \l{Setting Breakpoints} - \li \l{Local Variables and Function Parameters} - \li \l{Evaluating Expressions} + \li \l{Viewing call stack trace} + \li \l{Setting breakpoints} + \li \l{Local variables and function parameters} + \li \l{Evaluating expressions} \endlist - \li \l{Debugging a Qt Quick Application} - \li \l{Profiling QML Applications} + \li \l{Debugging a Qt Quick application} + \li \l{Profiling QML applications} \list \li \l{Profile QML applications} \endlist \endlist \endlist - \li \l{Advanced Designer Topics} + \li \l{Advanced designer topics} \list \omit \li Extending Component Functionality \endomit - \li \l{UI Files} - \li \l{Managing Data Collection} + \li \l{UI files} + \li \l{Managing data collection} \list - \li \l {Collecting Usage Statistics} - \li \l {Collecting User Feedback} - \li \l {Reporting Crashes} + \li \l {Collecting usage statistics} + \li \l {Collecting user feedback} + \li \l {Reporting crashes} \endlist - \li \l {Packaging Applications} + \li \l {Packaging applications} \endlist - \li \l{Developer Topics} + \li \l{Developer topics} \list - \li \l{Finding the Qt Runtime Version} + \li \l{Finding the Qt runtime version} \li \l{Using Git} \li \l{Use external tools} \li \l{Accessing output, issue, and warning messages} \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 {\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 {Developing applications for MCUs} \li \l {Connecting MCUs with Qt Creator} \endlist \endlist @@ -254,10 +255,10 @@ \li \l {Search from documentation} \li \l {Select the help start page} \endlist - \li \l{Supported Platforms} + \li \l{Supported platforms} \endlist - \li \l{Technical Support} - \li \l{Licenses and Acknowledgments} - \li \l{What's New} + \li \l{Technical support} + \li \l{Licenses and acknowledgments}{Licenses and acknowledgments} + \li \l{What's new} \endlist */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc index 2fd95ac7665..b9a02b4bc4b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-tutorials.qdoc @@ -26,7 +26,7 @@ Before you start, take a look at the following topics to familiarize yourself with the parts of \QDS in general, and the \uicontrol Design - mode in particular: \l{User Interface} and \l{Design Views}. + mode in particular: \l{User interface} and \l{Design views}. In addition to these tutorials, \QDS comes with examples that you can open from the \uicontrol Examples tab in the \uicontrol Welcome mode. For more diff --git a/doc/qtdesignstudio/src/qtdesignstudio-use-cases.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-use-cases.qdoc index 54e90305c0c..9919de7c2b0 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-use-cases.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-use-cases.qdoc @@ -6,7 +6,7 @@ \previouspage studio-projects.html \nextpage studio-terms.html - \title Use Cases + \title Use cases The following table summarizes the main use cases of \QDS with links to more information: @@ -39,7 +39,7 @@ \row \li \l{Wireframing} \li \l{Prototyping} - \li \l{Asset Creation with Other Tools} - \li \l{Implementing Applications} + \li \l{Asset creation with other tools} + \li \l{Implementing applications} \endtable */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc new file mode 100644 index 00000000000..42588f7d2b4 --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-using-effect-maker-effects.qdoc @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + + \page qt-using-effect-maker-effects.html + \nextpage qtquick-motion-design.html + \previouspage creator-exporting-qml.html + + \title Using the Qt Quick Effect Maker effects + + \QQEM is integrated into \QDS for creating shader effects. To create an + effect, you first create the effect file in \QDS, and then you edit it in \QQEM. + + For more information about \QQEM, see the + \l{https://doc.qt.io/qt-6/qtquickeffectmaker-index.html}{Qt Quick Effect Maker Manual}. + + \section1 Creating an effect file + + To create an effect file in \QDS: + + \list 1 + \li Right-click in the \uicontrol Assets view and + select \uicontrol {New Effect}. + \QDS creates an effect file and opens it in \QQEM. + \image qt-quick-effect-maker.webp + \li Edit the effect. + \li In \QQEM, go to \uicontrol File > \uicontrol Save. + \li With the default settings, select \uicontrol OK. + \image effect-maker-export.png + \endlist + + Now, you can close \QQEM and return to \QDS and apply the + effect. + + \section1 Applying an effect + + You can apply effects to components in \QDS. To do so, drag the effect + from the \uicontrol Assets view to the component in the \uicontrol 2D or + \uicontrol Navigator view. + + \image apply-effect-maker-effect.webp + +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index bad0da25798..72cb4c00cf1 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -5,8 +5,8 @@ \page index.html \nextpage studio-getting-started.html - \keyword {Qt Design Studio Manual} - \title Qt Design Studio Documentation + \keyword {Qt Design Studio documentation} + \title Qt Design Studio documentation \raw HTML