5import QGroundControl.Controls
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 property bool touchActive: false ///< true: thumb is currently down on the pad
19 property real _centerXY: width / 2
20 property bool _processTouchPoints: false
21 property color _fgColor: QGroundControl.globalPalette.text
22 property color _bgColor: QGroundControl.globalPalette.window
23 property real _hatWidth: ScreenTools.defaultFontPixelHeight
24 property real _hatWidthHalf: _hatWidth / 2
25 property bool calculateYAxisMutex: true
26 property real stickPositionX: _centerXY
27 property real stickPositionY: !yAxisReCenter ? height : height / 2
28 property bool alreadyCreated: false
30 QGCMapPalette { id: mapPal }
32 onStickPositionXChanged: calculateXAxis()
33 onStickPositionYChanged: calculateYAxis()
34 onYAxisPositiveRangeOnlyChanged: calculateYAxis()
35 onYAxisReCenterChanged: yAxisReCentered()
37 function yAxisReCentered() {
39 yAxis = yAxisPositiveRangeOnly ? 0.5 : 0
40 stickPositionY = _joyRoot.height / 2
42 if( !alreadyCreated && !yAxisReCenter ) {
43 yAxis = yAxisPositiveRangeOnly ? 0 : -1
44 stickPositionY = _joyRoot.height
46 if ( alreadyCreated && !yAxisReCenter ){
47 yAxis = yAxisPositiveRangeOnly ? 0.5 : 0
48 stickPositionY = _joyRoot.height / 2
54 //We prevent Joystick to move while the screen is resizing
55 function resize( yPositionAfterResize ) {
56 if(_joyRoot.height <= 0) {
59 calculateYAxisMutex = false
60 stickPositionY = ( 1 - ( ( yPositionAfterResize + ( !yAxisPositiveRangeOnly ? 1 : 0) ) / ( yAxisPositiveRangeOnly ? 1 : 2 ) )) * _joyRoot.height // Reverse the CalculateYAxis Procedure
61 stickPositionX = _joyRoot.width / 2 // Manual recenter
62 calculateYAxisMutex = true
65 function calculateXAxis() {
66 if(!_joyRoot.visible) {
69 var xAxisTemp = stickPositionX / width
75 function calculateYAxis() {
76 if(!_joyRoot.visible) {
79 if(!calculateYAxisMutex) {
82 var fullRange = yAxisPositiveRangeOnly ? 1 : 2
83 var pctUp = 1.0 - (stickPositionY / height)
84 var rangeUp = pctUp * fullRange
85 if (!yAxisPositiveRangeOnly) {
92 _processTouchPoints = false
93 _centerXY = _joyRoot.width / 2 // Reload before using it to make sure of using the right value
94 // Move control back to original position
98 // Re-Center sticks as needed
99 stickPositionX = _centerXY
101 stickPositionY = _centerXY
105 function thumbDown(touchPoints) {
106 // Position the control around the initial thumb position
107 _centerXY = _joyRoot.width / 2 // make sure to know the correct center of the item
109 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
110 var maxDelta = _joyRoot.x > uiTotalWidth / 2 ? uiTotalWidth - uiRealX - _joyRoot.x - _centerXY : uiRealX
111 var isRightJoystick = _joyRoot.x > uiTotalWidth / 2 ? true : false
113 // Check if new xDelta will make joystick to be beyond screen boundaries or can cause a misclick
114 if (!limitOffset && isRightJoystick && touchPoints[0].x <= maxDelta || !limitOffset && !isRightJoystick && touchPoints[0].x >= maxDelta) {
115 xPositionDelta = touchPoints[0].x - _centerXY
116 } else if (limitOffset && !isRightJoystick && touchPoints[0].x >= _centerXY * 0.25 && touchPoints[0].x <= _centerXY * 2) { // more offset at the side near to the center
117 xPositionDelta = touchPoints[0].x - _centerXY
118 } else if (limitOffset && isRightJoystick && touchPoints[0].x >= 0 && touchPoints[0].x <= _centerXY * 1.75) {
119 xPositionDelta = touchPoints[0].x - _centerXY
124 if (yAxisPositiveRangeOnly) {
125 yPositionDelta = touchPoints[0].y - stickPositionY
127 yPositionDelta = touchPoints[0].y - _centerXY
129 // We need to wait until we move the control to the right position before we process touch points
130 _processTouchPoints = true
134 // Keep in for debugging
136 QGCLabel { text: xAxis }
137 QGCLabel { text: yAxis }
143 source: "/res/JoystickBezelLight.png"
155 anchors.margins: parent.width / 4
158 border.color: _fgColor
166 border.color: _fgColor
174 visible: yAxisPositiveRangeOnly
175 height: ScreenTools.defaultFontPixelHeight
177 sourceSize.height: height
179 fillMode: Image.PreserveAspectFit
180 source: "/res/clockwise-arrow.svg"
181 anchors.right: parent.right
182 anchors.rightMargin: ScreenTools.defaultFontPixelWidth
183 anchors.verticalCenter: parent.verticalCenter
188 visible: yAxisPositiveRangeOnly
189 height: ScreenTools.defaultFontPixelHeight
191 sourceSize.height: height
193 fillMode: Image.PreserveAspectFit
194 source: "/res/counter-clockwise-arrow.svg"
195 anchors.left: parent.left
196 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
197 anchors.verticalCenter: parent.verticalCenter
202 visible: yAxisPositiveRangeOnly
203 height: ScreenTools.defaultFontPixelHeight
205 sourceSize.height: height
207 fillMode: Image.PreserveAspectFit
208 source: "/res/chevron-up.svg"
209 anchors.top: parent.top
210 anchors.topMargin: ScreenTools.defaultFontPixelWidth
211 anchors.horizontalCenter: parent.horizontalCenter
216 visible: yAxisPositiveRangeOnly
217 height: ScreenTools.defaultFontPixelHeight
219 sourceSize.height: height
221 fillMode: Image.PreserveAspectFit
222 source: "/res/chevron-down.svg"
223 anchors.bottom: parent.bottom
224 anchors.bottomMargin: ScreenTools.defaultFontPixelWidth
225 anchors.horizontalCenter: parent.horizontalCenter
231 radius: _hatWidthHalf
232 border.color: _fgColor
234 color: Qt.rgba(_fgColor.r, _fgColor.g, _fgColor.b, 0.5)
235 x: stickPositionX - _hatWidthHalf
236 y: stickPositionY - _hatWidthHalf
243 if (_processTouchPoints) {
244 _joyRoot.stickPositionX = Math.max(Math.min(touchPoint.x, _joyRoot.width), 0)
248 if (_processTouchPoints) {
249 _joyRoot.stickPositionY = Math.max(Math.min(touchPoint.y, _joyRoot.height), 0)
254 MultiPointTouchArea {
256 anchors.bottomMargin: yAxisReCenter ? 0 : -_hatWidthHalf
257 minimumTouchPoints: 1
258 maximumTouchPoints: 1
259 touchPoints: [ TouchPoint { id: touchPoint } ]
260 onPressed: touchPoints => {
261 _joyRoot.touchActive = true
262 _joyRoot.thumbDown(touchPoints)
265 _joyRoot.touchActive = false
269 _joyRoot.touchActive = false