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 00000000000..b3c1bd883f9 Binary files /dev/null and b/share/qtcreator/qml/qmlpuppet/mockfiles/meshes/scalerod.mesh differ 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