Files
qt-creator/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml
Miikka Heikkinen ef95674378 QmlDesigner: Initialize 3D edit view tool state at initial show
Whenever the tool state of 3D edit view changes, creator is notified
to store the state. The stored state is then restored to 3D edit view
when it is restarted. The state persists only for the lifetime of the
creator.

Change-Id: I888b3ba82693a17a2f163924fbae1cfa27593890
Fixes: QDS-1351
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2020-01-17 14:57:44 +00:00

612 lines
22 KiB
QML

/****************************************************************************
**
** 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.12
import QtQuick.Window 2.12
import QtQuick3D 1.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import MouseArea3D 1.0
Window {
id: viewWindow
width: 1024
height: 768
visible: true
title: "3D Edit View"
// 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 alias showEditLight: btnEditViewLight.toggled
property alias usePerspective: btnPerspective.toggled
property alias globalOrientation: btnLocalGlobal.toggled
property Node selectedNode: null // This is non-null only in single selection case
property var selectedNodes: [] // All selected nodes
property var lightGizmos: []
property var cameraGizmos: []
property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
signal selectionChanged(var selectedNodes)
signal commitObjectProperty(var object, var propName)
signal changeObjectProperty(var object, var propName)
onUsePerspectiveChanged: _generalHelper.storeToolState("usePerspective", usePerspective)
onShowEditLightChanged: _generalHelper.storeToolState("showEditLight", showEditLight)
onGlobalOrientationChanged: _generalHelper.storeToolState("globalOrientation", globalOrientation)
function updateToolStates(toolStates) {
// Init the stored state so we don't unnecessarily reflect changes back to creator
_generalHelper.initToolStates(toolStates);
if ("showEditLight" in toolStates)
showEditLight = toolStates.showEditLight;
if ("usePerspective" in toolStates)
usePerspective = toolStates.usePerspective;
if ("globalOrientation" in toolStates)
globalOrientation = toolStates.globalOrientation;
var groupIndex;
var group;
var i;
if ("groupSelect" in toolStates) {
groupIndex = toolStates.groupSelect;
group = toolbarButtons.buttonGroups["groupSelect"];
for (i = 0; i < group.length; ++i)
group[i].selected = (i === groupIndex);
}
if ("groupTransform" in toolStates) {
groupIndex = toolStates.groupTransform;
group = toolbarButtons.buttonGroups["groupTransform"];
for (i = 0; i < group.length; ++i)
group[i].selected = (i === groupIndex);
}
if ("editCamState" in toolStates)
cameraControl.restoreCameraState(toolStates.editCamState);
}
function ensureSelectionBoxes(count) {
var needMore = count - selectionBoxes.length
if (needMore > 0) {
var component = Qt.createComponent("SelectionBox.qml");
if (component.status === Component.Ready) {
for (var i = 0; i < needMore; ++i) {
var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry");
var box = component.createObject(mainSceneHelpers, {"view3D": editView,
"geometryName": geometryName});
selectionBoxes[selectionBoxes.length] = box;
}
}
}
}
function selectObjects(objects) {
// Create selection boxes as necessary. One more box than is actually needed is created, so
// that we always have a previously created box to use for new selection.
// This fixes an occasional visual glitch when creating a new box.
ensureSelectionBoxes(objects.length + 1)
var i;
for (i = 0; i < objects.length; ++i)
selectionBoxes[i].targetNode = objects[i];
for (i = objects.length; i < selectionBoxes.length; ++i)
selectionBoxes[i].targetNode = null;
selectedNodes = objects;
if (objects.length === 0 || objects.length > 1)
selectedNode = null;
else
selectedNode = objects[0];
}
function handleObjectClicked(object, multi) {
var theObject = object;
if (btnSelectGroup.selected) {
while (theObject && theObject.parent !== scene)
theObject = theObject.parent;
}
// Object selection logic:
// Regular click: Clear any multiselection, single-selects the clicked object
// Ctrl-click: No objects selected: Act as single select
// One or more objects selected: Multiselect
// Null object always clears entire selection
var newSelection = [];
if (object !== null) {
if (multi && selectedNodes.length > 0) {
var deselect = false;
for (var i = 0; i < selectedNodes.length; ++i) {
// Multiselecting already selected object clears that object from selection
if (selectedNodes[i] !== object)
newSelection[newSelection.length] = selectedNodes[i];
else
deselect = true;
}
if (!deselect)
newSelection[newSelection.length] = object;
} else {
newSelection[0] = theObject;
}
}
selectObjects(newSelection);
selectionChanged(newSelection);
}
function addLightGizmo(obj)
{
// Insert into first available gizmo
for (var i = 0; i < lightGizmos.length; ++i) {
if (!lightGizmos[i].targetNode) {
lightGizmos[i].targetNode = obj;
return;
}
}
// No free gizmos available, create a new one
var component = Qt.createComponent("LightGizmo.qml");
if (component.status === Component.Ready) {
var gizmo = component.createObject(overlayScene,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes});
lightGizmos[lightGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
function addCameraGizmo(obj)
{
// Insert into first available gizmo
for (var i = 0; i < cameraGizmos.length; ++i) {
if (!cameraGizmos[i].targetNode) {
cameraGizmos[i].targetNode = obj;
return;
}
}
// No free gizmos available, create a new one
var component = Qt.createComponent("CameraGizmo.qml");
if (component.status === Component.Ready) {
var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var gizmo = component.createObject(
overlayScene,
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect, "selectedNodes": selectedNodes});
cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
// Work-around the fact that the projection matrix for the camera is not calculated until
// the first frame is rendered, so any initial calls to mapFrom3DScene() will fail.
Component.onCompleted: {
selectObjects([]);
_generalHelper.requestOverlayUpdate();
}
onWidthChanged: _generalHelper.requestOverlayUpdate();
onHeightChanged: _generalHelper.requestOverlayUpdate();
Node {
id: overlayScene
PerspectiveCamera {
id: overlayPerspectiveCamera
clipFar: editPerspectiveCamera.clipFar
clipNear: editPerspectiveCamera.clipNear
position: editPerspectiveCamera.position
rotation: editPerspectiveCamera.rotation
}
OrthographicCamera {
id: overlayOrthoCamera
clipFar: editOrthoCamera.clipFar
clipNear: editOrthoCamera.clipNear
position: editOrthoCamera.position
rotation: editOrthoCamera.rotation
scale: editOrthoCamera.scale
}
MouseArea3D {
id: gizmoDragHelper
view3D: overlayView
}
MoveGizmo {
id: moveGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
globalOrientation: viewWindow.globalOrientation
visible: viewWindow.selectedNode && btnMove.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onPositionCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "position")
onPositionMove: viewWindow.changeObjectProperty(viewWindow.selectedNode, "position")
}
ScaleGizmo {
id: scaleGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
visible: viewWindow.selectedNode && btnScale.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onScaleCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "scale")
onScaleChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "scale")
}
RotateGizmo {
id: rotateGizmo
scale: autoScale.getScale(Qt.vector3d(7, 7, 7))
highlightOnHover: true
targetNode: viewWindow.selectedNode
globalOrientation: viewWindow.globalOrientation
visible: viewWindow.selectedNode && btnRotate.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onRotateCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "rotation")
onRotateChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "rotation")
}
AutoScaleHelper {
id: autoScale
view3D: overlayView
position: moveGizmo.scenePosition
orientation: moveGizmo.orientation
}
Line3D {
id: pivotLine
visible: viewWindow.selectedNode
name: "3D Edit View Pivot Line"
color: "#ddd600"
function flipIfNeeded(vec) {
if (viewWindow.selectedNode.orientation === Node.LeftHanded)
return vec;
else
return Qt.vector3d(vec.x, vec.y, -vec.z);
}
startPos: viewWindow.selectedNode ? flipIfNeeded(viewWindow.selectedNode.scenePosition)
: Qt.vector3d(0, 0, 0)
Connections {
target: viewWindow
onSelectedNodeChanged: {
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
viewWindow.selectedNode));
}
}
Connections {
target: viewWindow.selectedNode
onSceneTransformChanged: {
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
viewWindow.selectedNode));
}
}
Model {
id: pivotCap
source: "#Sphere"
scale: autoScale.getScale(Qt.vector3d(0.03, 0.03, 0.03))
position: pivotLine.startPos
materials: [
DefaultMaterial {
id: lineMat
lighting: DefaultMaterial.NoLighting
cullingMode: Material.DisableCulling
emissiveColor: pivotLine.color
}
]
}
}
}
Rectangle {
anchors.fill: parent
focus: true
gradient: Gradient {
GradientStop { position: 1.0; color: "#222222" }
GradientStop { position: 0.0; color: "#999999" }
}
MouseArea {
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;
}
}
DropArea {
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
}
Overlay2D {
id: gizmoLabel
targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo
targetView: overlayView
visible: targetNode.dragging
Rectangle {
color: "white"
x: -width / 2
y: -height - 8
width: gizmoLabelText.width + 4
height: gizmoLabelText.height + 4
border.width: 1
Text {
id: gizmoLabelText
text: {
var l = Qt.locale();
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
}
}
}
EditCameraController {
id: cameraControl
camera: editView.camera
anchors.fill: parent
view3d: editView
}
}
Rectangle { // toolbar
id: toolbar
color: "#9F000000"
width: 35
height: toolbarButtons.height
Column {
id: toolbarButtons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 5
padding: 5
// Button groups must be defined in parent object of buttons
property var buttonGroups: {
"groupSelect": [btnSelectGroup, btnSelectItem],
"groupTransform": [btnMove, btnRotate, btnScale]
}
ToolBarButton {
id: btnSelectItem
selected: true
tooltip: qsTr("Select Item")
shortcut: "Q"
currentShortcut: selected ? "" : shortcut
tool: "item_selection"
buttonGroup: "groupSelect"
}
ToolBarButton {
id: btnSelectGroup
tooltip: qsTr("Select Group")
shortcut: "Q"
currentShortcut: btnSelectItem.currentShortcut === shortcut ? "" : shortcut
tool: "group_selection"
buttonGroup: "groupSelect"
}
Rectangle { // separator
width: 25
height: 1
color: "#f1f1f1"
anchors.horizontalCenter: parent.horizontalCenter
}
ToolBarButton {
id: btnMove
selected: true
tooltip: qsTr("Move current selection")
shortcut: "W"
currentShortcut: shortcut
tool: "move"
buttonGroup: "groupTransform"
}
ToolBarButton {
id: btnRotate
tooltip: qsTr("Rotate current selection")
shortcut: "E"
currentShortcut: shortcut
tool: "rotate"
buttonGroup: "groupTransform"
}
ToolBarButton {
id: btnScale
tooltip: qsTr("Scale current selection")
shortcut: "R"
currentShortcut: shortcut
tool: "scale"
buttonGroup: "groupTransform"
}
Rectangle { // separator
width: 25
height: 1
color: "#f1f1f1"
anchors.horizontalCenter: parent.horizontalCenter
}
ToolBarButton {
id: btnFit
tooltip: qsTr("Fit camera to current selection")
shortcut: "F"
currentShortcut: shortcut
tool: "fit"
togglable: false
onSelectedChanged: {
if (selected) {
var targetNode = viewWindow.selectedNodes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.rotation, true);
}
}
}
}
}
AxisHelper {
anchors.right: parent.right
anchors.top: parent.top
width: 100
height: width
editCameraCtrl: cameraControl
selectedNode : viewWindow.selectedNodes.length ? selectionBoxes[0].model : null
}
Rectangle { // top controls bar
color: "#aa000000"
width: 290
height: btnPerspective.height + 10
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: 100
Row {
padding: 5
anchors.fill: parent
ToggleButton {
id: btnPerspective
width: 105
tooltip: qsTr("Toggle Perspective / Orthographic Projection")
states: [{iconId: "ortho", text: qsTr("Orthographic")}, {iconId: "persp", text: qsTr("Perspective")}]
}
ToggleButton {
id: btnLocalGlobal
width: 65
tooltip: qsTr("Toggle Global / Local Orientation")
states: [{iconId: "local", text: qsTr("Local")}, {iconId: "global", text: qsTr("Global")}]
}
ToggleButton {
id: btnEditViewLight
width: 110
toggleBackground: true
tooltip: qsTr("Toggle Edit Light")
states: [{iconId: "edit_light_off", text: qsTr("Edit Light Off")}, {iconId: "edit_light_on", text: qsTr("Edit Light On")}]
}
}
}
Text {
id: helpText
property string modKey: _generalHelper.isMacOS ? qsTr("Option") : qsTr("Alt")
color: "white"
text: qsTr("Camera controls: ") + modKey
+ qsTr(" + mouse press and drag. Left: Rotate, Middle: Pan, Right/Wheel: Zoom.")
anchors.bottom: parent.bottom
}
}