From 46049bac323badde4d619df459d77f0e26cd16d8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 23 May 2022 15:17:36 +0300 Subject: [PATCH 01/15] QmlDesigner: Remove hardcoded timeout from 3D import puppet It doesn't seem that this timeout is needed. Fixes: QDS-6909 Change-Id: I7794d8548acd75fbb220a20458d091f413b5fcbb Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: --- .../qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp index 5f448b90da5..1f24aaca8f8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp @@ -77,9 +77,8 @@ void import3D(const QString &sourceAsset, const QString &outDir, const QString & } } - // Allow little time for file operations to finish - QTimer::singleShot(2000, nullptr, []() { - qApp->exit(0); + QTimer::singleShot(0, nullptr, []() { + qApp->quit(); }); } From 80ea026fd8e8dcdb5bda48f66e48a2fdf787b6be Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 May 2022 14:14:22 +0300 Subject: [PATCH 02/15] QmlDesigner: Refactor library icon generation for imported 3D assets Previously, icon generation was done at import time, but that was wasteful, as we now have image cache backed icon generation available for component library icons. Added the few remaining missing bits to support icon generation for image cache and disabled the old icon generation implementation for Qt6. A few issues in fit algorithm for preview image generation were also uncovered and fixed to make icons render scene in comparable size to the old version. Qt5 imports still generate using old way since component library 3D previews generation doesn't work on Qt5. Fixes: QDS-6205 Change-Id: I5418fa19d86e81adcd184be023f1dfbc813d0bf5 Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Samuel Ghinet Reviewed-by: Qt CI Bot --- .../qtcreator/qml/qmlpuppet/editor3d_qt6.qrc | 1 - .../mockfiles/qt5/MaterialNodeView.qml | 2 +- .../mockfiles/qt5/ModelNode3DImageView.qml | 19 +++- .../qmlpuppet/mockfiles/qt5/ModelNodeView.qml | 6 +- .../qmlpuppet/mockfiles/qt5/NodeNodeView.qml | 4 +- .../mockfiles/qt6/IconRenderer3D.qml | 88 ------------------- .../mockfiles/qt6/MaterialNodeView.qml | 2 +- .../mockfiles/qt6/ModelNode3DImageView.qml | 19 +++- .../qmlpuppet/mockfiles/qt6/ModelNodeView.qml | 4 +- .../qmlpuppet/mockfiles/qt6/NodeNodeView.qml | 4 +- .../qml2puppet/editor3d/generalhelper.cpp | 31 +++++-- .../qml2puppet/editor3d/generalhelper.h | 2 +- .../quick3drenderablenodeinstance.cpp | 11 +++ .../instances/quick3drenderablenodeinstance.h | 1 + .../itemlibrary/itemlibraryassetimporter.cpp | 30 +++++-- .../itemlibraryiconimageprovider.cpp | 4 +- .../imagecache/imagecachecollector.cpp | 13 ++- .../include/imagecacheauxiliarydata.h | 11 ++- .../metainfo/subcomponentmanager.cpp | 7 +- 19 files changed, 132 insertions(+), 127 deletions(-) delete mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc index e8d66548c32..05459423349 100644 --- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc +++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc @@ -32,7 +32,6 @@ mockfiles/qt6/FadeHandle.qml mockfiles/qt6/HelperGrid.qml mockfiles/qt6/IconGizmo.qml - mockfiles/qt6/IconRenderer3D.qml mockfiles/qt6/LightGizmo.qml mockfiles/qt6/LightIconGizmo.qml mockfiles/qt6/LightModel.qml diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index 06320524bf3..c36d8c227c3 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -32,7 +32,7 @@ View3D { property Material previewMaterial - function fitToViewPort() + function fitToViewPort(closeUp) { // No need to zoom this view, this is here just to avoid runtime warnings } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index de3e3ee431c..a6d5c6b1db5 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -41,6 +41,8 @@ Item { property var modelViewComponent property var nodeViewComponent + property bool closeUp: false + function destroyView() { previewObject = null; @@ -96,7 +98,15 @@ Item { function fitToViewPort() { - view.fitToViewPort(); + view.fitToViewPort(closeUp); + } + + // Enables/disables icon mode. When in icon mode, camera is zoomed bit closer to reduce margins + // and the background is removed, in order to generate a preview suitable for library icons. + function setIconMode(enable) + { + closeUp = enable; + backgroundRect.visible = !enable; } View3D { @@ -108,10 +118,15 @@ Item { id: contentItem anchors.fill: parent - Rectangle { + Item { id: viewRect anchors.fill: parent + } + Rectangle { + id: backgroundRect + anchors.fill: parent + z: -1 gradient: Gradient { GradientStop { position: 1.0; color: "#222222" } GradientStop { position: 0.0; color: "#999999" } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml index e61a9a8fb25..5c8b9e1cd72 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml @@ -34,11 +34,11 @@ View3D { property Model sourceModel - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin - _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root, + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml index 91008abb251..e8931719d7f 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml @@ -32,11 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml deleted file mode 100644 index 623bf21cdec..00000000000 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconRenderer3D.qml +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -import QtQuick 6.0 -import QtQuick3D 6.0 - -Item { - id: viewRoot - width: 1024 - height: 1024 - visible: true - - property alias view3D: view3D - property alias camPos: viewCamera.position - - function setSceneToBox() - { - selectionBox.targetNode = view3D.importScene; - } - - function fitAndHideBox() - { - cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true, true); - if (cameraControl._zoomFactor < 0.1) - view3D.importScene.scale = view3D.importScene.scale.times(10); - if (cameraControl._zoomFactor > 10) - view3D.importScene.scale = view3D.importScene.scale.times(0.1); - - selectionBox.visible = false; - } - - View3D { - id: view3D - camera: viewCamera - environment: sceneEnv - - SceneEnvironment { - id: sceneEnv - antialiasingMode: SceneEnvironment.MSAA - antialiasingQuality: SceneEnvironment.VeryHigh - } - - PerspectiveCamera { - id: viewCamera - position: Qt.vector3d(-200, 200, 200) - eulerRotation: Qt.vector3d(-45, -45, 0) - } - - DirectionalLight { - rotation: viewCamera.rotation - } - - SelectionBox { - id: selectionBox - view3D: view3D - geometryName: "SB" - } - - EditCameraController { - id: cameraControl - camera: view3D.camera - view3d: view3D - ignoreToolState: true - } - } -} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index aae5891e082..ed5cfea56f4 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -32,7 +32,7 @@ View3D { property Material previewMaterial - function fitToViewPort() + function fitToViewPort(closeUp) { // No need to zoom this view, this is here just to avoid runtime warnings } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 4bd07e57d12..5caac0047c4 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -41,6 +41,8 @@ Item { property var modelViewComponent property var nodeViewComponent + property bool closeUp: false + function destroyView() { previewObject = null; @@ -96,17 +98,30 @@ Item { function fitToViewPort() { - view.fitToViewPort(); + view.fitToViewPort(closeUp); + } + + // Enables/disables icon mode. When in icon mode, camera is zoomed bit closer to reduce margins + // and the background is removed, in order to generate a preview suitable for library icons. + function setIconMode(enable) + { + closeUp = enable; + backgroundRect.visible = !enable; } Item { id: contentItem anchors.fill: parent - Rectangle { + Item { id: viewRect anchors.fill: parent + } + Rectangle { + id: backgroundRect + anchors.fill: parent + z: -1 gradient: Gradient { GradientStop { position: 1.0; color: "#222222" } GradientStop { position: 0.0; color: "#999999" } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml index 19b3c4c35d7..1762e3c9a4d 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml @@ -34,11 +34,11 @@ View3D { property Model sourceModel - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, sourceModel, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml index a48eb665191..7bbfbf281ac 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml @@ -32,11 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - function fitToViewPort() + function fitToViewPort(closeUp) { // The magic number is the distance from camera default pos to origin _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, - 1040); + 1040, closeUp); } SceneEnvironment { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index 3b774c3f4a6..7e843012be6 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -308,7 +308,7 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul // and recalculating bounds for every frame is not a problem. void GeneralHelper::calculateNodeBoundsAndFocusCamera( QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance) + float defaultLookAtDistance, bool closeUp) { QVector3D minBounds; QVector3D maxBounds; @@ -317,7 +317,9 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( QVector3D extents = maxBounds - minBounds; QVector3D lookAt = minBounds + (extents / 2.f); - float maxExtent = qMax(extents.x(), qMax(extents.y(), extents.z())); + float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x()) + + qreal(extents.y()) * qreal(extents.y()) + + qreal(extents.z()) * qreal(extents.z())); // Reset camera position to default zoom QMatrix4x4 m = camera->sceneTransform(); @@ -328,9 +330,27 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( camera->setPosition(lookAt + newLookVector); - float newZoomFactor = maxExtent / 725.f; // Divisor taken from focusNodesToCamera function + // CloseUp divisor is used for icon generation, where we can allow some extreme models to go + // slightly out of bounds for better results generally. The other divisor is used for other + // previews, where the image is larger to begin with and we would also like some margin + // between preview edge and the rendered model, so we can be more conservative with the zoom. + // The divisor values are empirically selected to provide nice result. + float divisor = closeUp ? 1250.f : 1050.f; + float newZoomFactor = maxExtent / divisor; zoomCamera(viewPort, camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); + + if (auto perspectiveCamera = qobject_cast(camera)) { + // Fix camera near/far clips in case we are dealing with extreme zooms + const float cameraDist = qAbs((camera->position() - lookAt).length()); + const float minDist = cameraDist - (maxExtent / 2.f); + const float maxDist = cameraDist + (maxExtent / 2.f); + if (minDist < perspectiveCamera->clipNear() || maxDist > perspectiveCamera->clipFar()) { + perspectiveCamera->setClipNear(minDist * 0.99); + perspectiveCamera->setClipFar(maxDist * 1.01); + } + + } } // Aligns any cameras found in nodes list to a camera. @@ -823,12 +843,13 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec if (auto childNode = qobject_cast(child)) { QVector3D newMinBounds = minBounds; QVector3D newMaxBounds = maxBounds; - hasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true); + bool childHasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true); // Ignore any subtrees that do not have Model in them as we don't need those // for visual bounds calculations - if (hasModel) { + if (childHasModel) { minBoundsVec << newMinBounds; maxBoundsVec << newMaxBounds; + hasModel = true; } } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index 562848c7720..98974cfda9f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -77,7 +77,7 @@ public: bool closeUp = false); Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance); + float defaultLookAtDistance, bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp index 589f57cb5f1..cae64ad04c1 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.cpp @@ -57,6 +57,9 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer // In case this is the scene root, we need to create a dummy View3D for the scene // in preview puppets if (instanceId() == 0 && (!nodeInstanceServer()->isInformationServer())) { + nodeInstanceServer()->quickWindow()->setDefaultAlphaBuffer(true); + nodeInstanceServer()->quickWindow()->setColor(Qt::transparent); + auto helper = new QmlDesigner::Internal::GeneralHelper(); engine()->rootContext()->setContextProperty("_generalHelper", helper); @@ -199,6 +202,14 @@ QQuickItem *Quick3DRenderableNodeInstance::contentItem() const return m_dummyRootView; } +void Quick3DRenderableNodeInstance::setPropertyVariant(const PropertyName &name, + const QVariant &value) +{ + if (m_dummyRootView && name == "isLibraryIcon") + QMetaObject::invokeMethod(m_dummyRootView, "setIconMode", Q_ARG(QVariant, value)); + ObjectNodeInstance::setPropertyVariant(name, value); +} + Qt5NodeInstanceServer *Quick3DRenderableNodeInstance::qt5NodeInstanceServer() const { return qobject_cast(nodeInstanceServer()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h index ae03cb1b90f..208144cb52f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3drenderablenodeinstance.h @@ -52,6 +52,7 @@ public: QList stateInstances() const override; QQuickItem *contentItem() const override; + void setPropertyVariant(const PropertyName &name, const QVariant &value) override; protected: explicit Quick3DRenderableNodeInstance(QObject *node); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 7e22efe016f..e3aae0cb7d5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -401,7 +401,6 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) qmlInfo.append(outDir.relativeFilePath(qmlIt.filePath())); qmlInfo.append('\n'); - // Generate item library icon for qml file based on root component QFile qmlFile(qmlIt.filePath()); if (qmlFile.open(QIODevice::ReadOnly)) { QString iconFileName = outDir.path() + '/' @@ -410,6 +409,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) QString iconFileName2x = iconFileName + "@2x"; QByteArray content = qmlFile.readAll(); int braceIdx = content.indexOf('{'); + QString impVersionStr; + int impVersionMajor = -1; if (braceIdx != -1) { int nlIdx = content.lastIndexOf('\n', braceIdx); QByteArray rootItem = content.mid(nlIdx, braceIdx - nlIdx).trimmed(); @@ -423,8 +424,9 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) out << "canBeDroppedInView3D: true" << Qt::endl; file.close(); - // Add quick3D import unless it is already added - if (m_requiredImports.first().url() != "QtQuick3D") { + // Assume that all assets import the same QtQuick3D version, + // since they are being imported with same kit + if (impVersionMajor == -1) { QByteArray import3dStr{"import QtQuick3D"}; int importIdx = content.indexOf(import3dStr); if (importIdx != -1 && importIdx < braceIdx) { @@ -433,15 +435,25 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd) QByteArray versionStr = content.mid(importIdx, nlIdx - importIdx).trimmed(); // There could be 'as abc' after version, so just take first part QList parts = versionStr.split(' '); - QString impVersion; - if (parts.size() >= 1) - impVersion = QString::fromUtf8(parts[0]); - m_requiredImports.prepend(Import::createLibraryImport( - "QtQuick3D", impVersion)); + if (parts.size() >= 1) { + impVersionStr = QString::fromUtf8(parts[0]); + if (impVersionStr.isEmpty()) + impVersionMajor = 6; + else + impVersionMajor = impVersionStr.left(1).toInt(); + } } } + + // Add quick3D import unless it is already added + if (impVersionMajor > 0 + && m_requiredImports.first().url() != "QtQuick3D") { + m_requiredImports.prepend(Import::createLibraryImport( + "QtQuick3D", impVersionStr)); + } } - if (startIconProcess(24, iconFileName, qmlIt.filePath())) { + if (impVersionMajor > 0 && impVersionMajor < 6 + && startIconProcess(24, iconFileName, qmlIt.filePath())) { // Since icon is generated by external process, the file won't be // ready for asset gathering below, so assume its generation succeeds // and add it now. diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index e7dbbee0857..5144a5220fb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -92,7 +92,9 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS } }, Qt::QueuedConnection); - }); + }, + "libIcon", + ImageCache::LibraryIconAuxiliaryData{true}); return response.release(); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 530724315a0..7890086bd5f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -74,7 +74,7 @@ ImageCacheCollector::~ImageCacheCollector() = default; void ImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView state, - const ImageCache::AuxiliaryData &, + const ImageCache::AuxiliaryData &auxiliaryData, CaptureCallback captureCallback, AbortCallback abortCallback) { @@ -96,13 +96,22 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); + bool is3DRoot = !rewriterView.inErrorState() + && (rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") + || rewriterView.rootModelNode().isSubclassOf("Quick3D.Material")); + if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() - && !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) { + && !is3DRoot)) { if (abortCallback) abortCallback(ImageCache::AbortReason::Failed); return; } + if (is3DRoot) { + if (auto libIcon = Utils::get_if(&auxiliaryData)) + rewriterView.rootModelNode().setAuxiliaryData("isLibraryIcon@NodeInstance", libIcon->enable); + } + ModelNode stateNode = rewriterView.modelNodeForId(QString{state}); if (stateNode.isValid()) diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index ae87681626a..61660271fc9 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -54,7 +54,16 @@ public: QString text; }; -using AuxiliaryData = Utils::variant; +class LibraryIconAuxiliaryData +{ +public: + bool enable; +}; + +using AuxiliaryData = Utils::variant; enum class AbortReason : char { Abort, Failed }; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 9ff3289e459..342e0a65fb6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -445,10 +445,9 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const QString iconPath = qmlIt.fileInfo().absolutePath() + '/' + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name + Constants::QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX; - if (!QFileInfo::exists(iconPath)) - iconPath = defaultIconPath; - itemLibraryEntry.setLibraryEntryIconPath(iconPath); - itemLibraryEntry.setTypeIcon(QIcon(iconPath)); + if (QFileInfo::exists(iconPath)) + itemLibraryEntry.setLibraryEntryIconPath(iconPath); + itemLibraryEntry.setTypeIcon(QIcon(defaultIconPath)); // load hints file if exists QFile hintsFile(qmlIt.fileInfo().absolutePath() + '/' + name + ".hints"); From f6fdcfef536c1d4e6333cd30ddb6eebf122b5fe3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 30 May 2022 14:52:41 +0300 Subject: [PATCH 03/15] QmlDesigner: Remove a couple of work around puppet resets The issues that were worked around by these resets have been fixed. Fixes: QDS-7009 Change-Id: I7940b55c3f3edcfdcde9ad5f41cfa1d188d2bc96 Reviewed-by: Samuel Ghinet Reviewed-by: Mahmoud Badri --- .../designercore/instances/nodeinstanceview.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 2b490f5f96f..99c6b6f79cc 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -599,11 +599,9 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP // Reset puppet when particle emitter/affector is reparented to work around issue in // autodetecting the particle system it belongs to. QTBUG-101157 - // Reset is also needed when particle shapes are reparented. QTBUG-101882 - if (((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") + if ((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") || node.isSubclassOf("QtQuick.Particles3D.Affector3D")) - && node.property("system").toBindingProperty().expression().isEmpty()) - || node.isSubclassOf("QQuick3DParticleAbstractShape")) { + && node.property("system").toBindingProperty().expression().isEmpty()) { resetPuppet(); } } @@ -2179,9 +2177,8 @@ void NodeInstanceView::maybeResetOnPropertyChange(const PropertyName &name, cons { bool reset = false; if (flags & AbstractView::PropertiesAdded - && name == "model" && (node.isSubclassOf("QtQuick.Repeater") - || node.isSubclassOf("QtQuick3D.Repeater3D"))) { - // TODO: This is a workaround for QTBUG-97583 (2D) and QTBUG-97586 (3D): + && name == "model" && node.isSubclassOf("QtQuick.Repeater")) { + // TODO: This is a workaround for QTBUG-97583: // Reset puppet when repeater model is first added, if there is already a delegate if (node.hasProperty("delegate")) reset = true; From 255f7f836f6275fc9b115652c2e8306895fcc33b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 27 May 2022 15:22:39 +0200 Subject: [PATCH 04/15] StudioWelcomePage: Disable data download by default QML/Designer/EnableWelcomePageDownload is not set, no download or probing will start. Change-Id: Icadd29b2a8c0439ebd8435b987647f5d6f7167a7 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: hjk Reviewed-by: Tim Jenssen --- src/plugins/studiowelcome/examplecheckout.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index d0c3d40734c..c759f684bc1 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -57,6 +57,12 @@ using namespace Utils; +static bool enableDownload() +{ + const QString lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload"; + return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool(); +} + void ExampleCheckout::registerTypes() { static bool once = []() { @@ -186,6 +192,12 @@ bool FileDownloader::available() const void FileDownloader::probeUrl() { + if (!enableDownload()) { + m_available = false; + emit availableChanged(); + return; + } + auto request = QNetworkRequest(m_url); request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::UserVerifiedRedirectPolicy); @@ -447,6 +459,13 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) void DataModelDownloader::start() { + + if (!enableDownload()) { + m_available = false; + emit availableChanged(); + return; + } + m_fileDownloader.setUrl(QUrl::fromUserInput( "https://download.qt.io/learning/examples/qtdesignstudio/dataImports.zip")); From 449334e4199e110e5d1994c797df6130f9ea4597 Mon Sep 17 00:00:00 2001 From: Aaron Barany Date: Tue, 24 May 2022 15:30:20 -0700 Subject: [PATCH 05/15] LanguageClient: Avoid crashes when language client is invalid This can occur in situations such as clangd crashing. A similar guard is used in the perform() function, but can still crash in cancel() or handleSignatureResponse() if the client is destroyed beforehand. Fixes: QTCREATORBUG-27096 Change-Id: Ib33d306c5411bc31bc13de399e6c2ad0a89462f4 Reviewed-by: David Schulz --- src/plugins/languageclient/languageclientfunctionhint.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/languageclient/languageclientfunctionhint.cpp b/src/plugins/languageclient/languageclientfunctionhint.cpp index 9c33b632314..708fc399228 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.cpp +++ b/src/plugins/languageclient/languageclientfunctionhint.cpp @@ -103,6 +103,7 @@ IAssistProposal *FunctionHintProcessor::perform(const AssistInterface *interface void FunctionHintProcessor::cancel() { + QTC_ASSERT(m_client, return); if (running()) { m_client->cancelRequest(m_currentRequest.value()); m_client->removeAssistProcessor(this); @@ -112,6 +113,7 @@ void FunctionHintProcessor::cancel() void FunctionHintProcessor::handleSignatureResponse(const SignatureHelpRequest::Response &response) { + QTC_ASSERT(m_client, setAsyncProposalAvailable(nullptr); return); m_currentRequest.reset(); if (auto error = response.error()) m_client->log(error.value()); From 0a84ef4b2c4d0d044e6ebc99d8a1c4328b7b290c Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Wed, 25 May 2022 17:04:20 +0200 Subject: [PATCH 06/15] QmlDesigner: Refactor ImageCache Move the ImageCache from the ItemLibraryView to a more centralized location. Change-Id: Ic0721976650dad8af26c29b50fed8aec0fffbf75 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Miikka Heikkinen Reviewed-by: Marco Bubke --- .../itemlibrary/itemlibraryview.cpp | 86 ++----------------- .../components/itemlibrary/itemlibraryview.h | 13 +-- .../itemlibrary/itemlibrarywidget.cpp | 7 +- .../itemlibrary/itemlibrarywidget.h | 6 +- .../designercore/include/viewmanager.h | 5 +- .../designercore/model/viewmanager.cpp | 16 ++-- src/plugins/qmldesigner/qmldesignerplugin.cpp | 4 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 71 +++++++++++++-- .../qmldesigner/qmldesignerprojectmanager.h | 20 +++-- 9 files changed, 101 insertions(+), 127 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 9267e367935..21928ea06ae 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -30,12 +30,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -44,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -52,38 +45,9 @@ namespace QmlDesigner { -namespace { -ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) -{ - if (project) - return project->activeTarget(); - - return {}; -} -} // namespace - -class ItemLibraryView::ImageCacheData -{ -public: - Sqlite::Database database{Utils::PathString{ - Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, - Sqlite::JournalMode::Wal, - Sqlite::LockingMode::Normal}; - ImageCacheStorage storage{database}; - ImageCacheConnectionManager connectionManager; - ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}}; - ImageCacheFontCollector fontCollector; - ImageCacheGenerator generator{collector, storage}; - ImageCacheGenerator fontGenerator{fontCollector, storage}; - TimeStampProvider timeStampProvider; - AsynchronousImageCache cache{storage, generator, timeStampProvider}; - AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider}; - SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector}; -}; - -ItemLibraryView::ItemLibraryView(QObject* parent) - : AbstractView(parent) - +ItemLibraryView::ItemLibraryView(AsynchronousImageCache &imageCache) + : AbstractView() + , m_imageCache(imageCache) {} ItemLibraryView::~ItemLibraryView() @@ -97,11 +61,8 @@ bool ItemLibraryView::hasWidget() const WidgetInfo ItemLibraryView::widgetInfo() { - if (m_widget.isNull()) { - m_widget = new ItemLibraryWidget{imageCacheData()->cache, - imageCacheData()->asynchronousFontImageCache, - imageCacheData()->synchronousFontImageCache}; - } + if (m_widget.isNull()) + m_widget = new ItemLibraryWidget{m_imageCache}; return createWidgetInfo(m_widget.data(), new WidgetInfo::ToolBarWidgetDefaultFactory(m_widget.data()), @@ -182,43 +143,6 @@ void ItemLibraryView::usedImportsChanged(const QList &usedImports) m_widget->updateUsedImports(usedImports); } -ItemLibraryView::ImageCacheData *ItemLibraryView::imageCacheData() -{ - std::call_once(imageCacheFlag, [this]() { - m_imageCacheData = std::make_unique(); - auto setTargetInImageCache = - [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { - if (target == imageCacheData->collector.target()) - return; - - if (target) - imageCacheData->cache.clean(); - - imageCacheData->collector.setTarget(target); - }; - - if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { - m_imageCacheData->collector.setTarget(project->activeTarget()); - connect(project, - &ProjectExplorer::Project::activeTargetChanged, - this, - setTargetInImageCache); - } - connect(ProjectExplorer::SessionManager::instance(), - &ProjectExplorer::SessionManager::startupProjectChanged, - this, - [=](ProjectExplorer::Project *project) { - setTargetInImageCache(activeTarget(project)); - }); - }); - return m_imageCacheData.get(); -} - -AsynchronousImageCache &ItemLibraryView::imageCache() -{ - return imageCacheData()->cache; -} - void ItemLibraryView::documentMessagesChanged(const QList &errors, const QList &) { if (m_hasErrors && errors.isEmpty()) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h index b7b91eaf3f8..4a0925064fb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h @@ -29,19 +29,16 @@ #include -#include - namespace QmlDesigner { class ItemLibraryWidget; -class AsynchronousImageCache; class ItemLibraryView : public AbstractView { Q_OBJECT public: - ItemLibraryView(QObject* parent = nullptr); + ItemLibraryView(class AsynchronousImageCache &imageCache); ~ItemLibraryView() override; bool hasWidget() const override; @@ -58,17 +55,11 @@ public: void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; - AsynchronousImageCache &imageCache(); - protected: void updateImports(); private: - class ImageCacheData; - ImageCacheData *imageCacheData(); - - std::once_flag imageCacheFlag; - std::unique_ptr m_imageCacheData; + AsynchronousImageCache &m_imageCache; QPointer m_widget; bool m_hasErrors = false; QVariantMap m_importableExtensions3DMap; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 511ef37f196..75a564d7384 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -140,18 +140,13 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event) isHorizontalLayout = event->size().width() >= HORIZONTAL_LAYOUT_WIDTH_LIMIT; } -ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, - AsynchronousImageCache &asynchronousFontImageCache, - SynchronousImageCache &synchronousFontImageCache) +ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) : m_itemIconSize(24, 24) - , m_fontImageCache(synchronousFontImageCache) , m_itemLibraryModel(new ItemLibraryModel(this)) , m_addModuleModel(new ItemLibraryAddImportModel(this)) , m_itemsWidget(new QQuickWidget(this)) , m_imageCache{imageCache} { - Q_UNUSED(asynchronousFontImageCache) - m_compressionTimer.setInterval(200); m_compressionTimer.setSingleShot(true); ItemLibraryModel::registerQmlTypes(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index a7f23180730..be905f181ae 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -56,7 +56,6 @@ class Model; class ItemLibraryModel; class ItemLibraryAddImportModel; class ItemLibraryResourceView; -class SynchronousImageCache; class AsynchronousImageCache; class ImageCacheCollector; @@ -67,9 +66,7 @@ class ItemLibraryWidget : public QFrame public: Q_PROPERTY(bool subCompEditMode READ subCompEditMode NOTIFY subCompEditModeChanged) - ItemLibraryWidget(AsynchronousImageCache &imageCache, - AsynchronousImageCache &asynchronousFontImageCache, - SynchronousImageCache &synchronousFontImageCache); + ItemLibraryWidget(AsynchronousImageCache &imageCache); ~ItemLibraryWidget(); void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo); @@ -115,7 +112,6 @@ private: QTimer m_compressionTimer; QSize m_itemIconSize; - SynchronousImageCache &m_fontImageCache; QPointer m_itemLibraryInfo; QPointer m_itemLibraryModel; diff --git a/src/plugins/qmldesigner/designercore/include/viewmanager.h b/src/plugins/qmldesigner/designercore/include/viewmanager.h index 04f25b84f81..dd77c07f459 100644 --- a/src/plugins/qmldesigner/designercore/include/viewmanager.h +++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h @@ -47,12 +47,11 @@ class Edit3DView; namespace Internal { class DesignModeWidget; } class ViewManagerData; -class AsynchronousImageCache; class QMLDESIGNERCORE_EXPORT ViewManager { public: - ViewManager(); + ViewManager(class AsynchronousImageCache &imageCache); ~ViewManager(); void attachRewriterView(); @@ -107,8 +106,6 @@ public: void disableStandardViews(); void enableStandardViews(); - AsynchronousImageCache &imageCache(); - private: // functions Q_DISABLE_COPY(ViewManager) diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index 8ad61d320cf..8e96507cd53 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -62,6 +62,10 @@ static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData { public: + ViewManagerData(AsynchronousImageCache &imageCache) + : itemLibraryView(imageCache) + {} + InteractiveConnectionManager connectionManager; CapturingConnectionManager capturingConnectionManager; QmlModelState savedState; @@ -90,12 +94,13 @@ static CrumbleBar *crumbleBar() { return QmlDesignerPlugin::instance()->mainWidget()->crumbleBar(); } -ViewManager::ViewManager() - : d(std::make_unique()) +ViewManager::ViewManager(AsynchronousImageCache &imageCache) + : d(std::make_unique(imageCache)) { d->formEditorView.setGotoErrorCallback([this](int line, int column) { d->textEditorView.gotoCursorPosition(line, column); - if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget()) + if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance() + ->mainWidget()) designModeWidget->showDockWidget("TextEditor"); }); } @@ -438,11 +443,6 @@ void ViewManager::enableStandardViews() attachViewsExceptRewriterAndComponetView(); } -AsynchronousImageCache &ViewManager::imageCache() -{ - return d->itemLibraryView.imageCache(); -} - void ViewManager::addView(std::unique_ptr &&view) { d->additionalViews.push_back(std::move(view)); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index ae596bb9001..e6f40d14fc6 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -137,7 +137,7 @@ class QmlDesignerPluginPrivate { public: QmlDesignerProjectManager projectManager; - ViewManager viewManager; + ViewManager viewManager{projectManager.asynchronousImageCache()}; DocumentManager documentManager; ShortCutManager shortCutManager; SettingsPage settingsPage; @@ -670,7 +670,7 @@ void QmlDesignerPlugin::emitUsageStatisticsHelpRequested(const QString &identifi AsynchronousImageCache &QmlDesignerPlugin::imageCache() { - return m_instance->d->viewManager.imageCache(); + return m_instance->d->projectManager.asynchronousImageCache(); } void QmlDesignerPlugin::registerPreviewImageProvider(QQmlEngine *engine) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 538c3939f2f..43070221bcf 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,14 @@ namespace QmlDesigner { namespace { +ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) +{ + if (project) + return project->activeTarget(); + + return {}; +} + QString defaultImagePath() { return Core::ICore::resourcePath("qmldesigner/welcomepage/images/newThumbnail.png").toString(); @@ -89,7 +98,22 @@ public: } // namespace -class PreviewImageCacheData +class QmlDesignerProjectManager::ImageCacheData +{ +public: + Sqlite::Database database{Utils::PathString{ + Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, + Sqlite::JournalMode::Wal, + Sqlite::LockingMode::Normal}; + ImageCacheStorage storage{database}; + ImageCacheConnectionManager connectionManager; + ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}}; + ImageCacheGenerator generator{collector, storage}; + TimeStampProvider timeStampProvider; + AsynchronousImageCache asynchronousImageCache{storage, generator, timeStampProvider}; +}; + +class QmlDesignerProjectManager::PreviewImageCacheData { public: Sqlite::Database database{Utils::PathString{ @@ -100,7 +124,7 @@ public: AsynchronousExplicitImageCache cache{storage}; }; -class QmlDesignerProjectManagerProjectData +class QmlDesignerProjectManager::QmlDesignerProjectManagerProjectData { public: QmlDesignerProjectManagerProjectData(ImageCacheStorage &storage) @@ -117,7 +141,7 @@ public: }; QmlDesignerProjectManager::QmlDesignerProjectManager() - : m_imageCacheData{std::make_unique()} + : m_previewImageCacheData{std::make_unique()} { auto editorManager = ::Core::EditorManager::instance(); QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) { @@ -145,12 +169,17 @@ QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; void QmlDesignerProjectManager::registerPreviewImageProvider(QQmlEngine *engine) const { - auto imageProvider = std::make_unique(m_imageCacheData->cache, + auto imageProvider = std::make_unique(m_previewImageCacheData->cache, QImage{defaultImagePath()}); engine->addImageProvider("project_preview", imageProvider.release()); } +AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() +{ + return imageCacheData()->asynchronousImageCache; +} + void QmlDesignerProjectManager::editorOpened(::Core::IEditor *) {} void QmlDesignerProjectManager::currentEditorChanged(::Core::IEditor *) @@ -171,7 +200,7 @@ void QmlDesignerProjectManager::editorsClosed(const QList<::Core::IEditor *> &) void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) { - m_projectData = std::make_unique(m_imageCacheData->storage); + m_projectData = std::make_unique(m_previewImageCacheData->storage); m_projectData->activeTarget = project->activeTarget(); } @@ -183,4 +212,36 @@ void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project void QmlDesignerProjectManager::projectRemoved(::ProjectExplorer::Project *) {} +QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCacheData() +{ + std::call_once(imageCacheFlag, [this]() { + m_imageCacheData = std::make_unique(); + auto setTargetInImageCache = + [imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) { + if (target == imageCacheData->collector.target()) + return; + + if (target) + imageCacheData->asynchronousImageCache.clean(); + + imageCacheData->collector.setTarget(target); + }; + + if (auto project = ProjectExplorer::SessionManager::startupProject(); project) { + m_imageCacheData->collector.setTarget(project->activeTarget()); + QObject::connect(project, + &ProjectExplorer::Project::activeTargetChanged, + this, + setTargetInImageCache); + } + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, + [=](ProjectExplorer::Project *project) { + setTargetInImageCache(activeTarget(project)); + }); + }); + return m_imageCacheData.get(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index 4b993ba74d2..6b94fa9e6f4 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -26,8 +26,10 @@ #pragma once #include +#include #include +#include QT_FORWARD_DECLARE_CLASS(QQmlEngine) @@ -42,17 +44,22 @@ class Target; namespace QmlDesigner { -class QmlDesignerProjectManagerProjectData; -class PreviewImageCacheData; - -class QmlDesignerProjectManager +class QmlDesignerProjectManager : public QObject { + Q_OBJECT + + class QmlDesignerProjectManagerProjectData; + class PreviewImageCacheData; + class ImageCacheData; + public: QmlDesignerProjectManager(); ~QmlDesignerProjectManager(); void registerPreviewImageProvider(QQmlEngine *engine) const; + class AsynchronousImageCache &asynchronousImageCache(); + private: void editorOpened(::Core::IEditor *editor); void currentEditorChanged(::Core::IEditor *); @@ -60,9 +67,12 @@ private: void projectAdded(::ProjectExplorer::Project *project); void aboutToRemoveProject(::ProjectExplorer::Project *project); void projectRemoved(::ProjectExplorer::Project *project); + ImageCacheData *imageCacheData(); private: - std::unique_ptr m_imageCacheData; + std::once_flag imageCacheFlag; + std::unique_ptr m_imageCacheData; + std::unique_ptr m_previewImageCacheData; std::unique_ptr m_projectData; }; } // namespace QmlDesigner From 175343e24ac83b24bc5fa97d857c7fd0cf7c6568 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 30 May 2022 15:31:13 +0300 Subject: [PATCH 07/15] QmlDesigner: Highlight material editor properties upon asset drag When starting an asset drag in the assets view, highlight all supported properties in the material editor. Change-Id: I60935756e4c1384edcc284068163d08ebe529a05 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Samuel Ghinet Reviewed-by: Miikka Heikkinen --- .../imports/HelperWidgets/ComboBox.qml | 11 +++--- .../imports/StudioControls/ComboBox.qml | 30 +++++++++------ .../imports/StudioControls/ComboBoxInput.qml | 7 ++-- .../imports/StudioTheme/Values.qml | 1 + .../assetslibrary/assetslibrarywidget.cpp | 20 +++------- .../materialeditor/materialeditorview.cpp | 38 +++++++++++++++++++ .../materialeditor/materialeditorview.h | 4 ++ .../propertyeditor/propertyeditorvalue.cpp | 25 ++++++------ .../propertyeditor/propertyeditorvalue.h | 7 +++- .../qmldesigner/designercore/include/model.h | 7 ++-- .../qmldesigner/designercore/model/model.cpp | 4 +- 11 files changed, 101 insertions(+), 53 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 48ec9fa470a..b9d9f323169 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -41,6 +41,8 @@ StudioControls.ComboBox { onModelChanged: colorLogic.invalidate() + hasActiveDrag: comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag + // This is available in all editors. onValueTypeChanged: { @@ -83,16 +85,15 @@ StudioControls.ComboBox { onEntered: (drag) => { dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0] - - drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.isSupportedDrop(dropArea.assetPath) - comboBox.hasActiveDrag = drag.accepted + drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag + comboBox.hasActiveHoverDrag = drag.accepted } - onExited: comboBox.hasActiveDrag = false + onExited: comboBox.hasActiveHoverDrag = false onDropped: { comboBox.backendValue.commitDrop(dropArea.assetPath) - comboBox.hasActiveDrag = false + comboBox.hasActiveHoverDrag = false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml index fbd4f530626..70cbdf000ec 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml @@ -39,7 +39,8 @@ T.ComboBox { && myComboBox.enabled property bool edit: myComboBox.activeFocus && myComboBox.editable property bool open: comboBoxPopup.opened - property bool hasActiveDrag: false + property bool hasActiveDrag: false // an item that can be dropped on the combobox is being dragged + property bool hasActiveHoverDrag: false // an item that can be dropped on the combobox is being hovered on the combobox property bool dirty: false // user modification flag @@ -49,6 +50,9 @@ T.ComboBox { property alias textInput: comboBoxInput + property int borderWidth: myComboBox.hasActiveHoverDrag ? StudioTheme.Values.borderHover + : StudioTheme.Values.border + signal compressedActivated(int index, int reason) enum ActivatedReason { EditingFinished, Other } @@ -57,7 +61,7 @@ T.ComboBox { height: StudioTheme.Values.defaultControlHeight leftPadding: actionIndicator.width - rightPadding: popupIndicator.width + StudioTheme.Values.border + rightPadding: popupIndicator.width + myComboBox.borderWidth font.pixelSize: StudioTheme.Values.myFontSize wheelEnabled: false @@ -87,6 +91,7 @@ T.ComboBox { myControl: myComboBox text: myComboBox.editText + borderWidth: myComboBox.borderWidth onEditingFinished: { comboBoxInput.deselect() @@ -108,16 +113,16 @@ T.ComboBox { myControl: myComboBox myPopup: myComboBox.popup x: comboBoxInput.x + comboBoxInput.width - y: StudioTheme.Values.border - width: StudioTheme.Values.checkIndicatorWidth - StudioTheme.Values.border - height: StudioTheme.Values.checkIndicatorHeight - (StudioTheme.Values.border * 2) + y: myComboBox.borderWidth + width: StudioTheme.Values.checkIndicatorWidth - myComboBox.borderWidth + height: StudioTheme.Values.checkIndicatorHeight - myComboBox.borderWidth * 2 } background: Rectangle { id: comboBoxBackground color: StudioTheme.Values.themeControlBackground border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border + border.width: myComboBox.borderWidth x: actionIndicator.width width: myComboBox.width - actionIndicator.width height: myComboBox.height @@ -144,7 +149,7 @@ T.ComboBox { width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding - (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth + 2 : 0) // TODO Magic number - height: StudioTheme.Values.height - 2 * StudioTheme.Values.border + height: StudioTheme.Values.height - 2 * myComboBox.borderWidth padding: 0 enabled: model.enabled === undefined ? true : model.enabled @@ -198,9 +203,9 @@ T.ComboBox { popup: T.Popup { id: comboBoxPopup - x: actionIndicator.width + StudioTheme.Values.border + x: actionIndicator.width + myComboBox.borderWidth y: myComboBox.height - width: myComboBox.width - actionIndicator.width - (StudioTheme.Values.border * 2) + width: myComboBox.width - actionIndicator.width - myComboBox.borderWidth * 2 // TODO Setting the height on the popup solved the problem with the popup of height 0, // but it has the problem that it sometimes extend over the border of the actual window // and is then cut off. @@ -208,7 +213,7 @@ T.ComboBox { + comboBoxPopup.bottomPadding, myComboBox.Window.height - topMargin - bottomMargin, StudioTheme.Values.maxComboBoxPopupHeight) - padding: StudioTheme.Values.border + padding: myComboBox.borderWidth margins: 0 // If not defined margin will be -1 closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside @@ -252,8 +257,9 @@ T.ComboBox { PropertyChanges { target: comboBoxBackground color: StudioTheme.Values.themeControlBackground - border.color: hasActiveDrag ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeControlOutline + border.color: myComboBox.hasActiveDrag ? StudioTheme.Values.themeInteraction + : StudioTheme.Values.themeControlOutline + border.width: myComboBox.borderWidth } }, // This state is intended for ComboBoxes which aren't editable, but have focus e.g. via diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml index de7dcc1f8c3..c6b91dc1ad0 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml @@ -34,6 +34,7 @@ TextInput { property bool edit: textInput.activeFocus property bool hover: mouseArea.containsMouse && textInput.enabled + property int borderWidth: StudioTheme.Values.border z: 2 font: myControl.font @@ -55,11 +56,11 @@ TextInput { Rectangle { id: textInputBackground - x: StudioTheme.Values.border - y: StudioTheme.Values.border + x: textInput.borderWidth + y: textInput.borderWidth z: -1 width: textInput.width - height: StudioTheme.Values.height - (StudioTheme.Values.border * 2) + height: StudioTheme.Values.height - textInput.borderWidth * 2 color: StudioTheme.Values.themeControlBackground border.width: 0 } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 75d21a8e29b..d9bf43db746 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -86,6 +86,7 @@ QtObject { property real marginTopBottom: 4 property real border: 1 + property real borderHover: 3 property real maxComboBoxPopupHeight: Math.round(300 * values.scaleFactor) property real maxTextAreaPopupHeight: Math.round(150 * values.scaleFactor) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 221f9ec0f4c..7cf91158d65 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -81,30 +81,20 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event) if (obj == m_assetsWidget.data()) QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut"); } else if (event->type() == QMouseEvent::MouseMove) { - if (!m_assetsToDrag.isEmpty()) { + if (!m_assetsToDrag.isEmpty() && !m_model.isNull()) { QMouseEvent *me = static_cast(event); if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) { - auto drag = new QDrag(this); - drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); QMimeData *mimeData = new QMimeData; mimeData->setData(Constants::MIME_TYPE_ASSETS, m_assetsToDrag.join(',').toUtf8()); - drag->setMimeData(mimeData); - drag->exec(); - drag->deleteLater(); - + m_model->startDrag(mimeData, + m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); m_assetsToDrag.clear(); } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { m_assetsToDrag.clear(); - QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); - if (view) { - NavigatorWidget *navView = qobject_cast(view); - if (navView) { - navView->setDragType(""); - navView->update(); - } - } + if (m_model) + m_model->endDrag(); } return QObject::eventFilter(obj, event); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 1b4ce59fb50..d231ba1e510 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -29,6 +29,7 @@ #include "materialeditorcontextobject.h" #include "propertyeditorvalue.h" #include "materialeditortransaction.h" +#include "assetslibrarywidget.h" #include #include @@ -49,6 +50,7 @@ #include #include #include +#include #include #include @@ -798,6 +800,42 @@ void MaterialEditorView::customNotification(const AbstractView *view, const QStr } } +void QmlDesigner::MaterialEditorView::highlightSupportedProperties(bool highlight) +{ + DesignerPropertyMap &propMap = m_qmlBackEnd->backendValuesPropertyMap(); + const QStringList propNames = propMap.keys(); + + for (const QString &propName : propNames) { + if (propName.endsWith("Map")) { + QObject *propEditorValObj = propMap.value(propName).value(); + PropertyEditorValue *propEditorVal = qobject_cast(propEditorValObj); + propEditorVal->setHasActiveDrag(highlight); + } + } +} + +void MaterialEditorView::dragStarted(QMimeData *mimeData) +{ + if (!mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) + return; + + const QStringList assetPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); + bool isImage = Utils::anyOf(assetPaths, [] (const QString &assetPath) { + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; + return assetType == Constants::MIME_TYPE_ASSET_IMAGE; + }); + + if (!isImage) // only image assets are dnd supported + return; + + highlightSupportedProperties(); +} + +void MaterialEditorView::dragEnded() +{ + highlightSupportedProperties(false); +} + // from model to material editor void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value) { diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index 8ab30a63f44..3e8632d26f6 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -73,6 +73,9 @@ public: void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; + void dragStarted(QMimeData *mimeData) override; + void dragEnded() override; + void changeValue(const QString &name); void changeExpression(const QString &name); void exportPropertyAsAlias(const QString &name); @@ -93,6 +96,7 @@ private: static QString materialEditorResourcesPath(); void reloadQml(); + void highlightSupportedProperties(bool highlight = true); QString generateIdFromName(const QString &name); void ensureMaterialLibraryNode(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 8b1eea6dc97..1ef95365e13 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -274,6 +274,19 @@ bool PropertyEditorValue::isTranslated() const return false; } +bool PropertyEditorValue::hasActiveDrag() const +{ + return m_hasActiveDrag; +} + +void PropertyEditorValue::setHasActiveDrag(bool val) +{ + if (m_hasActiveDrag != val) { + m_hasActiveDrag = val; + emit hasActiveDragChanged(); + } +} + static bool isAllowedSubclassType(const QString &type, const QmlDesigner::NodeMetaInfo &metaInfo) { if (!metaInfo.isValid()) @@ -371,18 +384,6 @@ void PropertyEditorValue::setEnumeration(const QString &scope, const QString &na setValueWithEmit(QVariant::fromValue(newEnumeration)); } -bool PropertyEditorValue::isSupportedDrop(const QString &path) -{ - QString suffix = "*." + QFileInfo(path).suffix().toLower(); - - if (m_modelNode.isSubclassOf("QtQuick3D.Material") && nameAsQString().endsWith("Map")) - return QmlDesigner::AssetsLibraryModel::supportedImageSuffixes().contains(suffix); - - // TODO: handle support for other object properties dnd here (like image source) - - return false; -} - void PropertyEditorValue::exportPropertyAsAlias() { emit exportPropertyAsAliasRequested(nameAsQString()); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 2a2b09d5c8c..4ae156dc921 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -82,6 +82,7 @@ class PropertyEditorValue : public QObject Q_PROPERTY(bool isBound READ isBound NOTIFY isBoundChanged FINAL) Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged FINAL) Q_PROPERTY(bool isTranslated READ isTranslated NOTIFY expressionChanged FINAL) + Q_PROPERTY(bool hasActiveDrag READ hasActiveDrag WRITE setHasActiveDrag NOTIFY hasActiveDragChanged FINAL) Q_PROPERTY(bool isIdList READ isIdList NOTIFY expressionChanged FINAL) Q_PROPERTY(QStringList expressionAsList READ getExpressionAsList NOTIFY expressionChanged FINAL) @@ -117,6 +118,9 @@ public: bool isTranslated() const; + bool hasActiveDrag() const; + void setHasActiveDrag(bool val); + bool isAvailable() const; QmlDesigner::PropertyName name() const; @@ -148,7 +152,6 @@ public: public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); - bool isSupportedDrop(const QString &path); signals: void valueChanged(const QString &name, const QVariant&); @@ -164,6 +167,7 @@ signals: void isBoundChanged(); void isValidChanged(); void isExplicitChanged(); + void hasActiveDragChanged(); private: QStringList generateStringList(const QString &string) const; @@ -176,6 +180,7 @@ private: bool m_isInSubState; bool m_isInModel; bool m_isBound; + bool m_hasActiveDrag = false; bool m_isValid; // if the property value belongs to a non-existing complexProperty it is invalid PropertyEditorNodeWrapper *m_complexNode; }; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 9a2bc14fe63..6b8dfab4314 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -35,6 +35,7 @@ #include QT_BEGIN_NAMESPACE +class QPixmap; class QUrl; QT_END_NAMESPACE @@ -44,7 +45,7 @@ namespace Internal { class ModelPrivate; class WriteLocker; class NodeMetaInfoPrivate; -} //Internal +} // namespace Internal class AnchorLine; class ModelNode; @@ -130,7 +131,7 @@ public: QString generateNewId(const QString &prefixName) const; QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; - void startDrag(QMimeData *mimeData, const QString iconPath = {}); + void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); protected: @@ -140,4 +141,4 @@ private: Internal::ModelPrivate *d; }; -} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d879271c0d4..4e38fb3cfb3 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1510,12 +1510,12 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP return newId; } -void Model::startDrag(QMimeData *mimeData, const QString iconPath) +void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) { d->notifyDragStarted(mimeData); auto drag = new QDrag(this); - drag->setPixmap(iconPath); + drag->setPixmap(icon); drag->setMimeData(mimeData); drag->exec(); drag->deleteLater(); From 9b5c8448f02da7dd6d444823c45d2c85be8fb5d4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 30 May 2022 13:13:44 +0300 Subject: [PATCH 08/15] QmlDesigner: Fix 3D application wizard quick3d import version handling Change-Id: I80eecdcd5ac9938715b084bd8b89c82d62aa7323 Reviewed-by: Thomas Hartmann Reviewed-by: --- .../studio_templates/projects/application-3d/wizard.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index ae3719243e6..cd1b310c711 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -243,21 +243,24 @@ "trKey": "Qt 5", "value": "({ - 'TargetQuickVersion': '2.15' + 'TargetQuickVersion': '2.15', + 'TargetQuick3DVersion': '1.15' })" }, { "trKey": "Qt 6.2", "value": "({ - 'TargetQuickVersion': '6.2' + 'TargetQuickVersion': '6.2', + 'TargetQuick3DVersion': '6.2' })" }, { "trKey": "Qt 6.3", "value": "({ - 'TargetQuickVersion': '6.3' + 'TargetQuickVersion': '6.3', + 'TargetQuick3DVersion': '6.3' })" } ] From fc2f3983bc40184a855401871ab894c903a3f212 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 23 May 2022 10:16:39 +0200 Subject: [PATCH 09/15] QtcProcess: Don't merge ready read signals We store pointers to signal objects inside m_signals list, so we can't easily merge the old ready read signal with the new one, since when flushing the m_signals from the main thread we are taking copy of m_signals, but this copy still holds the pointers to the original objects, and using them outside of locked mutex isn't safe. The possible solution would be to store simple data structures instead of signal objects allocated on heap. However, there is not really much gain of merging the ready read signals, so we get rid of it. Task-number: QTCREATORBUG-27578 Change-Id: Idd19a6fc1ebb4ccab1e4e367cfeb3f2f02a35512 Reviewed-by: Reviewed-by: hjk Reviewed-by: Eike Ziller --- src/libs/utils/launchersocket.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp index c232579e20d..d2ee1cf19fd 100644 --- a/src/libs/utils/launchersocket.cpp +++ b/src/libs/utils/launchersocket.cpp @@ -83,10 +83,6 @@ public: , m_stdErr(stdErr) {} QByteArray stdOut() const { return m_stdOut; } QByteArray stdErr() const { return m_stdErr; } - void mergeWith(ReadyReadSignal *newSignal) { - m_stdOut += newSignal->stdOut(); - m_stdErr += newSignal->stdErr(); - } private: QByteArray m_stdOut; QByteArray m_stdErr; @@ -268,16 +264,6 @@ void CallerHandle::appendSignal(LauncherSignal *launcherSignal) QMutexLocker locker(&m_mutex); QTC_ASSERT(isCalledFromLaunchersThread(), return); - // Merge ReadyRead signals into one. - if (launcherSignal->signalType() == CallerHandle::SignalType::ReadyRead - && !m_signals.isEmpty() - && m_signals.last()->signalType() == CallerHandle::SignalType::ReadyRead) { - ReadyReadSignal *lastSignal = static_cast(m_signals.last()); - ReadyReadSignal *newSignal = static_cast(launcherSignal); - lastSignal->mergeWith(newSignal); - delete newSignal; - return; - } m_signals.append(launcherSignal); } From c7891864182f610d30c8b67601e3f4617d14a7eb Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 20 May 2022 15:24:22 +0300 Subject: [PATCH 10/15] QmlDesigner: Handle picking of models under View3D component properly If a model defined inside the View3D component is picked on 3D editor, the parent View3D is selected instead as there is no instance for the model itself. This is similar to how Node based component picking works. Fixes: QDS-6934 Change-Id: I4f273972da8cb1c55f03cab323dd9804a5d10def Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../qmlpuppet/mockfiles/qt5/EditView3D.qml | 11 +- .../qmlpuppet/mockfiles/qt6/EditView3D.qml | 11 +- .../qml2puppet/editor3d/generalhelper.cpp | 10 +- .../qml2puppet/editor3d/generalhelper.h | 2 +- .../instances/nodeinstanceserver.cpp | 4 + .../qml2puppet/instances/nodeinstanceserver.h | 1 + .../qt5informationnodeinstanceserver.cpp | 136 ++++++++++++++++-- .../qt5informationnodeinstanceserver.h | 1 + .../instances/servernodeinstance.cpp | 4 + 9 files changed, 161 insertions(+), 19 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml index 904a062a623..cb9ece58b2d 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml @@ -329,6 +329,15 @@ Item { function handleObjectClicked(object, multi) { + if (object instanceof View3D) { + // View3D can be the resolved pick target in case the 3D editor is showing content + // of a component that has View3D as root. In that case locking is resolved on C++ side + // and we ignore multiselection. + selectObjects([]); + selectionChanged([object]); + return; + } + var clickedObject; // Click on locked object is treated same as click on empty space @@ -740,7 +749,7 @@ Item { handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), mouse.modifiers & Qt.ControlModifier); - if (pickResult.objectHit) { + if (pickResult.objectHit && pickResult.objectHit instanceof Node) { if (transformMode === EditView3D.TransformMode.Move) freeDraggerArea = moveGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Rotate) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml index 25297f8fc67..3431a1ce3c0 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml @@ -329,6 +329,15 @@ Item { function handleObjectClicked(object, multi) { + if (object instanceof View3D) { + // View3D can be the resolved pick target in case the 3D editor is showing content + // of a component that has View3D as root. In that case locking is resolved on C++ side + // and we ignore multiselection. + selectObjects([]); + selectionChanged([object]); + return; + } + var clickedObject; // Click on locked object is treated same as click on empty space @@ -892,7 +901,7 @@ Item { handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier); - if (pickResult.objectHit) { + if (pickResult.objectHit && pickResult.objectHit instanceof Node) { if (transformMode === EditView3D.TransformMode.Move) freeDraggerArea = moveGizmo.freeDraggerArea; else if (transformMode === EditView3D.TransformMode.Rotate) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index 7e843012be6..dd21622b5dc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -438,15 +438,15 @@ QQuick3DPickResult GeneralHelper::pickViewAt(QQuick3DViewport *view, float posX, return QQuick3DPickResult(); } -QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode) +QObject *GeneralHelper::resolvePick(QQuick3DNode *pickNode) { if (pickNode) { - // Check if the picked node actually specifies another node as the pick target + // Check if the picked node actually specifies another object as the pick target QVariant componentVar = pickNode->property("_pickTarget"); if (componentVar.isValid()) { - auto componentNode = componentVar.value(); - if (componentNode) - return componentNode; + auto componentObj = componentVar.value(); + if (componentObj) + return componentObj; } } return pickNode; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index 98974cfda9f..5bb1fa1662f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -85,7 +85,7 @@ public: Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property, const QVariant& value); Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY); - Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode); + Q_INVOKABLE QObject *resolvePick(QQuick3DNode *pickNode); Q_INVOKABLE bool isLocked(QQuick3DNode *node) const; Q_INVOKABLE bool isHidden(QQuick3DNode *node) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 8400c3d4474..41c67f70b68 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1561,6 +1561,10 @@ void NodeInstanceServer::handleInstanceHidden(const ServerNodeInstance &/*instan { } +void NodeInstanceServer::handlePickTarget(const ServerNodeInstance &/*instance*/) +{ +} + void NodeInstanceServer::setupState(qint32 stateInstanceId) { if (hasInstanceForId(stateInstanceId)) { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index ba51f5e5888..42f345ee4e5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -226,6 +226,7 @@ public: virtual void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors); virtual void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors); + virtual void handlePickTarget(const ServerNodeInstance &instance); virtual QImage grabWindow() = 0; virtual QImage grabItem(QQuickItem *item) = 0; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c3fc4d4e076..975a6ded6b9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -553,7 +553,18 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob auto obj = object.value(); if (obj) { ServerNodeInstance instance = instanceForObject(obj); - instanceList << instance; + // If instance is a View3D, make sure it is not locked + bool locked = false; + if (instance.isSubclassOf("QQuick3DViewport")) { + locked = instance.internalInstance()->isLockedInEditor(); + auto parentInst = instance.parent(); + while (!locked && parentInst.isValid()) { + locked = parentInst.internalInstance()->isLockedInEditor(); + parentInst = parentInst.parent(); + } + } + if (!locked) + instanceList << instance; #ifdef QUICK3D_PARTICLES_MODULE if (!skipSystemDeselect) { auto particleSystem = parentParticleSystem(instance.internalObject()); @@ -1448,8 +1459,9 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout() void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() { -#ifdef QUICK3D_MODULE for (auto obj : std::as_const(m_dynamicObjectConstructors)) { +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) +#ifdef QUICK3D_MODULE auto handleHiding = [this](QQuick3DNode *node) -> bool { if (node && hasInstanceForObject(node)) { ServerNodeInstance instance = instanceForObject(node); @@ -1464,8 +1476,22 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() if (auto pickTarget = obj->property("_pickTarget").value()) handleHiding(pickTarget); } - } #endif +#else + auto handlePicking = [this](QObject *object) -> bool { + if (object && hasInstanceForObject(object)) { + ServerNodeInstance instance = instanceForObject(object); + handlePickTarget(instance); + return true; + } + return false; + }; + if (!handlePicking(obj)) { + if (auto pickTarget = obj->property("_pickTarget").value()) + handlePicking(pickTarget); + } +#endif + } m_dynamicObjectConstructors.clear(); } @@ -2328,9 +2354,9 @@ void Qt5InformationNodeInstanceServer::handleInstanceLocked(const ServerNodeInst } } #else - Q_UNUSED(instance); - Q_UNUSED(enable); - Q_UNUSED(checkAncestors); + Q_UNUSED(instance) + Q_UNUSED(enable) + Q_UNUSED(checkAncestors) #endif } @@ -2384,6 +2410,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst // Don't override explicit hide in children handleInstanceHidden(quick3dInstance, edit3dHidden || isInstanceHidden, false); } else { +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) // Children of components do not have instances, but will still need to be pickable std::function checkChildren; checkChildren = [&](QQuick3DNode *checkNode) { @@ -2398,9 +2425,7 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst value = QVariant::fromValue(node); // Specify the actual pick target with dynamic property checkModel->setProperty("_pickTarget", value); -#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) checkModel->setPickable(!edit3dHidden); -#endif } else { auto checkRepeater = qobject_cast(checkNode); auto checkLoader = qobject_cast(checkNode); @@ -2432,13 +2457,102 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst }; if (auto childNode = qobject_cast(childItem)) checkChildren(childNode); +#endif } } } #else - Q_UNUSED(instance); - Q_UNUSED(enable); - Q_UNUSED(checkAncestors); + Q_UNUSED(instance) + Q_UNUSED(enable) + Q_UNUSED(checkAncestors) +#endif +} + +void Qt5InformationNodeInstanceServer::handlePickTarget(const ServerNodeInstance &instance) +{ +#if defined(QUICK3D_MODULE) && (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) + // Picking is dependent on hidden status prior to global picking support (<6.2.1), so it is + // handled in handleInstanceHidden() method in those builds + + if (!ViewConfig::isQuick3DMode()) + return; + + QObject *obj = instance.internalObject(); + QList childItems; + if (auto node = qobject_cast(obj)) { + childItems = node->childItems(); + } else if (auto view = qobject_cast(obj)) { + // We only need to handle views that are components + // View is a component if its scene is not an instance and scene has node children that + // have no instances + QQuick3DNode *node = view->scene(); + if (node) { + if (hasInstanceForObject(node)) + return; + childItems = node->childItems(); + bool allHaveInstance = true; + for (const auto &childItem : childItems) { + if (qobject_cast(childItem) && !hasInstanceForObject(childItem)) { + allHaveInstance = false; + break; + } + } + if (allHaveInstance) + return; + } + } else { + return; + } + + for (auto childItem : qAsConst(childItems)) { + if (!hasInstanceForObject(childItem)) { + // Children of components do not have instances, but will still need to be pickable + // and redirect their pick to the component + std::function checkChildren; + checkChildren = [&](QQuick3DNode *checkNode) { + const auto childItems = checkNode->childItems(); + for (auto child : childItems) { + if (auto childNode = qobject_cast(child)) + checkChildren(childNode); + } + if (auto checkModel = qobject_cast(checkNode)) { + // Specify the actual pick target with dynamic property + checkModel->setProperty("_pickTarget", QVariant::fromValue(obj)); + } else { + auto checkRepeater = qobject_cast(checkNode); + auto checkLoader = qobject_cast(checkNode); +#if defined(QUICK3D_ASSET_UTILS_MODULE) + auto checkRunLoader = qobject_cast(checkNode); + if (checkRepeater || checkLoader || checkRunLoader) { +#else + if (checkRepeater || checkLoader) { +#endif + // Repeaters/loaders may not yet have created their children, so we set + // _pickTarget on them and connect the notifier. + if (checkNode->property("_pickTarget").isNull()) { + if (checkRepeater) { + QObject::connect(checkRepeater, &QQuick3DRepeater::objectAdded, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); +#if defined(QUICK3D_ASSET_UTILS_MODULE) + } else if (checkRunLoader) { + QObject::connect(checkRunLoader, &QQuick3DRuntimeLoader::statusChanged, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); +#endif + } else { + QObject::connect(checkLoader, &QQuick3DLoader::loaded, + this, &Qt5InformationNodeInstanceServer::handleDynamicAddObject); + } + } + checkNode->setProperty("_pickTarget", QVariant::fromValue(obj)); + } + } + }; + if (auto childNode = qobject_cast(childItem)) + checkChildren(childNode); + } + } +#else + Q_UNUSED(instance) #endif } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index b6375076b66..291b2c3eecd 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -80,6 +80,7 @@ public: void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override; void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors) override; + void handlePickTarget(const ServerNodeInstance &instance) override; bool isInformationServer() const override; void handleDynamicAddObject(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp index a4fdde97ec2..2f4be3e8e9b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp @@ -323,8 +323,12 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags()); +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1) // Handle hidden state to initialize pickable state nodeInstanceServer->handleInstanceHidden(instance, false, false); +#else + nodeInstanceServer->handlePickTarget(instance); +#endif return instance; } From c9b140eb7e10bf583f69b88f4c2049233b94e6db Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 31 May 2022 15:49:50 +0300 Subject: [PATCH 11/15] QmlDesigner: Correct asset to material editor dnd behavior Only check the first asset when dnd multiple assets to be in sync with the dnd handling in the QML side after drop. Change-Id: I32ecf6be87284dc8e32f51f674ba082fab7cad7a Reviewed-by: Miikka Heikkinen --- .../components/materialeditor/materialeditorview.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index d231ba1e510..29b07bb3b6d 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -50,7 +50,6 @@ #include #include #include -#include #include #include @@ -819,13 +818,10 @@ void MaterialEditorView::dragStarted(QMimeData *mimeData) if (!mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) return; - const QStringList assetPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); - bool isImage = Utils::anyOf(assetPaths, [] (const QString &assetPath) { - QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; - return assetType == Constants::MIME_TYPE_ASSET_IMAGE; - }); + const QString assetPath = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',')[0]; + QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; - if (!isImage) // only image assets are dnd supported + if (assetType != Constants::MIME_TYPE_ASSET_IMAGE) // currently only image assets have dnd-supported properties return; highlightSupportedProperties(); From d99a6d2efe3b56263835b67cc32d30d3bd0963dc Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 31 May 2022 16:08:50 +0200 Subject: [PATCH 12/15] QmlDesigner: Fix wizards Since we define an import for QtQuick to distinguish between Qt 6.2 and Qt 6.3, the way we determine "IsQt6Project" was broken. This patch reuses "IsQt6Project" now to determine the default style name and if we need a versions for imports. Task-number: QDS-7053 Change-Id: I7b5fc0b76e71c2874f5e6ac8457ba1deb87bb999 Reviewed-by: Miikka Heikkinen --- .../projects/application-3d/wizard.json | 10 ++++++---- .../projects/application/wizard.json | 10 +++++----- .../projects/desktop-launcher/wizard.json | 11 ++++++----- .../projects/mobile-scroll/wizard.json | 12 ++++++------ .../projects/mobile-stack/wizard.json | 10 ++++++---- .../projects/mobile-swipe/wizard.json | 10 ++++++---- 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index cd1b310c711..9d60367997a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -17,21 +17,23 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, { "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 51acc18af26..3e7ef11beb7 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -20,20 +20,20 @@ { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: value('QtQuickVersion') === '' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "DefaultStyle", "value": "%{JS: value('QtQuickVersion') === '' ? 'Basic' : 'Default'}" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "DefaultStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? 'Basic' : 'Default'}" }, + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index f687b2ce385..20a3baba9c7 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -20,17 +20,19 @@ { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": @@ -235,7 +237,6 @@ "data": { "index": 1, - "items": "items": [ { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index d12fc38bdc4..3117c7e0544 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -18,19 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, - { "key": "UIClassName", "value": "Screen01" }, - { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index 1b76c290359..cb083e84f4b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -18,17 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index 19e5865fb83..1c1137afddb 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -18,17 +18,19 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, - { "key": "QtQuickControlsStyle", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt5", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyle}" }, + { "key": "QtQuickControlsStyleInternalQt6", "value": "%{JS: value('QtQuickControlsStyleInternalQt5') === 'Default' ? 'Basic' : value('QtQuickControlsStyleInternalQt5')}" }, + { "key": "QtQuickControlsStyle", "value": "%{JS: value('IsQt6Project') === 'true' ? value('QtQuickControlsStyleInternalQt6') : value('QtQuickControlsStyleInternalQt5')}" }, { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, - { "key": "ApplicationImport", "value": "%{JS: value('QtQuickVersion') === '' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "UseVirtualKeyboard", "value": "%{JS: false}" }, - { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') === '' }" }, - { "key": "ImportModuleVersion", "value": "%{JS: value('QtQuickVersion') === '' ? '' : '1.0'}" } + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } ], "pages": From c3b90c0ce5e7c97fdac0d09619ab16ce71dafb14 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Tue, 31 May 2022 16:19:29 +0300 Subject: [PATCH 13/15] Allow changing the 3D Editor's background color Task-number: QDS-6585 Change-Id: Ibcac69d3792b521b29dfbdce2d49557d36de99a9 Reviewed-by: Mahmoud Badri --- .../qmlpuppet/commands/createscenecommand.h | 9 +- .../commands/view3dactioncommand.cpp | 38 ++++--- .../qmlpuppet/commands/view3dactioncommand.h | 9 +- .../nodeinstanceserverinterface.cpp | 1 + .../qmlpuppet/mockfiles/qt5/EditView3D.qml | 15 ++- .../qmlpuppet/mockfiles/qt6/EditView3D.qml | 15 ++- .../qt5informationnodeinstanceserver.cpp | 48 +++++--- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../edit3d/backgroundcolorselection.cpp | 103 ++++++++++++++++++ .../edit3d/backgroundcolorselection.h | 47 ++++++++ .../qmldesigner/components/edit3d/edit3d.qrc | 2 + .../components/edit3d/edit3dactions.cpp | 7 +- .../components/edit3d/edit3dview.cpp | 61 +++++++++++ .../components/edit3d/edit3dview.h | 5 + .../components/edit3d/edit3dwidget.cpp | 18 +++ .../components/edit3d/edit3dwidget.h | 4 + .../edit3d/images/color_palette.png | Bin 0 -> 363 bytes .../edit3d/images/color_palette@2x.png | Bin 0 -> 713 bytes .../instances/nodeinstanceview.cpp | 21 +++- src/plugins/qmldesigner/designersettings.cpp | 1 + src/plugins/qmldesigner/designersettings.h | 1 + .../qmldesigner/qmldesignerconstants.h | 3 + src/plugins/qmldesigner/qmldesignericons.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 24 files changed, 369 insertions(+), 44 deletions(-) create mode 100644 src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h create mode 100644 src/plugins/qmldesigner/components/edit3d/images/color_palette.png create mode 100644 src/plugins/qmldesigner/components/edit3d/images/color_palette@2x.png diff --git a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h index 1eb6ba5018d..c69d478875d 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include "instancecontainer.h" @@ -58,7 +60,8 @@ public: const QString &language, QSize captureImageMinimumSize, QSize captureImageMaximumSize, - qint32 stateInstanceId) + qint32 stateInstanceId, + const QList &edit3dBackgroundColor) : instances(instanceContainer) , reparentInstances(reparentContainer) , ids(idVector) @@ -74,6 +77,7 @@ public: , captureImageMinimumSize(captureImageMinimumSize) , captureImageMaximumSize(captureImageMaximumSize) , stateInstanceId{stateInstanceId} + , edit3dBackgroundColor{edit3dBackgroundColor} {} friend QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) @@ -93,6 +97,7 @@ public: out << command.stateInstanceId; out << command.captureImageMinimumSize; out << command.captureImageMaximumSize; + out << command.edit3dBackgroundColor; return out; } @@ -114,6 +119,7 @@ public: in >> command.stateInstanceId; in >> command.captureImageMinimumSize; in >> command.captureImageMaximumSize; + in >> command.edit3dBackgroundColor; return in; } @@ -134,6 +140,7 @@ public: QSize captureImageMinimumSize; QSize captureImageMaximumSize; qint32 stateInstanceId = 0; + QList edit3dBackgroundColor; }; QDebug operator<<(QDebug debug, const CreateSceneCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp index 211fe6d54bd..c32f7b0ffe9 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp +++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.cpp @@ -30,24 +30,27 @@ namespace QmlDesigner { -View3DActionCommand::View3DActionCommand(Type type, bool enable) +View3DActionCommand::View3DActionCommand(Type type, const QVariant &value) : m_type(type) - , m_enabled(enable) - , m_position(0) + , m_value(value) { } View3DActionCommand::View3DActionCommand(int pos) : m_type(ParticlesSeek) - , m_enabled(true) - , m_position(pos) + , m_value(pos) { } bool View3DActionCommand::isEnabled() const { - return m_enabled; + return m_value.toBool(); +} + +QVariant View3DActionCommand::value() const +{ + return m_value; } View3DActionCommand::Type View3DActionCommand::type() const @@ -57,29 +60,32 @@ View3DActionCommand::Type View3DActionCommand::type() const int View3DActionCommand::position() const { - return m_position; + bool ok = false; + int result = m_value.toInt(&ok); + if (!ok) { + qWarning() << "View3DActionCommand: returning a position that is not int; command type = " + << m_type; + } + + return result; } QDataStream &operator<<(QDataStream &out, const View3DActionCommand &command) { - out << qint32(command.isEnabled()); + out << command.value(); out << qint32(command.type()); - out << qint32(command.position()); return out; } QDataStream &operator>>(QDataStream &in, View3DActionCommand &command) { - qint32 enabled; + QVariant value; qint32 type; - qint32 pos; - in >> enabled; + in >> value; in >> type; - in >> pos; - command.m_enabled = bool(enabled); + command.m_value = value; command.m_type = View3DActionCommand::Type(type); - command.m_position = pos; return in; } @@ -88,7 +94,7 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command) { return debug.nospace() << "View3DActionCommand(type: " << command.m_type << "," - << command.m_enabled << ")"; + << command.m_value << ")\n"; } } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h index cb34c253f9e..cc3611df764 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h @@ -26,6 +26,7 @@ #pragma once #include +#include namespace QmlDesigner { @@ -55,20 +56,22 @@ public: ParticlesPlay, ParticlesRestart, ParticlesSeek, + SelectBackgroundColor, + ResetBackgroundColor, }; - explicit View3DActionCommand(Type type, bool enable); + explicit View3DActionCommand(Type type, const QVariant &value); View3DActionCommand() = default; bool isEnabled() const; + QVariant value() const; Type type() const; int position() const; private: Type m_type = Empty; - bool m_enabled = false; - int m_position = 0; + QVariant m_value; protected: View3DActionCommand(int pos); diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp index 413906267ac..a8b3c319408 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp @@ -142,6 +142,7 @@ void NodeInstanceServerInterface::registerCommands() registerCommand("View3DActionCommand"); registerCommand("RequestModelNodePreviewImageCommand"); registerCommand>("QPairIntInt"); + registerCommand>("QColorList"); registerCommand("ChangeLanguageCommand"); registerCommand("ChangePreviewImageSizeCommand"); registerCommand("CapturedDataCommand"); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml index cb9ece58b2d..9a95ca34b98 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml @@ -45,6 +45,8 @@ Item { property bool usePerspective: true property bool globalOrientation: false property alias contentItem: contentItem + property color backgroundGradientColorStart: "#222222" + property color backgroundGradientColorEnd: "#999999" enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -212,6 +214,15 @@ Item { cameraControl.alignView(selectedNodes); } + function updateViewStates(viewStates) + { + if ("selectBackgroundColor" in viewStates) { + var color = viewStates.selectBackgroundColor + backgroundGradientColorStart = color[0]; + backgroundGradientColorEnd = color[1]; + } + } + // If resetToDefault is true, tool states not specifically set to anything will be reset to // their default state. function updateToolStates(toolStates, resetToDefault) @@ -730,8 +741,8 @@ Item { anchors.fill: parent gradient: Gradient { - GradientStop { position: 1.0; color: "#222222" } - GradientStop { position: 0.0; color: "#999999" } + GradientStop { position: 1.0; color: backgroundGradientColorStart } + GradientStop { position: 0.0; color: backgroundGradientColorEnd } } MouseArea { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml index 3431a1ce3c0..228154a9a49 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml @@ -46,6 +46,8 @@ Item { property bool usePerspective: true property bool globalOrientation: false property alias contentItem: contentItem + property color backgroundGradientColorStart: "#222222" + property color backgroundGradientColorEnd: "#999999" enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -206,6 +208,15 @@ Item { cameraControl.alignView(selectedNodes); } + function updateViewStates(viewStates) + { + if ("selectBackgroundColor" in viewStates) { + var color = viewStates.selectBackgroundColor + backgroundGradientColorStart = color[0]; + backgroundGradientColorEnd = color[1]; + } + } + // If resetToDefault is true, tool states not specifically set to anything will be reset to // their default state. function updateToolStates(toolStates, resetToDefault) @@ -875,8 +886,8 @@ Item { anchors.fill: parent gradient: Gradient { - GradientStop { position: 1.0; color: "#222222" } - GradientStop { position: 0.0; color: "#999999" } + GradientStop { position: 1.0; color: backgroundGradientColorStart } + GradientStop { position: 0.0; color: backgroundGradientColorEnd } } MouseArea { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 975a6ded6b9..4b369bb1c5a 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1954,6 +1954,12 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com #ifdef IMPORT_QUICK3D_ASSETS QTimer::singleShot(0, this, &Qt5InformationNodeInstanceServer::resolveImportSupport); #endif + + if (!command.edit3dBackgroundColor.isEmpty()) { + View3DActionCommand backgroundColorCommand(View3DActionCommand::SelectBackgroundColor, + QVariant::fromValue(command.edit3dBackgroundColor)); + view3DAction(backgroundColorCommand); + } } void Qt5InformationNodeInstanceServer::sendChildrenChangedCommand(const QList &childList) @@ -2169,18 +2175,19 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c if (!m_editView3DSetupDone) return; - QVariantMap updatedState; + QVariantMap updatedToolState; + QVariantMap updatedViewState; int renderCount = 1; switch (command.type()) { case View3DActionCommand::MoveTool: - updatedState.insert("transformMode", 0); + updatedToolState.insert("transformMode", 0); break; case View3DActionCommand::RotateTool: - updatedState.insert("transformMode", 1); + updatedToolState.insert("transformMode", 1); break; case View3DActionCommand::ScaleTool: - updatedState.insert("transformMode", 2); + updatedToolState.insert("transformMode", 2); break; case View3DActionCommand::FitToView: QMetaObject::invokeMethod(m_editView3DData.rootItem, "fitToView"); @@ -2192,38 +2199,42 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignViewToCamera"); break; case View3DActionCommand::SelectionModeToggle: - updatedState.insert("selectionMode", command.isEnabled() ? 1 : 0); + updatedToolState.insert("selectionMode", command.isEnabled() ? 1 : 0); break; case View3DActionCommand::CameraToggle: - updatedState.insert("usePerspective", command.isEnabled()); + updatedToolState.insert("usePerspective", command.isEnabled()); // It can take a couple frames to properly update icon gizmo positions renderCount = 2; break; case View3DActionCommand::OrientationToggle: - updatedState.insert("globalOrientation", command.isEnabled()); + updatedToolState.insert("globalOrientation", command.isEnabled()); break; case View3DActionCommand::EditLightToggle: - updatedState.insert("showEditLight", command.isEnabled()); + updatedToolState.insert("showEditLight", command.isEnabled()); break; case View3DActionCommand::ShowGrid: - updatedState.insert("showGrid", command.isEnabled()); + updatedToolState.insert("showGrid", command.isEnabled()); break; case View3DActionCommand::ShowSelectionBox: - updatedState.insert("showSelectionBox", command.isEnabled()); + updatedToolState.insert("showSelectionBox", command.isEnabled()); break; case View3DActionCommand::ShowIconGizmo: - updatedState.insert("showIconGizmo", command.isEnabled()); + updatedToolState.insert("showIconGizmo", command.isEnabled()); break; case View3DActionCommand::ShowCameraFrustum: - updatedState.insert("showCameraFrustum", command.isEnabled()); + updatedToolState.insert("showCameraFrustum", command.isEnabled()); break; + case View3DActionCommand::SelectBackgroundColor: { + updatedViewState.insert("selectBackgroundColor", command.value()); + break; + } #ifdef QUICK3D_PARTICLES_MODULE case View3DActionCommand::ShowParticleEmitter: - updatedState.insert("showParticleEmitter", command.isEnabled()); + updatedToolState.insert("showParticleEmitter", command.isEnabled()); break; case View3DActionCommand::ParticlesPlay: m_particleAnimationPlaying = command.isEnabled(); - updatedState.insert("particlePlay", command.isEnabled()); + updatedToolState.insert("particlePlay", command.isEnabled()); if (m_particleAnimationPlaying) { m_particleAnimationDriver->play(); m_particleAnimationDriver->setSeekerEnabled(false); @@ -2249,12 +2260,17 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c break; } - if (!updatedState.isEmpty()) { + if (!updatedToolState.isEmpty()) { QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateToolStates", - Q_ARG(QVariant, updatedState), + Q_ARG(QVariant, updatedToolState), Q_ARG(QVariant, QVariant::fromValue(false))); } + if (!updatedViewState.isEmpty()) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateViewStates", + Q_ARG(QVariant, updatedViewState)); + } + render3DEditView(renderCount); } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6a13cfd2211..9f2c3e091cb 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -162,6 +162,7 @@ extend_qtc_plugin(QmlDesigner edit3dcanvas.cpp edit3dcanvas.h edit3dactions.cpp edit3dactions.h edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h + backgroundcolorselection.cpp backgroundcolorselection.h edit3d.qrc ) diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp new file mode 100644 index 00000000000..c947d8e1559 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "backgroundcolorselection.h" + +#include +#include +#include +#include + +using namespace QmlDesigner; + +namespace { +QList readBackgroundColorConfiguration() +{ + QVariant var = QmlDesigner::DesignerSettings::getValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); + + if (!var.isValid()) + return {}; + + auto colorNameList = var.value>(); + QTC_ASSERT(colorNameList.size() == 2, return {}); + + return {colorNameList[0], colorNameList[1]}; +} + +void setBackgroundColorConfiguration(const QList &colorConfig) +{ + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor, + QVariant::fromValue(colorConfig)); + view->view3DAction(cmd); +} + +void saveBackgroundColorConfiguration(const QList &colorConfig) +{ + QList colorsSaved = {colorConfig[0].name(), colorConfig[1].name()}; + QmlDesigner::DesignerSettings::setValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, + QVariant::fromValue(colorsSaved)); +} + +} // namespace + +QPointer BackgroundColorSelection::createDialog(QWidget *parent) +{ + auto dialog = new QColorDialog(parent); + + dialog->setModal(true); + dialog->setAttribute(Qt::WA_DeleteOnClose); + + const QList oldColorConfig = readBackgroundColorConfiguration(); + + dialog->show(); + + QObject::connect(dialog, &QColorDialog::currentColorChanged, dialog, [](const QColor &color) { + setBackgroundColorConfiguration({color, color}); + }); + + QObject::connect(dialog, &QColorDialog::colorSelected, dialog, [](const QColor &color) { + saveBackgroundColorConfiguration({color, color}); + }); + + if (!oldColorConfig.isEmpty()) { + QObject::connect(dialog, &QColorDialog::rejected, dialog, [oldColorConfig]() { + setBackgroundColorConfiguration(oldColorConfig); + }); + } + + return dialog; +} + +void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *parent) +{ + if (m_dialog) + return; + + m_dialog = BackgroundColorSelection::createDialog(parent); + QTC_ASSERT(m_dialog, return); +} diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h new file mode 100644 index 00000000000..d90693f03c5 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace QmlDesigner { +class BackgroundColorSelection : public QObject +{ + Q_OBJECT + +public: + explicit BackgroundColorSelection(QObject *parent = nullptr) + : QObject{parent} + {} + + static void showBackgroundColorSelectionWidget(QWidget *parent); + +private: + static QPointer createDialog(QWidget *parent); + inline static QPointer m_dialog; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3d.qrc b/src/plugins/qmldesigner/components/edit3d/edit3d.qrc index bf042b645b2..5784ac0e446 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3d.qrc +++ b/src/plugins/qmldesigner/components/edit3d/edit3d.qrc @@ -44,5 +44,7 @@ images/align_camera_on@2x.png images/align_view_on.png images/align_view_on@2x.png + images/color_palette.png + images/color_palette@2x.png diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index c5240a0cbd6..76343584c9c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -48,9 +48,10 @@ Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, void Edit3DActionTemplate::actionTriggered(bool b) { - if (m_type != View3DActionCommand::Empty) { - QmlDesignerPlugin::instance()->viewManager().nodeInstanceView() - ->view3DAction(View3DActionCommand(m_type, b)); + if (m_type != View3DActionCommand::Empty && m_type != View3DActionCommand::SelectBackgroundColor) { + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(m_type, b); + view->view3DAction(cmd); } if (m_action) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 6102ce475e5..28b6cc93cc7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -42,6 +42,8 @@ #include #include +#include + #include #include @@ -336,6 +338,32 @@ void Edit3DView::createEdit3DActions() QKeySequence(Qt::Key_G), true, true, {}, {}, nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); + SelectionContextOperation showBackgroundColorSelection = [this](const SelectionContext &) { + BackgroundColorSelection::showBackgroundColorSelectionWidget(edit3DWidget()); + }; + + m_backgroundColorSelectionAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, View3DActionCommand::SelectBackgroundColor, + QCoreApplication::translate("SelectBackgroundColorAction", "Select Background color"), + {}, false, false, {}, {}, showBackgroundColorSelection, + QCoreApplication::translate("SelectBackgroundColorAction", "Choose a color for the background.")); + + m_resetBackgroundColorAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, View3DActionCommand::ResetBackgroundColor, + QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color"), + {}, false, false, {}, {}, [](const SelectionContext &) { + QList colors = {QRgb(0x222222), QRgb(0x999999)}; + auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); + View3DActionCommand cmd(View3DActionCommand::SelectBackgroundColor, QVariant::fromValue(colors)); + view->view3DAction(cmd); + + QList colorsToSave = {colors[0].name(), colors[1].name()}; + QmlDesigner::DesignerSettings::setValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, + QVariant::fromValue(colorsToSave)); + }, + QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color to the default value.")); + m_showSelectionBoxAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionCommand::ShowSelectionBox, QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"), @@ -438,6 +466,29 @@ void Edit3DView::createEdit3DActions() QKeySequence(), false, false, Utils::Icons::EYE_OPEN_TOOLBAR.icon(), {}, visibilityTogglesTrigger); + SelectionContextOperation backgroundColorActionsTrigger = [this](const SelectionContext &) { + if (!edit3DWidget()->backgroundColorMenu()) + return; + + QPoint pos; + const auto &actionWidgets = m_backgrondColorMenuAction->action()->associatedWidgets(); + for (auto actionWidget : actionWidgets) { + if (auto button = qobject_cast(actionWidget)) { + pos = button->mapToGlobal(QPoint(0, 0)); + break; + } + } + + edit3DWidget()->showBackgroundColorMenu(!edit3DWidget()->backgroundColorMenu()->isVisible(), + pos); + }; + + m_backgrondColorMenuAction = new Edit3DAction( + QmlDesigner::Constants::EDIT3D_BACKGROUND_COLOR_ACTIONS, View3DActionCommand::Empty, + QCoreApplication::translate("BackgroundColorMenuActions", "Background Color Actions"), + QKeySequence(), false, false, Icons::COLOR_PALETTE.icon(), + {}, backgroundColorActionsTrigger); + m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -455,6 +506,8 @@ void Edit3DView::createEdit3DActions() m_leftActions << m_alignViewAction; m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction; + m_leftActions << nullptr; + m_leftActions << m_backgrondColorMenuAction; m_rightActions << m_particleViewModeAction; m_rightActions << m_particlesPlayAction; @@ -467,6 +520,9 @@ void Edit3DView::createEdit3DActions() m_visibilityToggleActions << m_showIconGizmoAction; m_visibilityToggleActions << m_showCameraFrustumAction; m_visibilityToggleActions << m_showParticleEmitterAction; + + m_backgroundColorActions << m_backgroundColorSelectionAction; + m_backgroundColorActions << m_resetBackgroundColorAction; } QVector Edit3DView::leftActions() const @@ -484,6 +540,11 @@ QVector Edit3DView::visibilityToggleActions() const return m_visibilityToggleActions; } +QVector Edit3DView::backgroundColorActions() const +{ + return m_backgroundColorActions; +} + 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 40e21708734..e5cb2aba51b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -74,6 +74,7 @@ public: QVector leftActions() const; QVector rightActions() const; QVector visibilityToggleActions() const; + QVector backgroundColorActions() const; void setSeeker(SeekerSlider *slider); void addQuick3DImport(); @@ -88,6 +89,7 @@ private: QVector m_leftActions; QVector m_rightActions; QVector m_visibilityToggleActions; + QVector m_backgroundColorActions; Edit3DAction *m_selectionModeAction = nullptr; Edit3DAction *m_moveToolAction = nullptr; Edit3DAction *m_rotateToolAction = nullptr; @@ -99,6 +101,8 @@ private: Edit3DAction *m_orientationModeAction = nullptr; Edit3DAction *m_editLightAction = nullptr; Edit3DAction *m_showGridAction = nullptr; + Edit3DAction *m_backgroundColorSelectionAction = nullptr; + Edit3DAction *m_resetBackgroundColorAction = nullptr; Edit3DAction *m_showSelectionBoxAction = nullptr; Edit3DAction *m_showIconGizmoAction = nullptr; Edit3DAction *m_showCameraFrustumAction = nullptr; @@ -108,6 +112,7 @@ private: Edit3DAction *m_particlesPlayAction = nullptr; Edit3DAction *m_particlesRestartAction = nullptr; Edit3DAction *m_visibilityTogglesAction = nullptr; + Edit3DAction *m_backgrondColorMenuAction = nullptr; SeekerSlider *m_seeker = nullptr; int particlemode; ModelCache m_canvasCache; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 72cb0ec21c8..6f685123cfd 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -141,6 +141,9 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : m_visibilityTogglesMenu = new Edit3DVisibilityTogglesMenu(this); handleActions(view->visibilityToggleActions(), m_visibilityTogglesMenu, false); + m_backgroundColorMenu = new Edit3DVisibilityTogglesMenu(this); + handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); + view->setSeeker(seeker); seeker->setToolTip(QLatin1String("Seek particle system time when paused.")); @@ -201,6 +204,21 @@ void Edit3DWidget::showVisibilityTogglesMenu(bool show, const QPoint &pos) m_visibilityTogglesMenu->close(); } +QMenu *Edit3DWidget::backgroundColorMenu() const +{ + return m_backgroundColorMenu.data(); +} + +void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) +{ + if (m_backgroundColorMenu.isNull()) + return; + if (show) + m_backgroundColorMenu->popup(pos); + else + m_backgroundColorMenu->close(); +} + void Edit3DWidget::linkActivated(const QString &link) { Q_UNUSED(link) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 5a4ed48e28e..7d40fd54715 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -51,6 +51,9 @@ public: QMenu *visibilityTogglesMenu() const; void showVisibilityTogglesMenu(bool show, const QPoint &pos); + QMenu *backgroundColorMenu() const; + void showBackgroundColorMenu(bool show, const QPoint &pos); + protected: void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; void dropEvent(QDropEvent *dropEvent) override; @@ -65,6 +68,7 @@ private: QPointer m_toolBox; Core::IContext *m_context = nullptr; QPointer m_visibilityTogglesMenu; + QPointer m_backgroundColorMenu; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/images/color_palette.png b/src/plugins/qmldesigner/components/edit3d/images/color_palette.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5035b3658b17f874c6d8207b647886572f17cc GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUgxKMnugliMmjpC`ub)D2G*vgHfClv=H|8*7V%M0NpW$>@$q?CSp_*c z#rgTAg@t8BMdiiCRb^#06&3Z>)eSW@&Gq%&9Uc8WJ(K$TCr_9#b<(71lP6D~GG*?p zS@UMkUNnFH;spzqEL^yB(V}IG7cXDB^yZBl_wU{N@#DwOpFjWp{rm6VzyJUL+h=XO z$iTqR<>}%WA|c6okkhFtfPwXZ`jkt{GnP)>5_3zm^j-b-G$sZChK2(c>%H&lxN~f~ z>U~PZ|EgGG{L;7o_r1FvQhn&t$3L=S;^*^QHs4{`ZmGKEDfx;r}hy1V;( zdM5PtPMJ7y#?+~EX3ktNckZJ3^Or7Kv~2O>HLF&wU%Phe=FQu-Y}vVe`>q{3_UziV zclYjnd-fdIx9{No{YMTRI(Fp9@uNpi96NUM`0-OGPMkV<^7N@w=gyuzf9~AH3l}b5 zx^(r*m1|e8UcYwj#`WvBZr;3e`}UjHuiw3U_x}C+pFe;8`t|Gg@85s_{{8pw-~a#r z6?~?sKV_ znakY!xp(jU;&+?pu>Vy4qrF0Ova@6Qreu@Qe}W-l40Ajg)_;<%I2L+>-RMa_UW8tZal1ares&^4Nix+5(UW_ zzKng$IcFT)q#8vIrJuFqu>B_XM0>*YoZb8pl@s4PJgF?MN;t^rHktia$p7zO>dvvV Y2O3Y`Vs&^G0|Nttr>mdKI;Vst0O#yQN&o-= literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 99c6b6f79cc..73d15136db6 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -982,6 +982,17 @@ QList filterNodesForSkipItems(const QList &nodeList) return filteredNodeList; } +QList readBackgroundColorConfiguration(const QVariant &var) +{ + if (!var.isValid()) + return {}; + + auto colorNameList = var.value>(); + QTC_ASSERT(colorNameList.size() == 2, return {}); + + return {colorNameList[0], colorNameList[1]}; +} + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList nodeList = allModelNodes(); @@ -1136,6 +1147,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0)) stateInstanceId = stateNode.internalId(); + auto value = QmlDesigner::DesignerSettings::getValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); + + QList edit3dBackgroundColor; + if (value.isValid()) + edit3dBackgroundColor = readBackgroundColorConfiguration(value); + return CreateSceneCommand( instanceContainerList, reparentContainerList, @@ -1156,7 +1174,8 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, - stateInstanceId); + stateInstanceId, + edit3dBackgroundColor); } ClearSceneCommand NodeInstanceView::createClearSceneCommand() const diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index aad91e06b48..9c1aec1b364 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -80,6 +80,7 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); restoreValue(settings, DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER, false); restoreValue(settings, DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, true); + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QList{"#222222", "#999999"}); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index e3fd4470da0..8c249fc65e0 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -49,6 +49,7 @@ const char WARNING_FOR_QML_FILES_INSTEAD_OF_UIQML_FILES[] = "WarnAboutQmlFilesIn const char WARNING_FOR_DESIGNER_FEATURES_IN_EDITOR[] = "WarnAboutQtQuickDesignerFeaturesInCodeEditor"; const char SHOW_DEBUGVIEW[] = "ShowQtQuickDesignerDebugView"; const char ENABLE_DEBUGVIEW[] = "EnableQtQuickDesignerDebugView"; +const char EDIT3DVIEW_BACKGROUND_COLOR[] = "Edit3DViewBackgroundColor"; const char ALWAYS_SAVE_IN_CRUMBLEBAR[] = "AlwaysSaveInCrumbleBar"; const char USE_DEFAULT_PUPPET[] = "UseDefaultQml2Puppet"; const char PUPPET_TOPLEVEL_BUILD_DIRECTORY[] = "PuppetToplevelBuildDirectory"; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index efefc381a1f..8dc2b947496 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -65,6 +65,8 @@ const char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle"; const char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; const char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; +const char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; +const char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.ResetBackgroundColor"; const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox"; const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo"; const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum"; @@ -74,6 +76,7 @@ const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeTo const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; +const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; diff --git a/src/plugins/qmldesigner/qmldesignericons.h b/src/plugins/qmldesigner/qmldesignericons.h index 75ec4ab2d3f..437e420a48e 100644 --- a/src/plugins/qmldesigner/qmldesignericons.h +++ b/src/plugins/qmldesigner/qmldesignericons.h @@ -91,6 +91,8 @@ const Utils::Icon EDIT3D_ALIGN_CAMERA_ON({ {":/edit3d/images/align_camera_on.png", Utils::Theme::IconsBaseColor}}); const Utils::Icon EDIT3D_ALIGN_VIEW_ON({ {":/edit3d/images/align_view_on.png", Utils::Theme::IconsBaseColor}}); +const Utils::Icon COLOR_PALETTE({ + {":/edit3d/images/color_palette.png", Utils::Theme::IconsBaseColor}}); } // Icons } // QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 1326701ef72..2d0055b8c93 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -526,6 +526,8 @@ Project { "debugview/debugviewwidget.ui", "edit3d/edit3dview.cpp", "edit3d/edit3dview.h", + "edit3d/backgroundcolorselection.cpp", + "edit3d/backgroundcolorselection.h", "edit3d/edit3dwidget.cpp", "edit3d/edit3dwidget.h", "edit3d/edit3dcanvas.cpp", From e5ce1f98f70bc8838a73ae854def5cb198d250b0 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Wed, 1 Jun 2022 14:56:12 +0300 Subject: [PATCH 14/15] Fix build error: Allow changing the 3D Editor's background color Task-number: QDS-6585 Change-Id: Id87e5403a3c6d398aa9ebf85cc97cc4c9bf7a2a2 Reviewed-by: Mahmoud Badri --- .../components/edit3d/backgroundcolorselection.cpp | 6 +++++- .../components/edit3d/backgroundcolorselection.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp index c947d8e1559..76db41a177e 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.cpp @@ -65,7 +65,7 @@ void saveBackgroundColorConfiguration(const QList &colorConfig) } // namespace -QPointer BackgroundColorSelection::createDialog(QWidget *parent) +QColorDialog *BackgroundColorSelection::createDialog(QWidget *parent) { auto dialog = new QColorDialog(parent); @@ -100,4 +100,8 @@ void BackgroundColorSelection::showBackgroundColorSelectionWidget(QWidget *paren m_dialog = BackgroundColorSelection::createDialog(parent); QTC_ASSERT(m_dialog, return); + + QObject::connect(m_dialog, &QWidget::destroyed, m_dialog, [&]() { + m_dialog = nullptr; + }); } diff --git a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h index d90693f03c5..d8832f40fda 100644 --- a/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h +++ b/src/plugins/qmldesigner/components/edit3d/backgroundcolorselection.h @@ -40,8 +40,8 @@ public: static void showBackgroundColorSelectionWidget(QWidget *parent); private: - static QPointer createDialog(QWidget *parent); - inline static QPointer m_dialog; + static QColorDialog *createDialog(QWidget *parent); + inline static QColorDialog *m_dialog = nullptr; }; } // namespace QmlDesigner From 6469f99995d3328e3292245ac4925b526b456f46 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 2 Jun 2022 08:03:19 +0200 Subject: [PATCH 15/15] QmlDesigner: Fix build with tests Amends c3b90c0ce5e. Change-Id: I52eee2e9bcf78a1fb7315927efc861115b96a16a Reviewed-by: Thomas Hartmann --- .../designercore/instances/nodeinstanceview.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 73d15136db6..1cb07d95ed1 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1147,9 +1147,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0)) stateInstanceId = stateNode.internalId(); - auto value = QmlDesigner::DesignerSettings::getValue( - QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); - + auto value +#ifndef QMLDESIGNER_TEST + = QmlDesigner::DesignerSettings::getValue( + QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); +#else + = QColor(); +#endif QList edit3dBackgroundColor; if (value.isValid()) edit3dBackgroundColor = readBackgroundColorConfiguration(value);