diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml index 67ae4c7b295..488198911b3 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml @@ -40,7 +40,7 @@ DirectionalDraggable { _targetStartPos.x + sceneRelativeDistance.x, _targetStartPos.y + sceneRelativeDistance.y, _targetStartPos.z + sceneRelativeDistance.z); - return targetNode.parent.mapPositionFromScene(newScenePos); + return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; } onDragged: { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml index 25fe455f49d..34808aecde7 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml @@ -37,9 +37,9 @@ View3D { Node { OrthographicCamera { id: axisHelperCamera - rotation: editCameraCtrl.camera.rotation - position: editCameraCtrl.camera.position.minus(editCameraCtrl._lookAtPoint) - .normalized().times(600) + rotation: editCameraCtrl.camera ? editCameraCtrl.camera.rotation : Qt.vector3d(0, 0, 0) + position: editCameraCtrl.camera ? editCameraCtrl.camera.position.minus(editCameraCtrl._lookAtPoint) + .normalized().times(600) : Qt.vector3d(0, 0, 0) } AutoScaleHelper { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml index 9dd78568456..77252881184 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml @@ -46,6 +46,9 @@ Item { property Camera _prevCamera: null function restoreCameraState(cameraState) { + if (!camera) + return; + _lookAtPoint = cameraState[0]; _zoomFactor = cameraState[1]; camera.position = cameraState[2]; @@ -55,6 +58,9 @@ Item { } function storeCameraState(delay) { + if (!camera) + return; + var cameraState = []; cameraState[0] = _lookAtPoint; cameraState[1] = _zoomFactor; @@ -66,6 +72,9 @@ Item { function focusObject(targetObject, rotation, updateZoom) { + if (!camera) + return; + camera.rotation = rotation; var newLookAtAndZoom = _generalHelper.focusObjectToCamera( camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, updateZoom); @@ -76,16 +85,19 @@ Item { function zoomRelative(distance) { + if (!camera) + return; + _zoomFactor = _generalHelper.zoomCamera(camera, distance, _defaultCameraLookAtDistance, _lookAtPoint, _zoomFactor, true); } Component.onCompleted: { - cameraCtrl._defaultCameraLookAtDistance = cameraCtrl.camera.position.length(); + cameraCtrl._defaultCameraLookAtDistance = Qt.vector3d(0, 600, -600).length(); } onCameraChanged: { - if (_prevCamera) { + if (camera && _prevCamera) { // Reset zoom on previous camera to ensure it's properties are good to copy to new cam _generalHelper.zoomCamera(_prevCamera, 0, _defaultCameraLookAtDistance, _lookAtPoint, 1, false); @@ -106,7 +118,7 @@ Item { hoverEnabled: false anchors.fill: parent onPositionChanged: { - if (mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) { + if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier && cameraCtrl._dragging) { var currentPoint = Qt.vector3d(mouse.x, mouse.y, 0); if (cameraCtrl._button == Qt.LeftButton) { _generalHelper.orbitCamera(cameraCtrl.camera, cameraCtrl._startRotation, @@ -124,7 +136,7 @@ Item { } } onPressed: { - if (mouse.modifiers === Qt.AltModifier) { + if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) { cameraCtrl._dragging = true; cameraCtrl._startRotation = cameraCtrl.camera.rotation; cameraCtrl._startPosition = cameraCtrl.camera.position; @@ -147,9 +159,11 @@ Item { onCanceled: handleRelease() onWheel: { - // Emprically determined divisor for nice zoom - cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); - cameraCtrl.storeCameraState(500); + if (cameraCtrl.camera) { + // Emprically determined divisor for nice zoom + cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); + cameraCtrl.storeCameraState(500); + } } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index 36b10b9fc91..ad569f0f611 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -41,7 +41,8 @@ Window { // need all those flags otherwise the title bar disappears after setting WindowStaysOnTopHint flag later flags: Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint - property alias scene: editView.importScene + property Node activeScene: null + property View3D editView: null property alias showEditLight: btnEditViewLight.toggled property alias usePerspective: btnPerspective.toggled @@ -63,6 +64,31 @@ Window { onShowEditLightChanged: _generalHelper.storeToolState("showEditLight", showEditLight) onGlobalOrientationChanged: _generalHelper.storeToolState("globalOrientation", globalOrientation) + onActiveSceneChanged: { + // importScene cannot be updated after initial set, so we need to reconstruct entire View3D + var component = Qt.createComponent("SceneView3D.qml"); + if (component.status === Component.Ready) { + var oldView = editView; + + if (editView) + editView.visible = false; + + editView = component.createObject(viewRect, + {"usePerspective": usePerspective, + "showSceneLight": showEditLight, + "importScene": activeScene, + "z": 1}); + editView.usePerspective = Qt.binding(function() {return usePerspective;}); + editView.showSceneLight = Qt.binding(function() {return showEditLight;}); + + selectionBoxes.length = 0; + ensureSelectionBoxes(1); + + if (oldView) + oldView.destroy(); + } + } + function updateToolStates(toolStates) { // Init the stored state so we don't unnecessarily reflect changes back to creator _generalHelper.initToolStates(toolStates); @@ -103,9 +129,13 @@ Window { if (component.status === Component.Ready) { for (var i = 0; i < needMore; ++i) { var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry"); - var box = component.createObject(mainSceneHelpers, {"view3D": editView, + var boxParent = null; + if (editView) + boxParent = editView.sceneHelpers; + var box = component.createObject(boxParent, {"view3D": editView, "geometryName": geometryName}); selectionBoxes[selectionBoxes.length] = box; + box.view3D = Qt.binding(function() {return editView;}); } } } @@ -133,7 +163,7 @@ Window { function handleObjectClicked(object, multi) { var theObject = object; if (btnSelectGroup.selected) { - while (theObject && theObject.parent !== scene) + while (theObject && theObject.parent !== activeScene) theObject = theObject.parent; } // Object selection logic: @@ -162,11 +192,12 @@ Window { selectionChanged(newSelection); } - function addLightGizmo(obj) + function addLightGizmo(scene, obj) { // Insert into first available gizmo for (var i = 0; i < lightGizmos.length; ++i) { if (!lightGizmos[i].targetNode) { + lightGizmos[i].scene = scene; lightGizmos[i].targetNode = obj; return; } @@ -177,18 +208,21 @@ Window { if (component.status === Component.Ready) { var gizmo = component.createObject(overlayScene, {"view3D": overlayView, "targetNode": obj, - "selectedNodes": selectedNodes}); + "selectedNodes": selectedNodes, "scene": scene, + "activeScene": activeScene}); lightGizmos[lightGizmos.length] = gizmo; gizmo.clicked.connect(handleObjectClicked); gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return activeScene;}); } } - function addCameraGizmo(obj) + function addCameraGizmo(scene, obj) { // Insert into first available gizmo for (var i = 0; i < cameraGizmos.length; ++i) { if (!cameraGizmos[i].targetNode) { + cameraGizmos[i].scene = scene; cameraGizmos[i].targetNode = obj; return; } @@ -200,11 +234,13 @@ Window { var gizmo = component.createObject( overlayScene, {"view3D": overlayView, "targetNode": obj, "geometryName": geometryName, - "viewPortRect": viewPortRect, "selectedNodes": selectedNodes}); + "viewPortRect": viewPortRect, "selectedNodes": selectedNodes, + "scene": scene, "activeScene": activeScene}); cameraGizmos[cameraGizmos.length] = gizmo; gizmo.clicked.connect(handleObjectClicked); gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;}); gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return activeScene;}); } } @@ -233,19 +269,19 @@ Window { PerspectiveCamera { id: overlayPerspectiveCamera - clipFar: editPerspectiveCamera.clipFar - clipNear: editPerspectiveCamera.clipNear - position: editPerspectiveCamera.position - rotation: editPerspectiveCamera.rotation + clipFar: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipFar : 1000 + clipNear: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipNear : 1 + position: viewWindow.editView ? viewWindow.editView.perpectiveCamera.position : Qt.vector3d(0, 0, 0) + rotation: viewWindow.editView ? viewWindow.editView.perpectiveCamera.rotation : Qt.vector3d(0, 0, 0) } OrthographicCamera { id: overlayOrthoCamera - clipFar: editOrthoCamera.clipFar - clipNear: editOrthoCamera.clipNear - position: editOrthoCamera.position - rotation: editOrthoCamera.rotation - scale: editOrthoCamera.scale + clipFar: viewWindow.editView ? viewWindow.editView.orthoCamera.clipFar : 1000 + clipNear: viewWindow.editView ? viewWindow.editView.orthoCamera.clipNear : 1 + position: viewWindow.editView ? viewWindow.editView.orthoCamera.position : Qt.vector3d(0, 0, 0) + rotation: viewWindow.editView ? viewWindow.editView.orthoCamera.rotation : Qt.vector3d(0, 0, 0) + scale: viewWindow.editView ? viewWindow.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0) } MouseArea3D { @@ -349,6 +385,7 @@ Window { } Rectangle { + id: viewRect anchors.fill: parent focus: true @@ -361,11 +398,13 @@ Window { anchors.fill: parent acceptedButtons: Qt.LeftButton onClicked: { - var pickResult = editView.pick(mouse.x, mouse.y); - handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), - mouse.modifiers & Qt.ControlModifier); - if (!pickResult.objectHit) - mouse.accepted = false; + if (viewWindow.editView) { + var pickResult = viewWindow.editView.pick(mouse.x, mouse.y); + handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), + mouse.modifiers & Qt.ControlModifier); + if (!pickResult.objectHit) + mouse.accepted = false; + } } } @@ -373,57 +412,12 @@ Window { anchors.fill: parent } - View3D { - id: editView - anchors.fill: parent - camera: usePerspective ? editPerspectiveCamera : editOrthoCamera - - Node { - id: mainSceneHelpers - - HelperGrid { - id: helperGrid - lines: 50 - step: 50 - } - - PointLight { - id: editLight - visible: showEditLight - position: usePerspective ? editPerspectiveCamera.position - : editOrthoCamera.position - quadraticFade: 0 - linearFade: 0 - } - - // Initial camera position and rotation should be such that they look at origin. - // Otherwise EditCameraController._lookAtPoint needs to be initialized to correct - // point. - PerspectiveCamera { - id: editPerspectiveCamera - z: -600 - y: 600 - rotation.x: 45 - clipFar: 100000 - clipNear: 1 - } - - OrthographicCamera { - id: editOrthoCamera - z: -600 - y: 600 - rotation.x: 45 - clipFar: 100000 - clipNear: -10000 - } - } - } - View3D { id: overlayView anchors.fill: parent camera: usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera importScene: overlayScene + z: 2 } Overlay2D { @@ -431,6 +425,7 @@ Window { targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo targetView: overlayView visible: targetNode.dragging + z: 3 Rectangle { color: "white" @@ -463,9 +458,9 @@ Window { EditCameraController { id: cameraControl - camera: editView.camera + camera: viewWindow.editView ? viewWindow.editView.camera : null anchors.fill: parent - view3d: editView + view3d: viewWindow.editView } } @@ -557,10 +552,10 @@ Window { togglable: false onSelectedChanged: { - if (selected) { + if (viewWindow.editView && selected) { var targetNode = viewWindow.selectedNodes.length > 0 ? selectionBoxes[0].model : null; - cameraControl.focusObject(targetNode, editView.camera.rotation, true); + cameraControl.focusObject(targetNode, viewWindow.editView.camera.rotation, true); } } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/IconGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/IconGizmo.qml index 2f78458bbdd..a198cae04bc 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/IconGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/IconGizmo.qml @@ -30,6 +30,8 @@ import QtGraphicalEffects 1.12 Node { id: iconGizmo + property Node activeScene: null + property Node scene: null property View3D view3D property bool highlightOnHover: true property Node targetNode: null @@ -50,7 +52,7 @@ Node { position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0) rotation: targetNode ? targetNode.sceneRotation : Qt.vector3d(0, 0, 0) - visible: targetNode ? targetNode.visible : false + visible: activeScene === scene && (targetNode ? targetNode.visible : false) Overlay2D { id: iconOverlay diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarMoveHandle.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarMoveHandle.qml index f79ae6e248e..591f1196dea 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarMoveHandle.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarMoveHandle.qml @@ -40,7 +40,7 @@ PlanarDraggable { _targetStartPos.x + sceneRelativeDistance.x, _targetStartPos.y + sceneRelativeDistance.y, _targetStartPos.z + sceneRelativeDistance.z); - return targetNode.parent.mapPositionFromScene(newScenePos); + return targetNode.parent ? targetNode.parent.mapPositionFromScene(newScenePos) : newScenePos; } onDragged: { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/SceneView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/SceneView3D.qml new file mode 100644 index 00000000000..09139716619 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/SceneView3D.qml @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 2.12 +import QtQuick3D 1.14 + +View3D { + id: sceneView + anchors.fill: parent + + property bool usePerspective: false + property bool showSceneLight: false + property alias sceneHelpers: sceneHelpers + property alias perpectiveCamera: scenePerspectiveCamera + property alias orthoCamera: sceneOrthoCamera + + camera: usePerspective ? scenePerspectiveCamera : sceneOrthoCamera + + Node { + id: sceneHelpers + + HelperGrid { + id: helperGrid + lines: 50 + step: 50 + } + + PointLight { + id: sceneLight + visible: showSceneLight + position: usePerspective ? scenePerspectiveCamera.position + : sceneOrthoCamera.position + quadraticFade: 0 + linearFade: 0 + } + + // Initial camera position and rotation should be such that they look at origin. + // Otherwise EditCameraController._lookAtPoint needs to be initialized to correct + // point. + PerspectiveCamera { + id: scenePerspectiveCamera + z: -600 + y: 600 + rotation.x: 45 + clipFar: 100000 + clipNear: 1 + } + + OrthographicCamera { + id: sceneOrthoCamera + z: -600 + y: 600 + rotation.x: 45 + clipFar: 100000 + clipNear: -10000 + } + } +} diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp index 910501a9e4f..9d8de9dc178 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp @@ -154,8 +154,11 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb // Explicitly set local transform of root node to target node parent's global transform // to avoid having to reparent the selection box. This has to be done directly on render // nodes. - targetRN->parent->calculateGlobalVariables(); - QMatrix4x4 m = targetRN->parent->globalTransform; + QMatrix4x4 m; + if (targetRN->parent) { + targetRN->parent->calculateGlobalVariables(); + m = targetRN->parent->globalTransform; + } rootRN->localTransform = m; rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); rootRN->calculateGlobalVariables(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 501145166c9..0413a3bef9c 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -81,6 +81,12 @@ #include #include +#ifdef QUICK3D_MODULE +#include +#include +#include +#endif + namespace QmlDesigner { static QVariant objectToVariant(QObject *object) @@ -255,12 +261,12 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue( const PropertyName &propertyName, ValuesModifiedCommand::TransactionOption option) { - PropertyName targetPopertyName; + PropertyName targetPropertyName; // Position is a special case, because the position can be 'position.x 'or simply 'x'. // We prefer 'x'. if (propertyName != "position") - targetPopertyName = propertyName; + targetPropertyName = propertyName; auto *obj = node.value(); @@ -275,7 +281,7 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue( // We do have to split position into position.x, position.y, position.z ValuesModifiedCommand command = createValuesModifiedCommand(vectorToPropertyValue( instance, - targetPopertyName, + targetPropertyName, obj->property(propertyName))); command.transactionOption = option; @@ -318,14 +324,32 @@ void Qt5InformationNodeInstanceServer::handleToolStateChanged(const QString &too QVariant::fromValue(data)}); } -void Qt5InformationNodeInstanceServer::updateViewPortRect() +void Qt5InformationNodeInstanceServer::handleView3DSizeChange() { - QRectF viewPortrect(0, 0, m_viewPortInstance.internalObject()->property("width").toDouble(), - m_viewPortInstance.internalObject()->property("height").toDouble()); + QObject *view3D = sender(); + if (view3D == m_active3DView.internalObject()) + updateView3DRect(view3D); +} + +void Qt5InformationNodeInstanceServer::updateView3DRect(QObject *view3D) +{ + QRectF viewPortrect(0., 0., 1000., 1000.); + if (view3D) { + viewPortrect = QRectF(0., 0., view3D->property("width").toDouble(), + view3D->property("height").toDouble()); + } QQmlProperty viewPortProperty(m_editView3D, "viewPortRect", context()); viewPortProperty.write(viewPortrect); } +void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D() +{ + QQmlProperty sceneProperty(m_editView3D, "activeScene", context()); + sceneProperty.write(objectToVariant(m_active3DScene)); + if (m_active3DView.isValid()) + updateView3DRect(m_active3DView.internalObject()); +} + Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : Qt5NodeInstanceServer(nodeInstanceClient) { @@ -425,65 +449,147 @@ void Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout() changeSelection(m_pendingSelectionChangeCommand); } -QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport( - const QList &instanceList) const -{ - for (const ServerNodeInstance &instance : instanceList) { - if (instance.isSubclassOf("QQuick3DViewport")) { - QObject *rootObj = nullptr; - int viewChildCount = 0; - for (const ServerNodeInstance &child : instanceList) { /* Look for scene node */ - /* The QQuick3DViewport always creates a root node. - * This root node contains the complete scene. */ - if (child.isSubclassOf("QQuick3DNode") && child.parent() == instance) { - // Implicit root node is not visible in editor, so there is often another node - // added below it that serves as the actual scene root node. - // If the found root is the only node child of the view, assume that is the case. - ++viewChildCount; - if (!rootObj) - rootObj = child.internalObject(); - } - } - if (viewChildCount == 1) - return rootObj; - else if (rootObj) - return rootObj->property("parent").value(); - } - } - return nullptr; -} - void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( const QList &instanceList) const { - QObjectList cameras; - QObjectList lights; + QHash cameras; + QHash lights; for (const ServerNodeInstance &instance : instanceList) { if (instance.isSubclassOf("QQuick3DCamera")) - cameras << instance.internalObject(); + cameras[find3DSceneRoot(instance)] << instance.internalObject(); else if (instance.isSubclassOf("QQuick3DAbstractLight")) - lights << instance.internalObject(); + lights[find3DSceneRoot(instance)] << instance.internalObject(); } - for (auto &obj : qAsConst(cameras)) { - QMetaObject::invokeMethod(m_editView3D, "addCameraGizmo", - Q_ARG(QVariant, objectToVariant(obj))); + auto cameraIt = cameras.constBegin(); + while (cameraIt != cameras.constEnd()) { + const auto cameraObjs = cameraIt.value(); + for (auto &obj : cameraObjs) { + QMetaObject::invokeMethod(m_editView3D, "addCameraGizmo", + Q_ARG(QVariant, objectToVariant(cameraIt.key())), + Q_ARG(QVariant, objectToVariant(obj))); + } + ++cameraIt; } - for (auto &obj : qAsConst(lights)) { - QMetaObject::invokeMethod(m_editView3D, "addLightGizmo", - Q_ARG(QVariant, objectToVariant(obj))); + auto lightIt = lights.constBegin(); + while (lightIt != lights.constEnd()) { + const auto lightObjs = lightIt.value(); + for (auto &obj : lightObjs) { + QMetaObject::invokeMethod(m_editView3D, "addLightGizmo", + Q_ARG(QVariant, objectToVariant(lightIt.key())), + Q_ARG(QVariant, objectToVariant(obj))); + } + ++lightIt; } } -ServerNodeInstance Qt5InformationNodeInstanceServer::findViewPort( - const QList &instanceList) +void Qt5InformationNodeInstanceServer::addViewPorts(const QList &instanceList) { for (const ServerNodeInstance &instance : instanceList) { - if (instance.isSubclassOf("QQuick3DViewport")) - return instance; + if (instance.isSubclassOf("QQuick3DViewport")) { + m_view3Ds << instance; + QObject *obj = instance.internalObject(); + QObject::connect(obj, SIGNAL(widthChanged()), this, SLOT(handleView3DSizeChange())); + QObject::connect(obj, SIGNAL(heightChanged()), this, SLOT(handleView3DSizeChange())); + } } - return ServerNodeInstance(); +} + +ServerNodeInstance Qt5InformationNodeInstanceServer::findView3DForInstance(const ServerNodeInstance &instance) const +{ +#ifdef QUICK3D_MODULE + if (!instance.isValid()) + return {}; + + // View3D of an instance is one of the following, in order of priority: + // - Any direct ancestor View3D of the instance + // - Any View3D that specifies the instance's scene as importScene + ServerNodeInstance checkInstance = instance; + while (checkInstance.isValid()) { + if (checkInstance.isSubclassOf("QQuick3DViewport")) + return checkInstance; + else + checkInstance = checkInstance.parent(); + } + + // If no ancestor View3D was found, check if the scene root is specified as importScene in + // some View3D. + QObject *sceneRoot = find3DSceneRoot(instance); + for (const auto &view3D : qAsConst(m_view3Ds)) { + auto view = qobject_cast(view3D.internalObject()); + if (sceneRoot == view->importScene()) + return view3D; + } +#endif + return {}; +} + +QObject *Qt5InformationNodeInstanceServer::find3DSceneRoot(const ServerNodeInstance &instance) const +{ +#ifdef QUICK3D_MODULE + // The root of a 3D scene is any QQuick3DNode that doesn't have QQuick3DNode as parent. + // One exception is QQuick3DSceneRootNode that has only a single child QQuick3DNode (not + // a subclass of one, but exactly QQuick3DNode). In that case we consider the single child node + // to be the scene root (as QQuick3DSceneRootNode is not visible in the navigator scene graph). + + if (!instance.isValid()) + return nullptr; + + QQuick3DNode *childNode = nullptr; + auto countChildNodes = [&childNode](QQuick3DViewport *view) -> int { + QQuick3DNode *sceneNode = view->scene(); + QList children = sceneNode->childItems(); + int nodeCount = 0; + for (const auto &child : children) { + auto nodeChild = qobject_cast(child); + if (nodeChild) { + ++nodeCount; + childNode = nodeChild; + } + } + return nodeCount; + }; + + // In case of View3D is selected, the root scene is whatever is contained in View3D, or + // importScene, in case there is no content in View3D + QObject *obj = instance.internalObject(); + auto view = qobject_cast(obj); + if (view) { + int nodeCount = countChildNodes(view); + if (nodeCount == 0) + return view->importScene(); + else if (nodeCount == 1) + return childNode; + else + return view->scene(); + } + + ServerNodeInstance checkInstance = instance; + bool foundNode = checkInstance.isSubclassOf("QQuick3DNode"); + while (checkInstance.isValid()) { + ServerNodeInstance parentInstance = checkInstance.parent(); + if (parentInstance.isSubclassOf("QQuick3DViewport")) { + view = qobject_cast(parentInstance.internalObject()); + int nodeCount = countChildNodes(view); + if (nodeCount == 1) + return checkInstance.internalObject(); + else + return view->scene(); + } else if (parentInstance.isSubclassOf("QQuick3DNode")) { + foundNode = true; + checkInstance = parentInstance; + } else { + if (!foundNode) { + // We haven't found any node yet, continue the search + checkInstance = parentInstance; + } else { + return checkInstance.internalObject(); + } + } + } +#endif + return nullptr; } void Qt5InformationNodeInstanceServer::setup3DEditView(const QList &instanceList, @@ -491,38 +597,28 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QListsetParent(m_editView3D); - sceneProperty.write(objectToVariant(m_rootNode)); - QQmlProperty parentProperty(m_rootNode, "parent", context()); - parentProperty.write(objectToVariant(m_editView3D)); - QQmlProperty showLightProperty(m_editView3D, "showLight", context()); - showLightProperty.write(showCustomLight); - - m_viewPortInstance = findViewPort(instanceList); - if (m_viewPortInstance.internalObject()) { - QObject::connect(m_viewPortInstance.internalObject(), SIGNAL(widthChanged()), - this, SLOT(updateViewPortRect())); - QObject::connect(m_viewPortInstance.internalObject(), SIGNAL(heightChanged()), - this, SLOT(updateViewPortRect())); - updateViewPortRect(); } + updateActiveSceneToEditView3D(); + createCameraAndLightGizmos(instanceList); QMetaObject::invokeMethod(m_editView3D, "updateToolStates", Q_ARG(QVariant, toolStates)); @@ -711,19 +807,33 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm return; } + // Find a scene root of the selection to update active scene shown const QVector instanceIds = command.instanceIds(); QVariantList selectedObjs; + QObject *firstSceneRoot = nullptr; + ServerNodeInstance firstInstance; for (qint32 id : instanceIds) { if (hasInstanceForId(id)) { ServerNodeInstance instance = instanceForId(id); + QObject *sceneRoot = find3DSceneRoot(instance); + if (!firstSceneRoot && sceneRoot) { + firstSceneRoot = sceneRoot; + firstInstance = instance; + } QObject *object = nullptr; - if (instance.isSubclassOf("QQuick3DNode")) + if (firstSceneRoot && sceneRoot == firstSceneRoot && instance.isSubclassOf("QQuick3DNode")) object = instance.internalObject(); - if (object && object != m_rootNode) + if (object && (firstSceneRoot != object || instance.isSubclassOf("QQuick3DModel"))) selectedObjs << objectToVariant(object); } } + if (firstSceneRoot && m_active3DScene != firstSceneRoot) { + m_active3DScene = firstSceneRoot; + m_active3DView = findView3DForInstance(firstInstance); + updateActiveSceneToEditView3D(); + } + // Ensure the UI has enough selection box items. If it doesn't yet have them, which can be the // case when the first selection processed is a multiselection, we wait a bit as // using the new boxes immediately leads to visual glitches. diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index adc18674880..e8a782a7df9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -62,7 +62,7 @@ private slots: void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName); void handleObjectPropertyChange(const QVariant &object, const QVariant &propName); void handleToolStateChanged(const QString &tool, const QVariant &toolState); - void updateViewPortRect(); + void handleView3DSizeChange(); protected: void collectItemChangesAndSendChangeCommands() override; @@ -81,9 +81,10 @@ private: QObject *createEditView3D(QQmlEngine *engine); void setup3DEditView(const QList &instanceList, const QVariantMap &toolStates); - QObject *findRootNodeOf3DViewport(const QList &instanceList) const; void createCameraAndLightGizmos(const QList &instanceList) const; - ServerNodeInstance findViewPort(const QList &instanceList); + void addViewPorts(const QList &instanceList); + ServerNodeInstance findView3DForInstance(const ServerNodeInstance &instance) const; + QObject *find3DSceneRoot(const ServerNodeInstance &instance) const; QVector vectorToPropertyValue(const ServerNodeInstance &instance, const PropertyName &propertyName, const QVariant &variant); @@ -91,8 +92,13 @@ private: const PropertyName &propertyName, ValuesModifiedCommand::TransactionOption option); bool dropAcceptable(QDragMoveEvent *event) const; + void updateView3DRect(QObject *view3D); + void updateActiveSceneToEditView3D(); QObject *m_editView3D = nullptr; + QVector m_view3Ds; + ServerNodeInstance m_active3DView; + QObject *m_active3DScene = nullptr; QSet m_parentChangedSet; QList m_completedComponentList; QList m_tokenList; @@ -100,8 +106,6 @@ private: QTimer m_selectionChangeTimer; QVariant m_changedNode; PropertyName m_changedProperty; - ServerNodeInstance m_viewPortInstance; - QObject *m_rootNode = nullptr; ChangeSelectionCommand m_pendingSelectionChangeCommand; }; diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc index 4174d4b5b72..79ffb4934f2 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc @@ -27,6 +27,7 @@ mockfiles/ToggleButton.qml mockfiles/RotateGizmo.qml mockfiles/RotateRing.qml + mockfiles/SceneView3D.qml mockfiles/SelectionBox.qml mockfiles/AxisHelper.qml mockfiles/AxisHelperArm.qml