QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
Viewer3DCameraControls.qml
Go to the documentation of this file.
1import QtQuick
2
3import QGroundControl.Viewer3D
4
5/// Gazebo-style input handling for the 3D viewer. Forwards mouse/touch gestures
6/// to a Viewer3DCameraController:
7/// - Left drag: pan (picked ground point stays under the cursor)
8/// - Middle drag or Shift+left drag: orbit around the point under the cursor
9/// - Right drag: zoom toward/away from the pressed point (up = in, down = out)
10/// - Wheel: zoom toward/away from the point under the cursor
11/// - Pinch: zoom toward the gesture centroid
12/// - Two-finger twist: rotate heading around the point under the centroid
13/// - Two-finger vertical swipe: tilt around the point under the centroid
14Item {
15 id: root
16
17 property Viewer3DCameraController controller
18
19 DragHandler {
20 id: panHandler
21 target: null
22 acceptedButtons: Qt.LeftButton
23 acceptedModifiers: Qt.NoModifier
24 onActiveChanged: {
25 if (active) {
26 root.controller.beginPan(centroid.pressPosition)
27 // Event compression can collapse the whole drag into the
28 // activating event with no centroid change afterwards, so
29 // apply the movement accumulated so far right away
30 root.controller.panTo(centroid.position)
31 }
32 }
33 onCentroidChanged: {
34 if (active) {
35 root.controller.panTo(centroid.position)
36 }
37 }
38 }
39
40 DragHandler {
41 id: orbitHandler
42 target: null
43 acceptedButtons: Qt.MiddleButton
44 // acceptedButtons does not filter touch points (they have no
45 // buttons), so keep touch away from the button-driven handlers
46 acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
47 onActiveChanged: {
48 if (active) {
49 root.controller.beginOrbit(centroid.pressPosition)
50 root.controller.orbitTo(centroid.position)
51 }
52 }
53 onCentroidChanged: {
54 if (active) {
55 root.controller.orbitTo(centroid.position)
56 }
57 }
58 }
59
60 // Gazebo-style right-drag zoom: drag up zooms in, down zooms out
61 DragHandler {
62 id: dragZoomHandler
63 target: null
64 acceptedButtons: Qt.RightButton
65 acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
66 property real _lastY: 0
67 onActiveChanged: {
68 if (active) {
69 _lastY = centroid.pressPosition.y
70 root.controller.zoom((_lastY - centroid.position.y) * 3, centroid.pressPosition)
71 _lastY = centroid.position.y
72 }
73 }
74 onCentroidChanged: {
75 if (active) {
76 root.controller.zoom((_lastY - centroid.position.y) * 3, centroid.pressPosition)
77 _lastY = centroid.position.y
78 }
79 }
80 }
81
82 // Trackpad-friendly orbit: macOS trackpads have no easy right-drag
83 DragHandler {
84 id: shiftOrbitHandler
85 target: null
86 acceptedButtons: Qt.LeftButton
87 acceptedModifiers: Qt.ShiftModifier
88 onActiveChanged: {
89 if (active) {
90 root.controller.beginOrbit(centroid.pressPosition)
91 root.controller.orbitTo(centroid.position)
92 }
93 }
94 onCentroidChanged: {
95 if (active) {
96 root.controller.orbitTo(centroid.position)
97 }
98 }
99 }
100
101 WheelHandler {
102 id: zoomHandler
103 target: null
104 onWheel: (event) => {
105 root.controller.zoom(event.angleDelta.y, Qt.point(event.x, event.y))
106 }
107 }
108
109 // The PinchHandler owns all two-finger gestures: scale zooms, twist
110 // rotates, and vertical centroid movement tilts (Google Earth style:
111 // swipe up tilts toward the horizon, gain full height = 180 deg). A
112 // separate two-finger DragHandler for tilt would race the PinchHandler
113 // for the exclusive grab, randomly making tilt swipes unresponsive.
114 PinchHandler {
115 id: pinchHandler
116 target: null
117 property real _lastScale: 1.0
118 property real _lastRotation: 0.0
119 property real _lastY: 0
120 onActiveChanged: {
121 _lastScale = 1.0
122 _lastRotation = 0.0
123 if (active) {
124 // Apply the movement consumed before activation right away:
125 // touch-move compression can leave no centroid change between
126 // activation and release, so waiting for onCentroidChanged
127 // would drop the whole swipe
128 _lastY = centroid.pressPosition.y
129 root.controller.tiltBy(((_lastY - centroid.position.y) / root.height) * 180, centroid.position)
130 _lastY = centroid.position.y
131 }
132 }
133 onCentroidChanged: {
134 if (active) {
135 root.controller.tiltBy(((_lastY - centroid.position.y) / root.height) * 180, centroid.position)
136 _lastY = centroid.position.y
137 }
138 }
139 onActiveScaleChanged: {
140 if (active && (activeScale > 0)) {
141 root.controller.zoomBy(_lastScale / activeScale, centroid.position)
142 _lastScale = activeScale
143 }
144 }
145 // Two-finger twist: the world follows the fingers. activeRotation is
146 // positive for a visually clockwise twist (y-down screen), which
147 // matches a heading increase, so it feeds through directly.
148 onActiveRotationChanged: {
149 if (active) {
150 root.controller.rotateBy(activeRotation - _lastRotation, centroid.position)
151 _lastRotation = activeRotation
152 }
153 }
154 }
155}