Skip to content

Commit eeb22e2

Browse files
committed
Fix virtual joysticks triggering pinch-to-zoom on map
Restrict the map's PinchHandler grabPermissions so it cannot steal touch grabs from MultiPointTouchArea items (the joystick thumb pads). Previously, when both thumbs were on the virtual joysticks simultaneously, the PinchHandler's default CanTakeOverFromItems permission allowed it to interpret the two touch points as a pinch gesture and zoom the map instead of letting the joysticks operate independently. Fixes #13450
1 parent 1c64a14 commit eeb22e2

1 file changed

Lines changed: 76 additions & 42 deletions

File tree

src/FlightMap/FlightMap.qml

Lines changed: 76 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -116,24 +116,6 @@ Map {
116116
signal mapRightClicked(var position)
117117
signal mapPressAndHold(var position)
118118

119-
PinchHandler {
120-
id: pinchHandler
121-
target: null
122-
123-
property var pinchStartCentroid
124-
125-
onActiveChanged: {
126-
if (active) {
127-
pinchStartCentroid = _map.toCoordinate(pinchHandler.centroid.position, false)
128-
}
129-
}
130-
onScaleChanged: (delta) => {
131-
let newZoomLevel = Math.max(_map.zoomLevel + Math.log2(delta), 0)
132-
_map.zoomLevel = newZoomLevel
133-
_map.alignCoordinateToPoint(pinchStartCentroid, pinchHandler.centroid.position)
134-
}
135-
}
136-
137119
WheelHandler {
138120
// workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
139121
// Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
@@ -151,57 +133,109 @@ Map {
151133
}
152134

153135
// We specifically do not use a DragHandler for panning. It just causes too many problems if you overlay anything else like a Flickable above it.
154-
// Causes all sorts of crazy problems where dragging/scrolling no longerr works on items above in the hierarchy.
136+
// Causes all sorts of crazy problems where dragging/scrolling no longer works on items above in the hierarchy.
155137
// Since we are using a MouseArea we also can't use TapHandler for clicks. So we handle that here as well.
138+
// Pinch-to-zoom is also handled here rather than via PinchHandler to prevent the handler from stealing
139+
// touch grabs from items above (e.g. virtual joystick pads). See GitHub issue #13450.
156140
MultiPointTouchArea {
157141
id: multiTouchArea
158142
anchors.fill: parent
159-
maximumTouchPoints: 1
143+
maximumTouchPoints: 2
160144
mouseEnabled: true
161145

146+
touchPoints: [
147+
TouchPoint { id: tp1 },
148+
TouchPoint { id: tp2 }
149+
]
150+
162151
property bool dragActive: false
152+
property bool pinchActive: false
153+
property bool wasMultiTouch: false
163154
property real lastMouseX
164155
property real lastMouseY
165156
property bool isPressed: false
166157
property bool pressAndHold: false
158+
property real pinchStartDist
159+
property real pinchStartZoom
160+
property var pinchStartCentroid
161+
162+
function pinchDistance() {
163+
let dx = tp2.x - tp1.x
164+
let dy = tp2.y - tp1.y
165+
return Math.sqrt(dx * dx + dy * dy)
166+
}
167167

168168
onPressed: (touchPoints) => {
169-
lastMouseX = touchPoints[0].x
170-
lastMouseY = touchPoints[0].y
171-
isPressed = true
172-
pressAndHold = false
173-
pressAndHoldTimer.start()
169+
if (!pinchActive && !dragActive) {
170+
lastMouseX = tp1.x
171+
lastMouseY = tp1.y
172+
isPressed = true
173+
pressAndHold = false
174+
pressAndHoldTimer.start()
175+
}
174176
}
175177

176178
onGestureStarted: (gesture) => {
177-
dragActive = true
178179
gesture.grab()
179-
mapPanStart()
180+
if (!pinchActive && !dragActive) {
181+
dragActive = true
182+
mapPanStart()
183+
}
180184
}
181185

182186
onUpdated: (touchPoints) => {
183-
if (dragActive) {
184-
let deltaX = touchPoints[0].x - lastMouseX
185-
let deltaY = touchPoints[0].y - lastMouseY
187+
if (tp1.pressed && tp2.pressed) {
188+
if (!pinchActive) {
189+
// Transition to pinch
190+
pinchActive = true
191+
wasMultiTouch = true
192+
dragActive = false
193+
pinchStartDist = pinchDistance()
194+
pinchStartZoom = _map.zoomLevel
195+
let cx = (tp1.x + tp2.x) / 2
196+
let cy = (tp1.y + tp2.y) / 2
197+
pinchStartCentroid = _map.toCoordinate(Qt.point(cx, cy), false)
198+
pressAndHoldTimer.stop()
199+
}
200+
let currentDist = pinchDistance()
201+
if (pinchStartDist > 0) {
202+
let scale = currentDist / pinchStartDist
203+
_map.zoomLevel = pinchStartZoom + Math.log2(scale)
204+
let cx = (tp1.x + tp2.x) / 2
205+
let cy = (tp1.y + tp2.y) / 2
206+
_map.alignCoordinateToPoint(pinchStartCentroid, Qt.point(cx, cy))
207+
}
208+
} else if (dragActive && !pinchActive) {
209+
let deltaX = tp1.x - lastMouseX
210+
let deltaY = tp1.y - lastMouseY
186211
if (Math.abs(deltaX) >= 1.0 || Math.abs(deltaY) >= 1.0) {
187-
_map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
188-
lastMouseX = touchPoints[0].x
189-
lastMouseY = touchPoints[0].y
212+
_map.pan(lastMouseX - tp1.x, lastMouseY - tp1.y)
213+
lastMouseX = tp1.x
214+
lastMouseY = tp1.y
190215
}
191216
}
192217
}
193218

194219
onReleased: (touchPoints) => {
195-
isPressed = false
196-
pressAndHoldTimer.stop()
197-
if (dragActive) {
198-
_map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
199-
dragActive = false
200-
mapPanStop()
201-
} else if (!pressAndHold) {
202-
mapClicked(Qt.point(touchPoints[0].x, touchPoints[0].y))
220+
if (!tp1.pressed && !tp2.pressed) {
221+
// All fingers up
222+
isPressed = false
223+
pressAndHoldTimer.stop()
224+
if (pinchActive) {
225+
pinchActive = false
226+
} else if (dragActive) {
227+
_map.pan(lastMouseX - touchPoints[0].x, lastMouseY - touchPoints[0].y)
228+
dragActive = false
229+
mapPanStop()
230+
} else if (!pressAndHold && !wasMultiTouch) {
231+
mapClicked(Qt.point(touchPoints[0].x, touchPoints[0].y))
232+
}
233+
pressAndHold = false
234+
wasMultiTouch = false
235+
} else if (pinchActive) {
236+
// One finger lifted during pinch - end pinch but don't start panning
237+
pinchActive = false
203238
}
204-
pressAndHold = false
205239
}
206240

207241
Timer {

0 commit comments

Comments
 (0)