From c67965fb2932a129ba6aa87875007fd5b70b24fd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 31 Oct 2019 10:46:14 +0200 Subject: [PATCH] QmlDesigner: Add ScaleGizmo to 3D edit view ScaleGizmo allows scaling in the direction of local or global axes, as well as uniform scaling. Any scale component cannot be made negative with ScaleGizmo. Change-Id: I9b98d9593e07ded340178b07b73fa1b72421ba20 Fixes: QDS-1195 Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../qml/qmlpuppet/mockfiles/Arrow.qml | 91 +--------- .../mockfiles/DirectionalDraggable.qml | 124 ++++++++++++++ .../qml/qmlpuppet/mockfiles/EditView3D.qml | 59 +++++-- .../qml/qmlpuppet/mockfiles/MoveGizmo.qml | 24 +-- .../qml/qmlpuppet/mockfiles/ScaleGizmo.qml | 159 ++++++++++++++++++ .../qml/qmlpuppet/mockfiles/ScaleRod.qml | 72 ++++++++ .../qmlpuppet/mockfiles/meshes/scalerod.mesh | Bin 0 -> 1756 bytes .../qml2puppet/editor3d/mousearea3d.cpp | 91 +++++++++- .../qml2puppet/editor3d/mousearea3d.h | 15 +- .../qt5informationnodeinstanceserver.cpp | 49 +++--- .../qt5informationnodeinstanceserver.h | 11 +- share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc | 4 + 12 files changed, 561 insertions(+), 138 deletions(-) create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/ScaleRod.qml create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/meshes/scalerod.mesh diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml index 0176a55dfaf..67ae4c7b295 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/Arrow.qml @@ -27,104 +27,29 @@ import QtQuick 2.0 import QtQuick3D 1.0 import MouseArea3D 1.0 -Model { +DirectionalDraggable { id: arrow - rotationOrder: Node.XYZr source: "meshes/arrow.mesh" - property View3D view3D - property alias color: material.emissiveColor - property Node targetNode: null - property bool dragging: false - - readonly property bool hovering: mouseAreaYZ.hovering || mouseAreaXZ.hovering - - property var _pointerPosPressed - property var _targetStartPos - signal positionCommit() signal positionMove() - materials: DefaultMaterial { - id: material - emissiveColor: "white" - lighting: DefaultMaterial.NoLighting - } - - function handlePressed(mouseArea, pointerPosition) + function localPos(sceneRelativeDistance) { - if (!targetNode) - return; - - var maskedPosition = Qt.vector3d(pointerPosition.x, 0, 0); - _pointerPosPressed = mouseArea.mapPositionToScene(maskedPosition); - var sp = targetNode.scenePosition; - _targetStartPos = Qt.vector3d(sp.x, sp.y, sp.z); - dragging = true; - } - - function posInParent(mouseArea, pointerPosition) - { - var maskedPosition = Qt.vector3d(pointerPosition.x, 0, 0); - var scenePointerPos = mouseArea.mapPositionToScene(maskedPosition); - var sceneRelativeDistance = Qt.vector3d( - scenePointerPos.x - _pointerPosPressed.x, - scenePointerPos.y - _pointerPosPressed.y, - scenePointerPos.z - _pointerPosPressed.z); - var newScenePos = Qt.vector3d( _targetStartPos.x + sceneRelativeDistance.x, _targetStartPos.y + sceneRelativeDistance.y, _targetStartPos.z + sceneRelativeDistance.z); - return targetNode.parent.mapPositionFromScene(newScenePos); } - function handleDragged(mouseArea, pointerPosition) - { - if (!targetNode) - return; - - targetNode.position = posInParent(mouseArea, pointerPosition); - arrow.positionMove(); + onDragged: { + targetNode.position = localPos(sceneRelativeDistance); + positionMove(); } - function handleReleased(mouseArea, pointerPosition) - { - if (!targetNode) - return; - - targetNode.position = posInParent(mouseArea, pointerPosition); - dragging = false; - arrow.positionCommit(); - } - - MouseArea3D { - id: mouseAreaYZ - view3D: arrow.view3D - x: 0 - y: -1.5 - width: 12 - height: 3 - rotation: Qt.vector3d(0, 0, 90) - grabsMouse: targetNode - onPressed: arrow.handlePressed(mouseAreaYZ, pointerPosition) - onDragged: arrow.handleDragged(mouseAreaYZ, pointerPosition) - onReleased: arrow.handleReleased(mouseAreaYZ, pointerPosition) - } - - MouseArea3D { - id: mouseAreaXZ - view3D: arrow.view3D - x: 0 - y: -1.5 - width: 12 - height: 3 - rotation: Qt.vector3d(0, 90, 90) - grabsMouse: targetNode - onPressed: arrow.handlePressed(mouseAreaXZ, pointerPosition) - onDragged: arrow.handleDragged(mouseAreaXZ, pointerPosition) - onReleased: arrow.handleReleased(mouseAreaXZ, pointerPosition) + onReleased: { + targetNode.position = localPos(sceneRelativeDistance); + positionCommit(); } } - diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml new file mode 100644 index 00000000000..243744210ee --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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.0 +import QtQuick3D 1.0 +import MouseArea3D 1.0 + +Model { + id: rootModel + rotationOrder: Node.XYZr + + property View3D view3D + property alias color: material.emissiveColor + property Node targetNode: null + property bool dragging: false + property bool active: false + + readonly property bool hovering: mouseAreaYZ.hovering || mouseAreaXZ.hovering + + property var _pointerPosPressed + property var _targetStartPos + + signal pressed(var mouseArea) + signal dragged(var mouseArea, vector3d sceneRelativeDistance) + signal released(var mouseArea, vector3d sceneRelativeDistance) + + materials: DefaultMaterial { + id: material + emissiveColor: "white" + lighting: DefaultMaterial.NoLighting + } + + function handlePressed(mouseArea, scenePos) + { + if (!targetNode) + return; + + var maskedPosition = Qt.vector3d(scenePos.x, 0, 0); + _pointerPosPressed = mouseArea.mapPositionToScene(maskedPosition); + var sp = targetNode.scenePosition; + _targetStartPos = Qt.vector3d(sp.x, sp.y, sp.z); + dragging = true; + pressed(mouseArea); + } + + function calcRelativeDistance(mouseArea, scenePos) + { + var maskedPosition = Qt.vector3d(scenePos.x, 0, 0); + var scenePointerPos = mouseArea.mapPositionToScene(maskedPosition); + return Qt.vector3d(scenePointerPos.x - _pointerPosPressed.x, + scenePointerPos.y - _pointerPosPressed.y, + scenePointerPos.z - _pointerPosPressed.z); + } + + function handleDragged(mouseArea, scenePos) + { + if (!targetNode) + return; + + dragged(mouseArea, calcRelativeDistance(mouseArea, scenePos)); + } + + function handleReleased(mouseArea, scenePos) + { + if (!targetNode) + return; + + released(mouseArea, calcRelativeDistance(mouseArea, scenePos)); + dragging = false; + } + + MouseArea3D { + id: mouseAreaYZ + view3D: rootModel.view3D + x: 0 + y: -1.5 + width: 12 + height: 3 + rotation: Qt.vector3d(0, 0, 90) + grabsMouse: targetNode + active: rootModel.active + onPressed: rootModel.handlePressed(mouseAreaYZ, scenePos) + onDragged: rootModel.handleDragged(mouseAreaYZ, scenePos) + onReleased: rootModel.handleReleased(mouseAreaYZ, scenePos) + } + + MouseArea3D { + id: mouseAreaXZ + view3D: rootModel.view3D + x: 0 + y: -1.5 + width: 12 + height: 3 + rotation: Qt.vector3d(0, 90, 90) + grabsMouse: targetNode + active: rootModel.active + onPressed: rootModel.handlePressed(mouseAreaXZ, scenePos) + onDragged: rootModel.handleDragged(mouseAreaXZ, scenePos) + onReleased: rootModel.handleReleased(mouseAreaXZ, scenePos) + } +} + diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index b227cee23e1..1dcca048e72 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -48,8 +48,8 @@ Window { property var cameraGizmos: [] signal objectClicked(var object) - signal commitObjectPosition(var object) - signal moveObjectPosition(var object) + signal commitObjectProperty(var object, var propName) + signal changeObjectProperty(var object, var propName) function selectObject(object) { selectedNode = object; @@ -113,11 +113,26 @@ Window { position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition : Qt.vector3d(0, 0, 0) globalOrientation: globalControl.checked - visible: selectedNode + visible: selectedNode && moveToolControl.checked view3D: overlayView - onPositionCommit: viewWindow.commitObjectPosition(selectedNode) - onPositionMove: viewWindow.moveObjectPosition(selectedNode) + onPositionCommit: viewWindow.commitObjectProperty(selectedNode, "position") + onPositionMove: viewWindow.changeObjectProperty(selectedNode, "position") + } + + ScaleGizmo { + id: scaleGizmo + scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) + highlightOnHover: true + targetNode: viewWindow.selectedNode + position: viewWindow.selectedNode ? viewWindow.selectedNode.scenePosition + : Qt.vector3d(0, 0, 0) + globalOrientation: globalControl.checked + visible: selectedNode && scaleToolControl.checked + view3D: overlayView + + onScaleCommit: viewWindow.commitObjectProperty(selectedNode, "scale") + onScaleChange: viewWindow.changeObjectProperty(selectedNode, "scale") } AutoScaleHelper { @@ -186,11 +201,11 @@ Window { Overlay2D { id: gizmoLabel - targetNode: moveGizmo + targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo targetView: overlayView offsetX: 0 offsetY: 45 - visible: moveGizmo.dragging + visible: targetNode.dragging Rectangle { color: "white" @@ -203,11 +218,18 @@ Window { id: gizmoLabelText text: { var l = Qt.locale(); - selectedNode - ? qsTr("x:") + Number(selectedNode.position.x).toLocaleString(l, 'f', 1) - + qsTr(" y:") + Number(selectedNode.position.y).toLocaleString(l, 'f', 1) - + qsTr(" z:") + Number(selectedNode.position.z).toLocaleString(l, 'f', 1) - : ""; + var targetProperty; + if (viewWindow.selectedNode) { + if (gizmoLabel.targetNode === moveGizmo) + targetProperty = viewWindow.selectedNode.position; + else + targetProperty = viewWindow.selectedNode.scale; + return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1) + + qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1) + + qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1); + } else { + return ""; + } } anchors.centerIn: parent } @@ -252,6 +274,19 @@ Window { text: qsTr("Use Global Orientation") onCheckedChanged: cameraControl.forceActiveFocus() } + Column { + x: 8 + RadioButton { + id: moveToolControl + checked: false + text: qsTr("Move Tool") + } + RadioButton { + id: scaleToolControl + checked: true + text: qsTr("Scale Tool") + } + } } Text { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml index 7ad5a2a0113..c035f9c85ed 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml @@ -49,8 +49,9 @@ Node { rotation: Qt.vector3d(0, 0, -90) targetNode: moveGizmo.targetNode color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) - : Qt.rgba(1, 0, 0, 1) + : Qt.rgba(1, 0, 0, 1) view3D: moveGizmo.view3D + active: moveGizmo.visible onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() @@ -61,9 +62,10 @@ Node { objectName: "Arrow Y" rotation: Qt.vector3d(0, 0, 0) targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) - : Qt.rgba(0, 0, 1, 1) + color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) + : Qt.rgba(0, 0.6, 0, 1) view3D: moveGizmo.view3D + active: moveGizmo.visible onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() @@ -74,9 +76,10 @@ Node { objectName: "Arrow Z" rotation: Qt.vector3d(90, 0, 0) targetNode: moveGizmo.targetNode - color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) - : Qt.rgba(0, 0.6, 0, 1) + color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) + : Qt.rgba(0, 0, 1, 1) view3D: moveGizmo.view3D + active: moveGizmo.visible onPositionCommit: moveGizmo.positionCommit() onPositionMove: moveGizmo.positionMove() @@ -108,13 +111,14 @@ Node { rotation: view3D.camera.rotation grabsMouse: moveGizmo.targetNode priority: 1 + active: moveGizmo.visible property var _pointerPosPressed property var _targetStartPos - function posInParent(pointerPosition) + function localPos(scenePos) { - var scenePointerPos = mapPositionToScene(pointerPosition); + var scenePointerPos = mapPositionToScene(scenePos); var sceneRelativeDistance = Qt.vector3d( scenePointerPos.x - _pointerPosPressed.x, scenePointerPos.y - _pointerPosPressed.y, @@ -132,7 +136,7 @@ Node { if (!moveGizmo.targetNode) return; - _pointerPosPressed = mapPositionToScene(pointerPosition); + _pointerPosPressed = mapPositionToScene(scenePos); var sp = moveGizmo.targetNode.scenePosition; _targetStartPos = Qt.vector3d(sp.x, sp.y, sp.z); } @@ -140,14 +144,14 @@ Node { if (!moveGizmo.targetNode) return; - moveGizmo.targetNode.position = posInParent(pointerPosition); + moveGizmo.targetNode.position = localPos(scenePos); moveGizmo.positionMove(); } onReleased: { if (!moveGizmo.targetNode) return; - moveGizmo.targetNode.position = posInParent(pointerPosition); + moveGizmo.targetNode.position = localPos(scenePos); moveGizmo.positionCommit(); } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml new file mode 100644 index 00000000000..7f4a4f69b8a --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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.0 +import QtQuick3D 1.0 +import MouseArea3D 1.0 + +Node { + id: scaleGizmo + + property View3D view3D + property bool highlightOnHover: false + property Node targetNode: null + property bool globalOrientation: true + readonly property bool dragging: scaleRodX.dragging || scaleRodY.dragging || scaleRodZ.dragging + || centerMouseArea.dragging + + signal scaleCommit() + signal scaleChange() + + Node { + rotation: globalOrientation || !targetNode ? Qt.vector3d(0, 0, 0) : targetNode.sceneRotation + + ScaleRod { + id: scaleRodX + objectName: "scaleRod X" + rotation: Qt.vector3d(0, 0, -90) + targetNode: scaleGizmo.targetNode + color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(1, 0, 0, 1)) + : Qt.rgba(1, 0, 0, 1) + view3D: scaleGizmo.view3D + active: scaleGizmo.visible + + onScaleCommit: scaleGizmo.scaleCommit() + onScaleChange: scaleGizmo.scaleChange() + } + + ScaleRod { + id: scaleRodY + objectName: "scaleRod Y" + rotation: Qt.vector3d(0, 0, 0) + targetNode: scaleGizmo.targetNode + color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0.6, 0, 1)) + : Qt.rgba(0, 0.6, 0, 1) + view3D: scaleGizmo.view3D + active: scaleGizmo.visible + + onScaleCommit: scaleGizmo.scaleCommit() + onScaleChange: scaleGizmo.scaleChange() + } + + ScaleRod { + id: scaleRodZ + objectName: "scaleRod Z" + rotation: Qt.vector3d(90, 0, 0) + targetNode: scaleGizmo.targetNode + color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0, 0, 1, 1)) + : Qt.rgba(0, 0, 1, 1) + view3D: scaleGizmo.view3D + active: scaleGizmo.visible + + onScaleCommit: scaleGizmo.scaleCommit() + onScaleChange: scaleGizmo.scaleChange() + } + } + + Model { + id: centerCube + + source: "#Cube" + scale: Qt.vector3d(0.024, 0.024, 0.024) + materials: DefaultMaterial { + id: material + emissiveColor: highlightOnHover + && (centerMouseArea.hovering || centerMouseArea.dragging) + ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) + : Qt.rgba(0.5, 0.5, 0.5, 1) + lighting: DefaultMaterial.NoLighting + } + + MouseArea3D { + id: centerMouseArea + view3D: scaleGizmo.view3D + x: -60 + y: -60 + width: 120 + height: 120 + rotation: view3D.camera.rotation + grabsMouse: scaleGizmo.targetNode + priority: 1 + active: scaleGizmo.visible + + property var _startScale + property var _startScreenPos + + function localScale(screenPos) + { + var yDelta = screenPos.y - _startScreenPos.y; + if (yDelta === 0) + return; + var scaler = 1.0 + (yDelta * 0.025); + if (scaler === 0 ) + scaler = 0.0001; + if (scaler < 0) + scaler = -scaler; + return Qt.vector3d(scaler * _startScale.x, + scaler * _startScale.y, + scaler * _startScale.z); + } + + onPressed: { + if (!scaleGizmo.targetNode) + return; + + // Recreate vector so we don't follow the changes in targetNode.scale + _startScale = Qt.vector3d(scaleGizmo.targetNode.scale.x, + scaleGizmo.targetNode.scale.y, + scaleGizmo.targetNode.scale.z); + _startScreenPos = screenPos; + } + onDragged: { + if (!scaleGizmo.targetNode) + return; + + scaleGizmo.targetNode.scale = localScale(screenPos); + scaleGizmo.scaleChange(); + } + onReleased: { + if (!scaleGizmo.targetNode) + return; + + scaleGizmo.targetNode.scale = localScale(screenPos); + scaleGizmo.scaleCommit(); + } + } + } +} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleRod.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleRod.qml new file mode 100644 index 00000000000..4ef6d8a0a72 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleRod.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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.0 +import QtQuick3D 1.0 +import MouseArea3D 1.0 + +DirectionalDraggable { + id: scaleRod + source: "meshes/scalerod.mesh" + + signal scaleCommit() + signal scaleChange() + + property var _startScale + + Model { + source: "#Cube" + y: 10 + scale: Qt.vector3d(0.025, 0.025, 0.025) + materials: DefaultMaterial { + id: material + emissiveColor: scaleRod.color + lighting: DefaultMaterial.NoLighting + } + } + + function localScale(mouseArea, sceneRelativeDistance) + { + return mouseArea.getNewScale(targetNode, _startScale, _pointerPosPressed, + sceneRelativeDistance, sceneScale.x); + } + + onPressed: { + // Recreate vector so we don't follow the changes in targetNode.sceneScale + _startScale = Qt.vector3d(targetNode.sceneScale.x, + targetNode.sceneScale.y, + targetNode.sceneScale.z); + } + + onDragged: { + targetNode.scale = localScale(mouseArea, sceneRelativeDistance); + scaleChange(); + } + + onReleased: { + targetNode.scale = localScale(mouseArea, sceneRelativeDistance); + scaleCommit(); + } +} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/meshes/scalerod.mesh b/share/qtcreator/qml/qmlpuppet/mockfiles/meshes/scalerod.mesh new file mode 100644 index 0000000000000000000000000000000000000000..b3c1bd883f98d2d72b6de7aa5fbef1f6512f4fba GIT binary patch literal 1756 zcmeaRUvPq%fq`KI8v{cHD+2>70|SEv6g#joFo5JV*ccc*7#J7|*cccX85kH=urYuE zI|Bm)69WUo2UZ3KE(VBNki<_UJ`YHo4I&RxE5X3Pzysxj%vNAvV30uK>wpYkU|`^6 zU|>irDJhCC$S(#7fy@W<^YV*wk>yLv3_$WAd%*G~sTC!Oc_1MMKA0IvnIK(>ISdR8 zXV0A38?uFQ)7dj;Y#A6D_A{J4vmUIN;eZ{O2J;*2XU$w_eBPaL35W)%1L^<>o)~=@XU^;enS-zoq_4pa!pCl3$QDK;h`s3UBvu~-1I+ylVE;8h{Kx>YkAa~9 z2m<_aoUHJ z&Oqv*`au38Rv$QA!0v~{p96Zh?1%UroUXy~0C6X>KBzmv3gPjNtZy$U{;=x_A2h#%}vQ81`Z0twu#RZxVnK{NpFU94jO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp index 4c1fdcc78e6..fe84ed13a62 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp @@ -60,6 +60,11 @@ bool MouseArea3D::grabsMouse() const return m_grabsMouse; } +bool MouseArea3D::active() const +{ + return m_active; +} + qreal MouseArea3D::x() const { return m_x; @@ -103,6 +108,15 @@ void MouseArea3D::setGrabsMouse(bool grabsMouse) emit grabsMouseChanged(grabsMouse); } +void MouseArea3D::setActive(bool active) +{ + if (m_active == active) + return; + + m_active = active; + emit activeChanged(active); +} + void MouseArea3D::setX(qreal x) { if (qFuzzyCompare(m_x, x)) @@ -190,6 +204,73 @@ QVector3D MouseArea3D::rayIntersectsPlane(const QVector3D &rayPos0, return rayPos0 + distanceFromRayPos0ToPlane * rayDirection; } +// Get a new scale based on a relative scene distance along an axis (used to adjust scale via drag) +// This function never returns a negative scaling +QVector3D MouseArea3D::getNewScale(QQuick3DNode *node, const QVector3D &startScale, + const QVector3D &pressPos, + const QVector3D &sceneRelativeDistance, float scaler) +{ + if (node) { + // Note: This only returns correct scale when scale is positive + auto getScale = [&](const QMatrix4x4 &m) -> QVector3D { + return QVector3D(m.column(0).length(), m.column(1).length(), m.column(2).length()); + }; + const float constantDragScaler = 0.1f; + const float nonZeroValue = 0.0001f; + + if (qFuzzyIsNull(scaler)) + scaler = nonZeroValue; + + const QVector3D scenePos = node->scenePosition(); + const QMatrix4x4 parentTransform = node->parentNode()->sceneTransform(); + QMatrix4x4 newTransform = node->sceneTransform(); + QVector3D normalRelDist = sceneRelativeDistance.normalized(); + float direction = QVector3D::dotProduct((pressPos - scenePos).normalized(), normalRelDist); + float magnitude = constantDragScaler * sceneRelativeDistance.length() / scaler; + + // Reset everything but rotation to ensure translation and scale do not affect rotate below + newTransform(0,3) = 0; + newTransform(1,3) = 0; + newTransform(2,3) = 0; + QVector3D curScale = getScale(newTransform); + if (qFuzzyIsNull(curScale.x())) + curScale.setX(nonZeroValue); + if (qFuzzyIsNull(curScale.y())) + curScale.setY(nonZeroValue); + if (qFuzzyIsNull(curScale.z())) + curScale.setZ(nonZeroValue); + newTransform.scale({1.f / curScale.x(), 1.f / curScale.y(), 1.f / curScale.z()}); + + // Rotate relative distance according to object rotation + normalRelDist = newTransform.inverted().map(normalRelDist).normalized(); + + // Ensure scaling is always positive/negative according to direction + normalRelDist.setX(qAbs(normalRelDist.x())); + normalRelDist.setY(qAbs(normalRelDist.y())); + normalRelDist.setZ(qAbs(normalRelDist.z())); + QVector3D scaleVec = normalRelDist; + scaleVec *= magnitude; + if (direction > 0) { + scaleVec.setX(scaleVec.x() + 1.f); + scaleVec.setY(scaleVec.y() + 1.f); + scaleVec.setZ(scaleVec.z() + 1.f); + } else { + scaleVec.setX(1.f - scaleVec.x()); + scaleVec.setY(1.f - scaleVec.y()); + scaleVec.setZ(1.f - scaleVec.z()); + } + scaleVec *= startScale; + + newTransform = parentTransform; + newTransform.scale(scaleVec); + + const QMatrix4x4 localTransform = parentTransform.inverted() * newTransform; + return getScale(localTransform); + } + + return startScale; +} + QVector3D MouseArea3D::getMousePosInPlane(const QPointF &mousePosInView) const { const QVector3D mousePos1(float(mousePosInView.x()), float(mousePosInView.y()), 0); @@ -207,8 +288,8 @@ QVector3D MouseArea3D::getMousePosInPlane(const QPointF &mousePosInView) const bool MouseArea3D::eventFilter(QObject *, QEvent *event) { - if (m_grabsMouse && s_mouseGrab && s_mouseGrab != this - && (m_priority <= s_mouseGrab->m_priority || s_mouseGrab->m_dragging)) { + if (!m_active || (m_grabsMouse && s_mouseGrab && s_mouseGrab != this + && (m_priority <= s_mouseGrab->m_priority || s_mouseGrab->m_dragging))) { return false; } @@ -227,7 +308,7 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) m_mousePosInPlane = getMousePosInPlane(mouseEvent->pos()); if (mouseOnTopOfMouseArea(m_mousePosInPlane)) { setDragging(true); - emit pressed(m_mousePosInPlane); + emit pressed(m_mousePosInPlane, mouseEvent->globalPos()); if (m_grabsMouse) { if (s_mouseGrab && s_mouseGrab != this) { s_mouseGrab->setDragging(false); @@ -250,7 +331,7 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) if (qFuzzyCompare(mousePosInPlane.z(), -1)) mousePosInPlane = m_mousePosInPlane; setDragging(false); - emit released(mousePosInPlane); + emit released(mousePosInPlane, mouseEvent->globalPos()); if (m_grabsMouse) { if (s_mouseGrab && s_mouseGrab != this) { s_mouseGrab->setDragging(false); @@ -290,7 +371,7 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) if (m_dragging && !qFuzzyCompare(mousePosInPlane.z(), -1)) { m_mousePosInPlane = mousePosInPlane; - emit dragged(mousePosInPlane); + emit dragged(mousePosInPlane, mouseEvent->globalPos()); } break; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h index 99a34be353d..a7e1fa261e7 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h @@ -49,6 +49,7 @@ class MouseArea3D : public QQuick3DNode Q_PROPERTY(bool hovering READ hovering NOTIFY hoveringChanged) Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged) + Q_PROPERTY(int active READ active WRITE setActive NOTIFY activeChanged) Q_INTERFACES(QQmlParserStatus) @@ -66,10 +67,12 @@ public: bool hovering() const; bool dragging() const; bool grabsMouse() const; + bool active() const; public slots: void setView3D(QQuick3DViewport *view3D); void setGrabsMouse(bool grabsMouse); + void setActive(bool active); void setX(qreal x); void setY(qreal y); @@ -82,6 +85,10 @@ public slots: const QVector3D &planePos, const QVector3D &planeNormal) const; + Q_INVOKABLE QVector3D getNewScale(QQuick3DNode *node, const QVector3D &startScale, + const QVector3D &pressPos, + const QVector3D &sceneRelativeDistance, float scaler); + signals: void view3DChanged(); @@ -93,9 +100,10 @@ signals: void hoveringChanged(); void draggingChanged(); - void pressed(const QVector3D &pointerPosition); - void released(const QVector3D &pointerPosition); - void dragged(const QVector3D &pointerPosition); + void activeChanged(bool active); + void pressed(const QVector3D &scenePos, const QPoint &screenPos); + void released(const QVector3D &scenePos, const QPoint &screenPos); + void dragged(const QVector3D &scenePos, const QPoint &screenPos); void grabsMouseChanged(bool grabsMouse); protected: @@ -118,6 +126,7 @@ private: bool m_hovering = false; bool m_dragging = false; + bool m_active = false; QVector3D getMousePosInPlane(const QPointF &mousePosInView) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index d97cc2d5038..dbbd8f050e9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -97,12 +97,12 @@ QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine) } QObject::connect(window, SIGNAL(objectClicked(QVariant)), this, SLOT(objectClicked(QVariant))); - QObject::connect(window, SIGNAL(commitObjectPosition(QVariant)), - this, SLOT(handleObjectPositionCommit(QVariant))); - QObject::connect(window, SIGNAL(moveObjectPosition(QVariant)), - this, SLOT(handleObjectPositionMove(QVariant))); - QObject::connect(&m_moveTimer, &QTimer::timeout, - this, &Qt5InformationNodeInstanceServer::handleObjectPositionMoveTimeout); + QObject::connect(window, SIGNAL(commitObjectProperty(QVariant, QVariant)), + this, SLOT(handleObjectPropertyCommit(QVariant, QVariant))); + QObject::connect(window, SIGNAL(changeObjectProperty(QVariant, QVariant)), + this, SLOT(handleObjectPropertyChange(QVariant, QVariant))); + QObject::connect(&m_propertyChangeTimer, &QTimer::timeout, + this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout); //For macOS we have to use the 4.1 core profile QSurfaceFormat surfaceFormat = window->requestedFormat(); @@ -188,28 +188,36 @@ void Qt5InformationNodeInstanceServer::modifyVariantValue( } } -void Qt5InformationNodeInstanceServer::handleObjectPositionCommit(const QVariant &object) +void Qt5InformationNodeInstanceServer::handleObjectPropertyCommit(const QVariant &object, + const QVariant &propName) { - modifyVariantValue(object, "position", ValuesModifiedCommand::TransactionOption::End); - m_movedNode = {}; - m_moveTimer.stop(); + modifyVariantValue(object, propName.toByteArray(), + ValuesModifiedCommand::TransactionOption::End); + m_changedNode = {}; + m_changedProperty = {}; + m_propertyChangeTimer.stop(); } -void Qt5InformationNodeInstanceServer::handleObjectPositionMove(const QVariant &object) +void Qt5InformationNodeInstanceServer::handleObjectPropertyChange(const QVariant &object, + const QVariant &propName) { - if (m_movedNode.isNull()) { - modifyVariantValue(object, "position", ValuesModifiedCommand::TransactionOption::Start); - } else { - if (!m_moveTimer.isActive()) - m_moveTimer.start(); + PropertyName propertyName(propName.toByteArray()); + if (m_changedProperty != propertyName || m_changedNode != object) { + if (!m_changedNode.isNull()) + handleObjectPropertyCommit(m_changedNode, m_changedProperty); + modifyVariantValue(object, propertyName, + ValuesModifiedCommand::TransactionOption::Start); + } else if (!m_propertyChangeTimer.isActive()) { + m_propertyChangeTimer.start(); } - m_movedNode = object; + m_changedNode = object; + m_changedProperty = propertyName; } Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : Qt5NodeInstanceServer(nodeInstanceClient) { - m_moveTimer.setInterval(100); + m_propertyChangeTimer.setInterval(100); } void Qt5InformationNodeInstanceServer::sendTokenBack() @@ -282,9 +290,10 @@ void Qt5InformationNodeInstanceServer::modifyProperties( nodeInstanceClient()->valuesModified(createValuesModifiedCommand(properties)); } -void Qt5InformationNodeInstanceServer::handleObjectPositionMoveTimeout() +void Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout() { - modifyVariantValue(m_movedNode, "position", ValuesModifiedCommand::TransactionOption::None); + modifyVariantValue(m_changedNode, m_changedProperty, + ValuesModifiedCommand::TransactionOption::None); } QObject *Qt5InformationNodeInstanceServer::findRootNodeOf3DViewport( diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index e8a0291a9f8..918d18db672 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -51,8 +51,8 @@ public: private slots: void objectClicked(const QVariant &object); - void handleObjectPositionCommit(const QVariant &object); - void handleObjectPositionMove(const QVariant &object); + void handleObjectPropertyCommit(const QVariant &object, const QVariant &propName); + void handleObjectPropertyChange(const QVariant &object, const QVariant &propName); protected: void collectItemChangesAndSendChangeCommands() override; @@ -64,7 +64,7 @@ protected: void modifyProperties(const QVector &properties); private: - void handleObjectPositionMoveTimeout(); + void handleObjectPropertyChangeTimeout(); QObject *createEditView3D(QQmlEngine *engine); void setup3DEditView(const QList &instanceList); QObject *findRootNodeOf3DViewport(const QList &instanceList) const; @@ -81,8 +81,9 @@ private: QSet m_parentChangedSet; QList m_completedComponentList; QList m_tokenList; - QTimer m_moveTimer; - QVariant m_movedNode; + QTimer m_propertyChangeTimer; + QVariant m_changedNode; + PropertyName m_changedProperty; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc index 4bbd30e8fcb..ec02e9d2567 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc @@ -15,7 +15,11 @@ mockfiles/LightGizmo.qml mockfiles/IconGizmo.qml mockfiles/Overlay2D.qml + mockfiles/DirectionalDraggable.qml + mockfiles/ScaleRod.qml + mockfiles/ScaleGizmo.qml mockfiles/meshes/arrow.mesh + mockfiles/meshes/scalerod.mesh mockfiles/images/camera-pick-icon.png mockfiles/images/camera-pick-icon@2x.png mockfiles/images/light-pick-icon.png