QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
JoystickThumbPad.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3
4import QGroundControl
5import QGroundControl.Controls
6
7Item {
8 id: _joyRoot
9
10 property alias lightColors: mapPal.lightColors ///< true: use light colors from QGCMapPalette for drawing
11 property real xAxis: 0 ///< Value range [-1,1], negative values left stick, positive values right stick
12 property real yAxis: 0 ///< Value range [-1,1], negative values down stick, positive values up stick
13 property bool yAxisPositiveRangeOnly: false ///< true: value range [0,1], false: value range [-1,1]
14 property bool yAxisReCenter: false ///< true: snaps back to center on release, false: stays at current position on release
15 property real xPositionDelta: 0 ///< Amount to move the control on x axis
16 property real yPositionDelta: 0 ///< Amount to move the control on y axis
17
18 property real _centerXY: width / 2
19 property bool _processTouchPoints: false
20 property color _fgColor: QGroundControl.globalPalette.text
21 property color _bgColor: QGroundControl.globalPalette.window
22 property real _hatWidth: ScreenTools.defaultFontPixelHeight
23 property real _hatWidthHalf: _hatWidth / 2
24 property bool calculateYAxisMutex: true
25 property real stickPositionX: _centerXY
26 property real stickPositionY: !yAxisReCenter ? height : height / 2
27 property bool alreadyCreated: false
28
29 QGCMapPalette { id: mapPal }
30
31 onStickPositionXChanged: calculateXAxis()
32 onStickPositionYChanged: calculateYAxis()
33 onYAxisPositiveRangeOnlyChanged: calculateYAxis()
34 onYAxisReCenterChanged: yAxisReCentered()
35
36 function yAxisReCentered() {
37 if( yAxisReCenter ) {
38 yAxis = yAxisPositiveRangeOnly ? 0.5 : 0
39 stickPositionY = _joyRoot.height / 2
40 }
41 if( !alreadyCreated && !yAxisReCenter ) {
42 yAxis = yAxisPositiveRangeOnly ? 0 : -1
43 stickPositionY = _joyRoot.height
44 }
45 if ( alreadyCreated && !yAxisReCenter ){
46 yAxis = yAxisPositiveRangeOnly ? 0.5 : 0
47 stickPositionY = _joyRoot.height / 2
48 }
49 alreadyCreated = true
50 return yAxis
51 }
52
53 //We prevent Joystick to move while the screen is resizing
54 function resize( yPositionAfterResize ) {
55 if(_joyRoot.height <= 0) {
56 return;
57 }
58 calculateYAxisMutex = false
59 stickPositionY = ( 1 - ( ( yPositionAfterResize + ( !yAxisPositiveRangeOnly ? 1 : 0) ) / ( yAxisPositiveRangeOnly ? 1 : 2 ) )) * _joyRoot.height // Reverse the CalculateYAxis Procedure
60 stickPositionX = _joyRoot.width / 2 // Manual recenter
61 calculateYAxisMutex = true
62 }
63
64 function calculateXAxis() {
65 if(!_joyRoot.visible) {
66 return;
67 }
68 var xAxisTemp = stickPositionX / width
69 xAxisTemp *= 2.0
70 xAxisTemp -= 1.0
71 xAxis = xAxisTemp
72 }
73
74 function calculateYAxis() {
75 if(!_joyRoot.visible) {
76 return;
77 }
78 if(!calculateYAxisMutex) {
79 return;
80 }
81 var fullRange = yAxisPositiveRangeOnly ? 1 : 2
82 var pctUp = 1.0 - (stickPositionY / height)
83 var rangeUp = pctUp * fullRange
84 if (!yAxisPositiveRangeOnly) {
85 rangeUp -= 1
86 }
87 yAxis = rangeUp
88 }
89
90 function reCenter() {
91 _processTouchPoints = false
92 _centerXY = _joyRoot.width / 2 // Reload before using it to make sure of using the right value
93 // Move control back to original position
94 xPositionDelta = 0
95 yPositionDelta = 0
96
97 // Re-Center sticks as needed
98 stickPositionX = _centerXY
99 if (yAxisReCenter) {
100 stickPositionY = _centerXY
101 }
102 }
103
104 function thumbDown(touchPoints) {
105 // Position the control around the initial thumb position
106 _centerXY = _joyRoot.width / 2 // make sure to know the correct center of the item
107
108 var limitOffset = uiRealX >= _joyRoot.width / 2 ? true : false // as the joystick become small the UI too so we limit the maxOffset for reCentering joystick to prevent misclicks
109 var maxDelta = _joyRoot.x > uiTotalWidth / 2 ? uiTotalWidth - uiRealX - _joyRoot.x - _centerXY : uiRealX
110 var isRightJoystick = _joyRoot.x > uiTotalWidth / 2 ? true : false
111
112 // Check if new xDelta will make joystick to be beyond screen boundaries or can cause a misclick
113 if (!limitOffset && isRightJoystick && touchPoints[0].x <= maxDelta || !limitOffset && !isRightJoystick && touchPoints[0].x >= maxDelta) {
114 xPositionDelta = touchPoints[0].x - _centerXY
115 } else if (limitOffset && !isRightJoystick && touchPoints[0].x >= _centerXY * 0.25 && touchPoints[0].x <= _centerXY * 2) { // more offset at the side near to the center
116 xPositionDelta = touchPoints[0].x - _centerXY
117 } else if (limitOffset && isRightJoystick && touchPoints[0].x >= 0 && touchPoints[0].x <= _centerXY * 1.75) {
118 xPositionDelta = touchPoints[0].x - _centerXY
119 } else {
120 return;
121 }
122
123 if (yAxisPositiveRangeOnly) {
124 yPositionDelta = touchPoints[0].y - stickPositionY
125 } else {
126 yPositionDelta = touchPoints[0].y - _centerXY
127 }
128 // We need to wait until we move the control to the right position before we process touch points
129 _processTouchPoints = true
130 }
131
132 /*
133 // Keep in for debugging
134 Column {
135 QGCLabel { text: xAxis }
136 QGCLabel { text: yAxis }
137 }
138 */
139
140 Image {
141 anchors.fill: parent
142 source: "/res/JoystickBezelLight.png"
143 mipmap: true
144 smooth: true
145 }
146
147 Rectangle {
148 anchors.fill: parent
149 radius: width / 2
150 color: _bgColor
151 opacity: 0.5
152
153 Rectangle {
154 anchors.margins: parent.width / 4
155 anchors.fill: parent
156 radius: width / 2
157 border.color: _fgColor
158 border.width: 2
159 color: "transparent"
160 }
161
162 Rectangle {
163 anchors.fill: parent
164 radius: width / 2
165 border.color: _fgColor
166 border.width: 2
167 color: "transparent"
168 }
169 }
170
171 QGCColoredImage {
172 color: _fgColor
173 visible: yAxisPositiveRangeOnly
174 height: ScreenTools.defaultFontPixelHeight
175 width: height
176 sourceSize.height: height
177 mipmap: true
178 fillMode: Image.PreserveAspectFit
179 source: "/res/clockwise-arrow.svg"
180 anchors.right: parent.right
181 anchors.rightMargin: ScreenTools.defaultFontPixelWidth
182 anchors.verticalCenter: parent.verticalCenter
183 }
184
185 QGCColoredImage {
186 color: _fgColor
187 visible: yAxisPositiveRangeOnly
188 height: ScreenTools.defaultFontPixelHeight
189 width: height
190 sourceSize.height: height
191 mipmap: true
192 fillMode: Image.PreserveAspectFit
193 source: "/res/counter-clockwise-arrow.svg"
194 anchors.left: parent.left
195 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
196 anchors.verticalCenter: parent.verticalCenter
197 }
198
199 QGCColoredImage {
200 color: _fgColor
201 visible: yAxisPositiveRangeOnly
202 height: ScreenTools.defaultFontPixelHeight
203 width: height
204 sourceSize.height: height
205 mipmap: true
206 fillMode: Image.PreserveAspectFit
207 source: "/res/chevron-up.svg"
208 anchors.top: parent.top
209 anchors.topMargin: ScreenTools.defaultFontPixelWidth
210 anchors.horizontalCenter: parent.horizontalCenter
211 }
212
213 QGCColoredImage {
214 color: _fgColor
215 visible: yAxisPositiveRangeOnly
216 height: ScreenTools.defaultFontPixelHeight
217 width: height
218 sourceSize.height: height
219 mipmap: true
220 fillMode: Image.PreserveAspectFit
221 source: "/res/chevron-down.svg"
222 anchors.bottom: parent.bottom
223 anchors.bottomMargin: ScreenTools.defaultFontPixelWidth
224 anchors.horizontalCenter: parent.horizontalCenter
225 }
226
227 Rectangle {
228 width: _hatWidth
229 height: _hatWidth
230 radius: _hatWidthHalf
231 border.color: _fgColor
232 border.width: 1
233 color: Qt.rgba(_fgColor.r, _fgColor.g, _fgColor.b, 0.5)
234 x: stickPositionX - _hatWidthHalf
235 y: stickPositionY - _hatWidthHalf
236 }
237
238 Connections {
239 target: touchPoint
240
241 onXChanged: {
242 if (_processTouchPoints) {
243 _joyRoot.stickPositionX = Math.max(Math.min(touchPoint.x, _joyRoot.width), 0)
244 }
245 }
246 onYChanged: {
247 if (_processTouchPoints) {
248 _joyRoot.stickPositionY = Math.max(Math.min(touchPoint.y, _joyRoot.height), 0)
249 }
250 }
251 }
252
253 MultiPointTouchArea {
254 anchors.fill: parent
255 anchors.bottomMargin: yAxisReCenter ? 0 : -_hatWidthHalf
256 minimumTouchPoints: 1
257 maximumTouchPoints: 1
258 touchPoints: [ TouchPoint { id: touchPoint } ]
259 onPressed: touchPoints => _joyRoot.thumbDown(touchPoints)
260 onReleased: _joyRoot.reCenter()
261 }
262}