diff --git a/flutter/lib/common/widgets/gestures.dart b/flutter/lib/common/widgets/gestures.dart index 74b1642b7..0501ca453 100644 --- a/flutter/lib/common/widgets/gestures.dart +++ b/flutter/lib/common/widgets/gestures.dart @@ -25,6 +25,7 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { GestureDragStartCallback? onOneFingerPanStart; GestureDragUpdateCallback? onOneFingerPanUpdate; GestureDragEndCallback? onOneFingerPanEnd; + GestureDragCancelCallback? onOneFingerPanCancel; // twoFingerScale : scale + pan event GestureScaleStartCallback? onTwoFingerScaleStart; @@ -169,6 +170,27 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { DragEndDetails _getDragEndDetails(ScaleEndDetails d) => DragEndDetails(velocity: d.velocity); + + @override + void rejectGesture(int pointer) { + super.rejectGesture(pointer); + switch (_currentState) { + case GestureState.oneFingerPan: + if (onOneFingerPanCancel != null) { + onOneFingerPanCancel!(); + } + break; + case GestureState.twoFingerScale: + // Reset scale state if needed, currently self-contained + break; + case GestureState.threeFingerVerticalDrag: + // Reset drag state if needed, currently self-contained + break; + default: + break; + } + _currentState = GestureState.none; + } } class HoldTapMoveGestureRecognizer extends GestureRecognizer { @@ -717,6 +739,7 @@ RawGestureDetector getMixinGestureDetector({ GestureDragStartCallback? onOneFingerPanStart, GestureDragUpdateCallback? onOneFingerPanUpdate, GestureDragEndCallback? onOneFingerPanEnd, + GestureDragCancelCallback? onOneFingerPanCancel, GestureScaleUpdateCallback? onTwoFingerScaleUpdate, GestureScaleEndCallback? onTwoFingerScaleEnd, GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate, @@ -765,6 +788,7 @@ RawGestureDetector getMixinGestureDetector({ ..onOneFingerPanStart = onOneFingerPanStart ..onOneFingerPanUpdate = onOneFingerPanUpdate ..onOneFingerPanEnd = onOneFingerPanEnd + ..onOneFingerPanCancel = onOneFingerPanCancel ..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate ..onTwoFingerScaleEnd = onTwoFingerScaleEnd ..onThreeFingerVerticalDragUpdate = onThreeFingerVerticalDragUpdate; diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 95a716042..2c97ea147 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -158,7 +158,8 @@ class _RawTouchGestureDetectorRegionState final isMoved = await ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy); if (isMoved) { - if (lastTapDownDetails != null) { + // If pan already handled 'down', don't send it again. + if (lastTapDownDetails != null && !_touchModePanStarted) { await inputModel.tapDown(MouseButtons.left); } await inputModel.tapUp(MouseButtons.left); @@ -424,6 +425,14 @@ class _RawTouchGestureDetectorRegionState } } + // Reset `_touchModePanStarted` if the one-finger pan gesture is cancelled + // or rejected by the gesture arena. Without this, the flag can remain + // stuck in the "started" state and cause issues such as the Magic Mouse + // double-click problem on iPad with magic mouse. + onOneFingerPanCancel() { + _touchModePanStarted = false; + } + // scale + pan event onTwoFingerScaleStart(ScaleStartDetails d) { _lastTapDownDetails = null; @@ -557,6 +566,7 @@ class _RawTouchGestureDetectorRegionState instance ..onOneFingerPanUpdate = onOneFingerPanUpdate ..onOneFingerPanEnd = onOneFingerPanEnd + ..onOneFingerPanCancel = onOneFingerPanCancel ..onTwoFingerScaleStart = onTwoFingerScaleStart ..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate ..onTwoFingerScaleEnd = onTwoFingerScaleEnd