From f146b846cd4f7d6175ff1bc9cd4c6ac46438c8a3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 2 Feb 2022 17:56:16 +0200 Subject: [PATCH] QmlDesigner: Implement proper preview for pure 3D scenes Refactored the existing 3D node preview generation to be done synchronously, which enables their use also for creating state and item library previews for 3D nodes with ease. Fixes: QDS-5785 Change-Id: Ib493eccbc239f33bcad3301673a865494616a901 Reviewed-by: Reviewed-by: Thomas Hartmann --- .../mockfiles/qt5/MaterialNodeView.qml | 5 + .../mockfiles/qt5/ModelNode3DImageView.qml | 19 +-- .../qmlpuppet/mockfiles/qt5/ModelNodeView.qml | 29 +--- .../qmlpuppet/mockfiles/qt5/NodeNodeView.qml | 39 +---- .../mockfiles/qt6/MaterialNodeView.qml | 5 + .../mockfiles/qt6/ModelNode3DImageView.qml | 19 +-- .../qmlpuppet/mockfiles/qt6/ModelNodeView.qml | 30 +--- .../qmlpuppet/mockfiles/qt6/NodeNodeView.qml | 39 +---- .../qml2puppet/editor3d/generalhelper.cpp | 159 ++++++++++++++++++ .../qml2puppet/editor3d/generalhelper.h | 5 + .../instances/nodeinstanceserver.cpp | 5 + .../qml2puppet/instances/nodeinstanceserver.h | 2 + .../instances/objectnodeinstance.cpp | 5 + .../qml2puppet/instances/objectnodeinstance.h | 1 + .../qt5captureimagenodeinstanceserver.cpp | 9 +- .../qt5informationnodeinstanceserver.cpp | 108 +++++------- .../qt5informationnodeinstanceserver.h | 22 --- .../instances/qt5nodeinstanceserver.h | 2 +- .../qt5previewnodeinstanceserver.cpp | 5 + .../instances/qt5previewnodeinstanceserver.h | 1 + .../instances/quick3dnodeinstance.cpp | 119 +++++++++++++ .../instances/quick3dnodeinstance.h | 10 ++ .../instances/quickitemnodeinstance.cpp | 5 + .../instances/quickitemnodeinstance.h | 1 + .../instances/servernodeinstance.cpp | 2 +- .../itemlibrary/itemlibraryitem.cpp | 3 +- .../previewtooltip/previewimagetooltip.ui | 10 +- .../imagecache/imagecachecollector.cpp | 3 +- .../metainfo/subcomponentmanager.cpp | 1 + 29 files changed, 420 insertions(+), 243 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index f52bcf4bee9..48ed09cba70 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -32,6 +32,11 @@ View3D { property Material previewMaterial + function fitToViewPort() + { + // No need to zoom this view, this is here just to avoid runtime warnings + } + SceneEnvironment { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index 324a020d6fa..de3e3ee431c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -41,8 +41,6 @@ Item { property var modelViewComponent property var nodeViewComponent - property bool ready: false - function destroyView() { previewObject = null; @@ -58,8 +56,6 @@ Item { createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); - - previewObject = obj; } function createViewForMaterial(material) @@ -70,6 +66,8 @@ Item { // Always recreate the view to ensure material is up to date if (materialViewComponent.status === Component.Ready) view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); + + previewObject = material; } function createViewForModel(model) @@ -80,6 +78,8 @@ Item { // Always recreate the view to ensure model is up to date if (modelViewComponent.status === Component.Ready) view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); + + previewObject = model; } function createViewForNode(node) @@ -90,16 +90,13 @@ Item { // Always recreate the view to ensure node is up to date if (nodeViewComponent.status === Component.Ready) view = nodeViewComponent.createObject(viewRect, {"importScene": node}); + + previewObject = node; } - function afterRender() + function fitToViewPort() { - if (previewObject instanceof Node) { - view.fitToViewPort(); - ready = view.ready; - } else { - ready = true; - } + view.fitToViewPort(); } View3D { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml index dc10f441e1c..e61a9a8fb25 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml @@ -32,24 +32,13 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property real prevZoomFactor: -1 property Model sourceModel function fitToViewPort() { - cameraControl.focusObject(model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - model.scale = model.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - model.scale = model.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -58,14 +47,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 @@ -75,15 +56,15 @@ View3D { id: theCamera z: 600 y: 600 + x: 600 eulerRotation.x: -45 + eulerRotation.y: -45 clipFar: 10000 clipNear: 1 } Model { id: model - eulerRotation.y: 45 - source: sourceModel.source geometry: sourceModel.geometry diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml index b41c74af54e..91008abb251 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml @@ -32,30 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property bool first: true - property real prevZoomFactor: -1 - function fitToViewPort() { - if (first) { - first = false; - selectionBox.targetNode = root.importScene; - } else { - cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - root.importScene.scale = root.importScene.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - root.importScene.scale = root.importScene.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - selectionBox.visible = false; - } - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -64,20 +45,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - SelectionBox { - id: selectionBox - view3D: root - geometryName: "NodeNodeViewSB" - } - - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index 6103df98c2b..0b6c7bd2144 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -32,6 +32,11 @@ View3D { property Material previewMaterial + function fitToViewPort() + { + // No need to zoom this view, this is here just to avoid runtime warnings + } + SceneEnvironment { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 36d4cea8553..4bd07e57d12 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -41,8 +41,6 @@ Item { property var modelViewComponent property var nodeViewComponent - property bool ready: false - function destroyView() { previewObject = null; @@ -58,8 +56,6 @@ Item { createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); - - previewObject = obj; } function createViewForMaterial(material) @@ -70,6 +66,8 @@ Item { // Always recreate the view to ensure material is up to date if (materialViewComponent.status === Component.Ready) view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); + + previewObject = material; } function createViewForModel(model) @@ -80,6 +78,8 @@ Item { // Always recreate the view to ensure model is up to date if (modelViewComponent.status === Component.Ready) view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); + + previewObject = model; } function createViewForNode(node) @@ -90,16 +90,13 @@ Item { // Always recreate the view to ensure node is up to date if (nodeViewComponent.status === Component.Ready) view = nodeViewComponent.createObject(viewRect, {"importScene": node}); + + previewObject = node; } - function afterRender() + function fitToViewPort() { - if (previewObject instanceof Node) { - view.fitToViewPort(); - ready = view.ready; - } else { - ready = true; - } + view.fitToViewPort(); } Item { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml index 7fe6a114163..d6574f660ca 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml @@ -32,24 +32,13 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property real prevZoomFactor: -1 property Model sourceModel function fitToViewPort() { - cameraControl.focusObject(model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - model.scale = model.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - model.scale = model.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -58,14 +47,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 @@ -75,16 +56,15 @@ View3D { id: theCamera z: 600 y: 600 + x: 600 eulerRotation.x: -45 + eulerRotation.y: -45 clipFar: 10000 clipNear: 1 } Model { id: model - readonly property bool _edit3dLocked: true // Make this non-pickable - eulerRotation.y: 45 - source: sourceModel.source geometry: sourceModel.geometry diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml index 49d88d4c8a9..a48eb665191 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml @@ -32,30 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property bool first: true - property real prevZoomFactor: -1 - function fitToViewPort() { - if (first) { - first = false; - selectionBox.targetNode = root.importScene; - } else { - cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - root.importScene.scale = root.importScene.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - root.importScene.scale = root.importScene.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - selectionBox.visible = false; - } - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -64,20 +45,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - SelectionBox { - id: selectionBox - view3D: root - geometryName: "NodeNodeViewSB" - } - - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index f0131c80895..9d7d4b05ce9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -55,6 +55,11 @@ const QString _globalStateId = QStringLiteral("@GTS"); // global tool state const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _rootSizeKey = QStringLiteral("rootSize"); +static const float floatMin = std::numeric_limits::lowest(); +static const float floatMax = std::numeric_limits::max(); +static const QVector3D maxVec = QVector3D(floatMax, floatMax, floatMax); +static const QVector3D minVec = QVector3D(floatMin, floatMin, floatMin); + GeneralHelper::GeneralHelper() : QObject() { @@ -269,6 +274,37 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul return QVector4D(lookAt, cameraZoomFactor); } +// This function can be used to synchronously focus camera on a node, which doesn't have to be +// a selection box for bound calculations to work. This is used to focus the view for +// various preview image generations, where doing things asynchronously is not good +// and recalculating bounds for every frame is not a problem. +void GeneralHelper::calculateNodeBoundsAndFocusCamera( + QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, + float defaultLookAtDistance) +{ + QVector3D minBounds; + QVector3D maxBounds; + + getBounds(viewPort, node, minBounds, maxBounds); + + QVector3D extents = maxBounds - minBounds; + QVector3D lookAt = minBounds + (extents / 2.f); + float maxExtent = qMax(extents.x(), qMax(extents.y(), extents.z())); + + // Reset camera position to default zoom + QMatrix4x4 m = camera->sceneTransform(); + const float *dataPtr(m.data()); + QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]); + newLookVector.normalize(); + newLookVector *= defaultLookAtDistance; + + camera->setPosition(lookAt + newLookVector); + + float newZoomFactor = maxExtent / 725.f; // Divisor taken from focusNodesToCamera function + + zoomCamera(viewPort, camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); +} + // Aligns any cameras found in nodes list to a camera. // Only position and rotation are copied, rest of the camera properties stay the same. void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) @@ -727,6 +763,129 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const return mat44::getPosition(sceneTransform); } +// Calculate bounds for given node, including all child nodes. +// Returns true if the tree contains at least one Model node. +bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds, + QVector3D &maxBounds, bool recursive) +{ + if (!node) { + const float halfExtent = 100.f; + minBounds = {-halfExtent, -halfExtent, -halfExtent}; + maxBounds = {halfExtent, halfExtent, halfExtent}; + return false; + } + + QMatrix4x4 localTransform; + auto nodePriv = QQuick3DObjectPrivate::get(node); + auto renderNode = static_cast(nodePriv->spatialNode); + + if (recursive && renderNode) { + if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) + renderNode->calculateLocalTransform(); + localTransform = renderNode->localTransform; + } + + QVector3D localMinBounds = maxVec; + QVector3D localMaxBounds = minVec; + + // Find bounds for children + QVector minBoundsVec; + QVector maxBoundsVec; + const auto children = node->childItems(); + bool hasModel = false; + for (const auto child : children) { + if (auto childNode = qobject_cast(child)) { + QVector3D newMinBounds = minBounds; + QVector3D newMaxBounds = maxBounds; + hasModel = 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) { + minBoundsVec << newMinBounds; + maxBoundsVec << newMaxBounds; + } + } + } + + auto combineMinBounds = [](QVector3D &target, const QVector3D &source) { + target.setX(qMin(source.x(), target.x())); + target.setY(qMin(source.y(), target.y())); + target.setZ(qMin(source.z(), target.z())); + }; + auto combineMaxBounds = [](QVector3D &target, const QVector3D &source) { + target.setX(qMax(source.x(), target.x())); + target.setY(qMax(source.y(), target.y())); + target.setZ(qMax(source.z(), target.z())); + }; + auto transformCorner = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget, + const QVector3D &corner) { + QVector3D mappedCorner = m.map(corner); + combineMinBounds(minTarget, mappedCorner); + combineMaxBounds(maxTarget, mappedCorner); + }; + auto transformCorners = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget, + const QVector3D &minCorner, const QVector3D &maxCorner) { + transformCorner(m, minTarget, maxTarget, minCorner); + transformCorner(m, minTarget, maxTarget, maxCorner); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), minCorner.y(), maxCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), maxCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), maxCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), maxCorner.z())); + }; + + // Combine all child bounds + for (const auto &newBounds : qAsConst(minBoundsVec)) + combineMinBounds(localMinBounds, newBounds); + for (const auto &newBounds : qAsConst(maxBoundsVec)) + combineMaxBounds(localMaxBounds, newBounds); + + if (qobject_cast(node)) { + if (auto renderModel = static_cast(renderNode)) { + QWindow *window = static_cast(view3D->window()); + if (window) { + QSSGRef context; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); +#else + context = QQuick3DObjectPrivate::get(node)->sceneManager->rci; +#endif + if (!context.isNull()) { + auto bufferManager = context->bufferManager(); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) + QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); +#else + QSSGBounds3 bounds = bufferManager->getModelBounds(renderModel); +#endif + QVector3D center = bounds.center(); + QVector3D extents = bounds.extents(); + QVector3D localMin = center - extents; + QVector3D localMax = center + extents; + + combineMinBounds(localMinBounds, localMin); + combineMaxBounds(localMaxBounds, localMax); + + hasModel = true; + } + } + } + } else { + combineMinBounds(localMinBounds, {}); + combineMaxBounds(localMaxBounds, {}); + } + + if (localMaxBounds == minVec) { + localMinBounds = {}; + localMaxBounds = {}; + } + + // Transform local space bounding box to parent space + transformCorners(localTransform, minBounds, maxBounds, localMinBounds, localMaxBounds); + + return hasModel; +} + } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index 06068601537..46e6019dd58 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -72,6 +72,9 @@ public: const QVariant &nodes, QQuick3DViewport *viewPort, float oldZoom, bool updateZoom = true, bool closeUp = false); + Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint); @@ -126,6 +129,8 @@ protected: private: void handlePendingToolStateUpdate(); QVector3D pivotScenePosition(QQuick3DNode *node) const; + bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds, + QVector3D &maxBounds, bool recursive = false); QTimer m_overlayUpdateTimer; QTimer m_toolStateUpdateTimer; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index b37486f0481..d496aff3889 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1588,6 +1588,11 @@ bool NodeInstanceServer::isInformationServer() const return false; } +bool NodeInstanceServer::isPreviewServer() const +{ + return false; +} + static QString baseProperty(const QString &property) { int index = property.indexOf('.'); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index f2255bd891d..ba51f5e5888 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -229,8 +229,10 @@ public: virtual QImage grabWindow() = 0; virtual QImage grabItem(QQuickItem *item) = 0; + virtual bool renderWindow() = 0; virtual bool isInformationServer() const; + virtual bool isPreviewServer() const; void addAnimation(QQuickAbstractAnimation *animation); QVector animations() const; QVariant animationDefaultValue(int index) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 4eb8cdd65f4..9f3a1d6b3bc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -180,6 +180,11 @@ bool ObjectNodeInstance::isLayoutable() const return false; } +bool ObjectNodeInstance::isRenderable() const +{ + return false; +} + bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const { return false; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h index 67e1663496c..88e2ca26770 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h @@ -103,6 +103,7 @@ public: virtual bool isQuickItem() const; virtual bool isQuickWindow() const; virtual bool isLayoutable() const; + virtual bool isRenderable() const; virtual bool equalGraphicsItem(QGraphicsItem *item) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp index c0d763c906b..cff435bdd49 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp @@ -44,10 +44,10 @@ QImage renderImage(ServerNodeInstance rootNodeInstance) QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); if (previewImageSize.isEmpty()) - previewImageSize = {300, 300}; + previewImageSize = {150, 150}; - if (previewImageSize.width() > 300 || previewImageSize.height() > 300) - previewImageSize.scale({300, 300}, Qt::KeepAspectRatio); + if (previewImageSize.width() > 150 || previewImageSize.height() > 150) + previewImageSize.scale({150, 150}, Qt::KeepAspectRatio); QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); @@ -68,7 +68,8 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands( inFunction = true; auto rooNodeInstance = rootNodeInstance(); - rooNodeInstance.rootQuickItem()->setClip(true); + if (QQuickItem *qitem = rooNodeInstance.rootQuickItem()) + qitem->setClip(true); DesignerSupport::polishItems(quickWindow()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index afde832afcb..e2bd56562d5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1007,17 +1007,17 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView() void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() { #ifdef QUICK3D_MODULE - m_modelNode3DImageViewAsyncData.cleanup(); if (m_modelNode3DImageViewData.rootItem) { QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); if (!m_modelNode3DImageViewData.contentItem) m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem); + QImage renderImage; if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) { - m_modelNode3DImageViewAsyncData.renderImage - = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()]; - modelNode3DImageViewSendImageToCreator(); + renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()]; } else { + bool createdFromComponent = false; + QObject *instanceObj = nullptr; ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId()); if (!m_modelNodePreviewImageCommand.componentPath().isEmpty() && instance.isSubclassOf("QQuick3DNode")) { @@ -1026,15 +1026,14 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() // wouldn't want the children of the Node to appear in the preview. QQmlComponent component(engine()); component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath())); - m_modelNode3DImageViewAsyncData.instanceObj = qobject_cast(component.create()); - if (!m_modelNode3DImageViewAsyncData.instanceObj) { + instanceObj = qobject_cast(component.create()); + if (!instanceObj) { qWarning() << "Could not create preview component: " << component.errors(); - m_modelNode3DImageViewAsyncData.cleanup(); return; } - m_modelNode3DImageViewAsyncData.createdFromComponent = true; + createdFromComponent = true; } else { - m_modelNode3DImageViewAsyncData.instanceObj = instance.internalObject(); + instanceObj = instance.internalObject(); } QSize renderSize = m_modelNodePreviewImageCommand.size(); if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { @@ -1055,69 +1054,53 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() QMetaObject::invokeMethod( m_modelNode3DImageViewData.rootItem, "createViewForObject", - Q_ARG(QVariant, objectToVariant(m_modelNode3DImageViewAsyncData.instanceObj))); + Q_ARG(QVariant, objectToVariant(instanceObj))); - // Selection box geometry updates have an asynchronous step, so we need to do rendering - // in asynchronous steps as well, since we are adjusting the selection box geometry - // while finding correct zoom level. - m_modelNode3DImageViewAsyncData.timer.start(); - } - } + // Need to render twice, first render updates spatial nodes + for (int i = 0; i < 2; ++i) { + if (i == 1) + QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "fitToViewPort" + , Qt::DirectConnection); + + updateNodesRecursive(m_modelNode3DImageViewData.contentItem); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { + renderImage = m_modelNode3DImageViewData.window->grabWindow(); + } else { + // Fake render loop signaling to update things like QML items as 3D textures + m_modelNode3DImageViewData.window->beforeSynchronizing(); + m_modelNode3DImageViewData.window->beforeRendering(); + + QSizeF size = qobject_cast(m_modelNode3DImageViewData.contentItem)->size(); + QRectF renderRect(QPointF(0., 0.), size); + renderImage = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem, + renderRect, size.toSize()); + m_modelNode3DImageViewData.window->afterRendering(); + } +#else + renderImage = grabRenderControl(m_modelNode3DImageViewData); #endif -} + } -void Qt5InformationNodeInstanceServer::modelNode3DImageViewSendImageToCreator() -{ - if (!m_modelNode3DImageViewAsyncData.renderImage.isNull()) { + QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); + + if (createdFromComponent) { + // If component changes, puppet will need a reset anyway, so we can cache the image + m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), + renderImage); + delete instanceObj; + } + } // Key number is selected so that it is unlikely to conflict other ImageContainer use. ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001); - imgContainer.setImage(m_modelNode3DImageViewAsyncData.renderImage); + imgContainer.setImage(renderImage); // send the rendered image to creator process nodeInstanceClient()->handlePuppetToCreatorCommand( {PuppetToCreatorCommand::RenderModelNodePreviewImage, QVariant::fromValue(imgContainer)}); - - m_modelNode3DImageViewAsyncData.cleanup(); } -} - -void Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep() -{ - ++m_modelNode3DImageViewAsyncData.count; - - updateNodesRecursive(m_modelNode3DImageViewData.contentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - m_modelNode3DImageViewAsyncData.renderImage = m_modelNode3DImageViewData.window->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - m_modelNode3DImageViewData.window->beforeSynchronizing(); - m_modelNode3DImageViewData.window->beforeRendering(); - - QSizeF size = qobject_cast(m_modelNode3DImageViewData.contentItem)->size(); - QRectF renderRect(QPointF(0., 0.), size); - m_modelNode3DImageViewAsyncData.renderImage - = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem, - renderRect, size.toSize()); - m_modelNode3DImageViewData.window->afterRendering(); - } -#else - m_modelNode3DImageViewAsyncData.renderImage = grabRenderControl(m_modelNode3DImageViewData); #endif - QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "afterRender"); - const bool ready = QQmlProperty::read(m_modelNode3DImageViewData.rootItem, "ready").value(); - if (ready || m_modelNode3DImageViewAsyncData.count >= 10) { - QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); - if (m_modelNode3DImageViewAsyncData.createdFromComponent) { - // If component changes, puppet will need a reset anyway, so we can cache the image - m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), - m_modelNode3DImageViewAsyncData.renderImage); - } - modelNode3DImageViewSendImageToCreator(); - } else { - m_modelNode3DImageViewAsyncData.timer.start(); - } } static QRectF itemBoundingRect(QQuickItem *item) @@ -1234,7 +1217,6 @@ Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceC m_render3DEditViewTimer.setSingleShot(true); m_inputEventTimer.setSingleShot(true); m_renderModelNodeImageViewTimer.setSingleShot(true); - m_modelNode3DImageViewAsyncData.timer.setSingleShot(true); m_dynamicAddObjectTimer.setSingleShot(true); #ifdef FPS_COUNTER @@ -1260,7 +1242,6 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer() m_render3DEditViewTimer.stop(); m_inputEventTimer.stop(); m_renderModelNodeImageViewTimer.stop(); - m_modelNode3DImageViewAsyncData.timer.stop(); m_dynamicAddObjectTimer.stop(); if (m_editView3DData.rootItem) @@ -1677,8 +1658,6 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList &instances); void handleInputEvents(); @@ -190,26 +188,6 @@ private: QObject *m_3dHelper = nullptr; int m_need3DEditViewRender = 0; QSet m_dynamicObjectConstructors; - - struct ModelNode3DImageViewAsyncData { - QTimer timer; - QImage renderImage; - int count = 0; - bool createdFromComponent = false; - QObject *instanceObj = nullptr; - - void cleanup() - { - timer.stop(); - count = 0; - renderImage = {}; - if (createdFromComponent) - delete instanceObj; - instanceObj = nullptr; - createdFromComponent = false; - } - }; - ModelNode3DImageViewAsyncData m_modelNode3DImageViewAsyncData; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index 4af451b61ae..a0c79296033 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -70,6 +70,7 @@ public: QImage grabWindow() override; QImage grabItem(QQuickItem *item) override; + bool renderWindow() override; static QQuickItem *parentEffectItem(QQuickItem *item); @@ -97,7 +98,6 @@ protected: virtual bool initRhi(RenderViewData &viewData); virtual QImage grabRenderControl(RenderViewData &viewData); - virtual bool renderWindow(); private: RenderViewData m_viewData; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index fb3113bdcd1..f61ba1a3421 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -133,4 +133,9 @@ void Qt5PreviewNodeInstanceServer::changePreviewImageSize( collectItemChangesAndSendChangeCommands(); } +bool Qt5PreviewNodeInstanceServer::isPreviewServer() const +{ + return true; +} + } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h index 182db45d405..6c97658466f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h @@ -39,6 +39,7 @@ public: void changeState(const ChangeStateCommand &command) override; void removeSharedMemory(const RemoveSharedMemoryCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; + bool isPreviewServer() const override; QImage renderPreviewImage(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp index 980da0da791..65bda831a6e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -26,6 +26,8 @@ #include "quick3dnodeinstance.h" #include "qt5nodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" +#include "quickitemnodeinstance.h" +#include "../editor3d/generalhelper.h" #include @@ -37,6 +39,7 @@ #include #ifdef QUICK3D_MODULE +#include #include #include #include @@ -45,8 +48,10 @@ #if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) #include #endif +#include #endif + namespace QmlDesigner { namespace Internal { @@ -57,6 +62,7 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node) Quick3DNodeInstance::~Quick3DNodeInstance() { + delete m_dummyRootView; } void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, @@ -87,10 +93,123 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo } } } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // In case this is the scene root, we need to create a dummy View3D for the scene + // in preview puppets + if (instanceId() == 0 && nodeInstanceServer()->isPreviewServer()) { + auto helper = new QmlDesigner::Internal::GeneralHelper(); + engine()->rootContext()->setContextProperty("_generalHelper", helper); + + QQmlComponent component(engine()); + component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml")); + m_dummyRootView = qobject_cast(component.create()); + + QMetaObject::invokeMethod( + m_dummyRootView, "createViewForNode", + Q_ARG(QVariant, QVariant::fromValue(object()))); + + nodeInstanceServer()->setRootItem(m_dummyRootView); + } +#endif #endif ObjectNodeInstance::initialize(objectNodeInstance, flags); } +QImage Quick3DNodeInstance::renderImage() const +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!isRootNodeInstance() || !m_dummyRootView) + return {}; + + QSize size(640, 480); + nodeInstanceServer()->quickWindow()->resize(size); + m_dummyRootView->setSize(size); + + // Just render the window once to update spatial nodes + nodeInstanceServer()->renderWindow(); + + QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection); + + QRectF renderBoundingRect = m_dummyRootView->boundingRect(); + QImage renderImage; + + if (QuickItemNodeInstance::unifiedRenderPath()) { + renderImage = nodeInstanceServer()->grabWindow(); + renderImage = renderImage.copy(renderBoundingRect.toRect()); + } else { + renderImage = nodeInstanceServer()->grabItem(m_dummyRootView); + } + + // When grabbing an offscreen window the device pixel ratio is 1 + renderImage.setDevicePixelRatio(1); + + return renderImage; +#endif + return {}; +} + +QImage Quick3DNodeInstance::renderPreviewImage(const QSize &previewImageSize) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!isRootNodeInstance() || !m_dummyRootView) + return {}; + + nodeInstanceServer()->quickWindow()->resize(previewImageSize); + m_dummyRootView->setSize(previewImageSize); + + // Just render the window once to update spatial nodes + nodeInstanceServer()->renderWindow(); + + QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection); + + QRectF previewItemBoundingRect = boundingRect(); + + if (previewItemBoundingRect.isValid()) { + const QSize size = previewImageSize; + if (m_dummyRootView->isVisible()) { + QImage image; + image = nodeInstanceServer()->grabWindow(); + image = image.copy(previewItemBoundingRect.toRect()); + image = image.scaledToWidth(size.width()); + return image; + } else { + QImage transparentImage(size, QImage::Format_ARGB32_Premultiplied); + transparentImage.fill(Qt::transparent); + return transparentImage; + } + } +#endif + return {}; +} + +bool Quick3DNodeInstance::isRenderable() const +{ + return m_dummyRootView; +} + +QRectF Quick3DNodeInstance::boundingRect() const +{ + if (m_dummyRootView) + return m_dummyRootView->boundingRect(); + return ObjectNodeInstance::boundingRect(); +} + +QList Quick3DNodeInstance::stateInstances() const +{ + QList instanceList; +#ifdef QUICK3D_MODULE + if (auto obj3D = quick3DNode()) { + const QList stateList = QQuick3DObjectPrivate::get(obj3D)->_states()->states(); + for (QQuickState *state : stateList) { + if (state && nodeInstanceServer()->hasInstanceForObject(state)) + instanceList.append(nodeInstanceServer()->instanceForObject(state)); + } + } +#endif + return instanceList; +} + Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const { return qobject_cast(nodeInstanceServer()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h index 892946ee1f6..322762af26a 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h @@ -47,12 +47,22 @@ public: void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, InstanceContainer::NodeFlags flags) override; + QImage renderImage() const override; + QImage renderPreviewImage(const QSize &previewImageSize) const override; + + bool isRenderable() const override; + QRectF boundingRect() const override; + + QList stateInstances() const override; + protected: explicit Quick3DNodeInstance(QObject *node); private: Qt5NodeInstanceServer *qt5NodeInstanceServer() const; QQuick3DNode *quick3DNode() const; + + QQuickItem *m_dummyRootView = nullptr; }; } // namespace Internal diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp index b78bf6328e7..41d0015ce56 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -571,6 +571,11 @@ bool QuickItemNodeInstance::isQuickItem() const return true; } +bool QuickItemNodeInstance::isRenderable() const +{ + return quickItem() && (!s_unifiedRenderPath || isRootNodeInstance()); +} + QList QuickItemNodeInstance::stateInstances() const { QList instanceList; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h index 46b22b8c9a8..50cf494e8e2 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h @@ -94,6 +94,7 @@ public: bool isResizable() const override; bool isMovable() const override; bool isQuickItem() const override; + bool isRenderable() const override; QList stateInstances() const override; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp index 6b2ba783402..9803f6d79e6 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp @@ -154,7 +154,7 @@ void ServerNodeInstance::setNodeSource(const QString &source) bool ServerNodeInstance::holdsGraphical() const { - return m_nodeInstance->isQuickItem(); + return m_nodeInstance->isRenderable(); } bool ServerNodeInstance::isComponentWrap() const diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp index 78e06f0b56b..c86e5360d1c 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp @@ -48,7 +48,8 @@ QString ItemLibraryItem::typeName() const QString ItemLibraryItem::itemLibraryIconPath() const { - if (m_itemLibraryEntry.customComponentSource().isEmpty()) { + if (m_itemLibraryEntry.customComponentSource().isEmpty() + || !m_itemLibraryEntry.libraryEntryIconPath().isEmpty()) { return QStringLiteral("image://qmldesigner_itemlibrary/") + m_itemLibraryEntry.libraryEntryIconPath(); } else { diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui index fec0ba8f384..6839c1b107d 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui @@ -6,8 +6,8 @@ 0 0 - 651 - 318 + 517 + 166 @@ -90,8 +90,8 @@ - 300 - 300 + 150 + 150 @@ -163,7 +163,7 @@ 0 - 1 + 3 diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index b4f50848b9a..f5164a97213 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -90,7 +90,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); - if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) { + if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() + && !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) { if (abortCallback) abortCallback(ImageCache::AbortReason::Failed); return; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index cede56f299d..a001a8b0c29 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -420,6 +420,7 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const itemLibraryEntry.setType(type.toUtf8(), 1, 0); itemLibraryEntry.setName(name); itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components")); + itemLibraryEntry.setCustomComponentSource(qmlIt.fileInfo().absoluteFilePath()); itemLibraryEntry.setRequiredImport(importUrl); QString iconPath = qmlIt.fileInfo().absolutePath() + '/' + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name