| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | import 'package:flutter/gestures.dart'; | 
					
						
							|  |  |  | import 'package:flutter/widgets.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  | enum GestureState { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   none, | 
					
						
							|  |  |  |   oneFingerPan, | 
					
						
							|  |  |  |   twoFingerScale, | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |   threeFingerVerticalDrag | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { | 
					
						
							|  |  |  |   CustomTouchGestureRecognizer({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     Object? debugOwner, | 
					
						
							|  |  |  |     Set<PointerDeviceKind>? supportedDevices, | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   }) : super( | 
					
						
							|  |  |  |           debugOwner: debugOwner, | 
					
						
							|  |  |  |           supportedDevices: supportedDevices, | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |     _init(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // oneFingerPan
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureDragStartCallback? onOneFingerPanStart; | 
					
						
							|  |  |  |   GestureDragUpdateCallback? onOneFingerPanUpdate; | 
					
						
							|  |  |  |   GestureDragEndCallback? onOneFingerPanEnd; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-10 11:27:16 +08:00
										 |  |  |   // twoFingerScale : scale + pan event
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureScaleStartCallback? onTwoFingerScaleStart; | 
					
						
							|  |  |  |   GestureScaleUpdateCallback? onTwoFingerScaleUpdate; | 
					
						
							|  |  |  |   GestureScaleEndCallback? onTwoFingerScaleEnd; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |   // threeFingerVerticalDrag
 | 
					
						
							|  |  |  |   GestureDragStartCallback? onThreeFingerVerticalDragStart; | 
					
						
							|  |  |  |   GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate; | 
					
						
							|  |  |  |   GestureDragEndCallback? onThreeFingerVerticalDragEnd; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |   var _currentState = GestureState.none; | 
					
						
							|  |  |  |   Timer? _debounceTimer; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void _init() { | 
					
						
							|  |  |  |     debugPrint("CustomTouchGestureRecognizer init"); | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |     // onStart = (d) {};
 | 
					
						
							|  |  |  |     onUpdate = (d) { | 
					
						
							|  |  |  |       _debounceTimer?.cancel(); | 
					
						
							|  |  |  |       if (d.pointerCount == 1 && _currentState != GestureState.oneFingerPan) { | 
					
						
							|  |  |  |         onOneFingerStartDebounce(d); | 
					
						
							|  |  |  |       } else if (d.pointerCount == 2 && | 
					
						
							|  |  |  |           _currentState != GestureState.twoFingerScale) { | 
					
						
							|  |  |  |         onTwoFingerStartDebounce(d); | 
					
						
							|  |  |  |       } else if (d.pointerCount == 3 && | 
					
						
							|  |  |  |           _currentState != GestureState.threeFingerVerticalDrag) { | 
					
						
							|  |  |  |         _currentState = GestureState.threeFingerVerticalDrag; | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |         if (onThreeFingerVerticalDragStart != null) { | 
					
						
							|  |  |  |           onThreeFingerVerticalDragStart!( | 
					
						
							|  |  |  |               DragStartDetails(globalPosition: d.localFocalPoint)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         debugPrint("start threeFingerScale"); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |       if (_currentState != GestureState.none) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         switch (_currentState) { | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |           case GestureState.oneFingerPan: | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             if (onOneFingerPanUpdate != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |               onOneFingerPanUpdate!(_getDragUpdateDetails(d)); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |           case GestureState.twoFingerScale: | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             if (onTwoFingerScaleUpdate != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |               onTwoFingerScaleUpdate!(d); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |           case GestureState.threeFingerVerticalDrag: | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |             if (onThreeFingerVerticalDragUpdate != null) { | 
					
						
							|  |  |  |               onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d)); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |           default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     onEnd = (d) { | 
					
						
							|  |  |  |       debugPrint("ScaleGestureRecognizer onEnd"); | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |       _debounceTimer?.cancel(); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       // end
 | 
					
						
							|  |  |  |       switch (_currentState) { | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |         case GestureState.oneFingerPan: | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           debugPrint("TwoFingerState.pan onEnd"); | 
					
						
							|  |  |  |           if (onOneFingerPanEnd != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             onOneFingerPanEnd!(_getDragEndDetails(d)); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |         case GestureState.twoFingerScale: | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           debugPrint("TwoFingerState.scale onEnd"); | 
					
						
							|  |  |  |           if (onTwoFingerScaleEnd != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             onTwoFingerScaleEnd!(d); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |         case GestureState.threeFingerVerticalDrag: | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |           debugPrint("ThreeFingerState.vertical onEnd"); | 
					
						
							|  |  |  |           if (onThreeFingerVerticalDragEnd != null) { | 
					
						
							|  |  |  |             onThreeFingerVerticalDragEnd!(_getDragEndDetails(d)); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |       _debounceTimer = Timer(Duration(milliseconds: 200), () { | 
					
						
							|  |  |  |         _currentState = GestureState.none; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void onOneFingerStartDebounce(ScaleUpdateDetails d) { | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |     start(ScaleUpdateDetails d) { | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |       _currentState = GestureState.oneFingerPan; | 
					
						
							|  |  |  |       if (onOneFingerPanStart != null) { | 
					
						
							|  |  |  |         onOneFingerPanStart!(DragStartDetails( | 
					
						
							|  |  |  |             localPosition: d.localFocalPoint, globalPosition: d.focalPoint)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |     if (_currentState != GestureState.none) { | 
					
						
							|  |  |  |       _debounceTimer = Timer(Duration(milliseconds: 200), () { | 
					
						
							|  |  |  |         start(d); | 
					
						
							|  |  |  |         debugPrint("debounce start oneFingerPan"); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       start(d); | 
					
						
							|  |  |  |       debugPrint("start oneFingerPan"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void onTwoFingerStartDebounce(ScaleUpdateDetails d) { | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |     start(ScaleUpdateDetails d) { | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |       _currentState = GestureState.twoFingerScale; | 
					
						
							|  |  |  |       if (onTwoFingerScaleStart != null) { | 
					
						
							|  |  |  |         onTwoFingerScaleStart!(ScaleStartDetails( | 
					
						
							|  |  |  |             localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |     if (_currentState == GestureState.threeFingerVerticalDrag) { | 
					
						
							|  |  |  |       _debounceTimer = Timer(Duration(milliseconds: 200), () { | 
					
						
							|  |  |  |         start(d); | 
					
						
							|  |  |  |         debugPrint("debounce start twoFingerScale"); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       start(d); | 
					
						
							|  |  |  |       debugPrint("start twoFingerScale"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) => | 
					
						
							|  |  |  |       DragUpdateDetails( | 
					
						
							|  |  |  |           globalPosition: d.focalPoint, | 
					
						
							|  |  |  |           localPosition: d.localFocalPoint, | 
					
						
							|  |  |  |           delta: d.focalPointDelta); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   DragEndDetails _getDragEndDetails(ScaleEndDetails d) => | 
					
						
							|  |  |  |       DragEndDetails(velocity: d.velocity); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HoldTapMoveGestureRecognizer extends GestureRecognizer { | 
					
						
							|  |  |  |   HoldTapMoveGestureRecognizer({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     Object? debugOwner, | 
					
						
							|  |  |  |     Set<PointerDeviceKind>? supportedDevices, | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   }) : super( | 
					
						
							|  |  |  |           debugOwner: debugOwner, | 
					
						
							|  |  |  |           supportedDevices: supportedDevices, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureDragStartCallback? onHoldDragStart; | 
					
						
							|  |  |  |   GestureDragUpdateCallback? onHoldDragUpdate; | 
					
						
							|  |  |  |   GestureDragDownCallback? onHoldDragDown; | 
					
						
							|  |  |  |   GestureDragCancelCallback? onHoldDragCancel; | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |   GestureDragEndCallback? onHoldDragEnd; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   bool _isStart = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 14:20:39 +08:00
										 |  |  |   Timer? _firstTapUpTimer; | 
					
						
							| 
									
										
										
										
											2022-04-04 01:21:44 +08:00
										 |  |  |   Timer? _secondTapDownTimer; | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   _TapTracker? _firstTap; | 
					
						
							|  |  |  |   _TapTracker? _secondTap; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |   PointerDownEvent? _lastPointerDownEvent; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   final Map<int, _TapTracker> _trackers = <int, _TapTracker>{}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool isPointerAllowed(PointerDownEvent event) { | 
					
						
							|  |  |  |     if (_firstTap == null) { | 
					
						
							|  |  |  |       switch (event.buttons) { | 
					
						
							|  |  |  |         case kPrimaryButton: | 
					
						
							|  |  |  |           if (onHoldDragStart == null && | 
					
						
							|  |  |  |               onHoldDragUpdate == null && | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |               onHoldDragCancel == null && | 
					
						
							|  |  |  |               onHoldDragEnd == null) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return super.isPointerAllowed(event); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void addAllowedPointer(PointerDownEvent event) { | 
					
						
							|  |  |  |     if (_firstTap != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       if (!_firstTap!.isWithinGlobalTolerance(event, kDoubleTapSlop)) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         // Ignore out-of-bounds second taps.
 | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       } else if (!_firstTap!.hasElapsedMinTime() || | 
					
						
							|  |  |  |           !_firstTap!.hasSameButton(event)) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         // Restart when the second tap is too close to the first (touch screens
 | 
					
						
							|  |  |  |         // often detect touches intermittently), or when buttons mismatch.
 | 
					
						
							|  |  |  |         _reset(); | 
					
						
							|  |  |  |         return _trackTap(event); | 
					
						
							|  |  |  |       } else if (onHoldDragDown != null) { | 
					
						
							|  |  |  |         invokeCallback<void>( | 
					
						
							|  |  |  |             'onHoldDragDown', | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             () => onHoldDragDown!(DragDownDetails( | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |                 globalPosition: event.position, | 
					
						
							|  |  |  |                 localPosition: event.localPosition))); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-04 01:21:44 +08:00
										 |  |  |     _trackTap(event); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _trackTap(PointerDownEvent event) { | 
					
						
							|  |  |  |     _stopFirstTapUpTimer(); | 
					
						
							|  |  |  |     _stopSecondTapDownTimer(); | 
					
						
							|  |  |  |     final _TapTracker tracker = _TapTracker( | 
					
						
							|  |  |  |       event: event, | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |       entry: GestureBinding.instance.gestureArena.add(event.pointer, this), | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       doubleTapMinTime: kDoubleTapMinTime, | 
					
						
							|  |  |  |       gestureSettings: gestureSettings, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     _trackers[event.pointer] = tracker; | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |     _lastPointerDownEvent = event; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     tracker.startTrackingPointer(_handleEvent, event.transform); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _handleEvent(PointerEvent event) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     final _TapTracker tracker = _trackers[event.pointer]!; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     if (event is PointerUpEvent) { | 
					
						
							|  |  |  |       if (_firstTap == null && _secondTap == null) { | 
					
						
							|  |  |  |         _registerFirstTap(tracker); | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |       } else if (_secondTap != null) { | 
					
						
							|  |  |  |         if (event.pointer == _secondTap!.pointer) { | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |           if (onHoldDragEnd != null) { | 
					
						
							|  |  |  |             onHoldDragEnd!(DragEndDetails()); | 
					
						
							|  |  |  |             _secondTap = null; | 
					
						
							|  |  |  |             _isStart = false; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         _reject(tracker); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else if (event is PointerDownEvent) { | 
					
						
							|  |  |  |       if (_firstTap != null && _secondTap == null) { | 
					
						
							|  |  |  |         _registerSecondTap(tracker); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else if (event is PointerMoveEvent) { | 
					
						
							|  |  |  |       if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |         if (_firstTap != null && _firstTap!.pointer == event.pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           // first tap move
 | 
					
						
							|  |  |  |           _reject(tracker); | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |         } else if (_secondTap != null && _secondTap!.pointer == event.pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |           // debugPrint("_secondTap move");
 | 
					
						
							|  |  |  |           // second tap move
 | 
					
						
							|  |  |  |           if (!_isStart) { | 
					
						
							|  |  |  |             _resolve(); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |           if (onHoldDragUpdate != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             onHoldDragUpdate!(DragUpdateDetails( | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |                 globalPosition: event.position, | 
					
						
							|  |  |  |                 localPosition: event.localPosition, | 
					
						
							|  |  |  |                 delta: event.delta)); | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else if (event is PointerCancelEvent) { | 
					
						
							|  |  |  |       _reject(tracker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void acceptGesture(int pointer) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void rejectGesture(int pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     _TapTracker? tracker = _trackers[pointer]; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     // If tracker isn't in the list, check if this is the first tap tracker
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       tracker = _firstTap; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // If tracker is still null, we rejected ourselves already
 | 
					
						
							|  |  |  |     if (tracker != null) { | 
					
						
							|  |  |  |       _reject(tracker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _resolve() { | 
					
						
							|  |  |  |     _stopSecondTapDownTimer(); | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     _firstTap?.entry.resolve(GestureDisposition.accepted); | 
					
						
							|  |  |  |     _secondTap?.entry.resolve(GestureDisposition.accepted); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     _isStart = true; | 
					
						
							|  |  |  |     // TODO start details
 | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |     if (onHoldDragStart != null) { | 
					
						
							|  |  |  |       onHoldDragStart!(DragStartDetails( | 
					
						
							|  |  |  |         kind: _lastPointerDownEvent?.kind, | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _reject(_TapTracker tracker) { | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |     try { | 
					
						
							|  |  |  |       _checkCancel(); | 
					
						
							|  |  |  |       _isStart = false; | 
					
						
							|  |  |  |       _trackers.remove(tracker.pointer); | 
					
						
							|  |  |  |       tracker.entry.resolve(GestureDisposition.rejected); | 
					
						
							|  |  |  |       _freezeTracker(tracker); | 
					
						
							|  |  |  |       _reset(); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint("Failed to _reject:$e"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     _reset(); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _reset() { | 
					
						
							|  |  |  |     _isStart = false; | 
					
						
							|  |  |  |     // debugPrint("reset");
 | 
					
						
							|  |  |  |     _stopFirstTapUpTimer(); | 
					
						
							|  |  |  |     _stopSecondTapDownTimer(); | 
					
						
							|  |  |  |     if (_firstTap != null) { | 
					
						
							|  |  |  |       if (_trackers.isNotEmpty) { | 
					
						
							|  |  |  |         _checkCancel(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Note, order is important below in order for the resolve -> reject logic
 | 
					
						
							|  |  |  |       // to work properly.
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       final _TapTracker tracker = _firstTap!; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _firstTap = null; | 
					
						
							|  |  |  |       _reject(tracker); | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |       GestureBinding.instance.gestureArena.release(tracker.pointer); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (_secondTap != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |         final _TapTracker tracker = _secondTap!; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         _secondTap = null; | 
					
						
							|  |  |  |         _reject(tracker); | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |         GestureBinding.instance.gestureArena.release(tracker.pointer); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _firstTap = null; | 
					
						
							|  |  |  |     _secondTap = null; | 
					
						
							|  |  |  |     _clearTrackers(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _registerFirstTap(_TapTracker tracker) { | 
					
						
							|  |  |  |     _startFirstTapUpTimer(); | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |     GestureBinding.instance.gestureArena.hold(tracker.pointer); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     // Note, order is important below in order for the clear -> reject logic to
 | 
					
						
							|  |  |  |     // work properly.
 | 
					
						
							|  |  |  |     _freezeTracker(tracker); | 
					
						
							|  |  |  |     _trackers.remove(tracker.pointer); | 
					
						
							|  |  |  |     _firstTap = tracker; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _registerSecondTap(_TapTracker tracker) { | 
					
						
							|  |  |  |     if (_firstTap != null) { | 
					
						
							|  |  |  |       _stopFirstTapUpTimer(); | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       _freezeTracker(_firstTap!); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _firstTap = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _startSecondTapDownTimer(); | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |     GestureBinding.instance.gestureArena.hold(tracker.pointer); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _secondTap = tracker; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _clearTrackers() { | 
					
						
							|  |  |  |     _trackers.values.toList().forEach(_reject); | 
					
						
							|  |  |  |     assert(_trackers.isEmpty); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _freezeTracker(_TapTracker tracker) { | 
					
						
							|  |  |  |     tracker.stopTrackingPointer(_handleEvent); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _startFirstTapUpTimer() { | 
					
						
							|  |  |  |     _firstTapUpTimer ??= Timer(kDoubleTapTimeout, _reset); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _startSecondTapDownTimer() { | 
					
						
							|  |  |  |     _secondTapDownTimer ??= Timer(kDoubleTapTimeout, _resolve); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _stopFirstTapUpTimer() { | 
					
						
							|  |  |  |     if (_firstTapUpTimer != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       _firstTapUpTimer!.cancel(); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _firstTapUpTimer = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _stopSecondTapDownTimer() { | 
					
						
							|  |  |  |     if (_secondTapDownTimer != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       _secondTapDownTimer!.cancel(); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _secondTapDownTimer = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _checkCancel() { | 
					
						
							|  |  |  |     if (onHoldDragCancel != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       invokeCallback<void>('onHoldDragCancel', onHoldDragCancel!); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   String get debugDescription => 'double tap'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DoubleFinerTapGestureRecognizer extends GestureRecognizer { | 
					
						
							|  |  |  |   DoubleFinerTapGestureRecognizer({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     Object? debugOwner, | 
					
						
							|  |  |  |     Set<PointerDeviceKind>? supportedDevices, | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   }) : super( | 
					
						
							|  |  |  |           debugOwner: debugOwner, | 
					
						
							|  |  |  |           supportedDevices: supportedDevices, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureTapDownCallback? onDoubleFinerTapDown; | 
					
						
							|  |  |  |   GestureTapDownCallback? onDoubleFinerTap; | 
					
						
							|  |  |  |   GestureTapCancelCallback? onDoubleFinerTapCancel; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-04 01:21:44 +08:00
										 |  |  |   Timer? _firstTapTimer; | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   _TapTracker? _firstTap; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |   PointerDownEvent? _lastPointerDownEvent; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   var _isStart = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final Set<int> _upTap = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final Map<int, _TapTracker> _trackers = <int, _TapTracker>{}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool isPointerAllowed(PointerDownEvent event) { | 
					
						
							|  |  |  |     if (_firstTap == null) { | 
					
						
							|  |  |  |       switch (event.buttons) { | 
					
						
							|  |  |  |         case kPrimaryButton: | 
					
						
							|  |  |  |           if (onDoubleFinerTapDown == null && | 
					
						
							|  |  |  |               onDoubleFinerTap == null && | 
					
						
							|  |  |  |               onDoubleFinerTapCancel == null) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return super.isPointerAllowed(event); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void addAllowedPointer(PointerDownEvent event) { | 
					
						
							|  |  |  |     debugPrint("addAllowedPointer"); | 
					
						
							|  |  |  |     if (_isStart) { | 
					
						
							|  |  |  |       // second
 | 
					
						
							|  |  |  |       if (onDoubleFinerTapDown != null) { | 
					
						
							|  |  |  |         final TapDownDetails details = TapDownDetails( | 
					
						
							|  |  |  |           globalPosition: event.position, | 
					
						
							|  |  |  |           localPosition: event.localPosition, | 
					
						
							|  |  |  |           kind: getKindForPointer(event.pointer), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         invokeCallback<void>( | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             'onDoubleFinerTapDown', () => onDoubleFinerTapDown!(details)); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // first tap
 | 
					
						
							|  |  |  |       _isStart = true; | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |       _lastPointerDownEvent = event; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _startFirstTapDownTimer(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-04 01:21:44 +08:00
										 |  |  |     _trackTap(event); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _trackTap(PointerDownEvent event) { | 
					
						
							|  |  |  |     final _TapTracker tracker = _TapTracker( | 
					
						
							|  |  |  |       event: event, | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |       entry: GestureBinding.instance.gestureArena.add(event.pointer, this), | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       doubleTapMinTime: kDoubleTapMinTime, | 
					
						
							|  |  |  |       gestureSettings: gestureSettings, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     _trackers[event.pointer] = tracker; | 
					
						
							|  |  |  |     // debugPrint("_trackers:$_trackers");
 | 
					
						
							|  |  |  |     tracker.startTrackingPointer(_handleEvent, event.transform); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _registerTap(tracker); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _handleEvent(PointerEvent event) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     final _TapTracker tracker = _trackers[event.pointer]!; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     if (event is PointerUpEvent) { | 
					
						
							|  |  |  |       debugPrint("PointerUpEvent"); | 
					
						
							|  |  |  |       _upTap.add(tracker.pointer); | 
					
						
							|  |  |  |     } else if (event is PointerMoveEvent) { | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |       if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         _reject(tracker); | 
					
						
							| 
									
										
										
										
											2023-07-17 20:48:58 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     } else if (event is PointerCancelEvent) { | 
					
						
							|  |  |  |       _reject(tracker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void acceptGesture(int pointer) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void rejectGesture(int pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     _TapTracker? tracker = _trackers[pointer]; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     // If tracker isn't in the list, check if this is the first tap tracker
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       tracker = _firstTap; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // If tracker is still null, we rejected ourselves already
 | 
					
						
							|  |  |  |     if (tracker != null) { | 
					
						
							|  |  |  |       _reject(tracker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _reject(_TapTracker tracker) { | 
					
						
							|  |  |  |     _trackers.remove(tracker.pointer); | 
					
						
							|  |  |  |     tracker.entry.resolve(GestureDisposition.rejected); | 
					
						
							|  |  |  |     _freezeTracker(tracker); | 
					
						
							|  |  |  |     if (_firstTap != null) { | 
					
						
							|  |  |  |       if (tracker == _firstTap) { | 
					
						
							|  |  |  |         _reset(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         _checkCancel(); | 
					
						
							|  |  |  |         if (_trackers.isEmpty) { | 
					
						
							|  |  |  |           _reset(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     _reset(); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _reset() { | 
					
						
							|  |  |  |     _stopFirstTapUpTimer(); | 
					
						
							|  |  |  |     _firstTap = null; | 
					
						
							|  |  |  |     _clearTrackers(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _registerTap(_TapTracker tracker) { | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |     GestureBinding.instance.gestureArena.hold(tracker.pointer); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     // Note, order is important below in order for the clear -> reject logic to
 | 
					
						
							|  |  |  |     // work properly.
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _clearTrackers() { | 
					
						
							|  |  |  |     _trackers.values.toList().forEach(_reject); | 
					
						
							|  |  |  |     assert(_trackers.isEmpty); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _freezeTracker(_TapTracker tracker) { | 
					
						
							|  |  |  |     tracker.stopTrackingPointer(_handleEvent); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _startFirstTapDownTimer() { | 
					
						
							|  |  |  |     _firstTapTimer ??= Timer(kDoubleTapTimeout, _timeoutCheck); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _stopFirstTapUpTimer() { | 
					
						
							|  |  |  |     if (_firstTapTimer != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       _firstTapTimer!.cancel(); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       _firstTapTimer = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _timeoutCheck() { | 
					
						
							|  |  |  |     _isStart = false; | 
					
						
							|  |  |  |     if (_upTap.length == 2) { | 
					
						
							|  |  |  |       _resolve(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       _reset(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _upTap.clear(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _resolve() { | 
					
						
							|  |  |  |     // TODO tap down details
 | 
					
						
							| 
									
										
										
										
											2023-07-25 22:09:42 +08:00
										 |  |  |     if (onDoubleFinerTap != null) { | 
					
						
							|  |  |  |       onDoubleFinerTap!(TapDownDetails( | 
					
						
							|  |  |  |         kind: _lastPointerDownEvent?.kind, | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     _trackers.forEach((key, value) { | 
					
						
							|  |  |  |       value.entry.resolve(GestureDisposition.accepted); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     _reset(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _checkCancel() { | 
					
						
							|  |  |  |     if (onDoubleFinerTapCancel != null) { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |       invokeCallback<void>('onHoldDragCancel', onDoubleFinerTapCancel!); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   String get debugDescription => 'double tap'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// TapTracker helps track individual tap sequences as part of a
 | 
					
						
							|  |  |  | /// larger gesture.
 | 
					
						
							|  |  |  | class _TapTracker { | 
					
						
							|  |  |  |   _TapTracker({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |     required PointerDownEvent event, | 
					
						
							|  |  |  |     required this.entry, | 
					
						
							|  |  |  |     required Duration doubleTapMinTime, | 
					
						
							|  |  |  |     required this.gestureSettings, | 
					
						
							| 
									
										
										
										
											2022-06-19 17:15:37 +01:00
										 |  |  |   })  : pointer = event.pointer, | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         _initialGlobalPosition = event.position, | 
					
						
							|  |  |  |         initialButtons = event.buttons, | 
					
						
							|  |  |  |         _doubleTapMinTimeCountdown = | 
					
						
							|  |  |  |             _CountdownZoned(duration: doubleTapMinTime); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   final DeviceGestureSettings? gestureSettings; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |   final int pointer; | 
					
						
							|  |  |  |   final GestureArenaEntry entry; | 
					
						
							|  |  |  |   final Offset _initialGlobalPosition; | 
					
						
							|  |  |  |   final int initialButtons; | 
					
						
							|  |  |  |   final _CountdownZoned _doubleTapMinTimeCountdown; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool _isTrackingPointer = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   void startTrackingPointer(PointerRoute route, Matrix4? transform) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     if (!_isTrackingPointer) { | 
					
						
							|  |  |  |       _isTrackingPointer = true; | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |       GestureBinding.instance.pointerRouter.addRoute(pointer, route, transform); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void stopTrackingPointer(PointerRoute route) { | 
					
						
							|  |  |  |     if (_isTrackingPointer) { | 
					
						
							|  |  |  |       _isTrackingPointer = false; | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |       GestureBinding.instance.pointerRouter.removeRoute(pointer, route); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool isWithinGlobalTolerance(PointerEvent event, double tolerance) { | 
					
						
							|  |  |  |     final Offset offset = event.position - _initialGlobalPosition; | 
					
						
							|  |  |  |     return offset.distance <= tolerance; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool hasElapsedMinTime() { | 
					
						
							|  |  |  |     return _doubleTapMinTimeCountdown.timeout; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool hasSameButton(PointerDownEvent event) { | 
					
						
							|  |  |  |     return event.buttons == initialButtons; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// CountdownZoned tracks whether the specified duration has elapsed since
 | 
					
						
							|  |  |  | /// creation, honoring [Zone].
 | 
					
						
							|  |  |  | class _CountdownZoned { | 
					
						
							| 
									
										
										
										
											2022-06-19 17:15:37 +01:00
										 |  |  |   _CountdownZoned({required Duration duration}) { | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |     Timer(duration, _onTimeout); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool _timeout = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool get timeout => _timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _onTimeout() { | 
					
						
							|  |  |  |     _timeout = true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RawGestureDetector getMixinGestureDetector({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   Widget? child, | 
					
						
							|  |  |  |   GestureTapUpCallback? onTapUp, | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |   GestureTapDownCallback? onDoubleTapDown, | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureDoubleTapCallback? onDoubleTap, | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |   GestureLongPressDownCallback? onLongPressDown, | 
					
						
							|  |  |  |   GestureLongPressCallback? onLongPress, | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureDragStartCallback? onHoldDragStart, | 
					
						
							|  |  |  |   GestureDragUpdateCallback? onHoldDragUpdate, | 
					
						
							|  |  |  |   GestureDragCancelCallback? onHoldDragCancel, | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |   GestureDragEndCallback? onHoldDragEnd, | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureTapDownCallback? onDoubleFinerTap, | 
					
						
							|  |  |  |   GestureDragStartCallback? onOneFingerPanStart, | 
					
						
							|  |  |  |   GestureDragUpdateCallback? onOneFingerPanUpdate, | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |   GestureDragEndCallback? onOneFingerPanEnd, | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |   GestureScaleUpdateCallback? onTwoFingerScaleUpdate, | 
					
						
							|  |  |  |   GestureScaleEndCallback? onTwoFingerScaleEnd, | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |   GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate, | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  | }) { | 
					
						
							|  |  |  |   return RawGestureDetector( | 
					
						
							|  |  |  |       child: child, | 
					
						
							|  |  |  |       gestures: <Type, GestureRecognizerFactory>{ | 
					
						
							|  |  |  |         // Official
 | 
					
						
							|  |  |  |         TapGestureRecognizer: | 
					
						
							|  |  |  |             GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( | 
					
						
							|  |  |  |                 () => TapGestureRecognizer(), (instance) { | 
					
						
							|  |  |  |           instance.onTapUp = onTapUp; | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |         DoubleTapGestureRecognizer: | 
					
						
							|  |  |  |             GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>( | 
					
						
							|  |  |  |                 () => DoubleTapGestureRecognizer(), (instance) { | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |           instance | 
					
						
							|  |  |  |             ..onDoubleTapDown = onDoubleTapDown | 
					
						
							|  |  |  |             ..onDoubleTap = onDoubleTap; | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |         LongPressGestureRecognizer: | 
					
						
							|  |  |  |             GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>( | 
					
						
							|  |  |  |                 () => LongPressGestureRecognizer(), (instance) { | 
					
						
							|  |  |  |           instance | 
					
						
							|  |  |  |             ..onLongPressDown = onLongPressDown | 
					
						
							|  |  |  |             ..onLongPress = onLongPress; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         }), | 
					
						
							|  |  |  |         // Customized
 | 
					
						
							|  |  |  |         HoldTapMoveGestureRecognizer: | 
					
						
							|  |  |  |             GestureRecognizerFactoryWithHandlers<HoldTapMoveGestureRecognizer>( | 
					
						
							|  |  |  |                 () => HoldTapMoveGestureRecognizer(), | 
					
						
							| 
									
										
										
										
											2023-06-23 17:05:48 +08:00
										 |  |  |                 (instance) => instance | 
					
						
							|  |  |  |                   ..onHoldDragStart = onHoldDragStart | 
					
						
							|  |  |  |                   ..onHoldDragUpdate = onHoldDragUpdate | 
					
						
							|  |  |  |                   ..onHoldDragCancel = onHoldDragCancel | 
					
						
							|  |  |  |                   ..onHoldDragEnd = onHoldDragEnd), | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |         DoubleFinerTapGestureRecognizer: GestureRecognizerFactoryWithHandlers< | 
					
						
							|  |  |  |                 DoubleFinerTapGestureRecognizer>( | 
					
						
							|  |  |  |             () => DoubleFinerTapGestureRecognizer(), (instance) { | 
					
						
							|  |  |  |           instance.onDoubleFinerTap = onDoubleFinerTap; | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |         CustomTouchGestureRecognizer: | 
					
						
							|  |  |  |             GestureRecognizerFactoryWithHandlers<CustomTouchGestureRecognizer>( | 
					
						
							|  |  |  |                 () => CustomTouchGestureRecognizer(), (instance) { | 
					
						
							|  |  |  |           instance | 
					
						
							|  |  |  |             ..onOneFingerPanStart = onOneFingerPanStart | 
					
						
							|  |  |  |             ..onOneFingerPanUpdate = onOneFingerPanUpdate | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |             ..onOneFingerPanEnd = onOneFingerPanEnd | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |             ..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |             ..onTwoFingerScaleEnd = onTwoFingerScaleEnd | 
					
						
							| 
									
										
										
										
											2022-05-12 17:30:39 +08:00
										 |  |  |             ..onThreeFingerVerticalDragUpdate = onThreeFingerVerticalDragUpdate; | 
					
						
							|  |  |  |         }), | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |       }); | 
					
						
							|  |  |  | } |