From 3066dce3b829f807a59deae243ce020c86516252 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 14 Dec 2022 14:48:21 +0100 Subject: [PATCH 01/37] QmlDesigner: Show error message if Qt is less than Qt 6.2 in template The cmake application template requires Qt 6.2 or higher. Otherwise we show an error message. Task-number: QDS-8110 Change-Id: Ib22edae8634727e17edf2bb19594549bd81a49b4 Reviewed-by: Tim Jenssen --- .../studio_templates/projects/common/CMakeLists.main.txt.tpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl index 6878a1023c3..9daf9759b19 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl @@ -10,6 +10,11 @@ set(CMAKE_AUTOMOC ON) find_package(QT NAMES Qt6 COMPONENTS Gui Qml Quick) find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick) +# To build this application you need Qt 6.2.0 or higher +if (Qt6_VERSION VERSION_LESS 6.2.0) +message(FATAL_ERROR "You need Qt 6.2.0 or newer to build the application.") +endif() + qt_add_executable(${CMAKE_PROJECT_NAME} src/main.cpp) # qt_standard_project_setup() requires Qt 6.3 or higher. See https://doc.qt.io/qt-6/qt-standard-project-setup.html for details. From f22a8a42a97746e38df126c7fa9aad6ec30b6788 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 14 Dec 2022 14:39:37 +0100 Subject: [PATCH 02/37] QmlDesigner: Avoid infinite recursion Task-number: QDS-7864 Change-Id: Idc7b44d23ff252a870357bafc2dd072e0b83befd Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index cdda880f84b..7914ca76437 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -562,9 +562,9 @@ QVector getObjectTypes(const ObjectValue *objectValue, const Conte const CppComponentValue * qmlObjectValue = value_cast(prototype); if (qmlObjectValue) - propertyList.append(getQmlTypes(qmlObjectValue, context, local, rec)); + propertyList.append(getQmlTypes(qmlObjectValue, context, local, rec + 1)); else - propertyList.append(getObjectTypes(prototype, context, local, rec)); + propertyList.append(getObjectTypes(prototype, context, local, rec + 1)); } return propertyList; From 973f74b8a0596b6104df38d891846400c33c8d69 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 14 Dec 2022 16:30:59 +0100 Subject: [PATCH 03/37] qmljs: improve readability Change-Id: I110c2b08bcdfc29b310b84378cd3b119ee7f5f91 Reviewed-by: Eike Ziller --- src/libs/qmljs/qmljsmodelmanagerinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index 5786171b6a6..1f689264147 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -63,7 +63,7 @@ static const char *qtQuickUISuffix = "ui.qml"; static void maybeAddPath(ViewerContext &context, const Utils::FilePath &path) { - if (!path.isEmpty() && !(context.paths.count(path) > 0)) + if (!path.isEmpty() && (context.paths.count(path) <= 0)) context.paths.insert(path); } From 6ae064619f4f3f6131493e2af107696a6d46c170 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Wed, 14 Dec 2022 14:09:15 +0100 Subject: [PATCH 04/37] Add an option to style merge from string data Task-number: QDS-8601 Change-Id: Icb56e5bc26c6cc3711c304641cce3cbbd6d7c8b2 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../componentcore/modelnodeoperations.cpp | 7 +++-- .../designercore/include/stylesheetmerger.h | 9 +++++- .../designercore/model/stylesheetmerger.cpp | 31 +++++++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 711b84c3798..5dd2b1dd5d6 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1525,8 +1525,11 @@ void mergeWithTemplate(const SelectionContext &selectionContext, ExternalDepende const QString templateFile = getTemplateDialog(projectPath); - if (QFileInfo::exists(templateFile)) - StylesheetMerger::styleMerge(selectionContext.view()->model(), templateFile, externalDependencies); + if (QFileInfo::exists(templateFile)) { + StylesheetMerger::styleMerge(Utils::FilePath::fromString(templateFile), + selectionContext.view()->model(), + externalDependencies); + } } void removeGroup(const SelectionContext &selectionContext) diff --git a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h index f4e09cf6f5b..036cb9df659 100644 --- a/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h +++ b/src/plugins/qmldesigner/designercore/include/stylesheetmerger.h @@ -5,6 +5,8 @@ #include "qmldesignercorelib_global.h" +#include "utils/filepath.h" + #include #include #include @@ -26,7 +28,12 @@ class QMLDESIGNERCORE_EXPORT StylesheetMerger public: StylesheetMerger(AbstractView*, AbstractView*); void merge(); - static void styleMerge(Model *model, const QString &templateFile, class ExternalDependenciesInterface &externalDependencies); + static void styleMerge(const Utils::FilePath &templateFile, + Model *model, + class ExternalDependenciesInterface &ed); + static void styleMerge(const QString &qmlTemplateString, + Model *model, + class ExternalDependenciesInterface &externalDependencies); private: void preprocessStyleSheet(); diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index dd377cc0180..6df3e58a1d1 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -515,11 +515,24 @@ void StylesheetMerger::merge() } } -void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, ExternalDependenciesInterface &externalDependencies) +void StylesheetMerger::styleMerge(const Utils::FilePath &templateFile, + Model *model, + ExternalDependenciesInterface &externalDependencies) +{ + Utils::FileReader reader; + + QTC_ASSERT(reader.fetch(templateFile), return ); + const QString qmlTemplateString = QString::fromUtf8(reader.data()); + StylesheetMerger::styleMerge(qmlTemplateString, model, externalDependencies); +} + +void StylesheetMerger::styleMerge(const QString &qmlTemplateString, + Model *model, + ExternalDependenciesInterface &externalDependencies) { Model *parentModel = model; - QTC_ASSERT(parentModel, return); + QTC_ASSERT(parentModel, return ); auto templateModel(Model::create("QtQuick.Item", 2, 1, parentModel)); Q_ASSERT(templateModel.get()); @@ -527,10 +540,6 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext templateModel->setFileUrl(parentModel->fileUrl()); QPlainTextEdit textEditTemplate; - Utils::FileReader reader; - - QTC_ASSERT(reader.fetch(Utils::FilePath::fromString(templateFile)), return); - QString qmlTemplateString = QString::fromUtf8(reader.data()); QString imports; for (const Import &import : parentModel->imports()) { @@ -541,13 +550,14 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext textEditTemplate.setPlainText(imports + qmlTemplateString); NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate); - QScopedPointer templateRewriterView(new RewriterView(externalDependencies, RewriterView::Amend)); + QScopedPointer templateRewriterView( + new RewriterView(externalDependencies, RewriterView::Amend)); templateRewriterView->setTextModifier(&textModifierTemplate); templateModel->attachView(templateRewriterView.data()); templateRewriterView->setCheckSemanticErrors(false); ModelNode templateRootNode = templateRewriterView->rootModelNode(); - QTC_ASSERT(templateRootNode.isValid(), return); + QTC_ASSERT(templateRootNode.isValid(), return ); auto styleModel(Model::create("QtQuick.Item", 2, 1, parentModel)); Q_ASSERT(styleModel.get()); @@ -556,11 +566,12 @@ void StylesheetMerger::styleMerge(Model *model, const QString &templateFile, Ext QPlainTextEdit textEditStyle; RewriterView *parentRewriterView = parentModel->rewriterView(); - QTC_ASSERT(parentRewriterView, return); + QTC_ASSERT(parentRewriterView, return ); textEditStyle.setPlainText(parentRewriterView->textModifierContent()); NotIndentingTextEditModifier textModifierStyle(&textEditStyle); - QScopedPointer styleRewriterView(new RewriterView(externalDependencies, RewriterView::Amend)); + QScopedPointer styleRewriterView( + new RewriterView(externalDependencies, RewriterView::Amend)); styleRewriterView->setTextModifier(&textModifierStyle); styleModel->attachView(styleRewriterView.data()); From 7e5ecfe8bcdf138292572a07386acbff1d491ffc Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Thu, 1 Dec 2022 22:05:55 +0200 Subject: [PATCH 05/37] Fix glitch in AssetsView when dragging files onto it Moved the DropArea from the AssetDelegate into the AssetsView, so that moving the cursor through the delegates no longer denies and then permits dragging. This glitch happened because the DropArea was inside the delegate, while the TreeView also has rowSpacing, which are areas that do not belong to the delegates, and for which you don't normally have drag & drop support. Task-number: QDS-8232 Change-Id: If49a384f25bb870105448156f436e048479e880c Reviewed-by: Miikka Heikkinen Reviewed-by: Thomas Hartmann --- .../itemLibraryQmlSources/AssetDelegate.qml | 46 +++------- .../itemLibraryQmlSources/AssetsView.qml | 88 ++++++++++++++++++- 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml index a28becff06b..7bfec10aa32 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml @@ -12,7 +12,7 @@ TreeViewDelegate { required property Item assetsRoot property bool hasChildWithDropHover: false - property bool isHoveringDrop: false + property bool isHighlighted: false readonly property string suffix: model.fileName.substr(-4) readonly property bool isFont: root.suffix === ".ttf" || root.suffix === ".otf" readonly property bool isEffect: root.suffix === ".qep" @@ -74,7 +74,7 @@ TreeViewDelegate { id: bg color: { - if (root.__isDirectory && (root.isHoveringDrop || root.hasChildWithDropHover)) + if (root.__isDirectory && (root.isHighlighted || root.hasChildWithDropHover)) return StudioTheme.Values.themeInteraction if (!root.__isDirectory && root.assetsView.selectedAssets[root.__itemPath]) @@ -120,40 +120,6 @@ TreeViewDelegate { } } - DropArea { - id: treeDropArea - - enabled: true - anchors.fill: parent - - onEntered: (drag) => { - root.assetsRoot.updateDropExtFiles(drag) - root.isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0 - if (root.isHoveringDrop) - root.assetsView.startDropHoverOver(root.__currentRow) - } - - onDropped: (drag) => { - root.isHoveringDrop = false - root.assetsView.endDropHover(root.__currentRow) - - let dirPath = root.__isDirectory - ? model.filePath - : assetsModel.parentDirPath(model.filePath); - - rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles, - root.assetsRoot.dropComplexExtFiles, - dirPath) - } - - onExited: { - if (root.isHoveringDrop) { - root.isHoveringDrop = false - root.assetsView.endDropHover(root.__currentRow) - } - } - } - MouseArea { id: mouseArea @@ -247,6 +213,14 @@ TreeViewDelegate { } } // MouseArea + function getDirPath() + { + if (root.__isDirectory) + return model.filePath + else + return assetsModel.parentDirPath(model.filePath) + } + function __openContextMenuForCurrentRow() { let modelIndex = assetsModel.indexForPath(model.filePath) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml index 782cca5ebc6..dcffab432be 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml @@ -250,8 +250,12 @@ TreeView { function startDropHoverOver(row) { let index = root.__modelIndex(row) - if (assetsModel.isDirectory(index)) + if (assetsModel.isDirectory(index)) { + let item = root.__getDelegateItemForIndex(index) + if (item) + item.isHighlighted = true return + } let parentItem = root.__getDelegateParentForIndex(index) if (parentItem) @@ -261,8 +265,12 @@ TreeView { function endDropHover(row) { let index = root.__modelIndex(row) - if (assetsModel.isDirectory(index)) + if (assetsModel.isDirectory(index)) { + let item = root.__getDelegateItemForIndex(index) + if (item) + item.isHighlighted = false return + } let parentItem = root.__getDelegateParentForIndex(index) if (parentItem) @@ -292,6 +300,12 @@ TreeView { return root.itemAtCell(parentCell) } + function __getDelegateItemForIndex(index) + { + let cell = root.cellAtIndex(index) + return root.itemAtCell(cell) + } + function __modelIndex(row) { // The modelIndex() function exists since 6.3. In Qt 6.3, this modelIndex() function was a @@ -303,6 +317,76 @@ TreeView { return root.modelIndex(row, 0) } + DropArea { + id: dropArea + enabled: true + anchors.fill: parent + + property bool __isHoveringDrop: false + property int __rowHoveringOver: -1 + + function __rowAndItem(drag) + { + let pos = dropArea.mapToItem(root, drag.x, drag.y) + let cell = root.cellAtPos(pos.x, pos.y, true) + let item = root.itemAtCell(cell) + + return [cell.y, item] + } + + onEntered: (drag) => { + root.assetsRoot.updateDropExtFiles(drag) + + let [row, item] = dropArea.__rowAndItem(drag) + dropArea.__isHoveringDrop = drag.accepted && root.assetsRoot.dropSimpleExtFiles.length > 0 + + if (item && dropArea.__isHoveringDrop) + root.startDropHoverOver(row) + + dropArea.__rowHoveringOver = row + } + + onDropped: (drag) => { + let [row, item] = dropArea.__rowAndItem(drag) + + if (item) { + root.endDropHover(row) + + let dirPath = item.getDirPath() + + rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles, + root.assetsRoot.dropComplexExtFiles, + dirPath) + } + + dropArea.__isHoveringDrop = false + dropArea.__rowHoveringOver = -1 + } + + onPositionChanged: (drag) => { + let [row, item] = dropArea.__rowAndItem(drag) + + if (dropArea.__rowHoveringOver !== row && dropArea.__rowHoveringOver > -1) { + root.endDropHover(dropArea.__rowHoveringOver) + + if (item) + root.startDropHoverOver(row) + } + + dropArea.__rowHoveringOver = row + } + + onExited: { + if (!dropArea.__isHoveringDrop || dropArea.__rowHoveringOver === -1) + return + + root.endDropHover(dropArea.__rowHoveringOver) + + dropArea.__isHoveringDrop = false + dropArea.__rowHoveringOver = -1 + } + } + delegate: AssetDelegate { assetsView: root assetsRoot: root.assetsRoot From 8d08409f59a9dbc1d83600431301b7900dd0a9aa Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 14 Dec 2022 17:09:08 +0200 Subject: [PATCH 06/37] QmlDesigner fix: do not automatically open example project on download The user might click several example thumbnails, quickly one after another. This would normally issue multiple "open project" requests, and at the end the user doesn't know which of them actually opened. This might also potentially end up in a crash to someone. Fixed this by no longer opening the project automatically on open. Also, added a close button, on the progress bar for the downloading process, which the user can use to cancel an ongoing download. This would be helpful if the example project was opened by mistake, and the project is quite big. Task-number: QDS-7845 Change-Id: Ica3105aac77b8eb01c888f08050bd542599794f9 Reviewed-by: Qt CI Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Thomas Hartmann --- src/plugins/studiowelcome/examplecheckout.cpp | 33 +++++++++++++------ src/plugins/studiowelcome/examplecheckout.h | 5 +++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index da19fd7c5f4..7ed1ff674fe 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -76,13 +76,13 @@ void FileDownloader::start() auto request = QNetworkRequest(m_url); request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::UserVerifiedRedirectPolicy); - QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request); + m_reply = Utils::NetworkAccessManager::instance()->get(request); - QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { - m_tempFile.write(reply->readAll()); + QNetworkReply::connect(m_reply, &QNetworkReply::readyRead, this, [this]() { + m_tempFile.write(m_reply->readAll()); }); - QNetworkReply::connect(reply, + QNetworkReply::connect(m_reply, &QNetworkReply::downloadProgress, this, [this](qint64 current, qint64 max) { @@ -93,16 +93,21 @@ void FileDownloader::start() emit progressChanged(); }); - QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) { - emit reply->redirectAllowed(); + QNetworkReply::connect(m_reply, &QNetworkReply::redirected, [this](const QUrl &) { + emit m_reply->redirectAllowed(); }); - QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() { - if (reply->error()) { + QNetworkReply::connect(m_reply, &QNetworkReply::finished, this, [this]() { + if (m_reply->error()) { if (m_tempFile.exists()) m_tempFile.remove(); - qWarning() << Q_FUNC_INFO << m_url << reply->errorString(); - emit downloadFailed(); + + if (m_reply->error() != QNetworkReply::OperationCanceledError) { + qWarning() << Q_FUNC_INFO << m_url << m_reply->errorString(); + emit downloadFailed(); + } else { + emit downloadCanceled(); + } } else { m_tempFile.flush(); m_tempFile.close(); @@ -110,9 +115,17 @@ void FileDownloader::start() emit tempFileChanged(); emit finishedChanged(); } + + m_reply = nullptr; }); } +void FileDownloader::cancel() +{ + if (m_reply) + m_reply->abort(); +} + void FileDownloader::setUrl(const QUrl &url) { m_url = url; diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h index 7e7a0dff882..8c1446518c9 100644 --- a/src/plugins/studiowelcome/examplecheckout.h +++ b/src/plugins/studiowelcome/examplecheckout.h @@ -112,6 +112,7 @@ public: bool available() const; Q_INVOKABLE void start(); + Q_INVOKABLE void cancel(); signals: void finishedChanged(); @@ -123,6 +124,8 @@ signals: void lastModifiedChanged(); void availableChanged(); + void downloadCanceled(); + private: void probeUrl(); @@ -133,6 +136,8 @@ private: QFile m_tempFile; QDateTime m_lastModified; bool m_available = false; + + QNetworkReply *m_reply = nullptr; }; class DataModelDownloader : public QObject From 432c5cd6522d9c9cfe654f3c803e750c35dc1466 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 12 Dec 2022 19:25:44 +0100 Subject: [PATCH 07/37] QmlDesigner: Update docs for menu items relocation For the project export features menu items were grouped and relocated. To keep things relevant documents were updated. Fixes: QDS-8510 Change-Id: I93da0aced4ff606e4c53180be7ab07d5aa1004a0 Reviewed-by: Leena Miettinen --- .../studio-designer-developer-workflow.qdoc | 8 +++++--- .../src/qtdesignstudio-packaging.qdoc | 15 ++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 1753da63f7b..146501328c2 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -32,8 +32,8 @@ your project as a fully working C++ application with developers. If you add or remove QML files in \QDS, you have to regenerate the - \e CMakeLists.txt project configuration file by selecting \uicontrol Build - > \uicontrol Run > \uicontrol {Generate CMakeLists.txt Files}. + \e CMakeLists.txt project configuration file by selecting \uicontrol File + > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. If you use Git, you can clone an example project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0} @@ -99,6 +99,8 @@ This isn't mandatory. \li Generate CMake files and C++ source files that are used to compile the application into - an executable file by selecting \uicontrol Build > \uicontrol{Generate CMakeLists.txt files}. + an executable file by selecting \uicontrol File > \uicontrol {Export Project} > + \uicontrol {Generate CMake Build Files}. + \endlist */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc index a1e35898812..1e426f011e1 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc @@ -24,9 +24,9 @@ set of files (icons, translation files, and so on) and you don't want to run the risk of losing the files. - To package your application, select \uicontrol Build > - \uicontrol {Generate QRC Resource File}. Then select the - files to package in the \uicontrol {Add Resources} dialog. + To package your application, select \uicontrol File > + \uicontrol {Export Project} > \uicontrol {Generate QRC Resource File}. + Then select the files to package in the \uicontrol {Add Resources} dialog. \image studio-add-resources.png "Add Resources dialog" @@ -44,10 +44,11 @@ \section1 Embedding Resources into Applications - Alternatively, you can embedd the resources into your application by - selecting \uicontrol Build > \uicontrol {Generate Deployable Package}. - Select the location for the .qmlrc file, and then select the files to - embedd in the \uicontrol {Add Resources} dialog. + Alternatively, you can embed the resources into your application by + selecting \uicontrol File > \uicontrol {Export Project} > + \uicontrol {Generate Deployable Package}. Select the location for + the .qmlrc file, and then select the files to embed in the + \uicontrol {Add Resources} dialog. When you select \uicontrol OK, \QDS creates a resource collection file (.qmlrc) in the location you selected. From c476a1b807b25cc4a0529f59f3e13dabb0036e7b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 12 Dec 2022 15:43:54 +0200 Subject: [PATCH 08/37] QmlDesigner: Intercept stray mouse events on popup menus By default Qt Quick Controls Menu lets mouse events through to the underlying view if the event is not handled by the menu item. This is the case for all clicks on disabled items and right clicks on enabled items. To prevent unwanted click-throughs, added a mouse area to the background of StudioControls.Menu that eats all clicks. Fixes: QDS-8584 Change-Id: I08fa93317ffcaf518b350d44bb555d84741e246d Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: --- .../imports/StudioControls/Menu.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml index 306d4aeda98..d0fb8719ad1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml @@ -40,5 +40,13 @@ T.Menu { color: StudioTheme.Values.themeControlBackground border.color: StudioTheme.Values.themeControlOutline border.width: StudioTheme.Values.border + MouseArea { + // This mouse area is here to eat clicks that are not handled by menu items + // to prevent them going through to the underlying view. + // This is primarily problem with disabled menu items, but right clicks would go + // through enabled menu items as well. + anchors.fill: parent + acceptedButtons: Qt.AllButtons + } } } From bf4a94d6191821ebeb1295f4a261459b09a9fff7 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 13 Dec 2022 17:39:53 +0200 Subject: [PATCH 09/37] QmlDesigner: Fix dragging materials to Model in navigator Fixes: QDS-8537 Change-Id: I0d63676e9e7fc39166c1cd32a231547161b2f0a5 Reviewed-by: Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/navigator/navigatortreemodel.cpp | 5 +---- .../qmldesigner/components/navigator/navigatorview.cpp | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 66b6f640faf..a7a163ab18b 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -797,10 +797,7 @@ void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, int rowNu if (!targetNode.metaInfo().isQtQuick3DModel()) return; - QByteArray data = mimeData->data(Constants::MIME_TYPE_MATERIAL); - QDataStream stream(data); - qint32 internalId; - stream >> internalId; + qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt(); ModelNode matNode = m_view->modelNodeForInternalId(internalId); m_view->executeInTransaction(__FUNCTION__, [&] { diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 2175f786691..31106a87356 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -253,10 +253,7 @@ void NavigatorView::dragStarted(QMimeData *mimeData) m_widget->setDragType(itemLibraryEntry.typeName()); m_widget->update(); } else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) { - QByteArray data = mimeData->data(Constants::MIME_TYPE_MATERIAL); - QDataStream stream(data); - qint32 internalId; - stream >> internalId; + qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt(); ModelNode matNode = modelNodeForInternalId(internalId); m_widget->setDragType(matNode.metaInfo().typeName()); From 8b1233c11ef0b53647784d1d3afa2bd7fda833ad Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 15 Dec 2022 14:38:59 +0100 Subject: [PATCH 10/37] QmlDesigner: Fix mode switch crash Task-number: QDS-8599 Change-Id: I4bebd2ff4f2ce93788743304ff335d118e23ce68 Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/instances/nodeinstanceview.cpp | 3 ++- src/plugins/qmldesigner/designercore/model/abstractview.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 661b303a940..a1025cc9aa4 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -136,7 +136,8 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager m_resetTimer.setSingleShot(true); m_resetTimer.setInterval(100); QObject::connect(&m_resetTimer, &QTimer::timeout, [this] { - resetPuppet(); + if (isAttached()) + resetPuppet(); }); m_updateWatcherTimer.setSingleShot(true); m_updateWatcherTimer.setInterval(100); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 20d39362a0e..50497c58359 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -559,6 +559,7 @@ void AbstractView::resetView() void AbstractView::resetPuppet() { + QTC_ASSERT(isAttached(), return); emitCustomNotification(QStringLiteral("reset QmlPuppet")); } @@ -689,7 +690,8 @@ void AbstractView::emitCustomNotification(const QString &identifier, const QList void AbstractView::emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data) { - model()->d->notifyCustomNotification(this, identifier, nodeList, data); + if (model()) + model()->d->notifyCustomNotification(this, identifier, nodeList, data); } void AbstractView::emitInstancePropertyChange(const QList > &propertyList) From 51fe3ca59e878f4c87359e9777cd7057e760c553 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Wed, 14 Dec 2022 17:32:19 +0100 Subject: [PATCH 11/37] QmlDesigner: Update component integration instructions for CMake Starting with QDS 3.9 the cmake projects can download and add the Qt Quick Studio Components to the application project. This patch updates that notion into the document. Fixes: QDS-8603 Change-Id: I7555b04eca72fc09bc9c73b0954f58aec2f3ddd5 Reviewed-by: Mats Honkamaa Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- .../src/qtquick/qtquick-from-qmlproject-to-pro.qdoc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc b/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc index 1e6956a449a..72836730372 100644 --- a/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-from-qmlproject-to-pro.qdoc @@ -31,6 +31,13 @@ can be built with CMake. You can open the \e CMakeLists.txt project file in Qt Creator to continue developing the project. + \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 @@ -149,10 +156,8 @@ \section1 Adding Qt Quick Designer Components to Qt Installations - If you use Qt Quick Studio Components or Effects in your project, you have - to check out and install the \e {Qt Quick Designer Components} module from - \l{https://code.qt.io/cgit/qt-labs/qtquickdesigner-components.git/} - {Qt Code Review}. + 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 From 76565fa8d76ca350a01700d5fd546aefa3b64452 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Mon, 5 Dec 2022 00:23:38 +0100 Subject: [PATCH 12/37] QmlDesigner: add support for multiple qml backends This patch includes; * Fork of original QML runtime as an alternative QML backend for QDS * Flexible structure for adding/removing different types of QML interpreters Note: When forking the original QML the config.h is renamed as qmlconfiguration.h because it was clashing with sqlite and QmlRuntime.QmlConfiguration uses "magic" name matching. QmlConfiguration/qmlconfiguration is unlikely to conflict with anything. Task-number: QDS-8373 Change-Id: Ifaa1b766c717ce12d6b3c9ddbbc0665669797e36 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../designercore/instances/puppetstarter.cpp | 3 +- src/tools/qml2puppet/CMakeLists.txt | 32 +- src/tools/qml2puppet/qml2puppet/main.cpp | 28 ++ .../qml2puppet/qml2puppet/qml2puppetmain.cpp | 267 -------------- .../qml2puppet/runner/appmetadata.h | 99 +++++ .../qml2puppet/runner/puppet/configcrashpad.h | 68 ++++ .../qml2puppet/runner/puppet/qmlpuppet.cpp | 126 +++++++ .../qml2puppet/runner/puppet/qmlpuppet.h | 21 ++ .../qml2puppet/qml2puppet/runner/qmlbase.h | 111 ++++++ .../qml2puppet/runner/runtime/loadwatcher.h | 97 +++++ .../runner/runtime/qmlconfiguration.h | 66 ++++ .../qml2puppet/runner/runtime/qmlruntime.cpp | 348 ++++++++++++++++++ .../qml2puppet/runner/runtime/qmlruntime.h | 30 ++ src/tools/qml2puppet/qmlpuppet.qrc | 5 + .../qmlruntime/content/resizeItemToWindow.qml | 25 ++ .../qmlruntime/content/resizeWindowToItem.qml | 22 ++ .../runnerconf/qmlruntime/default.qml | 10 + 17 files changed, 1088 insertions(+), 270 deletions(-) create mode 100644 src/tools/qml2puppet/qml2puppet/main.cpp delete mode 100644 src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/runner/appmetadata.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/qmlbase.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h create mode 100644 src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp create mode 100644 src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h create mode 100644 src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml create mode 100644 src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeWindowToItem.qml create mode 100644 src/tools/qml2puppet/runnerconf/qmlruntime/default.qml diff --git a/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp b/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp index 268f43eb42d..56b3cdafc2d 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetstarter.cpp @@ -35,7 +35,7 @@ QProcessUniquePointer puppetProcess(const QString &puppetPath, processFinishCallback); if (forwardOutput == puppetMode || forwardOutput == "all") { - puppetProcess->setProcessChannelMode(QProcess::MergedChannels); + puppetProcess->setProcessChannelMode(QProcess::ForwardedChannels); QObject::connect(puppetProcess.get(), &QProcess::readyRead, processOutputCallback); } puppetProcess->setWorkingDirectory(workingDirectory); @@ -46,7 +46,6 @@ QProcessUniquePointer puppetProcess(const QString &puppetPath, else processArguments = {socketToken, puppetMode}; - processArguments.push_back("-graphicssystem raster"); processArguments.push_back(freeTypeOption); puppetProcess->start(puppetPath, processArguments); diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 074099be894..1976a2bc149 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -35,12 +35,22 @@ add_qtc_executable(qml2puppet Qt5::QuickPrivate Qt5::Network Qt5::GuiPrivate QmlPuppetCommunication SOURCES - qml2puppet/qml2puppetmain.cpp + qml2puppet/main.cpp qmlpuppet.qrc ) set_target_properties(qml2puppet PROPERTIES OUTPUT_NAME qml2puppet-${IDE_VERSION}) +execute_process( + COMMAND git describe --tags --always --dirty=+ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_SHA_RESULT + OUTPUT_VARIABLE GIT_SHA_OUTPUT + ERROR_VARIABLE GIT_SHA_ERROR +) + +add_definitions( -D GIT_SHA=${GIT_SHA_OUTPUT} ) + extend_qtc_executable(qml2puppet CONDITION Qt5_VERSION VERSION_GREATER_EQUAL 6.0.0 SOURCES @@ -166,6 +176,16 @@ extend_qtc_executable(qml2puppet animationdriver.cpp animationdriver.h ) +extend_qtc_executable(qml2puppet + DEPENDS app_version + SOURCES_PREFIX qml2puppet/runner + SOURCES + runtime/qmlruntime.h runtime/qmlruntime.cpp + runtime/qmlconfiguration.h runtime/loadwatcher.h + puppet/qmlpuppet.h puppet/qmlpuppet.cpp puppet/configcrashpad.h + qmlbase.h appmetadata.h +) + extend_qtc_executable(qml2puppet SOURCES_PREFIX qmlprivategate SOURCES @@ -186,11 +206,21 @@ extend_qtc_executable(qml2puppet PUBLIC_INCLUDES src/libs ) +extend_qtc_executable(qml2puppet +PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qml2puppet/runner/runtime +) + extend_qtc_executable(qml2puppet CONDITION TARGET Nanotrace DEPENDS Nanotrace ) +# Turn the tool into its own self-contained qml module +qt6_add_qml_module(qml2puppet + URI QmlRuntime.QmlConfiguration + VERSION 1.0 +) + if (QTC_STATIC_BUILD AND Qt5_VERSION VERSION_GREATER_EQUAL 6.0.0) qt6_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR}) endif() diff --git a/src/tools/qml2puppet/qml2puppet/main.cpp b/src/tools/qml2puppet/qml2puppet/main.cpp new file mode 100644 index 00000000000..66d6a43f0b1 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/main.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "runner/puppet/qmlpuppet.h" +#include "runner/runtime/qmlruntime.h" + +QmlBase *getQmlRunner(int &argc, char **argv) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--qml-runtime")){ + qInfo() << "Starting QML Runtime"; + return new QmlRuntime(argc, argv); + } + } +#endif + qInfo() << "Starting QML Puppet"; + return new QmlPuppet(argc, argv); +} + +int main(int argc, char *argv[]) +{ + QDSMeta::Logging::registerMessageHandler(); + QDSMeta::AppInfo::registerAppInfo("Qml2Puppet"); + + QmlBase *qmlRunner = getQmlRunner(argc, argv); + return qmlRunner->run(); +} diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp deleted file mode 100644 index 2d7518b81bc..00000000000 --- a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#include "iconrenderer/iconrenderer.h" -#include "import3d/import3d.h" - -#include -#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef ENABLE_QT_BREAKPAD -#include -#endif - -#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) -#define NOMINMAX -#include "client/crashpad_client.h" -#include "client/crash_report_database.h" -#include "client/settings.h" -#endif - -#ifdef Q_OS_WIN -#include -#endif - -namespace { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) -{ - QByteArray localMsg = msg.toLocal8Bit(); - switch (type) { - case QtDebugMsg: - fprintf(stderr, - "Debug: %s (%s:%u, %s)\n", - localMsg.constData(), - context.file, - context.line, - context.function); - break; - case QtInfoMsg: - fprintf(stderr, - "Info: %s (%s:%u, %s)\n", - localMsg.constData(), - context.file, - context.line, - context.function); - break; - case QtWarningMsg: - fprintf(stderr, - "Warning: %s (%s:%u, %s)\n", - localMsg.constData(), - context.file, - context.line, - context.function); - break; - case QtCriticalMsg: - fprintf(stderr, - "Critical: %s (%s:%u, %s)\n", - localMsg.constData(), - context.file, - context.line, - context.function); - break; - case QtFatalMsg: - fprintf(stderr, - "Fatal: %s (%s:%u, %s)\n", - localMsg.constData(), - context.file, - context.line, - context.function); - abort(); - } -} -#endif - -#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) -bool startCrashpad() -{ - using namespace crashpad; - - // Cache directory that will store crashpad information and minidumps - base::FilePath database(L"crashpad_reports"); - base::FilePath handler(L"crashpad_handler.exe"); - - // URL used to submit minidumps to - std::string url(CRASHPAD_BACKEND_URL); - - // Optional annotations passed via --annotations to the handler - std::map annotations; - annotations["qt-version"] = QT_VERSION_STR; - - // Optional arguments to pass to the handler - std::vector arguments; - arguments.push_back("--no-rate-limit"); - - CrashpadClient *client = new CrashpadClient(); - bool success = client->StartHandler( - handler, - database, - database, - url, - annotations, - arguments, - /* restartable */ true, - /* asynchronous_start */ true - ); - // TODO: research using this method, should avoid creating a separate CrashpadClient for the - // puppet (needed only on windows according to docs). -// client->SetHandlerIPCPipe(L"\\\\.\\pipe\\qml2puppet"); - - return success; -} -#endif - -int internalMain(QGuiApplication *application) -{ - QCoreApplication::setOrganizationName("QtProject"); - QCoreApplication::setOrganizationDomain("qt-project.org"); - QCoreApplication::setApplicationName("Qml2Puppet"); - QCoreApplication::setApplicationVersion("1.0.0"); - - if (application->arguments().count() < 2 - || (application->arguments().at(1) == "--readcapturedstream" && application->arguments().count() < 3) - || (application->arguments().at(1) == "--rendericon" && application->arguments().count() < 5) - || (application->arguments().at(1) == "--import3dAsset" && application->arguments().count() < 6)) { - qDebug() << "Usage:\n"; - qDebug() << "--test"; - qDebug() << "--version"; - qDebug() << "--readcapturedstream [control stream file]"; - qDebug() << "--rendericon "; - qDebug() << "--import3dAsset "; - - return -1; - } - - if (application->arguments().at(1) == "--readcapturedstream" && application->arguments().count() > 2) { - QFileInfo inputStreamFileInfo(application->arguments().at(2)); - if (!inputStreamFileInfo.exists()) { - qDebug() << "Input stream does not exist:" << inputStreamFileInfo.absoluteFilePath(); - - return -1; - } - - if (application->arguments().count() > 3) { - QFileInfo controlStreamFileInfo(application->arguments().at(3)); - if (!controlStreamFileInfo.exists()) { - qDebug() << "Output stream does not exist:" << controlStreamFileInfo.absoluteFilePath(); - - return -1; - } - } - } - - if (application->arguments().count() == 2 && application->arguments().at(1) == "--test") { - qDebug() << QCoreApplication::applicationVersion(); - QQmlEngine engine; - - QQmlComponent component(&engine); - component.setData("import QtQuick 2.0\nItem {\n}\n", QUrl::fromLocalFile("test.qml")); - - QObject *object = component.create(); - - if (object) { - qDebug() << "Basic QtQuick 2.0 working..."; - } else { - qDebug() << "Basic QtQuick 2.0 not working..."; - qDebug() << component.errorString(); - } - delete object; - return 0; - } - - if (application->arguments().count() == 2 && application->arguments().at(1) == "--version") { - std::cout << 2; - return 0; - } - - if (application->arguments().at(1) != "--readcapturedstream" && application->arguments().count() < 4) { - qDebug() << "Wrong argument count: " << application->arguments().count(); - return -1; - } - - if (application->arguments().at(1) == "--rendericon") { - int size = application->arguments().at(2).toInt(); - QString iconFileName = application->arguments().at(3); - QString iconSource = application->arguments().at(4); - - IconRenderer *iconRenderer = new IconRenderer(size, iconFileName, iconSource); - iconRenderer->setupRender(); - - return application->exec(); - } - - if (application->arguments().at(1) == "--import3dAsset") { - QString sourceAsset = application->arguments().at(2); - QString outDir = application->arguments().at(3); - QString options = application->arguments().at(4); - - Import3D::import3D(sourceAsset, outDir, options); - - return application->exec(); - } - -#ifdef ENABLE_QT_BREAKPAD - const QString libexecPath = QCoreApplication::applicationDirPath() + '/' + RELATIVE_LIBEXEC_PATH; - QtSystemExceptionHandler systemExceptionHandler(libexecPath); -#endif - -#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) - /* startCrashpad(); */ -#endif - - new QmlDesigner::Qt5NodeInstanceClientProxy(application); - -#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG) - SetErrorMode(SEM_NOGPFAULTERRORBOX); //We do not want to see any message boxes -#endif - - if (application->arguments().at(1) == "--readcapturedstream") - return 0; - - return application->exec(); -} -} // namespace - -int main(int argc, char *argv[]) -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - qInstallMessageHandler(myMessageOutput); -#endif - // Since we always render text into an FBO, we need to globally disable - // subpixel antialiasing and instead use gray. - qputenv("QSG_DISTANCEFIELD_ANTIALIASING", "gray"); -#ifdef Q_OS_MACOS - qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "true"); -#endif - - //If a style different from Desktop is set we have to use QGuiApplication - bool useGuiApplication = (!qEnvironmentVariableIsSet("QMLDESIGNER_FORCE_QAPPLICATION") - || qgetenv("QMLDESIGNER_FORCE_QAPPLICATION") != "true") - && qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE") - && qgetenv("QT_QUICK_CONTROLS_STYLE") != "Desktop"; - -#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER - Sqlite::LibraryInitializer::initialize(); -#endif - - if (useGuiApplication) { - QGuiApplication application(argc, argv); - return internalMain(&application); - } else { - QApplication application(argc, argv); - return internalMain(&application); - } -} diff --git a/src/tools/qml2puppet/qml2puppet/runner/appmetadata.h b/src/tools/qml2puppet/qml2puppet/runner/appmetadata.h new file mode 100644 index 00000000000..552c1334398 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/appmetadata.h @@ -0,0 +1,99 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH +// Qt-GPL-exception-1.0 +#pragma once + +#include +#include + +#include + +// Common functions can be used in all QDS apps +namespace QDSMeta { + +namespace Logging { +inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); +inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); +inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +inline void registerMessageHandler() +{ + qInstallMessageHandler( + [](QtMsgType type, const QMessageLogContext &context, const QString &msg) { + auto tPrinter = [&](const QString &msgPrefix) { + fprintf(stderr, + "%s: %s (%s:%u, %s)\n", + msgPrefix.toLocal8Bit().constData(), + msg.toLocal8Bit().constData(), + context.file, + context.line, + context.function); + }; + + if (type == QtDebugMsg) + tPrinter("Debug"); + else if (type == QtInfoMsg) + tPrinter("Info"); + else if (type == QtWarningMsg) + tPrinter("Warning"); + else if (type == QtCriticalMsg) + tPrinter("Critical"); + else if (type == QtFatalMsg) { + tPrinter("Fatal"); + abort(); + } + }); +} +#endif + +} // namespace Logging + +namespace AppInfo { + +#define STRINGIFY_INTERNAL(x) #x +#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x) + +inline void printAppInfo() +{ + qInfo() << Qt::endl + << "<< QDS Meta Info >>" << Qt::endl + << "App Info" << Qt::endl + << " - Name :" << Core::Constants::IDE_ID << Qt::endl + << " - Version :" << Core::Constants::IDE_VERSION_DISPLAY << Qt::endl + << " - Author :" << Core::Constants::IDE_AUTHOR << Qt::endl + << " - Year :" << Core::Constants::IDE_YEAR << Qt::endl + << " - App :" << QCoreApplication::applicationName() << Qt::endl + << "Build Info " << Qt::endl + << " - Date :" << __DATE__ << Qt::endl + << " - Commit :" << QStringLiteral(QDS_STRINGIFY(GIT_SHA)) << Qt::endl + << " - Qt Version :" << QT_VERSION_STR << Qt::endl + << "Compiler Info " << Qt::endl +#if defined(__GNUC__) + << " - GCC :" << __GNUC__ << Qt::endl + << " - GCC Minor :" << __GNUC_MINOR__ << Qt::endl + << " - GCC Patch :" << __GNUC_PATCHLEVEL__ << Qt::endl +#endif +#if defined(_MSC_VER) + << " - MSC Short :" << _MSC_VER << Qt::endl + << " - MSC Full :" << _MSC_FULL_VER << Qt::endl +#endif +#if defined(__clang__) + << " - clang maj :" << __clang_major__ << Qt::endl + << " - clang min :" << __clang_minor__ << Qt::endl + << " - clang patch :" << __clang_patchlevel__ << Qt::endl +#endif + << "<< End Of QDS Meta Info >>" << Qt::endl; + exit(0); +} + +inline void registerAppInfo(const QString &appName) +{ + QCoreApplication::setOrganizationName(Core::Constants::IDE_AUTHOR); + QCoreApplication::setOrganizationDomain("qt-project.org"); + QCoreApplication::setApplicationName(appName); + QCoreApplication::setApplicationVersion(Core::Constants::IDE_VERSION_LONG); +} + +} // namespace AppInfo +} // namespace QDSMeta diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h new file mode 100644 index 00000000000..39b45e5f1c3 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h @@ -0,0 +1,68 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#ifdef Q_OS_WIN +#include +#endif + +#define START_CRASHPAD +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +startCrashpad() +#endif + +#ifdef ENABLE_QT_BREAKPAD +#include +#endif + +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +#define NOMINMAX +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "client/settings.h" +#endif + +namespace { +#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) + bool startCrashpad() + { + using namespace crashpad; + + // Cache directory that will store crashpad information and minidumps + base::FilePath database(L"crashpad_reports"); + base::FilePath handler(L"crashpad_handler.exe"); + + // URL used to submit minidumps to + std::string url(CRASHPAD_BACKEND_URL); + + // Optional annotations passed via --annotations to the handler + std::map annotations; + annotations["qt-version"] = QT_VERSION_STR; + + // Optional arguments to pass to the handler + std::vector arguments; + arguments.push_back("--no-rate-limit"); + + CrashpadClient *client = new CrashpadClient(); + bool success = client->StartHandler(handler, + database, + database, + url, + annotations, + arguments, + /* restartable */ true, + /* asynchronous_start */ true); + // TODO: research using this method, should avoid creating a separate CrashpadClient for the + // puppet (needed only on windows according to docs). + // client->SetHandlerIPCPipe(L"\\\\.\\pipe\\qml2puppet"); + + return success; + } + +#ifdef ENABLE_QT_BREAKPAD + const QString libexecPath = QCoreApplication::applicationDirPath() + '/' + + RELATIVE_LIBEXEC_PATH; + QtSystemExceptionHandler systemExceptionHandler(libexecPath); +#endif +#endif +} diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp new file mode 100644 index 00000000000..45d29038b1e --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp @@ -0,0 +1,126 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qmlpuppet.h" + +#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER +#include +#endif + +#include +#include +#include + +#include "qml2puppet/iconrenderer/iconrenderer.h" +#include "qml2puppet/import3d/import3d.h" + +#include "configcrashpad.h" + +#include + +void QmlPuppet::initCoreApp() +{ + // Since we always render text into an FBO, we need to globally disable + // subpixel antialiasing and instead use gray. + qputenv("QSG_DISTANCEFIELD_ANTIALIASING", "gray"); +#ifdef Q_OS_MACOS + qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "true"); +#endif +#ifdef MULTILANGUAGE_TRANSLATIONPROVIDER + Sqlite::LibraryInitializer::initialize(); +#endif + + //If a style different from Desktop is set we have to use QGuiApplication + bool useGuiApplication = (!qEnvironmentVariableIsSet("QMLDESIGNER_FORCE_QAPPLICATION") + || qgetenv("QMLDESIGNER_FORCE_QAPPLICATION") != "true") + && qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE") + && qgetenv("QT_QUICK_CONTROLS_STYLE") != "Desktop"; +#ifndef QT_GUI_LIB + createCoreApp(); +#else +#if defined QT_WIDGETS_LIB + if (!useGuiApplication) + createCoreApp(); + else +#endif //QT_WIDGETS_LIB + createCoreApp(); +#endif //QT_GUI_LIB +} + +int QmlPuppet::startTestMode() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nItem {\n}\n", QUrl::fromLocalFile("test.qml")); + + if (!QSharedPointer(component.create())) { + qDebug() << "Basic QtQuick 2.0 not working..."; + qDebug() << component.errorString(); + return -1; + } + + qDebug() << "Basic QtQuick 2.0 working..."; + return 0; +} + +void QmlPuppet::populateParser() +{ + // we're not using the commandline parser but just populating the help text + m_argParser.addOptions( + {{"readcapturedstream", "Read captured stream.", "inputStream, [outputStream]"}, + {"rendericon", "Renders icon.", "size, fileName, sourceQml"}, + {"import3dAsset", "Import 3d asset.", "sourceAsset, outDir, importOptJson"}}); +} + +void QmlPuppet::initQmlRunner() +{ + if (m_coreApp->arguments().count() < 2 + || (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 3) + || (m_argParser.isSet("rendericon") && m_coreApp->arguments().count() < 5) + || (m_argParser.isSet("import3dAsset") && m_coreApp->arguments().count() < 6) + || (!m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() < 4)) { + qDebug() << "Wrong argument count: " << m_coreApp->arguments().count(); + m_argParser.showHelp(1); + } + + if (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() > 2) { + QString fileName = m_argParser.value("readcapturedstream"); + if (!QFile::exists(fileName)) { + qDebug() << "Input stream does not exist:" << fileName; + exit(-1); + } + + if (m_coreApp->arguments().count() > 3) { + fileName = m_coreApp->arguments().at(3); + if (!QFile::exists(fileName)) { + qDebug() << "Output stream does not exist:" << fileName; + exit(-1); + } + } + } + + if (m_argParser.isSet("rendericon")) { + int size = m_coreApp->arguments().at(2).toInt(); + QString iconFileName = m_coreApp->arguments().at(3); + QString iconSource = m_coreApp->arguments().at(4); + + m_iconRenderer.reset(new IconRenderer(size, iconFileName, iconSource)); + m_iconRenderer->setupRender(); + } else if (m_argParser.isSet("import3dAsset")) { + QString sourceAsset = m_coreApp->arguments().at(2); + QString outDir = m_coreApp->arguments().at(3); + QString options = m_coreApp->arguments().at(4); + + Import3D::import3D(sourceAsset, outDir, options); + } + + START_CRASHPAD; + new QmlDesigner::Qt5NodeInstanceClientProxy(m_coreApp.get()); + +#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG) + SetErrorMode(SEM_NOGPFAULTERRORBOX); //We do not want to see any message boxes +#endif + + if (m_argParser.isSet("readcapturedstream")) + exit(0); +} diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h new file mode 100644 index 00000000000..66838164cbb --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h @@ -0,0 +1,21 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../qmlbase.h" + +class IconRenderer; +class QmlPuppet : public QmlBase +{ + using QmlBase::QmlBase; + +private: + void initCoreApp() override; + void populateParser() override; + int startTestMode() override; + void initQmlRunner() override; + +private: + QSharedPointer m_iconRenderer; +}; diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlbase.h b/src/tools/qml2puppet/qml2puppet/runner/qmlbase.h new file mode 100644 index 00000000000..9bc41bef752 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/qmlbase.h @@ -0,0 +1,111 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH +// Qt-GPL-exception-1.0 + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "appmetadata.h" +#include + +#include +class QmlBase : public QObject +{ + Q_OBJECT +public: + struct AppArgs + { + public: + int argc; + char **argv; + }; + + QmlBase(int &argc, char **argv, QObject *parent = nullptr) + : QObject{parent} + , m_args({argc, argv}) + { + m_argParser.setApplicationDescription("QML Runtime Provider for QDS"); + m_argParser.addOptions( + {{"qml-puppet", "Run QML Puppet (default)"}, + {"qml-runtime", "Run QML Runtime"}, + {"appinfo", "Print build information"}, + {"test", "Run test mode"} + }); + } + + int run() + { + populateParser(); + initCoreApp(); + initParser(); + initQmlRunner(); + return m_coreApp->exec(); + } + + QSharedPointer coreApp() const { return m_coreApp; } + +protected: + virtual void initCoreApp() = 0; + virtual void populateParser() = 0; + virtual void initQmlRunner() = 0; + + virtual int startTestMode() + { + qDebug() << "Test mode is not implemented for this type of runner"; + return 0; + } + + template + void createCoreApp() + { + m_coreApp.reset(new T(m_args.argc, m_args.argv)); + } + + QSharedPointer m_coreApp; + QCommandLineParser m_argParser; + QSharedPointer m_qmlEngine; + + AppArgs m_args; + +private: + void initParser() + { + QCommandLineOption optHelp = m_argParser.addHelpOption(); + QCommandLineOption optVers = m_argParser.addVersionOption(); + + if (!m_coreApp) { + qCritical() << "Cannot initialize coreapp!"; + m_argParser.showHelp(); + } + + if (!m_argParser.parse(m_coreApp->arguments())) { + std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl + << std::endl; + m_argParser.showHelp(1); + } else if (m_argParser.isSet(optVers)) { + m_argParser.showVersion(); + } else if (m_argParser.isSet(optHelp)) { + m_argParser.showHelp(0); + } else if (m_argParser.isSet("appinfo")) { + QDSMeta::AppInfo::printAppInfo(); + } else if (m_argParser.isSet("test")) { + exit(startTestMode()); + } + } +}; diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h b/src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h new file mode 100644 index 00000000000..737557a45cc --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h @@ -0,0 +1,97 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmlconfiguration.h" + +#include "QtQml/qqmlcomponent.h" +#include +#include + +//// Listens to the appEngine signals to determine if all files failed to load +class LoadWatcher : public QObject +{ + // Q_OBJECT +public: + LoadWatcher(QQmlApplicationEngine *e, int expected, Config *conf) + : QObject(e) + , qae(e) + , conf(conf) + , expectedFileCount(expected) + { + connect(e, &QQmlApplicationEngine::objectCreated, this, &LoadWatcher::checkFinished); + // QQmlApplicationEngine also connects quit() to QCoreApplication::quit + // and exit() to QCoreApplication::exit but if called before exec() + // then QCoreApplication::quit or QCoreApplication::exit does nothing + connect(e, &QQmlEngine::quit, this, &LoadWatcher::quit); + connect(e, &QQmlEngine::exit, this, &LoadWatcher::exit); + } + + int returnCode = 0; + bool earlyExit = false; + +public Q_SLOTS: + void checkFinished(QObject *o, const QUrl &url) + { + Q_UNUSED(url); + if (o) { + checkForWindow(o); + if (conf && qae) { + for (PartialScene *ps : std::as_const(conf->completers)) { + if (o->inherits(ps->itemType().toUtf8().constData())) + contain(o, ps->container()); + } + } + } + if (haveWindow) + return; + + if (!--expectedFileCount) { + printf("qml: Did not load any objects, exiting.\n"); + exit(2); + QCoreApplication::exit(2); + } + } + + void quit() + { + // Will be checked before calling exec() + earlyExit = true; + returnCode = 0; + } + void exit(int retCode) + { + earlyExit = true; + returnCode = retCode; + } + +private: + void contain(QObject *o, const QUrl &containPath) + { + QQmlComponent c(qae, containPath); + QObject *o2 = c.create(); + if (!o2) + return; + o2->setParent(this); + checkForWindow(o2); + bool success = false; + int idx; + if ((idx = o2->metaObject()->indexOfProperty("containedObject")) != -1) + success = o2->metaObject()->property(idx).write(o2, QVariant::fromValue(o)); + if (!success) + o->setParent(o2); // Set QObject parent, and assume container will react as needed + } + void checkForWindow(QObject *o) + { + if (o->isWindowType() && o->inherits("QQuickWindow")) + haveWindow = true; + } + +private: + QQmlApplicationEngine *qae; + Config *conf; + + bool haveWindow = false; + int expectedFileCount; +}; diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h new file mode 100644 index 00000000000..7563d7ce781 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h @@ -0,0 +1,66 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +class PartialScene : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl container READ container WRITE setContainer NOTIFY containerChanged) + Q_PROPERTY(QString itemType READ itemType WRITE setItemType NOTIFY itemTypeChanged) + QML_ELEMENT + QML_ADDED_IN_VERSION(1, 0) +public: + PartialScene(QObject *parent = nullptr) + : QObject(parent) + {} + + const QUrl container() const { return m_container; } + const QString itemType() const { return m_itemType; } + + void setContainer(const QUrl &a) + { + if (a == m_container) + return; + m_container = a; + emit containerChanged(); + } + void setItemType(const QString &a) + { + if (a == m_itemType) + return; + m_itemType = a; + emit itemTypeChanged(); + } + +signals: + void containerChanged(); + void itemTypeChanged(); + +private: + QUrl m_container; + QString m_itemType; +}; + +class Config : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty sceneCompleters READ sceneCompleters) + Q_CLASSINFO("DefaultProperty", "sceneCompleters") + QML_NAMED_ELEMENT(Configuration) + QML_ADDED_IN_VERSION(1, 0) +public: + Config(QObject *parent = nullptr) + : QObject(parent) + {} + + QQmlListProperty sceneCompleters() + { + return QQmlListProperty(this, &completers); + } + + QList completers; +}; diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp new file mode 100644 index 00000000000..e3e962483f8 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp @@ -0,0 +1,348 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include + +#include "loadwatcher.h" +#include "qmlruntime.h" + +#include +#include +#if QT_CONFIG(qml_animation) +#include +#endif + +#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms +#define QSL QStringLiteral + +void QmlRuntime::populateParser() +{ + m_argParser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + m_argParser.setOptionsAfterPositionalArgumentsMode( + QCommandLineParser::ParseAsPositionalArguments); + + m_argParser.addOptions( + {{QStringList() << QSL("a") << QSL("apptype"), + QSL("Select which application class to use. Default is gui."), + QSL("core|gui|widget")}, // just for translation + + {QSL("I"), QSL("Prepend the given path to the import paths."), QSL("path")}, + + {QSL("f"), QSL("Load the given file as a QML file."), QSL("file")}, + + {QStringList() << QSL("c") << QSL("config"), + QSL("Load the given built-in configuration or configuration file."), + QSL("file")}, + + {QStringList() << QSL("list-conf"), QSL("List the built-in configurations.")}, + + {QSL("translation"), QSL("Load the given file as the translations file."), QSL("file")}, + +#ifdef QT_GUI_LIB + // OpenGL options + {QSL("desktop"), QSL("Force use of desktop OpenGL (AA_UseDesktopOpenGL).")}, + + {QSL("gles"), QSL("Force use of GLES (AA_UseOpenGLES).")}, + + {QSL("software"), QSL("Force use of software rendering (AA_UseSoftwareOpenGL).")}, + + {QSL("core-profile"), QSL("Force use of OpenGL Core Profile.")}, + + {QSL("disable-context-sharing"), + QSL("Disable the use of a shared GL context for QtQuick Windows")}, +#endif // QT_GUI_LIB + + // Debugging and verbosity options + {QSL("quiet"), QSL("Suppress all output.")}, + + {QSL("verbose"), + QSL("Print information about what qml is doing, like specific file URLs being loaded.")}, + + {QSL("slow-animations"), QSL("Run all animations in slow motion.")}, + + {QSL("fixed-animations"), QSL("Run animations off animation tick rather than wall time.")}, + + {QStringList() << QSL("r") << QSL("rhi"), + QSL("Set the backend for the Qt graphics abstraction (RHI). " + "Backend is one of: default, vulkan, metal, d3d11, gl"), + QSL("backend")}, + + {QSL("S"), QSL("Add selector to the list of QQmlFileSelectors."), QSL("selector")}}); + + // Positional arguments + m_argParser.addPositionalArgument( + "files", + QSL("Any number of QML files can be loaded. They will share the same engine."), + "[files...]"); + m_argParser.addPositionalArgument("args", + QSL("Arguments after '--' are ignored, but passed through to " + "the application.arguments variable in QML."), + "[-- args...]"); +} + +void QmlRuntime::initCoreApp() +{ + bool glShareContexts = true; + + // these attributes must be set before the QCoreApp is initialized + for (int i = 0; i < m_args.argc; i++) { + if (!strcmp(m_args.argv[i], "-desktop") || !strcmp(m_args.argv[i], "--desktop")) { + QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + } else if (!strcmp(m_args.argv[i], "-gles") || !strcmp(m_args.argv[i], "--gles")) { + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); + } else if (!strcmp(m_args.argv[i], "-software") || !strcmp(m_args.argv[i], "--software")) { + QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + } else if (!strcmp(m_args.argv[i], "-disable-context-sharing") + || !strcmp(m_args.argv[i], "--disable-context-sharing")) { + glShareContexts = false; + } + } + + if (glShareContexts) + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + + // since we handled all attributes above, now we can initialize the core app + for (int i = 0; i < m_args.argc; i++) { + if (!strcmp(m_args.argv[i], "--apptype") || !strcmp(m_args.argv[i], "-a") + || !strcmp(m_args.argv[i], "-apptype")) { + if (i + 1 < m_args.argc) { + ++i; + if (!strcmp(m_args.argv[i], "core")) { + createCoreApp(); + } + else if (!strcmp(m_args.argv[i], "gui")) { + createCoreApp(); + } +#ifdef QT_WIDGETS_LIB + else if (!strcmp(m_args.argv[i], "widget")) { + createCoreApp(); + static_cast(m_coreApp.get()) + ->setWindowIcon(QIcon(m_iconResourcePath)); + } +#endif // QT_WIDGETS_LIB + } + } + } +} + +void QmlRuntime::initQmlRunner() +{ + m_qmlEngine.reset(new QQmlApplicationEngine()); + + QStringList files; + QString confFile; + QString translationFile; + + if (!m_argParser.parse(QCoreApplication::arguments())) { + qWarning() << m_argParser.errorText(); + exit(1); + } + + // TODO: replace below logging modes with a proper logging category + m_verboseMode = m_argParser.isSet("verbose"); + m_quietMode = (!m_verboseMode && m_argParser.isSet("quiet")); + // FIXME: need to re-evaluate. we have our own message handler. + // if (quietMode) { + // qInstallMessageHandler(quietMessageHandler); + // QLoggingCategory::setFilterRules(QStringLiteral("*=false")); + // } + + if (m_argParser.isSet("list-conf")) { + listConfFiles(); + exit(0); + } + +#if QT_CONFIG(qml_animation) + if (m_argParser.isSet("slow-animations")) + QUnifiedTimer::instance()->setSlowModeEnabled(true); + if (m_argParser.isSet("fixed-animations")) + QUnifiedTimer::instance()->setConsistentTiming(true); +#endif + const auto valsImportPath = m_argParser.values("I"); + for (const QString &importPath : valsImportPath) + m_qmlEngine->addImportPath(importPath); + + QStringList customSelectors; + + const auto valsSelectors = m_argParser.values("S"); + for (const QString &selector : valsSelectors) + customSelectors.append(selector); + + if (!customSelectors.isEmpty()) + m_qmlEngine->setExtraFileSelectors(customSelectors); + + if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE") + || qEnvironmentVariableIsSet("QML_CORE_PROFILE") || m_argParser.isSet("core-profile")) { + QSurfaceFormat surfaceFormat; + surfaceFormat.setStencilBufferSize(8); + surfaceFormat.setDepthBufferSize(24); + surfaceFormat.setVersion(4, 1); + surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); + QSurfaceFormat::setDefaultFormat(surfaceFormat); + } + + if (m_argParser.isSet("config")) + confFile = m_argParser.value("config"); + if (m_argParser.isSet("translation")) + translationFile = m_argParser.value("translation"); + if (m_argParser.isSet("rhi")) { + const QString rhiBackend = m_argParser.value("rhi"); + if (rhiBackend == QLatin1String("default")) + qunsetenv("QSG_RHI_BACKEND"); + else + qputenv("QSG_RHI_BACKEND", rhiBackend.toLatin1()); + } + + const auto valsPosArgs = m_argParser.positionalArguments(); + files << m_argParser.values("f"); + for (const QString &posArg : valsPosArgs) { + if (posArg == QLatin1String("--")) + break; + else + files << posArg; + } + +#if QT_CONFIG(translation) + // Need to be installed before QQmlApplicationEngine's automatic translation loading + // (qt_ translations are loaded there) + if (!translationFile.isEmpty()) { + QTranslator translator; + + if (translator.load(translationFile)) { + m_coreApp->installTranslator(&translator); + if (m_verboseMode) + qInfo() << "qml: Loaded translation file %s\n", + qPrintable(QDir::toNativeSeparators(translationFile)); + } else { + if (!m_quietMode) + qInfo() << "qml: Could not load the translation file %s\n", + qPrintable(QDir::toNativeSeparators(translationFile)); + } + } +#else + if (!translationFile.isEmpty() && !quietMode) + qInfo() << "qml: Translation file specified, but Qt built without translation support.\n"); +#endif + + if (files.size() <= 0) { +#if defined(Q_OS_DARWIN) + if (qobject_cast(m_coreApp.data())) { + m_exitTimerId = static_cast(m_coreApp.get()) + ->startTimer(FILE_OPEN_EVENT_WAIT_TIME); + } else +#endif + { + if (!m_quietMode) + qCritical() << "No files specified. Terminating.\n"; + exit(1); + } + } + + loadConf(confFile, !m_verboseMode); + + // Load files + QScopedPointer lw(new LoadWatcher(m_qmlEngine.data(), files.size(), m_conf.data())); + + for (const QString &path : std::as_const(files)) { + QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile); + if (m_verboseMode) + qInfo() << "qml: loading %s\n", qPrintable(url.toString()); + m_qmlEngine->load(url); + } + + if (lw->earlyExit) + exit(lw->returnCode); +} + +void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app on failure +{ + const QString defaultFileName = QLatin1String("default.qml"); + QUrl settingsUrl; + bool builtIn = false; //just for keeping track of the warning + if (override.isEmpty()) { + QFileInfo fi; + fi.setFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, defaultFileName)); + if (fi.exists()) { + settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); + } else { + // If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path + fi.setFile(m_confResourcePath + defaultFileName); + settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); + builtIn = true; + } + } else { + QFileInfo fi; + fi.setFile(m_confResourcePath + override + QLatin1String(".qml")); + if (fi.exists()) { + settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); + builtIn = true; + } else { + fi.setFile(QDir(QStandardPaths::locate(QStandardPaths::AppConfigLocation, + override, + QStandardPaths::LocateDirectory)), + m_confResourcePath); + if (fi.exists()) + settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); + else + fi.setFile(override); + if (!fi.exists()) { + qCritical() << "qml: Couldn't find required configuration file: %s\n", + qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())); + exit(1); + } + settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); + } + } + + if (!quiet) { + qInfo() << "qml: %s\n", QLibraryInfo::build(); + if (builtIn) { + qInfo() << "qml: Using built-in configuration: %s\n", + qPrintable(override.isEmpty() ? defaultFileName : override); + } else { + qInfo() << "qml: Using configuration: %s\n", + qPrintable(settingsUrl.isLocalFile() + ? QDir::toNativeSeparators(settingsUrl.toLocalFile()) + : settingsUrl.toString()); + } + } + + // TODO: When we have better engine control, ban QtQuick* imports on this engine + QQmlEngine e2; + QQmlComponent c2(&e2, settingsUrl); + m_conf.reset(qobject_cast(c2.create())); + + if (!m_conf) { + qCritical() << "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString()); + exit(1); + } +} + +void QmlRuntime::listConfFiles() +{ + const QDir confResourceDir(m_confResourcePath); + qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")); + for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files)) + qInfo() << " %s\n", qPrintable(fi.baseName()); + qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")); + bool foundOther = false; + const QStringList otherLocations = QStandardPaths::standardLocations( + QStandardPaths::AppConfigLocation); + for (const auto &confDirPath : otherLocations) { + const QDir confDir(confDirPath); + for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { + foundOther = true; + if (m_verboseMode) + qInfo() << " %s\n", qPrintable(fi.absoluteFilePath()); + else + qInfo() << " %s\n", qPrintable(fi.baseName()); + } + } + if (!foundOther) + qInfo() << " %s\n", qPrintable(QCoreApplication::translate("main", "none")); + if (m_verboseMode) { + qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:")); + for (const auto &confDirPath : otherLocations) + qInfo() << " %s\n", qPrintable(confDirPath); + } +} diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h new file mode 100644 index 00000000000..c69481ca039 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h @@ -0,0 +1,30 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../qmlbase.h" +#include "qmlconfiguration.h" + +class QmlRuntime : public QmlBase +{ + using QmlBase::QmlBase; + +private: + void initCoreApp() override; + void populateParser() override; + void initQmlRunner() override; + + void listConfFiles(); + void loadConf(const QString &override, bool quiet); + + const QString m_iconResourcePath = QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png"); + const QString m_confResourcePath = QStringLiteral(":/runner/runnerconf/qmlruntime/"); + + + QSharedPointer m_conf; + bool m_verboseMode = false; + bool m_quietMode = false; + int m_exitTimerId = -1; +}; + diff --git a/src/tools/qml2puppet/qmlpuppet.qrc b/src/tools/qml2puppet/qmlpuppet.qrc index 162c955ac14..7ec26950034 100644 --- a/src/tools/qml2puppet/qmlpuppet.qrc +++ b/src/tools/qml2puppet/qmlpuppet.qrc @@ -12,4 +12,9 @@ mockfiles/ToolBarButton.qml mockfiles/ToggleButton.qml + + runnerconf/qmlruntime/default.qml + runnerconf/qmlruntime/content/resizeItemToWindow.qml + runnerconf/qmlruntime/content/resizeWindowToItem.qml + diff --git a/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml new file mode 100644 index 00000000000..ca4618ba734 --- /dev/null +++ b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeItemToWindow.qml @@ -0,0 +1,25 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick.Window 2.0 +import QtQuick 2.0 + +Window { + property Item containedObject: null + property bool __resizeGuard: false + onContainedObjectChanged: { + if (containedObject == undefined || containedObject == null) { + visible = false + return + } + __resizeGuard = true + width = containedObject.width + height = containedObject.height + containedObject.parent = contentItem + visible = true + __resizeGuard = false + } + onWidthChanged: if (!__resizeGuard && containedObject) + containedObject.width = width + onHeightChanged: if (!__resizeGuard && containedObject) + containedObject.height = height +} diff --git a/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeWindowToItem.qml b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeWindowToItem.qml new file mode 100644 index 00000000000..30029bf973f --- /dev/null +++ b/src/tools/qml2puppet/runnerconf/qmlruntime/content/resizeWindowToItem.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick.Window 2.0 +import QtQuick 2.0 + +Window { + property Item containedObject: null + onContainedObjectChanged: { + if (containedObject == undefined || containedObject == null) { + visible = false + return + } + width = Qt.binding(function () { + return containedObject.width + }) + height = Qt.binding(function () { + return containedObject.height + }) + containedObject.parent = contentItem + visible = true + } +} diff --git a/src/tools/qml2puppet/runnerconf/qmlruntime/default.qml b/src/tools/qml2puppet/runnerconf/qmlruntime/default.qml new file mode 100644 index 00000000000..961f2a890bd --- /dev/null +++ b/src/tools/qml2puppet/runnerconf/qmlruntime/default.qml @@ -0,0 +1,10 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QmlRuntime.QmlConfiguration 1.0 + +Configuration { + PartialScene { + itemType: "QQuickItem" + container: Qt.resolvedUrl("content/resizeItemToWindow.qml") + } +} From f9ecc8b57ae9c09dcb57085174f66ac6897b6efa Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 15 Dec 2022 17:32:00 +0200 Subject: [PATCH 13/37] QmlDesigner: Ensure all View3Ds are rendered when one is If View3Ds share resources such as materials, changing such shared resource doesn't in all cases make all using View3Ds dirty until one of the views is rendered. For purposes of rendering 2D view, this is too late, as we determine the list of items to render before any is rendered. Fixed the issue by ensuring all View3D items in the scene are added to the list of items to render if any of them are added to that list. Fixes: QDS-8346 Change-Id: Ib18ce0d37978857efdb67e15976e1fecdc1b6c96 Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Thomas Hartmann --- .../instances/nodeinstanceserver.cpp | 15 ++++++++++++ .../qml2puppet/instances/nodeinstanceserver.h | 1 + .../instances/qt5rendernodeinstanceserver.cpp | 24 +++++++++++++++---- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp index d3209e4ccbe..05692d000bd 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1043,6 +1043,21 @@ QList NodeInstanceServer::allGroupStateInstances() const return groups; } +QList NodeInstanceServer::allView3DInstances() const +{ + QList view3Ds; + std::copy_if(nodeInstances().cbegin(), + nodeInstances().cend(), + std::back_inserter(view3Ds), + [](const ServerNodeInstance &instance) { + return instance.isValid() + && ServerNodeInstance::isSubclassOf(instance.internalObject(), + QByteArrayLiteral("QQuick3DViewport")); + }); + + return view3Ds; +} + void NodeInstanceServer::setStateInstance(const ServerNodeInstance &stateInstance) { m_activeStateInstance = stateInstance; diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h index 59236a236fc..70b0c841859 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h @@ -179,6 +179,7 @@ public: ServerNodeInstance rootNodeInstance() const; QList allGroupStateInstances() const; + QList allView3DInstances() const; void notifyPropertyChange(qint32 instanceid, const PropertyName &propertyName); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 07212ec4590..223a7249b1e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -56,6 +56,7 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() if (quickWindow() && nodeInstanceClient()->bytesToWrite() < 10000) { bool windowDirty = false; + bool hasView3D = false; foreach (QQuickItem *item, allItems()) { if (item) { if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { @@ -65,8 +66,13 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() } } else { if (hasInstanceForObject(item)) { - if (QQuickDesignerSupport::isDirty(item, QQuickDesignerSupport::ContentUpdateMask)) + if (QQuickDesignerSupport::isDirty(item, QQuickDesignerSupport::ContentUpdateMask)) { + if (!hasView3D && ServerNodeInstance::isSubclassOf( + item, QByteArrayLiteral("QQuick3DViewport"))) { + hasView3D = true; + } m_dirtyInstanceSet.insert(instanceForObject(item)); + } if (QQuickItem *effectParent = parentEffectItem(item)) { if ((QQuickDesignerSupport::isDirty( item, @@ -100,9 +106,19 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()})); } else { if (!m_dirtyInstanceSet.isEmpty()) { - nodeInstanceClient()->pixmapChanged( - createPixmapChangedCommand(QtHelpers::toList(m_dirtyInstanceSet))); - m_dirtyInstanceSet.clear(); + auto renderList = QtHelpers::toList(m_dirtyInstanceSet); + + // If there is a View3D to be rendered, add all other View3Ds to be rendered + // as well, in case they share materials. + if (hasView3D) { + const QList view3Ds = allView3DInstances(); + for (auto &view3D : view3Ds) { + if (!m_dirtyInstanceSet.contains(view3D)) + renderList.append(view3D); + } + } + + nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand(renderList)); } } From aaf2e6f0f780681a2f93108823fced585b7e66af Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Thu, 15 Dec 2022 17:50:23 +0200 Subject: [PATCH 14/37] QmlDesigner: Fix asset images becoming invalid after delete + recreate The problem was that, for "invalid assets" (i.e. pixmaps of assets that could not be read / did not exist on disk), the "default" thumbnail was saved in the cache -- and thus, when the asset re-appeared on disk, the "default" (invalid) thumbnail was shown. Task-number: QDS-8591 Change-Id: Iec2f3e704c1deef2e7a7c75532518639fdc677e5 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../assetslibrary/assetslibraryiconprovider.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index bc56e9b7f3f..e9d3b42855c 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -25,13 +25,17 @@ QPixmap AssetsLibraryIconProvider::requestPixmap(const QString &id, QSize *size, pixmap = m_thumbnails[id]; } else { pixmap = fetchPixmap(id, requestedSize); - if (pixmap.isNull()) + bool haveValidImage = true; + if (pixmap.isNull()) { pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png"); + haveValidImage = false; + } if (requestedSize.isValid()) pixmap = pixmap.scaled(requestedSize, Qt::KeepAspectRatio); - m_thumbnails[id] = pixmap; + if (haveValidImage) + m_thumbnails[id] = pixmap; } if (size) { From 0feeb37ef71d64e0848ed6e36854f019236ead74 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 14 Dec 2022 19:35:36 +0200 Subject: [PATCH 15/37] QmlDesigner: Fix textures created from assets can have invalid source When creating a texture from an image in the asset library, the "source" for the texture was wrong for images outside of the "content/images" directory of the project. Task-number: QDS-8535 Change-Id: I30a28a7cb63748ce0fb81396ca59c7ccc9e83340 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/createtexture.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index 91e40959806..a17a61e84ea 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -65,19 +65,27 @@ ModelNode CreateTexture::createTextureFromImage(const QString &assetPath, AddTex NodeMetaInfo metaInfo = m_view->model()->qtQuick3DTextureMetaInfo(); - Utils::FilePath imagePath = ModelNodeOperations::getImagesDefaultDirectory() - .pathAppended(Utils::FilePath::fromString(assetPath).fileName()); - QString sourceVal = imagePath.relativePathFrom( - QmlDesigner::DocumentManager::currentFilePath()).toString(); + Utils::FilePath currentDocumentPath = QmlDesigner::DocumentManager::currentFilePath(); + Utils::FilePath imageTargetPath; + if (m_importFile) { + QString assetName = Utils::FilePath::fromString(assetPath).fileName(); + // if the asset had to be imported from somewhere else, then assetPath is the source where + // the asset was taken from, and we have to compute where it was placed in the project. + imageTargetPath = ModelNodeOperations::getImagesDefaultDirectory().pathAppended(assetName); + } else { + imageTargetPath = Utils::FilePath::fromString(assetPath); + } - ModelNode newTexNode = m_view->getTextureDefaultInstance(sourceVal); + QString textureSource = imageTargetPath.relativePathFrom(currentDocumentPath).toString(); + + ModelNode newTexNode = m_view->getTextureDefaultInstance(textureSource); if (!newTexNode.isValid()) { newTexNode = m_view->createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(), metaInfo.minorVersion()); newTexNode.validId(); VariantProperty sourceProp = newTexNode.variantProperty("source"); - sourceProp.setValue(sourceVal); + sourceProp.setValue(textureSource); matLib.defaultNodeListProperty().reparentHere(newTexNode); } From bb37d782ca9fa4ccb56957a6273822ae393fbebd Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 14 Dec 2022 18:39:29 +0200 Subject: [PATCH 16/37] QmlDesigner: Optimize requesting an image info for tooltips Avoid loading an image for the sole purpose of getting the dimensions. Also small relevant tweaks. Change-Id: I3d11175340cb77d3634fe7e69481ad26db8a74ef Reviewed-by: Samuel Ghinet Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../materialbrowsertexturesmodel.cpp | 22 ++++----- .../instances/nodeinstanceview.cpp | 31 ++++--------- src/plugins/qmldesigner/utils/imageutils.cpp | 46 +++++++++++++++++++ src/plugins/qmldesigner/utils/imageutils.h | 17 +++++++ 5 files changed, 81 insertions(+), 36 deletions(-) create mode 100644 src/plugins/qmldesigner/utils/imageutils.cpp create mode 100644 src/plugins/qmldesigner/utils/imageutils.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 9e040dba0e6..8018e76044d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -22,6 +22,7 @@ add_qtc_library(QmlDesignerUtils STATIC SOURCES designersettings.cpp designersettings.h hdrimage.cpp hdrimage.h + imageutils.cpp imageutils.h qmldesignerutils_global.h ) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index f348580937d..9972a446227 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -3,8 +3,8 @@ #include "materialbrowsertexturesmodel.h" -#include "designeractionmanager.h" #include "designmodewidget.h" +#include "imageutils.h" #include "qmldesignerplugin.h" #include "qmlobjectnode.h" #include "variantproperty.h" @@ -52,24 +52,18 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role) return m_textureList.at(index.row()).internalId(); if (role == RoleTexToolTip) { - QString source = QmlObjectNode(m_textureList.at(index.row())).modelValue("source").toString(); + QString source = data(index, RoleTexSource).toString(); // absolute path if (source.isEmpty()) return tr("Texture has no source image."); - const QString noData = tr("Texture has no data."); + ModelNode texNode = m_textureList.at(index.row()); + QString info = ImageUtils::imageInfo(source); - auto op = QmlDesignerPlugin::instance()->viewManager().designerActionManager() - .modelNodePreviewOperation(m_textureList.at(index.row())); - if (!op) - return noData; + if (info.isEmpty()) + return tr("Texture has no data."); - QVariantMap imgMap = op(m_textureList.at(index.row())).toMap(); - if (imgMap.isEmpty()) - return noData; - - return QLatin1String("%1\n%2\n%3").arg(imgMap.value("id").toString(), - source.split('/').last(), - imgMap.value("info").toString()); + QString sourceRelative = QmlObjectNode(texNode).modelValue("source").toString(); + return QLatin1String("%1\n%2\n%3").arg(texNode.id(), sourceRelative, info); } return {}; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index a1025cc9aa4..c715b659787 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -25,6 +25,7 @@ #include "createscenecommand.h" #include "debugoutputcommand.h" #include "informationchangedcommand.h" +#include "imageutils.h" #include "inputeventcommand.h" #include "nodeabstractproperty.h" #include "nodeinstanceserverproxy.h" @@ -83,6 +84,8 @@ #include #include +#include +#include #include #include #include @@ -135,13 +138,13 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager // related to a single event to be received before we act. m_resetTimer.setSingleShot(true); m_resetTimer.setInterval(100); - QObject::connect(&m_resetTimer, &QTimer::timeout, [this] { + QObject::connect(&m_resetTimer, &QTimer::timeout, this, [this] { if (isAttached()) resetPuppet(); }); m_updateWatcherTimer.setSingleShot(true); m_updateWatcherTimer.setInterval(100); - QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, [this] { + QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, this, [this] { for (const auto &path : std::as_const(m_pendingUpdateDirs)) updateWatcher(path); m_pendingUpdateDirs.clear(); @@ -152,11 +155,11 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager // unnecessary generation when project with multiple shaders is opened. m_generateQsbFilesTimer.setSingleShot(true); m_generateQsbFilesTimer.setInterval(100); - QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] { + QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, this, [this] { handleShaderChanges(); }); - connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, + connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path) { const QSet pendingDirs = m_pendingUpdateDirs; for (const auto &pendingPath : pendingDirs) { @@ -172,7 +175,7 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager m_updateWatcherTimer.start(); }); - connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) { + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) { if (m_qsbTargets.contains(path)) { m_qsbTargets.insert(path, true); m_generateQsbFilesTimer.start(); @@ -1910,23 +1913,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio); imageData.pixmap.setDevicePixelRatio(ratio); imageData.time = modified; - - double imgSize = double(imageFi.size()); - static QStringList units({::QmlDesigner::NodeInstanceView::tr("B"), - ::QmlDesigner::NodeInstanceView::tr("KB"), - ::QmlDesigner::NodeInstanceView::tr("MB"), - ::QmlDesigner::NodeInstanceView::tr("GB")}); - int unitIndex = 0; - while (imgSize > 1024. && unitIndex < units.size() - 1) { - ++unitIndex; - imgSize /= 1024.; - } - imageData.info = QStringLiteral("%1 x %2\n%3%4 (%5)") - .arg(originalPixmap.width()) - .arg(originalPixmap.height()) - .arg(QString::number(imgSize, 'g', 3)) - .arg(units[unitIndex]) - .arg(imageFi.suffix()); + imageData.info = ImageUtils::imageInfo(imageSource); m_imageDataMap.insert(imageData.id, imageData); } } diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp new file mode 100644 index 00000000000..c1e3b8f4950 --- /dev/null +++ b/src/plugins/qmldesigner/utils/imageutils.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2022 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 "imageutils.h" + +#include +#include +#include + +namespace QmlDesigner { + +QString QmlDesigner::ImageUtils::imageInfo(const QString &path) +{ + QFileInfo info(path); + if (!info.exists()) + return {}; + + int width = 0; + int height = 0; + if (info.suffix() == "hdr") { + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return {}; + + while (!file.atEnd()) { + QByteArray line = file.readLine(); + if (sscanf(line.constData(), "-Y %d +X %d", &height, &width)) + break; + } + } else { + QSize size = QImageReader(path).size(); + width = size.width(); + height = size.height(); + } + + if (width == 0 && height == 0) + return {}; + + return QLatin1String("%1 x %2\n%3 (%4)") + .arg(QString::number(width), + QString::number(height), + QLocale::system().formattedDataSize(info.size(), 2, QLocale::DataSizeTraditionalFormat), + info.suffix()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/imageutils.h b/src/plugins/qmldesigner/utils/imageutils.h new file mode 100644 index 00000000000..3b740b76b1e --- /dev/null +++ b/src/plugins/qmldesigner/utils/imageutils.h @@ -0,0 +1,17 @@ +// Copyright (C) 2022 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 + +namespace QmlDesigner { + +class ImageUtils +{ +public: + ImageUtils(); + + static QString imageInfo(const QString &path); +}; + +} // namespace QmlDesigner From 965fa9450c3e3424e5e9e4a1bec3612a66594059 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Thu, 15 Dec 2022 16:38:38 +0200 Subject: [PATCH 17/37] QmlDesigner: Fix cancel button not reverting background color In the 3D view, when the user experiments with different background colors, but then decides to cancel, upon pressing the Cancel button and closing the dialog, the last color picked remained set. This happened on Qt6.4 and not on Qt6.3. The reason for this was the usage of Array.isArray() on an object exposed from C++ to QML of type QVariant that could hold either a QList or a a QColor. It looks like there is no supported way from qml/js to see if a variable is a list / sequence or a scalar. So the best way would be to always work with QList even though in most cases only one single QColor is being used. Task-number: QDS-8143 Change-Id: Ia7e8e1facad24439ad244305c213bb12e286105b Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../edit3d/backgroundcolorselection.cpp | 8 +++---- .../components/edit3d/edit3dview.cpp | 8 +++---- .../components/edit3d/edit3dviewconfig.h | 21 ++++-------------- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 22 +++++++------------ .../qt5informationnodeinstanceserver.cpp | 7 +++--- 5 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp index 07d65cb3c33..42f5bc45dd5 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -44,7 +44,7 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, [actionType, view](const QColor &color) { - Edit3DViewConfig::setColor(view, actionType, color); + Edit3DViewConfig::setColors(view, actionType, {color}); }); QObject::connect(dialog, &QColorDialog::colorSelected, dialog, @@ -52,13 +52,13 @@ QColorDialog *BackgroundColorSelection::createColorDialog(QWidget *parent, if (colorSelected) colorSelected(); - Edit3DViewConfig::saveColor(key, color); + Edit3DViewConfig::saveColors(key, {color}); }); - if (Edit3DViewConfig::isColorValid(oldColorConfig)) { + if (Edit3DViewConfig::colorsValid(oldColorConfig)) { QObject::connect(dialog, &QColorDialog::rejected, dialog, [actionType, oldColorConfig, view]() { - Edit3DViewConfig::setColor(view, actionType, oldColorConfig); + Edit3DViewConfig::setColors(view, actionType, oldColorConfig); }); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index b7efef60d6a..75c2138ffc0 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -389,12 +389,12 @@ Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAct auto operation = [this, syncBackgroundColorAction](const SelectionContext &) { QList bgColors = {QRgb(0x222222), QRgb(0x999999)}; - Edit3DViewConfig::setColor(this, View3DActionType::SelectBackgroundColor, bgColors); - Edit3DViewConfig::saveColor(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors); + Edit3DViewConfig::setColors(this, View3DActionType::SelectBackgroundColor, bgColors); + Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, bgColors); QColor gridColor{0xaaaaaa}; - Edit3DViewConfig::setColor(this, View3DActionType::SelectGridColor, gridColor); - Edit3DViewConfig::saveColor(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, gridColor); + Edit3DViewConfig::setColors(this, View3DActionType::SelectGridColor, {gridColor}); + Edit3DViewConfig::saveColors(DesignerSettingsKey::EDIT3DVIEW_GRID_COLOR, {gridColor}); if (syncBackgroundColorAction->isChecked()) { Edit3DViewConfig::set(this, View3DActionType::SyncBackgroundColor, false); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h index 267e27042c9..e0eba1115c9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dviewconfig.h @@ -28,17 +28,9 @@ public: }); } - static void setColor(AbstractView *view, View3DActionType type, const QList &colorConfig) + static void setColors(AbstractView *view, View3DActionType type, const QList &colorConfig) { - if (colorConfig.size() == 1) - setColor(view, type, colorConfig.at(0)); - else - setVariant(view, type, QVariant::fromValue(colorConfig)); - } - - static void setColor(AbstractView *view, View3DActionType type, const QColor &color) - { - setVariant(view, type, QVariant::fromValue(color)); + setVariant(view, type, QVariant::fromValue(colorConfig)); } template @@ -47,7 +39,7 @@ public: setVariant(view, type, QVariant::fromValue(value)); } - static void saveColor(const QByteArray &key, const QList &colorConfig) + static void saveColors(const QByteArray &key, const QList &colorConfig) { QStringList colorNames = Utils::transform(colorConfig, [](const QColor &color) { return color.name(); @@ -56,12 +48,7 @@ public: saveVariant(key, QVariant::fromValue(colorNames)); } - static void saveColor(const QByteArray &key, const QColor &color) - { - saveVariant(key, QVariant::fromValue(color.name())); - } - - static bool isColorValid(const QList &colorConfig) { return !colorConfig.isEmpty(); } + static bool colorsValid(const QList &colorConfig) { return !colorConfig.isEmpty(); } private: static void setVariant(AbstractView *view, View3DActionType type, const QVariant &colorConfig) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 819f1e2be90..7f7b148460b 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -194,24 +194,18 @@ Item { function updateViewStates(viewStates) { if ("selectBackgroundColor" in viewStates) { - if (Array.isArray(viewStates.selectBackgroundColor)) { - var colors = viewStates.selectBackgroundColor - if (colors.length === 1) { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[0]; - } else { - backgroundGradientColorStart = colors[0]; - backgroundGradientColorEnd = colors[1]; - } + var colors = viewStates.selectBackgroundColor + if (colors.length === 1) { + backgroundGradientColorStart = colors[0]; + backgroundGradientColorEnd = colors[0]; } else { - var color = viewStates.selectBackgroundColor - backgroundGradientColorStart = color; - backgroundGradientColorEnd = color; + backgroundGradientColorStart = colors[0]; + backgroundGradientColorEnd = colors[1]; } } - if ("selectGridColor" in viewStates) - viewRoot.gridColor = viewStates.selectGridColor + if ("selectGridColor" in viewStates && viewStates.selectGridColor.length === 1) + viewRoot.gridColor = viewStates.selectGridColor[0] } // If resetToDefault is true, tool states not specifically set to anything will be reset to diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index dbbe90c2f35..c2ef586c031 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -921,9 +921,9 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D([[maybe_unu if (toolStates.contains("syncBackgroundColor")) { bool sync = toolStates["syncBackgroundColor"].toBool(); if (sync) { - QColor color = helper->sceneEnvironmentColor(sceneId); + QList colors = {helper->sceneEnvironmentColor(sceneId)}; View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, - QVariant::fromValue(color)); + QVariant::fromValue(colors)); view3DAction(cmd); } } @@ -2274,9 +2274,10 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa if (toolStates.contains("syncBackgroundColor")) { bool sync = toolStates["syncBackgroundColor"].toBool(); + QList colors = {color}; if (sync) { View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, - QVariant::fromValue(color)); + QVariant::fromValue(colors)); view3DAction(cmd); } } From cb187d79127c7293dab60c5609cbadc606d741e7 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 16 Dec 2022 12:19:19 +0100 Subject: [PATCH 18/37] UpdateInfo: Fix long "show details" list Task-number: QDS-7599 Change-Id: Ib3114b7284ae591e140d4e42739760fe7e13fae5 Reviewed-by: Eike Ziller --- src/plugins/updateinfo/updateinfoplugin.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index 1be1e21827d..aa1f5463fb5 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -221,7 +222,15 @@ static void showUpdateInfo(const QList &updates, const std::functionsetText("

" + UpdateInfoPlugin::tr("Available updates:") + "

  • " + updateText + "

"); label->setContentsMargins(0, 0, 0, 8); - return label; + + auto scrollArea = new QScrollArea; + scrollArea->setWidget(label); + scrollArea->setFrameShape(QFrame::NoFrame); + scrollArea->viewport()->setAutoFillBackground(false); + + label->setAutoFillBackground(false); + + return scrollArea; }); ICore::infoBar()->removeInfo(InstallUpdates); // remove any existing notifications ICore::infoBar()->unsuppressInfo(InstallUpdates); From 90b8e482c94e133203d1e26f88b4c002d546c671 Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Tue, 13 Dec 2022 11:16:58 +0100 Subject: [PATCH 19/37] qml code model: load builtins import LinkPrivate::linkImports() would either load the or the module, but only process the module in LinkPrivate::populateImportedTypes(). Note: The module (from the package '') is the QtCreator magic replacement for the actual modules (from the package 'QML'). This means that if the were loaded, then QtObject would be recognized as an object from QtQuick. On the other hand, if the were found, then neither nor would have been imported and QtObject would not have been found in the imports (because neither nor would have been imported). The "-found" situation happens right after a "reset code model" while the "-found" appears when the qml code finally found the package, usually after running cmake or when a project was freshly opened in Qt Creator. Instead, always try to load both and module. Also, fix the builtins.qmltypes (that contains the qt creators magic types) to reflect that Component and QtObject are both available from the QML package (the module), as else one cannot import QtObject from QtQml after a code mode reset. Fixes: QTCREATORBUG-28287 Fixes: QTCREATORBUG-28375 Change-Id: I67084a169dc5ca8ec2474b721dbef83cd47037c7 Reviewed-by: Fabian Kosmale --- .../qml-type-descriptions/builtins.qmltypes | 4 +-- src/libs/qmljs/qmljslink.cpp | 29 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qml-type-descriptions/builtins.qmltypes b/share/qtcreator/qml-type-descriptions/builtins.qmltypes index a0c315086e1..dd8dcfb4172 100644 --- a/share/qtcreator/qml-type-descriptions/builtins.qmltypes +++ b/share/qtcreator/qml-type-descriptions/builtins.qmltypes @@ -265,7 +265,7 @@ Module { name: "QDeclarativeComponent" prototype: "QObject" exports: [ - "QtQuick/Component 1.0" + "QML/Component 1.0" ] exportMetaObjectRevisions: [ 0 @@ -3209,7 +3209,7 @@ Module { Component { name: "QObject" exports: [ - "QtQuick/QtObject 1.0" + "QML/QtObject 1.0" ] exportMetaObjectRevisions: [ 0 diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 2ce98efad3e..bd759b2f644 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -83,6 +83,7 @@ public: const Utils::FilePath &libraryPath); void loadImplicitDirectoryImports(Imports *imports, const Document::Ptr &doc); void loadImplicitDefaultImports(Imports *imports); + void loadImplicitBuiltinsImports(Imports *imports); void error(const Document::Ptr &doc, const SourceLocation &loc, const QString &message); void warning(const Document::Ptr &doc, const SourceLocation &loc, const QString &message); @@ -91,6 +92,8 @@ public: private: friend class Link; + void loadImplicitImports(Imports *imports, const QString &packageName, const QString &moduleName); + Snapshot m_snapshot; ValueOwner *m_valueOwner = nullptr; QList m_importPaths; @@ -255,8 +258,11 @@ void LinkPrivate::populateImportedTypes(Imports *imports, const Document::Ptr &d { importableModuleApis.clear(); - // implicit imports: the package is always available + // implicit imports: the package is always available (except when the QML package was loaded). + // In the latter case, still try to load the 's module . loadImplicitDefaultImports(imports); + // Load the import, if the QML package was loaded. + loadImplicitBuiltinsImports(imports); // implicit imports: // qml files in the same directory are available without explicit imports @@ -684,20 +690,19 @@ void LinkPrivate::loadImplicitDirectoryImports(Imports *imports, const Document: } } -void LinkPrivate::loadImplicitDefaultImports(Imports *imports) +void LinkPrivate::loadImplicitImports(Imports *imports, const QString &packageName, const QString &moduleName) { - const QString defaultPackage = CppQmlTypes::defaultPackage; - if (m_valueOwner->cppQmlTypes().hasModule(defaultPackage)) { + if (m_valueOwner->cppQmlTypes().hasModule(packageName)) { const ComponentVersion maxVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion); - const ImportInfo info = ImportInfo::moduleImport(defaultPackage, maxVersion, QString()); + const ImportInfo info = ImportInfo::moduleImport(packageName, maxVersion, QString()); Import import = importCache.value(ImportCacheKey(info)); if (!import.object) { import.valid = true; import.info = info; - import.object = new ObjectValue(m_valueOwner, QLatin1String("")); + import.object = new ObjectValue(m_valueOwner, moduleName); - const auto objects = m_valueOwner->cppQmlTypes().createObjectsForImport(defaultPackage, + const auto objects = m_valueOwner->cppQmlTypes().createObjectsForImport(packageName, maxVersion); for (const CppComponentValue *object : objects) import.object->setMember(object->className(), object); @@ -708,4 +713,14 @@ void LinkPrivate::loadImplicitDefaultImports(Imports *imports) } } +void LinkPrivate::loadImplicitDefaultImports(Imports *imports) +{ + loadImplicitImports(imports, CppQmlTypes::defaultPackage, QLatin1String("")); +} + +void LinkPrivate::loadImplicitBuiltinsImports(Imports *imports) +{ + loadImplicitImports(imports, QLatin1String("QML"), QLatin1String("")); +} + } // namespace QmlJS From 08d4eab619a5648cec768c9ba8c02a9adeb55fc3 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 19 Dec 2022 11:35:21 +0200 Subject: [PATCH 20/37] QmlDesigner: Create menu for Effects creation in the Asset Library - Add menu item for effect creation instead of new file dialog - New effect dialog with validating qml file name with qml naming conventions - Open Effect Maker automatically when an effect is created Task-number: QDS-8490 Task-number: QDS-8578 Change-Id: I04b075a0b283318906f309c7d394eda48577ae74 Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- .../AssetsContextMenu.qml | 12 ++ .../itemLibraryQmlSources/NewEffectDialog.qml | 105 ++++++++++++++++++ .../assetslibrary/assetslibrarymodel.cpp | 84 ++++++++++---- .../assetslibrary/assetslibrarymodel.h | 4 + 4 files changed, 182 insertions(+), 23 deletions(-) create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml index 6dedaf79012..d3b28d77bbe 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml @@ -183,4 +183,16 @@ StudioControls.Menu { } } } + + StudioControls.MenuItem { + text: qsTr("New Effect") + + NewEffectDialog { + id: newEffectDialog + parent: root.assetsView + dirPath: root.__dirPath + } + + onTriggered: newEffectDialog.open() + } } diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml new file mode 100644 index 00000000000..f57284e6782 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml @@ -0,0 +1,105 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Dialog { + id: root + + title: qsTr("Create New Effect") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + modal: true + + required property string dirPath + readonly property int __maxPath: 32 + + HelperWidgets.RegExpValidator { + id: effectNameValidator + regExp: /^[A-Z]\w[A-Za-z0-9_]*$/ + } + + ErrorDialog { + id: creationFailedDialog + title: qsTr("Could not create effect") + message: qsTr("An error occurred while trying to create the effect.") + } + + contentItem: Column { + spacing: 2 + + Row { + Text { + text: qsTr("Effect name: ") + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: effectName + + actionIndicator.visible: false + translationIndicator.visible: false + validator: effectNameValidator + + Keys.onEnterPressed: btnCreate.onClicked() + Keys.onReturnPressed: btnCreate.onClicked() + } + } + + Text { + text: qsTr("Effect name cannot be empty.") + color: "#ff0000" + anchors.right: parent.right + visible: effectName.text === "" + } + + Text { + text: qsTr("Effect path is too long.") + color: "#ff0000" + anchors.right: parent.right + visible: effectName.text.length > root.__maxPath + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + + HelperWidgets.Button { + id: btnCreate + + text: qsTr("Create") + enabled: effectName.text !== "" + && effectName.length >=3 + && effectName.text.length <= root.__maxPath + onClicked: { + const path = assetsModel.getUniqueEffectPath(root.dirPath, effectName.text) + if (assetsModel.createNewEffect(path)) + root.accept() + else + creationFailedDialog.open() + } + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } + + onOpened: { + const path = assetsModel.getUniqueEffectPath(root.dirPath, "Effect01") + effectName.text = path.split('/').pop().replace(".qep", '') + effectName.selectAll() + effectName.forceActiveFocus() + } +} diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 6587c89cf32..2d51bc4734a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -134,32 +134,10 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & bool AssetsLibraryModel::addNewFolder(const QString &folderPath) { QString iterPath = folderPath; - static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string QDir dir{folderPath}; while (dir.exists()) { - // if the folder name ends with a number, increment it - QRegularExpressionMatch match = rgx.match(iterPath); - if (match.hasMatch()) { // ends with a number - QString numStr = match.captured(0); - int num = match.captured(0).toInt(); - - // get number of padding zeros, ex: for "005" = 2 - int nPaddingZeros = 0; - for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); - - ++num; - - // if the incremented number's digits increased, decrease the padding zeros - if (std::fmod(std::log10(num), 1.0) == 0) - --nPaddingZeros; - - iterPath = folderPath.mid(0, match.capturedStart()) - + QString('0').repeated(nPaddingZeros) - + QString::number(num); - } else { - iterPath = folderPath + '1'; - } + iterPath = getUniqueName(iterPath); dir.setPath(iterPath); } @@ -186,6 +164,36 @@ bool AssetsLibraryModel::allFilePathsAreImages(const QStringList &filePaths) con }); } +QString AssetsLibraryModel::getUniqueEffectPath(const QString &parentFolder, const QString &effectName) +{ + auto genEffectPath = [=](const QString &name) { + return QString(parentFolder + "/" + name + ".qep"); + }; + + QString uniqueName = effectName; + QString path = genEffectPath(uniqueName); + QFileInfo file{path}; + + while (file.exists()) { + uniqueName = getUniqueName(uniqueName); + + path = genEffectPath(uniqueName); + file.setFile(path); + } + + return path; +} + +bool AssetsLibraryModel::createNewEffect(const QString &effectPath, bool openEffectMaker) +{ + bool created = QFile(effectPath).open(QIODevice::WriteOnly); + + if (created && openEffectMaker) + ModelNodeOperations::openEffectMaker(effectPath); + + return created; +} + bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QString path = m_sourceFsModel->filePath(sourceParent); @@ -242,6 +250,36 @@ void AssetsLibraryModel::syncHaveFiles() setHaveFiles(checkHaveFiles()); } +QString AssetsLibraryModel::getUniqueName(const QString &oldName) { + static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string + + QString uniqueName = oldName; + // if the folder name ends with a number, increment it + QRegularExpressionMatch match = rgx.match(uniqueName); + if (match.hasMatch()) { // ends with a number + QString numStr = match.captured(0); + int num = match.captured(0).toInt(); + + // get number of padding zeros, ex: for "005" = 2 + int nPaddingZeros = 0; + for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); + + ++num; + + // if the incremented number's digits increased, decrease the padding zeros + if (std::fmod(std::log10(num), 1.0) == 0) + --nPaddingZeros; + + uniqueName = oldName.mid(0, match.capturedStart()) + + QString('0').repeated(nPaddingZeros) + + QString::number(num); + } else { + uniqueName = oldName + '1'; + } + + return uniqueName; +} + void AssetsLibraryModel::setRootPath(const QString &newPath) { beginResetModel(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 99c96017084..6567ca8338b 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -47,6 +47,9 @@ public: Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex); Q_INVOKABLE bool allFilePathsAreImages(const QStringList &filePaths) const; + Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName); + Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true); + int columnCount(const QModelIndex &parent = QModelIndex()) const override { int result = QSortFilterProxyModel::columnCount(parent); @@ -79,6 +82,7 @@ private: void destroyBackendModel(); bool checkHaveFiles(const QModelIndex &parentIdx) const; bool checkHaveFiles() const; + QString getUniqueName(const QString &oldName); QString m_searchText; QString m_rootPath; From 970af9e64d1ef92987b366f4f2bd0604619de3cb Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 16 Dec 2022 13:28:43 +0200 Subject: [PATCH 21/37] QmlDesigner: Fix state preview rendering with multiple View3Ds Multiple View3Ds with shared resources such as materials don't always properly dirty themselves when a shared resource changes. Work around this issue by rendering the base state twice if there are multiple View3Ds. Fixes: QDS-8618 Change-Id: I3840a082ff8e55557afe5a5f139382d82cd6b184 Reviewed-by: Reviewed-by: Thomas Hartmann --- .../qt5previewnodeinstanceserver.cpp | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index 6f3fced1b78..125a128594d 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -53,28 +53,41 @@ void Qt5PreviewNodeInstanceServer::collectItemChangesAndSendChangeCommands() QQuickDesignerSupport::polishItems(quickWindow()); QVector imageContainerVector; + + // Base state needs to be rendered twice to properly render shared resources, + // if there is more than one View3D and at least one of them is dirty. + bool dirtyView3d = false; + const QList view3dInstances = allView3DInstances(); + for (const auto &instance : view3dInstances) { + if (QQuickDesignerSupport::isDirty(instance.rootQuickItem(), + QQuickDesignerSupport::ContentUpdateMask)) { + dirtyView3d = true; + break; + } + } + if (dirtyView3d) + renderPreviewImage(); imageContainerVector.append(ImageContainer(0, renderPreviewImage(), -1)); - QList stateInstances = rootNodeInstance().stateInstances(); + QList stateInstances = rootNodeInstance().stateInstances(); - const QList groupInstances = allGroupStateInstances(); + const QList groupInstances = allGroupStateInstances(); - for (ServerNodeInstance instance : groupInstances) { - stateInstances.append(instance.stateInstances()); - } + for (const ServerNodeInstance &instance : groupInstances) + stateInstances.append(instance.stateInstances()); - for (ServerNodeInstance instance : std::as_const(stateInstances)) { - instance.activateState(); - QImage previewImage = renderPreviewImage(); - if (!previewImage.isNull()) - imageContainerVector.append(ImageContainer(instance.instanceId(), - renderPreviewImage(), - instance.instanceId())); - instance.deactivateState(); - } + for (ServerNodeInstance instance : std::as_const(stateInstances)) { + instance.activateState(); + QImage previewImage = renderPreviewImage(); + if (!previewImage.isNull()) + imageContainerVector.append(ImageContainer(instance.instanceId(), + renderPreviewImage(), + instance.instanceId())); + instance.deactivateState(); + } nodeInstanceClient()->statePreviewImagesChanged( - StatePreviewImageChangedCommand(imageContainerVector)); + StatePreviewImageChangedCommand(imageContainerVector)); slowDownRenderTimer(); handleExtraRender(); From 0887174727de7ee31ade97886ff5070ce7028354 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 19 Dec 2022 10:59:40 +0200 Subject: [PATCH 22/37] QmlDesigner: Add "Group/Single selection" to the 3D context menu - "Toggle Group/Single selection mode" option is added to the 3D Editor's context menu. - All 3D actions are pushed into a map and are accessible by View3DActionType as the key. Task-number: QDS-8200 Change-Id: Ia5071ef8901b926ee4e4889fd840fc49c859bccd Reviewed-by: Mahmoud Badri --- .../components/edit3d/edit3dactions.cpp | 19 ++++++++--- .../components/edit3d/edit3dactions.h | 16 ++++++--- .../components/edit3d/edit3dview.cpp | 34 ++++++++++++++++++- .../components/edit3d/edit3dview.h | 7 ++++ .../components/edit3d/edit3dwidget.cpp | 9 +++++ .../components/edit3d/edit3dwidget.h | 1 + 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index d5c93ca4b97..5b271a4c13d 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 "edit3dview.h" -#include "edit3dwidget.h" #include #include @@ -18,7 +17,7 @@ namespace QmlDesigner { Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, SelectionContextOperation action, - AbstractView *view, + Edit3DView *view, View3DActionType type) : DefaultAction(description) , m_action(action) @@ -45,12 +44,14 @@ Edit3DAction::Edit3DAction(const QByteArray &menuId, bool checked, const QIcon &iconOff, const QIcon &iconOn, - AbstractView *view, + Edit3DView *view, SelectionContextOperation selectionAction, const QString &toolTip) : AbstractAction(new Edit3DActionTemplate(description, selectionAction, view, type)) , m_menuId(menuId) + , m_actionTemplate(qobject_cast(defaultAction())) { + view->registerEdit3DAction(this); action()->setShortcut(key); action()->setShortcutContext(Qt::WidgetWithChildrenShortcut); action()->setCheckable(checkable); @@ -73,11 +74,21 @@ Edit3DAction::Edit3DAction(const QByteArray &menuId, } } +Edit3DAction::~Edit3DAction() +{ + m_actionTemplate->m_view->unregisterEdit3DAction(this); +} + QByteArray Edit3DAction::category() const { return QByteArray(); } +View3DActionType Edit3DAction::actionType() const +{ + return m_actionTemplate->m_type; +} + bool Edit3DAction::isVisible([[maybe_unused]] const SelectionContext &selectionContext) const { return true; @@ -96,7 +107,7 @@ Edit3DCameraAction::Edit3DCameraAction(const QByteArray &menuId, bool checked, const QIcon &iconOff, const QIcon &iconOn, - AbstractView *view, + Edit3DView *view, SelectionContextOperation selectionAction) : Edit3DAction(menuId, type, description, key, checkable, checked, iconOff, iconOn, view, selectionAction) { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h index 14f63c3796e..a78b95d784a 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h @@ -10,19 +10,22 @@ namespace QmlDesigner { using SelectionContextOperation = std::function; +class Edit3DView; class Edit3DActionTemplate : public DefaultAction { + Q_OBJECT + public: Edit3DActionTemplate(const QString &description, SelectionContextOperation action, - AbstractView *view, + Edit3DView *view, View3DActionType type); void actionTriggered(bool b) override; SelectionContextOperation m_action; - AbstractView *m_view; + Edit3DView *m_view = nullptr; View3DActionType m_type; }; @@ -37,10 +40,12 @@ public: bool checked, const QIcon &iconOff, const QIcon &iconOn, - AbstractView *view, + Edit3DView *view, SelectionContextOperation selectionAction = nullptr, const QString &toolTip = {}); + virtual ~Edit3DAction(); + QByteArray category() const override; int priority() const override @@ -58,12 +63,15 @@ public: return m_menuId; } + View3DActionType actionType() const; + protected: bool isVisible(const SelectionContext &selectionContext) const override; bool isEnabled(const SelectionContext &selectionContext) const override; private: QByteArray m_menuId; + Edit3DActionTemplate *m_actionTemplate = nullptr; }; class Edit3DCameraAction : public Edit3DAction @@ -77,7 +85,7 @@ public: bool checked, const QIcon &iconOff, const QIcon &iconOn, - AbstractView *view, + Edit3DView *view, SelectionContextOperation selectionAction = nullptr); protected: diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 75c2138ffc0..038c658c51e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -38,7 +38,9 @@ Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies) } Edit3DView::~Edit3DView() -{} +{ + qDeleteAll(m_edit3DActions); +} void Edit3DView::createEdit3DWidget() { @@ -197,6 +199,31 @@ void Edit3DView::onEntriesChanged() m_compressionTimer.start(); } +void Edit3DView::registerEdit3DAction(Edit3DAction *action) +{ + View3DActionType actionType = action->actionType(); + if (actionType == View3DActionType::Empty) + return; + + if (m_edit3DActions.contains(actionType)) { + Edit3DAction *formerAction = m_edit3DActions.value(actionType); + if (formerAction == action) + return; + + qWarning() << Q_FUNC_INFO << __LINE__ << "Reregistering action for" << int(actionType); + delete formerAction; + } + + m_edit3DActions.insert(actionType, action); +} + +void Edit3DView::unregisterEdit3DAction(Edit3DAction *action) +{ + View3DActionType actionType = action->actionType(); + if (m_edit3DActions.value(actionType, nullptr) == action) + m_edit3DActions.remove(actionType); +} + void Edit3DView::handleEntriesChanged() { if (!model()) @@ -812,6 +839,11 @@ QVector Edit3DView::backgroundColorActions() const return m_backgroundColorActions; } +Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const +{ + return m_edit3DActions.value(type, nullptr); +} + void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index a14399e1c70..7653730480f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -57,6 +57,7 @@ public: QVector rightActions() const; QVector visibilityToggleActions() const; QVector backgroundColorActions() const; + Edit3DAction *edit3DAction(View3DActionType type) const; void setSeeker(SeekerSlider *slider); void addQuick3DImport(); @@ -77,6 +78,9 @@ private: None }; + void registerEdit3DAction(Edit3DAction *action); + void unregisterEdit3DAction(Edit3DAction *action); + void createEdit3DWidget(); void checkImports(); void handleEntriesChanged(); @@ -92,6 +96,7 @@ private: QVector m_rightActions; QVector m_visibilityToggleActions; QVector m_backgroundColorActions; + QMap m_edit3DActions; Edit3DAction *m_selectionModeAction = nullptr; Edit3DAction *m_moveToolAction = nullptr; Edit3DAction *m_rotateToolAction = nullptr; @@ -120,6 +125,8 @@ private: NodeAtPosReqType m_nodeAtPosReqType; QPoint m_contextMenuPos; QTimer m_compressionTimer; + + friend class Edit3DAction; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index d939d4bf50d..c6cc8e43717 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -232,6 +232,14 @@ void Edit3DWidget::createContextMenu() view()->setSelectedModelNode(parentNode); }); + QAction *defaultToggleGroupAction = view()->edit3DAction(View3DActionType::SelectionModeToggle)->action(); + m_toggleGroupAction = m_contextMenu->addAction(tr("Group Selection Mode"), [&](const bool &mode) { + view()->edit3DAction(View3DActionType::SelectionModeToggle)->action()->trigger(); + }); + connect(defaultToggleGroupAction, &QAction::toggled, m_toggleGroupAction, &QAction::setChecked); + m_toggleGroupAction->setCheckable(true); + m_toggleGroupAction->setChecked(defaultToggleGroupAction->isChecked()); + m_contextMenu->addSeparator(); } @@ -376,6 +384,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_alignCameraAction->setEnabled(isCamera); m_alignViewAction->setEnabled(isCamera); m_selectParentAction->setEnabled(selectionExcludingRoot); + m_toggleGroupAction->setEnabled(true); m_contextMenu->popup(mapToGlobal(pos)); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 25948d5d9aa..f1a9c09f445 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -72,6 +72,7 @@ private: QPointer m_alignCameraAction; QPointer m_alignViewAction; QPointer m_selectParentAction; + QPointer m_toggleGroupAction; QPointer m_createSubMenu; ModelNode m_contextMenuTarget; QVector3D m_contextMenuPos3d; From fb12660b0a097d75db2f05e2f3e7792ae84635cc Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Mon, 19 Dec 2022 16:50:24 +0200 Subject: [PATCH 23/37] QmlDesigner: Enable integration of the new create effect system - Remove old New Effect item from new file dialog - Add license checker for creating effects Task-number: QDS-8675 Task-number: QDS-8676 Change-Id: I83ed2c4021c9df3896018da19fbff76be92a7431 Reviewed-by: Mahmoud Badri --- .../AssetsContextMenu.qml | 1 + .../studio_templates/files/effect/file.qep | 0 .../studio_templates/files/effect/wizard.json | 64 ------------------- .../assetslibrary/assetslibrarymodel.cpp | 9 +++ .../assetslibrary/assetslibrarymodel.h | 2 + 5 files changed, 12 insertions(+), 64 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/effect/file.qep delete mode 100644 share/qtcreator/qmldesigner/studio_templates/files/effect/wizard.json diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml index d3b28d77bbe..3337be90ef0 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml @@ -186,6 +186,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("New Effect") + visible: assetsModel.canCreateEffects() NewEffectDialog { id: newEffectDialog diff --git a/share/qtcreator/qmldesigner/studio_templates/files/effect/file.qep b/share/qtcreator/qmldesigner/studio_templates/files/effect/file.qep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/share/qtcreator/qmldesigner/studio_templates/files/effect/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/effect/wizard.json deleted file mode 100644 index 3dedbb5080d..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/files/effect/wizard.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "version": 1, - "supportedProjectTypes": [ ], - "id": "J.QEP", - "category": "U.QEP", - "trDescription": "Creates an Effect Maker file.", - "trDisplayName": "Effect File (Effect Maker)", - "trDisplayCategory": "Effects", - "iconText": "qep", - "platformIndependent": true, - "enabled": "%{JS: value('Features').indexOf('QmlDesigner.Wizards.Enterprise') >= 0}", - "featuresRequired": [ "QmlDesigner.Wizards.Enterprise" ], - - "options": [ - { "key": "EffectFile", "value": "%{Class}.qep" }, - { "key": "DoNotOpenFile", "value": "true" } - ], - - "pages" : - [ - { - "trDisplayName": "Define Class", - "trShortTitle": "Details", - "typeId": "Fields", - "data" : - [ - { - "name": "Class", - "trDisplayName": "Effect 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}" - } - } - ] - } -], - "generators" : - [ - { - "typeId": "File", - "data": - { - "source": "file.qep", - "target": "%{TargetPath}/%{EffectFile}", - "openInEditor": false - } - } - ] -} diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 2d51bc4734a..e52f168f73d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -194,6 +194,15 @@ bool AssetsLibraryModel::createNewEffect(const QString &effectPath, bool openEff return created; } +bool AssetsLibraryModel::canCreateEffects() const +{ +#ifdef LICENSECHECKER + return checkLicense() == FoundLicense::enterprise; +#else + return true; +#endif +} + 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 6567ca8338b..b6cadd4eceb 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -50,6 +50,8 @@ public: Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName); Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true); + Q_INVOKABLE bool canCreateEffects() const; + int columnCount(const QModelIndex &parent = QModelIndex()) const override { int result = QSortFilterProxyModel::columnCount(parent); From e7b4ccf608ee81b424bb687eb756725da83d082a Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 14 Dec 2022 17:01:10 +0100 Subject: [PATCH 24/37] QmlProjectManager: Fix starting .qmlproject on Boot2Qt The "qmlscene" is called "qml" nowadays. Also give device settings more priority to cover cases where there is no Qt build setup at all. For this case, also demote the missing Qt build version from "Error" to "Warning". Task-number: QTCREATORBUG-28074 Change-Id: Ic44d2bee1965493925d21317d12d5c1f66ace88b Reviewed-by: Tim Jenssen --- src/plugins/qmlprojectmanager/qmlproject.cpp | 2 +- .../qmlprojectmanager/qmlprojectrunconfiguration.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 208d2482fc0..2f7cc3699d0 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -533,7 +533,7 @@ Tasks QmlProject::projectIssues(const Kit *k) const const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); if (!version) - result.append(createProjectTask(Task::TaskType::Error, tr("No Qt version set in kit."))); + result.append(createProjectTask(Task::TaskType::Warning, tr("No Qt version set in kit."))); IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (dev.isNull()) diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index ee5327cac0c..791a3c6a565 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -163,6 +163,14 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const return qmlViewer; Kit *kit = target()->kit(); + IDevice::ConstPtr dev = DeviceKitAspect::device(kit); + if (!dev.isNull()) { + const FilePath qmlRuntime = dev->qmlRunCommand(); + if (!qmlRuntime.isEmpty()) + return qmlRuntime; + } + + // If not given explicitly by device, try to pick it from $PATH. QtVersion *version = QtKitAspect::qtVersion(kit); if (!version) // No Qt version in Kit. Don't try to run QML runtime. return {}; @@ -174,13 +182,12 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const return isDesktop ? version->qmlRuntimeFilePath() : "qmlscene"; } - IDevice::ConstPtr dev = DeviceKitAspect::device(kit); if (dev.isNull()) // No device set. We don't know where a QML utility is. return {}; const FilePath qmlRuntime = dev->qmlRunCommand(); // If not given explicitly by device, try to pick it from $PATH. - return qmlRuntime.isEmpty() ? "qmlscene" : qmlRuntime; + return qmlRuntime.isEmpty() ? "qml" : qmlRuntime; } QString QmlProjectRunConfiguration::commandLineArguments() const From 4a5359cb868dbf869d4a91ba5a942297b7d8b776 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Thu, 15 Dec 2022 19:26:49 +0200 Subject: [PATCH 25/37] QmlDesigner: Extract asset type stuff from the AssetsLibraryModel This is in preparation for the task that will show metadata of assets. The functions for checking the asset type have little to nothing to do with the model. This change will also clean up the code a bit. Task-number: QDS-8177 Change-Id: Ibab28f5b63228f626f517a59e2442d2718c2fc07 Reviewed-by: Miikka Heikkinen Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetslibraryiconprovider.cpp | 19 +- .../assetslibrary/assetslibrarymodel.cpp | 86 +-------- .../assetslibrary/assetslibrarymodel.h | 16 +- .../assetslibrary/assetslibrarywidget.cpp | 31 +-- .../propertyeditorimageprovider.cpp | 18 +- src/plugins/qmldesigner/utils/asset.cpp | 182 ++++++++++++++++++ src/plugins/qmldesigner/utils/asset.h | 45 +++++ .../qt5informationnodeinstanceserver.cpp | 4 +- 9 files changed, 272 insertions(+), 130 deletions(-) create mode 100644 src/plugins/qmldesigner/utils/asset.cpp create mode 100644 src/plugins/qmldesigner/utils/asset.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 8018e76044d..ce772baf43c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -20,6 +20,7 @@ add_qtc_library(QmlDesignerUtils STATIC PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES + asset.cpp asset.h designersettings.cpp designersettings.h hdrimage.cpp hdrimage.h imageutils.cpp imageutils.h diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index e9d3b42855c..14a433e8602 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "assetslibraryiconprovider.h" -#include "assetslibrarymodel.h" +#include "asset.h" #include "modelnodeoperations.h" #include @@ -57,24 +57,25 @@ QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, co QPixmap AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize) const { - const QString suffix = "*." + id.split('.').last().toLower(); + Asset asset(id); + if (id == "browse") { return Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/browse.png"); - } else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) { + } else if (asset.isFont()) { return generateFontIcons(id, requestedSize); - } else if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { + } else if (asset.isImage()) { return Utils::StyleHelper::dpiSpecificImageFile(id); - } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { + } else if (asset.isTexture3D()) { return HdrImage{id}.toPixmap(); } else { QString type; - if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) + if (asset.isShader()) type = "shader"; - else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) + else if (asset.isAudio()) type = "sound"; - else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) + else if (asset.isVideo()) type = "video"; - else if (AssetsLibraryModel::supportedEffectMakerSuffixes().contains(suffix)) + else if (asset.isEffect()) type = QmlDesigner::ModelNodeOperations::getEffectIcon(id); QString pathTemplate = QString(":/AssetsLibrary/images/asset_%1%2.png").arg(type); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index e52f168f73d..0953eff9eb1 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -1,13 +1,13 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2022 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 #include #include -#include #include #include +#include "asset.h" #include "assetslibrarymodel.h" #include @@ -158,9 +158,7 @@ bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex) bool AssetsLibraryModel::allFilePathsAreImages(const QStringList &filePaths) const { return Utils::allOf(filePaths, [](const QString &path) { - const QString suffix = "*." + path.split('.').last().toLower(); - - return AssetsLibraryModel::supportedImageSuffixes().contains(suffix); + return Asset(path).isImage(); }); } @@ -299,7 +297,7 @@ void AssetsLibraryModel::setRootPath(const QString &newPath) m_rootPath = newPath; m_sourceFsModel->setRootPath(newPath); - m_sourceFsModel->setNameFilters(supportedSuffixes().values()); + m_sourceFsModel->setNameFilters(Asset::supportedSuffixes().values()); m_sourceFsModel->setNameFilterDisables(false); endResetModel(); @@ -374,80 +372,4 @@ QString AssetsLibraryModel::parentDirPath(const QString &path) const return filePath(parentIdx); } -const QStringList &AssetsLibraryModel::supportedImageSuffixes() -{ - static QStringList retList; - if (retList.isEmpty()) { - const QList suffixes = QImageReader::supportedImageFormats(); - for (const QByteArray &suffix : suffixes) - retList.append("*." + QString::fromUtf8(suffix)); - } - return retList; -} - -const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes() -{ - static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedShaderSuffixes() -{ - static const QStringList retList {"*.frag", "*.vert", - "*.glsl", "*.glslv", "*.glslf", - "*.vsh", "*.fsh"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedFontSuffixes() -{ - static const QStringList retList {"*.ttf", "*.otf"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedAudioSuffixes() -{ - static const QStringList retList {"*.wav", "*.mp3"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedVideoSuffixes() -{ - static const QStringList retList {"*.mp4"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() -{ - // These are file types only supported by 3D textures - static QStringList retList {"*.hdr", "*.ktx"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedEffectMakerSuffixes() -{ - // These are file types only supported by Effect Maker - static QStringList retList {"*.qep"}; - return retList; -} - -const QSet &AssetsLibraryModel::supportedSuffixes() -{ - static QSet allSuffixes; - if (allSuffixes.isEmpty()) { - auto insertSuffixes = [](const QStringList &suffixes) { - for (const auto &suffix : suffixes) - allSuffixes.insert(suffix); - }; - insertSuffixes(supportedImageSuffixes()); - insertSuffixes(supportedShaderSuffixes()); - insertSuffixes(supportedFontSuffixes()); - insertSuffixes(supportedAudioSuffixes()); - insertSuffixes(supportedVideoSuffixes()); - insertSuffixes(supportedTexture3DSuffixes()); - insertSuffixes(supportedEffectMakerSuffixes()); - } - return allSuffixes; -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index b6cadd4eceb..0538bedf42b 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -1,14 +1,14 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once +#include #include #include -#include -#include #include +#include namespace QmlDesigner { @@ -60,16 +60,6 @@ public: bool haveFiles() const { return m_haveFiles; } - static const QStringList &supportedImageSuffixes(); - static const QStringList &supportedFragmentShaderSuffixes(); - static const QStringList &supportedShaderSuffixes(); - static const QStringList &supportedFontSuffixes(); - static const QStringList &supportedAudioSuffixes(); - static const QStringList &supportedVideoSuffixes(); - static const QStringList &supportedTexture3DSuffixes(); - static const QStringList &supportedEffectMakerSuffixes(); - static const QSet &supportedSuffixes(); - signals: void directoryLoaded(const QString &path); void rootPathChanged(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 05ebb729869..cc9263eda46 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -3,8 +3,9 @@ #include "assetslibrarywidget.h" -#include "assetslibrarymodel.h" +#include "asset.h" #include "assetslibraryiconprovider.h" +#include "assetslibrarymodel.h" #include @@ -229,7 +230,7 @@ QSet AssetsLibraryWidget::supportedAssetSuffixes(bool complex) QSet suffixes; for (const AddResourceHandler &handler : handlers) { - if (AssetsLibraryModel::supportedSuffixes().contains(handler.filter) != complex) + if (Asset(handler.filter).isSupported() != complex) suffixes.insert(handler.filter); } @@ -290,32 +291,32 @@ void AssetsLibraryWidget::startDragAsset(const QStringList &assetPaths, const QP QPair AssetsLibraryWidget::getAssetTypeAndData(const QString &assetPath) { - QString suffix = "*." + assetPath.split('.').last().toLower(); - if (!suffix.isEmpty()) { - if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { + Asset asset(assetPath); + if (asset.hasSuffix()) { + if (asset.isImage()) { // Data: Image format (suffix) - return {Constants::MIME_TYPE_ASSET_IMAGE, suffix.toUtf8()}; - } else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) { + return {Constants::MIME_TYPE_ASSET_IMAGE, asset.suffix().toUtf8()}; + } else if (asset.isFont()) { // Data: Font family name QRawFont font(assetPath, 10); QString fontFamily = font.isValid() ? font.familyName() : ""; return {Constants::MIME_TYPE_ASSET_FONT, fontFamily.toUtf8()}; - } else if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) { + } else if (asset.isShader()) { // Data: shader type, frament (f) or vertex (v) return {Constants::MIME_TYPE_ASSET_SHADER, - AssetsLibraryModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; - } else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) { + asset.isFragmentShader() ? "f" : "v"}; + } else if (asset.isAudio()) { // No extra data for sounds return {Constants::MIME_TYPE_ASSET_SOUND, {}}; - } else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) { + } else if (asset.isVideo()) { // No extra data for videos return {Constants::MIME_TYPE_ASSET_VIDEO, {}}; - } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { + } else if (asset.isTexture3D()) { // Data: Image format (suffix) - return {Constants::MIME_TYPE_ASSET_TEXTURE3D, suffix.toUtf8()}; - } else if (AssetsLibraryModel::supportedEffectMakerSuffixes().contains(suffix)) { + return {Constants::MIME_TYPE_ASSET_TEXTURE3D, asset.suffix().toUtf8()}; + } else if (asset.isEffect()) { // Data: Effect Maker format (suffix) - return {Constants::MIME_TYPE_ASSET_EFFECT, suffix.toUtf8()}; + return {Constants::MIME_TYPE_ASSET_EFFECT, asset.suffix().toUtf8()}; } } return {}; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp index e7ff48450d1..fc5a09818ca 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "propertyeditorimageprovider.h" -#include "assetslibrarymodel.h" +#include "asset.h" #include #include @@ -16,12 +16,12 @@ namespace QmlDesigner { QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) { - const QString suffix = "*." + id.split('.').last().toLower(); + Asset asset(id); - if (suffix == "*.mesh") + if (asset.suffix() == "*.mesh") return m_smallImageCacheProvider.requestImageResponse(id, requestedSize); - if (suffix == "*.builtin") + if (asset.suffix() == "*.builtin") return m_smallImageCacheProvider.requestImageResponse("#" + id.split('.').first(), requestedSize); @@ -29,15 +29,15 @@ QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QSt QMetaObject::invokeMethod( response.get(), - [response = QPointer(response.get()), suffix, id, requestedSize] { - if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { - QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(id)); + [response = QPointer(response.get()), asset, requestedSize] { + if (asset.isImage()) { + QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(asset.id())); if (!image.isNull()) { response->setImage(image.scaled(requestedSize, Qt::KeepAspectRatio)); return; } - } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { - HdrImage hdr{id}; + } else if (asset.isTexture3D()) { + HdrImage hdr{asset.id()}; if (!hdr.image().isNull()) { response->setImage(hdr.image().scaled(requestedSize, Qt::KeepAspectRatio)); return; diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp new file mode 100644 index 00000000000..6a0a4658429 --- /dev/null +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -0,0 +1,182 @@ +// Copyright (C) 2022 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 + +#include "asset.h" + +namespace QmlDesigner { + +Asset::Asset(const QString &filePath) + : m_filePath(filePath) +{ + m_suffix = "*." + filePath.split('.').last().toLower(); +} + + +const QStringList &Asset::supportedImageSuffixes() +{ + static QStringList retList; + if (retList.isEmpty()) { + const QList suffixes = QImageReader::supportedImageFormats(); + for (const QByteArray &suffix : suffixes) + retList.append("*." + QString::fromUtf8(suffix)); + } + return retList; +} + +const QStringList &Asset::supportedFragmentShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; + return retList; +} + +const QStringList &Asset::supportedShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.vert", + "*.glsl", "*.glslv", "*.glslf", + "*.vsh", "*.fsh"}; + return retList; +} + +const QStringList &Asset::supportedFontSuffixes() +{ + static const QStringList retList {"*.ttf", "*.otf"}; + return retList; +} + +const QStringList &Asset::supportedAudioSuffixes() +{ + static const QStringList retList {"*.wav", "*.mp3"}; + return retList; +} + +const QStringList &Asset::supportedVideoSuffixes() +{ + static const QStringList retList {"*.mp4"}; + return retList; +} + +const QStringList &Asset::supportedTexture3DSuffixes() +{ + // These are file types only supported by 3D textures + static QStringList retList {"*.hdr", "*.ktx"}; + return retList; +} + +const QStringList &Asset::supportedEffectMakerSuffixes() +{ + // These are file types only supported by Effect Maker + static QStringList retList {"*.qep"}; + return retList; +} + +const QSet &Asset::supportedSuffixes() +{ + static QSet allSuffixes; + if (allSuffixes.isEmpty()) { + auto insertSuffixes = [](const QStringList &suffixes) { + for (const auto &suffix : suffixes) + allSuffixes.insert(suffix); + }; + insertSuffixes(supportedImageSuffixes()); + insertSuffixes(supportedShaderSuffixes()); + insertSuffixes(supportedFontSuffixes()); + insertSuffixes(supportedAudioSuffixes()); + insertSuffixes(supportedVideoSuffixes()); + insertSuffixes(supportedTexture3DSuffixes()); + insertSuffixes(supportedEffectMakerSuffixes()); + } + return allSuffixes; +} + +Asset::Type Asset::type() const +{ + if (supportedImageSuffixes().contains(m_suffix)) + return Asset::Type::Image; + + if (supportedFragmentShaderSuffixes().contains(m_suffix)) + return Asset::Type::FragmentShader; + + if (supportedShaderSuffixes().contains(m_suffix)) + return Asset::Type::Shader; + + if (supportedFontSuffixes().contains(m_suffix)) + return Asset::Type::Font; + + if (supportedAudioSuffixes().contains(m_suffix)) + return Asset::Type::Audio; + + if (supportedVideoSuffixes().contains(m_suffix)) + return Asset::Type::Video; + + if (supportedTexture3DSuffixes().contains(m_suffix)) + return Asset::Type::Texture3D; + + if (supportedEffectMakerSuffixes().contains(m_suffix)) + return Asset::Type::Effect; + + return Asset::Type::Unknown; +} + +bool Asset::isImage() const +{ + return type() == Asset::Type::Image; +} + +bool Asset::isFragmentShader() const +{ + return type() == Asset::Type::FragmentShader; +} + +bool Asset::isShader() const +{ + return type() == Asset::Type::Shader; +} + +bool Asset::isFont() const +{ + return type() == Asset::Type::Font; +} + +bool Asset::isAudio() const +{ + return type() == Asset::Type::Audio; +} + +bool Asset::isVideo() const +{ + return type() == Asset::Type::Video; +} + +bool Asset::isTexture3D() const +{ + return type() == Asset::Type::Texture3D; +} + +bool Asset::isEffect() const +{ + return type() == Asset::Type::Effect; +} + +const QString Asset::suffix() const +{ + return m_suffix; +} + +const QString Asset::id() const +{ + return m_filePath; +} + +bool Asset::isSupported() const +{ + return supportedSuffixes().contains(m_filePath); +} + +bool Asset::hasSuffix() const +{ + return !m_suffix.isEmpty(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h new file mode 100644 index 00000000000..279edb93e1b --- /dev/null +++ b/src/plugins/qmldesigner/utils/asset.h @@ -0,0 +1,45 @@ +// Copyright (C) 2022 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 + +namespace QmlDesigner { + +class Asset +{ +public: + enum Type { Unknown, Image, FragmentShader, Font, Audio, Video, Texture3D, Effect, Shader }; + + Asset(const QString &filePath); + + static const QStringList &supportedImageSuffixes(); + static const QStringList &supportedFragmentShaderSuffixes(); + static const QStringList &supportedShaderSuffixes(); + static const QStringList &supportedFontSuffixes(); + static const QStringList &supportedAudioSuffixes(); + static const QStringList &supportedVideoSuffixes(); + static const QStringList &supportedTexture3DSuffixes(); + static const QStringList &supportedEffectMakerSuffixes(); + static const QSet &supportedSuffixes(); + + const QString suffix() const; + const QString id() const; + bool hasSuffix() const; + + Type type() const; + bool isImage() const; + bool isFragmentShader() const; + bool isShader() const; + bool isFont() const; + bool isAudio() const; + bool isVideo() const; + bool isTexture3D() const; + bool isEffect() const; + bool isSupported() const; + +private: + QString m_filePath; + QString m_suffix; +}; + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c2ef586c031..c806edcdb65 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -921,7 +921,7 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D([[maybe_unu if (toolStates.contains("syncBackgroundColor")) { bool sync = toolStates["syncBackgroundColor"].toBool(); if (sync) { - QList colors = {helper->sceneEnvironmentColor(sceneId)}; + QList colors{helper->sceneEnvironmentColor(sceneId)}; View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, QVariant::fromValue(colors)); view3DAction(cmd); @@ -2274,7 +2274,7 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentColor(const PropertyVa if (toolStates.contains("syncBackgroundColor")) { bool sync = toolStates["syncBackgroundColor"].toBool(); - QList colors = {color}; + QList colors{color}; if (sync) { View3DActionCommand cmd(View3DActionType::SelectBackgroundColor, QVariant::fromValue(colors)); From e4af787ff6c301c95851aac91796412b1d3ffe35 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 16 Dec 2022 13:23:23 +0200 Subject: [PATCH 26/37] QmlDesigner: Add image info to content library texture tooltip Fixes: QDS-8489 Change-Id: I1afbf91ad12839cc93a46f0a608ab3bda135b76b Reviewed-by: Reviewed-by: Thomas Hartmann --- .../ContentLibraryTexture.qml | 17 +++++++++++++++++ .../contentlibrary/contentlibrarytexture.cpp | 7 ++++++- .../contentlibrary/contentlibrarytexture.h | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml index d6c24d4b2af..494bebd07cc 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml @@ -23,6 +23,7 @@ Image { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true onPressed: (mouse) => { if (mouse.button === Qt.LeftButton) @@ -31,4 +32,20 @@ Image { root.showContextMenu() } } + + ToolTip { + visible: mouseArea.containsMouse + // contentWidth is not calculated correctly by the toolTip (resulting in a wider tooltip than + // needed). Using a helper Text to calculate the correct width + contentWidth: helperText.width + bottomInset: -2 + text: modelData.textureToolTip + delay: 1000 + + Text { + id: helperText + text: modelData.textureToolTip + visible: false + } + } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 288e04aaff6..84cd0f98362 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -3,12 +3,17 @@ #include "contentlibrarytexture.h" +#include "imageutils.h" + namespace QmlDesigner { ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QString &path, const QUrl &icon) : QObject(parent) , m_path(path) - , m_icon(icon) {} + , m_icon(icon) +{ + m_toolTip = QLatin1String("%1\n%2").arg(path.split('/').last(), ImageUtils::imageInfo(path)); +} bool ContentLibraryTexture::filter(const QString &searchText) { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 88d50158325..468dfea09e4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -13,6 +13,7 @@ class ContentLibraryTexture : public QObject Q_OBJECT Q_PROPERTY(QString texturePath MEMBER m_path CONSTANT) + Q_PROPERTY(QString textureToolTip MEMBER m_toolTip CONSTANT) Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged) @@ -29,6 +30,7 @@ signals: private: QString m_path; + QString m_toolTip; QUrl m_icon; bool m_visible = true; From c4cc5768255f15c566418a7ffaf924344d0c5f18 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Tue, 20 Dec 2022 14:49:11 +0200 Subject: [PATCH 27/37] QmlDesigner: Create default effects folder in assets library Task-number: QDS-8166 Change-Id: I12bf86ec5983642295a20d254f06d0a4ab6ccdb1 Reviewed-by: Mahmoud Badri --- .../components/componentcore/modelnodeoperations.cpp | 9 +++++++-- .../components/componentcore/modelnodeoperations.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 5dd2b1dd5d6..bd9031ddc26 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1677,7 +1677,7 @@ void openEffectMaker(const QString &filePath) } } -Utils::FilePath getEffectsDirectory() +Utils::FilePath getEffectsImportDirectory() { QString defaultDir = "asset_imports/Effects"; Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); @@ -1691,6 +1691,11 @@ Utils::FilePath getEffectsDirectory() return effectsPath; } +QString getEffectsDefaultDirectory(const QString &defaultDir) +{ + return getAssetDefaultDirectory("effects", defaultDir); +} + QString getEffectIcon(const QString &effectPath) { const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); @@ -1718,7 +1723,7 @@ bool useLayerEffect() bool validateEffect(const QString &effectPath) { const QString effectName = QFileInfo(effectPath).baseName(); - Utils::FilePath effectsResDir = ModelNodeOperations::getEffectsDirectory(); + Utils::FilePath effectsResDir = ModelNodeOperations::getEffectsImportDirectory(); Utils::FilePath qmlPath = effectsResDir.resolvePath(effectName + "/" + effectName + ".qml"); if (!qmlPath.exists()) { QMessageBox msgBox; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 2e60b253195..1cee080c561 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -119,7 +119,8 @@ void addMouseAreaFill(const SelectionContext &selectionContext); void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); -QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsDirectory(); +QMLDESIGNERCORE_EXPORT Utils::FilePath getEffectsImportDirectory(); +QMLDESIGNERCORE_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir); void openEffectMaker(const QString &filePath); QString getEffectIcon(const QString &effectPath); bool useLayerEffect(); From f9a0b325d715fad239aebc133e2c653cfb3964d3 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Mon, 19 Dec 2022 20:21:43 +0200 Subject: [PATCH 28/37] QmlDesigner: Fix the right margin for the AssetsView Task-number: QDS-8352 Change-Id: If08cf7c39cea9dde8e241214c775685b4b1fd4f8 Reviewed-by: Mahmoud Badri --- .../itemLibraryQmlSources/AssetDelegate.qml | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml index 7bfec10aa32..2bbfff5223c 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml @@ -26,7 +26,12 @@ TreeViewDelegate { readonly property int __dirItemHeight: 21 implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight - implicitWidth: root.assetsView.width > 0 ? root.assetsView.width : 10 + implicitWidth: { + if (root.assetsView.verticalScrollBar.scrollBarVisible) + return root.assetsView.width - root.indentation - root.assetsView.verticalScrollBar.width + else + return root.assetsView.width - root.indentation + } leftMargin: root.__isDirectory ? 0 : thumbnailImage.width @@ -54,17 +59,6 @@ TreeViewDelegate { } } - onImplicitWidthChanged: { - // a small hack, to fix a glitch: when resizing the width of the tree view, - // the widths of the delegate items remain the same as before, unless we re-set - // that width explicitly. - var newWidth = root.implicitWidth - (root.assetsView.verticalScrollBar.scrollBarVisible - ? root.assetsView.verticalScrollBar.width - : 0) - bg.width = newWidth - bg.implicitWidth = newWidth - } - onDepthChanged: { if (root.depth > root.initialDepth && root.initialDepth >= 0) root.depth = root.initialDepth @@ -73,6 +67,8 @@ TreeViewDelegate { background: Rectangle { id: bg + width: root.implicitWidth + color: { if (root.__isDirectory && (root.isHighlighted || root.hasChildWithDropHover)) return StudioTheme.Values.themeInteraction From 1262c68f2ee22c7a13c000002ef7d918c6440e69 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 20 Dec 2022 15:00:23 +0100 Subject: [PATCH 29/37] qmlpuppet: fix standalone build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit app_version.h is not generated and found if a build just for the qml2puppet target is requested Change-Id: I9ef164d8e0c698d43eae74d2522029f9eff0f397 Reviewed-by: Burak Hancerli Reviewed-by: Henning Gründl --- src/tools/qml2puppet/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 1976a2bc149..b6aa7a56d2b 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -19,6 +19,8 @@ if (NOT QT_CREATOR_API_DEFINED) include(QtCreatorAPI) endif() +configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES) + find_package(Qt5 COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml Core5Compat REQUIRED @@ -34,6 +36,8 @@ add_qtc_executable(qml2puppet Qt5::CorePrivate Qt5::Widgets Qt5::QmlPrivate Qt5::QuickPrivate Qt5::Network Qt5::GuiPrivate QmlPuppetCommunication + INCLUDES + ${CMAKE_CURRENT_BINARY_DIR} SOURCES qml2puppet/main.cpp qmlpuppet.qrc From d77cdc88d7702f9755eeec470f9d0cc9e23426a7 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 20 Dec 2022 16:01:54 +0100 Subject: [PATCH 30/37] qmlpuppet: fix build amends last commit Change-Id: I057912c49526d386707b893227f16410297d7b18 Reviewed-by: Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 3 +-- src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index b6aa7a56d2b..5fc29187bdf 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -37,7 +37,7 @@ add_qtc_executable(qml2puppet Qt5::QuickPrivate Qt5::Network Qt5::GuiPrivate QmlPuppetCommunication INCLUDES - ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} SOURCES qml2puppet/main.cpp qmlpuppet.qrc @@ -181,7 +181,6 @@ extend_qtc_executable(qml2puppet ) extend_qtc_executable(qml2puppet - DEPENDS app_version SOURCES_PREFIX qml2puppet/runner SOURCES runtime/qmlruntime.h runtime/qmlruntime.cpp diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp index 45d29038b1e..d75bd6b542d 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp @@ -11,8 +11,8 @@ #include #include -#include "qml2puppet/iconrenderer/iconrenderer.h" -#include "qml2puppet/import3d/import3d.h" +#include +#include #include "configcrashpad.h" From 74da3abb326f2a83b1d94e4cca0b958aac54be62 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 20 Dec 2022 18:25:46 +0100 Subject: [PATCH 31/37] qmlpuppet: fix crashpad build Change-Id: I02f5abce80d4ccc1ea711e1dcb01946d53f8b5a7 Reviewed-by: Reviewed-by: Tim Jenssen --- .../qml2puppet/runner/puppet/configcrashpad.h | 14 +++++++------- .../qml2puppet/runner/puppet/qmlpuppet.cpp | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h index 39b45e5f1c3..aec1cbdafad 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h @@ -6,11 +6,6 @@ #include #endif -#define START_CRASHPAD -#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) -startCrashpad() -#endif - #ifdef ENABLE_QT_BREAKPAD #include #endif @@ -63,6 +58,11 @@ namespace { const QString libexecPath = QCoreApplication::applicationDirPath() + '/' + RELATIVE_LIBEXEC_PATH; QtSystemExceptionHandler systemExceptionHandler(libexecPath); -#endif -#endif +#endif //#ifdef ENABLE_QT_BREAKPAD +#else //#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) + bool startCrashpad() + { + return false; + } +#endif //#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) } diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp index d75bd6b542d..f87387290aa 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp @@ -114,7 +114,8 @@ void QmlPuppet::initQmlRunner() Import3D::import3D(sourceAsset, outDir, options); } - START_CRASHPAD; + startCrashpad(); + new QmlDesigner::Qt5NodeInstanceClientProxy(m_coreApp.get()); #if defined(Q_OS_WIN) && defined(QT_NO_DEBUG) From e16af85f32045d6751d6588fd681eb75cfe2590c Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 20 Dec 2022 19:52:59 +0100 Subject: [PATCH 32/37] qmlpuppet: fix Qt5 builds Change-Id: Ie3fedef867bf2327b145b4ef4e5b282858076b23 Reviewed-by: Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/CMakeLists.txt | 40 +++++++++---------- .../qml2puppet/{runner => }/appmetadata.h | 3 -- .../{runner/puppet => }/configcrashpad.h | 0 src/tools/qml2puppet/qml2puppet/main.cpp | 9 +++-- .../qml2puppet/{runner => }/qmlbase.h | 0 .../{runner/puppet => }/qmlpuppet.cpp | 0 .../{runner/puppet => }/qmlpuppet.h | 0 .../runner/{runtime => }/loadwatcher.h | 0 .../runner/{runtime => }/qmlconfiguration.h | 0 .../runner/{runtime => }/qmlruntime.cpp | 0 .../runner/{runtime => }/qmlruntime.h | 0 11 files changed, 26 insertions(+), 26 deletions(-) rename src/tools/qml2puppet/qml2puppet/{runner => }/appmetadata.h (98%) rename src/tools/qml2puppet/qml2puppet/{runner/puppet => }/configcrashpad.h (100%) rename src/tools/qml2puppet/qml2puppet/{runner => }/qmlbase.h (100%) rename src/tools/qml2puppet/qml2puppet/{runner/puppet => }/qmlpuppet.cpp (100%) rename src/tools/qml2puppet/qml2puppet/{runner/puppet => }/qmlpuppet.h (100%) rename src/tools/qml2puppet/qml2puppet/runner/{runtime => }/loadwatcher.h (100%) rename src/tools/qml2puppet/qml2puppet/runner/{runtime => }/qmlconfiguration.h (100%) rename src/tools/qml2puppet/qml2puppet/runner/{runtime => }/qmlruntime.cpp (100%) rename src/tools/qml2puppet/qml2puppet/runner/{runtime => }/qmlruntime.h (100%) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 5fc29187bdf..c9d6961a133 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -40,6 +40,8 @@ add_qtc_executable(qml2puppet ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} SOURCES qml2puppet/main.cpp + qml2puppet/qmlbase.h qml2puppet/appmetadata.h + qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h qmlpuppet.qrc ) @@ -180,15 +182,6 @@ extend_qtc_executable(qml2puppet animationdriver.cpp animationdriver.h ) -extend_qtc_executable(qml2puppet - SOURCES_PREFIX qml2puppet/runner - SOURCES - runtime/qmlruntime.h runtime/qmlruntime.cpp - runtime/qmlconfiguration.h runtime/loadwatcher.h - puppet/qmlpuppet.h puppet/qmlpuppet.cpp puppet/configcrashpad.h - qmlbase.h appmetadata.h -) - extend_qtc_executable(qml2puppet SOURCES_PREFIX qmlprivategate SOURCES @@ -209,25 +202,32 @@ extend_qtc_executable(qml2puppet PUBLIC_INCLUDES src/libs ) -extend_qtc_executable(qml2puppet -PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qml2puppet/runner/runtime -) - extend_qtc_executable(qml2puppet CONDITION TARGET Nanotrace DEPENDS Nanotrace ) -# Turn the tool into its own self-contained qml module -qt6_add_qml_module(qml2puppet - URI QmlRuntime.QmlConfiguration - VERSION 1.0 -) +if (Qt5_VERSION VERSION_GREATER_EQUAL 6.4.0) + extend_qtc_executable(qml2puppet + DEFINES ENABLE_INTERNAL_QML_RUNTIME + PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qml2puppet/runner + SOURCES_PREFIX qml2puppet/runner + SOURCES + qmlruntime.h qmlruntime.cpp + qmlconfiguration.h loadwatcher.h + ) -if (QTC_STATIC_BUILD AND Qt5_VERSION VERSION_GREATER_EQUAL 6.0.0) - qt6_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR}) + # Turn the tool into its own self-contained qml module + qt_add_qml_module(qml2puppet + URI QmlRuntime.QmlConfiguration + VERSION 1.0 + ) + if (QTC_STATIC_BUILD) + qt_import_qml_plugins(qml2puppet PATH_TO_SCAN ${SRCDIR}) + endif() endif() + # Crashpad # only windows requires separate crashpad client per process until client->SetHandlerIPCPipe() # is implemented (check the TODO inside startCrashpad()) diff --git a/src/tools/qml2puppet/qml2puppet/runner/appmetadata.h b/src/tools/qml2puppet/qml2puppet/appmetadata.h similarity index 98% rename from src/tools/qml2puppet/qml2puppet/runner/appmetadata.h rename to src/tools/qml2puppet/qml2puppet/appmetadata.h index 552c1334398..7f11276e619 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/appmetadata.h +++ b/src/tools/qml2puppet/qml2puppet/appmetadata.h @@ -16,7 +16,6 @@ inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) inline void registerMessageHandler() { qInstallMessageHandler( @@ -45,8 +44,6 @@ inline void registerMessageHandler() } }); } -#endif - } // namespace Logging namespace AppInfo { diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/configcrashpad.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/puppet/configcrashpad.h rename to src/tools/qml2puppet/qml2puppet/configcrashpad.h diff --git a/src/tools/qml2puppet/qml2puppet/main.cpp b/src/tools/qml2puppet/qml2puppet/main.cpp index 66d6a43f0b1..84f273a31cb 100644 --- a/src/tools/qml2puppet/qml2puppet/main.cpp +++ b/src/tools/qml2puppet/qml2puppet/main.cpp @@ -1,12 +1,15 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 -#include "runner/puppet/qmlpuppet.h" -#include "runner/runtime/qmlruntime.h" +#include "qmlpuppet.h" + +#ifdef ENABLE_INTERNAL_QML_RUNTIME +#include "runner/qmlruntime.h" +#endif QmlBase *getQmlRunner(int &argc, char **argv) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +#ifdef ENABLE_INTERNAL_QML_RUNTIME for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], "--qml-runtime")){ qInfo() << "Starting QML Runtime"; diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/qmlbase.h rename to src/tools/qml2puppet/qml2puppet/qmlbase.h diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.cpp rename to src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp diff --git a/src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h b/src/tools/qml2puppet/qml2puppet/qmlpuppet.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/puppet/qmlpuppet.h rename to src/tools/qml2puppet/qml2puppet/qmlpuppet.h diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h b/src/tools/qml2puppet/qml2puppet/runner/loadwatcher.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/runtime/loadwatcher.h rename to src/tools/qml2puppet/qml2puppet/runner/loadwatcher.h diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h b/src/tools/qml2puppet/qml2puppet/runner/qmlconfiguration.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/runtime/qmlconfiguration.h rename to src/tools/qml2puppet/qml2puppet/runner/qmlconfiguration.h diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.cpp rename to src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp diff --git a/src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.h similarity index 100% rename from src/tools/qml2puppet/qml2puppet/runner/runtime/qmlruntime.h rename to src/tools/qml2puppet/qml2puppet/runner/qmlruntime.h From a049dbf8d31b9d2ee9b019528aaf20814e6baf4f Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 9 Dec 2022 16:39:53 +0100 Subject: [PATCH 33/37] QmlDesigner: Fix Navigation event filter for some Linuxe Change-Id: I4d629c5ec6866e497949455ef3cd59a216ffdbb6 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Bot --- .../components/componentcore/navigation2d.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp index 487f84229c4..46c5b665f90 100644 --- a/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp +++ b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp @@ -76,19 +76,19 @@ bool Navigation2dFilter::wheelEvent(QWheelEvent *event) bool zoomChangedConnected = QObject::isSignalConnected(zoomChangedSignal); if (zoomChangedConnected) { - const double globalMouseSpeed = - QmlDesignerPlugin::settings().value(DesignerSettingsKey::EDITOR_ZOOM_FACTOR).toDouble(); - - double speed = globalMouseSpeed/20.; - if (Utils::HostOsInfo::isMacHost()) - speed = 1.0/200.; - - if (QPointF delta = event->pixelDelta(); !delta.isNull()) { + double speed = 1.0 / 200.0; + bool isMac = Utils::HostOsInfo::isMacHost(); + if (QPointF delta = event->pixelDelta(); !delta.isNull() && isMac) { double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y(); emit zoomChanged(dist * speed, event->position()); event->accept(); return true; } else if (QPointF delta = event->angleDelta(); !delta.isNull()) { + + const double globalMouseSpeed = + QmlDesignerPlugin::settings().value(DesignerSettingsKey::EDITOR_ZOOM_FACTOR).toDouble(); + speed = globalMouseSpeed / 20.0; + constexpr double degreePerStep = 15.; constexpr double stepCount = 8.; double dist = std::abs(delta.x()) > std::abs(delta.y()) ? -delta.x() : delta.y(); From 01b71195f06e6dc45664c27b171a22ccf8b48070 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 21 Dec 2022 11:53:48 +0100 Subject: [PATCH 34/37] qmlpuppet: fix include if crashpad is enabled #include should come after #define NOMINMAX not sure why it was necessary as an explicit include it should come by the crashpad includes Change-Id: I53ccdd893e9d8e5d5fadd46f5a5f5c0c21af0cc5 Reviewed-by: Thomas Hartmann --- src/tools/qml2puppet/qml2puppet/configcrashpad.h | 4 +--- src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/configcrashpad.h index aec1cbdafad..7b663b84498 100644 --- a/src/tools/qml2puppet/qml2puppet/configcrashpad.h +++ b/src/tools/qml2puppet/qml2puppet/configcrashpad.h @@ -2,9 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once -#ifdef Q_OS_WIN -#include -#endif +#include #ifdef ENABLE_QT_BREAKPAD #include diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index f87387290aa..a004a604584 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -2,22 +2,21 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "qmlpuppet.h" +#include "configcrashpad.h" #ifdef MULTILANGUAGE_TRANSLATIONPROVIDER #include #endif -#include -#include -#include - #include #include -#include "configcrashpad.h" - #include +#include +#include +#include + void QmlPuppet::initCoreApp() { // Since we always render text into an FBO, we need to globally disable From f396b0b742e13cb4ee18dbb795ba9a3772de939f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 21 Dec 2022 11:56:27 +0100 Subject: [PATCH 35/37] qmlpuppet: add .tag reading for GIT_SHA Change-Id: I29f0663b9bbc6f68c7f802fa8fef4b12e091ee61 Reviewed-by: Thomas Hartmann --- src/tools/qml2puppet/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index c9d6961a133..307a6c42c1b 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -55,6 +55,11 @@ execute_process( ERROR_VARIABLE GIT_SHA_ERROR ) +#if we are not a git repository use the .tag file +if(NOT GIT_SHA_OUTPUT) + file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA_OUTPUT) +endif() + add_definitions( -D GIT_SHA=${GIT_SHA_OUTPUT} ) extend_qtc_executable(qml2puppet From 9a7f152b9663c94cba6de1f7ecdd77d4bc37b1ba Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Tue, 20 Dec 2022 13:07:30 +0100 Subject: [PATCH 36/37] QmlDesigner: Update Figma Bridge setup doc The old figma bridge setup for qt design studio instructions got outdated. So, this was updated considering the new version. Fixes: QDS-8514 Change-Id: I3c88558e0e804039e26d2e5f6f84d3a0df83f231 Reviewed-by: Mats Honkamaa Reviewed-by: Thomas Hartmann --- .../src/qtbridge/qtbridge-figma-setup.qdoc | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc index 4d71150dcaf..33d130f3360 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc @@ -10,11 +10,19 @@ \note \QBF is included in the \l{https://www.qt.io/pricing}{\QDS Enterprise license}. - \QBF is delivered with \QDS as a developer plugin that you can install to - the Desktop version of Figma. To install the plugin, open the Plugin Manager - of Figma and press the plus button to create a new plugin. Then choose the - \e Manifest.json file that comes with \QDS. + You need both Figma and Qt accounts to use \QBF in \QDS. + To use \QBF in \QDS: + \list 1 + \li Go to \l {https://www.figma.com/community/plugin/1167809465162924409/Qt-Bridge-for-Figma} + {\QBF plugin page}. + \li Select \uicontrol {Try it out}. + \li Select your logged in Figma account. + \li After the plugin loads, select \uicontrol Run. + \li Select \uicontrol Export, to get the \e {.qtbridge} file in your + local drive. + \li In \QDS, drag the file to the \uicontrol 2D, \uicontrol 3D, \uicontrol Assets, + or \uicontrol Navigator view in an open project . + \endlist - You can launch the Figma plugin from \uicontrol Plugins > - \uicontrol Development > \uicontrol {\QBF} in Figma. + You can launch the installed Figma plugin from \uicontrol Plugins > \uicontrol {\QBF} in Figma. */ From d5cf9ffd375e23dde4c4e51f4de7590dbbe64bf3 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 21 Dec 2022 16:32:45 +0100 Subject: [PATCH 37/37] qmlpuppet: fix standalone build Change-Id: I0a7da757e78a68f65163d4e383323d34a5b70670 Reviewed-by: Reviewed-by: Tim Jenssen --- src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index a004a604584..8a3822a86e7 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG) + #include +#endif + void QmlPuppet::initCoreApp() { // Since we always render text into an FBO, we need to globally disable