rustdesk/lib/widgets/gestures.dart

816 lines
24 KiB
Dart
Raw Normal View History

2022-02-16 23:08:23 +08:00
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
enum CustomTouchGestureState {
none,
oneFingerPan,
twoFingerScale,
twoFingerVerticalDrag,
twoFingerHorizontalDrag,
twoFingerPan
2022-02-16 23:08:23 +08:00
}
const kScaleSlop = kPrecisePointerPanSlop / 10;
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
// twoFingerScale
2022-02-17 15:22:14 +08:00
GestureScaleStartCallback? onTwoFingerScaleStart;
GestureScaleUpdateCallback? onTwoFingerScaleUpdate;
GestureScaleEndCallback? onTwoFingerScaleEnd;
2022-02-16 23:08:23 +08:00
// twoFingerVerticalDrag
2022-02-17 15:22:14 +08:00
GestureDragStartCallback? onTwoFingerVerticalDragStart;
GestureDragUpdateCallback? onTwoFingerVerticalDragUpdate;
GestureDragEndCallback? onTwoFingerVerticalDragEnd;
2022-02-16 23:08:23 +08:00
// twoFingerHorizontalDrag
2022-02-17 15:22:14 +08:00
GestureDragStartCallback? onTwoFingerHorizontalDragStart;
GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate;
GestureDragEndCallback? onTwoFingerHorizontalDragEnd;
2022-02-16 23:08:23 +08:00
// twoFingerPan
GestureDragStartCallback? onTwoFingerPanStart;
GestureDragUpdateCallback? onTwoFingerPanUpdate;
GestureDragEndCallback? onTwoFingerPanEnd;
2022-02-16 23:08:23 +08:00
void _init() {
debugPrint("CustomTouchGestureRecognizer init");
onStart = (d) {
if (d.pointerCount == 1) {
_currentState = CustomTouchGestureState.oneFingerPan;
if (onOneFingerPanStart != null) {
onOneFingerPanStart!(DragStartDetails(
localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
}
2022-02-16 23:08:23 +08:00
debugPrint("start pan");
} else if (d.pointerCount == 2) {
_currentState = CustomTouchGestureState.none;
startWatchTimer();
} else {
_currentState = CustomTouchGestureState.none;
_reset();
}
};
onUpdate = (d) {
if (_isWatch) {
_updateCompute(d);
return;
}
if (_currentState != CustomTouchGestureState.none) {
switch (_currentState) {
case CustomTouchGestureState.oneFingerPan:
if (onOneFingerPanUpdate != null) {
2022-02-17 15:22:14 +08:00
onOneFingerPanUpdate!(_getDragUpdateDetails(d));
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerScale:
if (onTwoFingerScaleUpdate != null) {
2022-02-17 15:22:14 +08:00
onTwoFingerScaleUpdate!(d);
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerHorizontalDrag:
if (onTwoFingerHorizontalDragUpdate != null) {
2022-02-17 15:22:14 +08:00
onTwoFingerHorizontalDragUpdate!(_getDragUpdateDetails(d));
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerVerticalDrag:
if (onTwoFingerVerticalDragUpdate != null) {
2022-02-17 15:22:14 +08:00
onTwoFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerPan:
if (onTwoFingerPanUpdate != null) {
onTwoFingerPanUpdate!(_getDragUpdateDetails(d));
}
break;
2022-02-16 23:08:23 +08:00
default:
break;
}
return;
}
};
onEnd = (d) {
debugPrint("ScaleGestureRecognizer onEnd");
// end
switch (_currentState) {
case CustomTouchGestureState.oneFingerPan:
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;
case CustomTouchGestureState.twoFingerScale:
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;
case CustomTouchGestureState.twoFingerHorizontalDrag:
debugPrint("TwoFingerState.horizontal onEnd");
if (onTwoFingerHorizontalDragEnd != null) {
2022-02-17 15:22:14 +08:00
onTwoFingerHorizontalDragEnd!(_getDragEndDetails(d));
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerVerticalDrag:
debugPrint("TwoFingerState.vertical onEnd");
if (onTwoFingerVerticalDragEnd != null) {
2022-02-17 15:22:14 +08:00
onTwoFingerVerticalDragEnd!(_getDragEndDetails(d));
2022-02-16 23:08:23 +08:00
}
break;
case CustomTouchGestureState.twoFingerPan:
debugPrint("TwoFingerState.twoFingerPan onEnd");
if (onTwoFingerPanEnd != null) {
onTwoFingerPanEnd!(_getDragEndDetails(d));
}
break;
2022-02-16 23:08:23 +08:00
default:
break;
}
_currentState = CustomTouchGestureState.none;
_reset();
};
}
var _currentState = CustomTouchGestureState.none;
var _isWatch = false;
2022-02-17 15:22:14 +08:00
Timer? _timer;
2022-02-16 23:08:23 +08:00
double _sumScale = 0;
double _sumVertical = 0;
double _sumHorizontal = 0;
void _clearSum() {
_sumScale = 0;
_sumVertical = 0;
_sumHorizontal = 0;
}
void _reset() {
_isWatch = false;
_clearSum();
2022-02-17 15:22:14 +08:00
if (_timer != null) _timer!.cancel();
2022-02-16 23:08:23 +08:00
}
void _updateCompute(ScaleUpdateDetails d) {
_sumScale += d.scale - 1;
_sumHorizontal += d.focalPointDelta.dx;
_sumVertical += d.focalPointDelta.dy;
// start
if (_sumScale.abs() > kScaleSlop) {
debugPrint("start Scale");
_currentState = CustomTouchGestureState.twoFingerScale;
if (onTwoFingerScaleStart != null) {
onTwoFingerScaleStart!(ScaleStartDetails(
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
2022-02-16 23:08:23 +08:00
}
_reset();
} else if (_sumVertical.abs() > kPrecisePointerPanSlop &&
_sumHorizontal.abs() < kPrecisePointerHitSlop) {
2022-02-16 23:08:23 +08:00
debugPrint("start Vertical");
if (onTwoFingerVerticalDragStart != null) {
_getDragStartDetails(d);
}
_currentState = CustomTouchGestureState.twoFingerVerticalDrag;
_reset();
} else if ((_sumHorizontal.abs() + _sumVertical.abs()) >
kPrecisePointerPanSlop) {
debugPrint("start TwoFingerPan");
_currentState = CustomTouchGestureState.twoFingerPan;
if (onTwoFingerPanStart != null) {
onTwoFingerPanStart!(_getDragStartDetails(d));
}
_reset();
2022-02-16 23:08:23 +08:00
}
}
void startWatchTimer() {
debugPrint("startWatchTimer");
_isWatch = true;
_clearSum();
2022-02-17 15:22:14 +08:00
if (_timer != null) _timer!.cancel();
2022-02-16 23:08:23 +08:00
_timer = Timer(const Duration(milliseconds: 200), _reset);
}
DragStartDetails _getDragStartDetails(ScaleUpdateDetails d) =>
DragStartDetails(
globalPosition: d.focalPoint,
localPosition: d.localFocalPoint,
);
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-02-16 23:08:23 +08:00
bool _isStart = false;
2022-02-17 15:22:14 +08:00
Timer? _firstTapUpTimer; // 第一次点击后的计时 超时未等到第二次操作则reject
Timer? _secondTapDownTimer; // 第二次点击后的计时 期间内有其他的操作则reject 超时则判定成功 drag update
_TapTracker? _firstTap;
_TapTracker? _secondTap;
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 &&
onHoldDragCancel == null) {
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)));
}
}
_trackTap(event); // 捕捉第一次tap
}
void _trackTap(PointerDownEvent event) {
_stopFirstTapUpTimer();
_stopSecondTapDownTimer();
final _TapTracker tracker = _TapTracker(
event: event,
2022-02-17 15:22:14 +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;
tracker.startTrackingPointer(_handleEvent, event.transform);
}
// 实际的逻辑应该是第二次down后一段时间没有抬起则表示start 刚好是双击取反
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);
} else {
// 检测到其他的抬起事件则取消
_reject(tracker);
}
} else if (event is PointerDownEvent) {
if (_firstTap != null && _secondTap == null) {
_registerSecondTap(tracker);
}
} else if (event is PointerMoveEvent) {
// 检测到first tap move 则取消检测到second tap move且已经通过竞技场则update
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();
}
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));
}
}
} 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
2022-02-17 15:22:14 +08:00
if (onHoldDragStart != null) onHoldDragStart!(DragStartDetails());
2022-02-16 23:08:23 +08:00
}
void _reject(_TapTracker tracker) {
_checkCancel();
_isStart = false;
_trackers.remove(tracker.pointer);
tracker.entry.resolve(GestureDisposition.rejected);
_freezeTracker(tracker);
_reset();
}
@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-02-17 15:22:14 +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-02-17 15:22:14 +08:00
GestureBinding.instance!.gestureArena.release(tracker.pointer);
2022-02-16 23:08:23 +08:00
}
}
// TODO 正确的释放资源
_firstTap = null;
_secondTap = null;
_clearTrackers();
}
void _registerFirstTap(_TapTracker tracker) {
_startFirstTapUpTimer();
2022-02-17 15:22:14 +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) {
// 清除first tap的状态
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-02-17 15:22:14 +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-02-17 15:22:14 +08:00
Timer? _firstTapTimer; // 第一次点击后的计时 超时未等到第二次操作则reject
_TapTracker? _firstTap;
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;
_startFirstTapDownTimer();
}
_trackTap(event); // 捕捉tap
}
void _trackTap(PointerDownEvent event) {
final _TapTracker tracker = _TapTracker(
event: event,
2022-02-17 15:22:14 +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);
}
// 实际的逻辑应该是第二次down后一段时间没有抬起则表示start 刚好是双击取反
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) {
if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop))
_reject(tracker);
} 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;
// TODO 正确的释放资源
_clearTrackers();
}
void _registerTap(_TapTracker tracker) {
2022-02-17 15:22:14 +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
2022-02-17 15:22:14 +08:00
if (onDoubleFinerTap != null) onDoubleFinerTap!(TapDownDetails());
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-02-16 23:08:23 +08:00
}) : assert(doubleTapMinTime != null),
assert(event != null),
assert(event.buttons != null),
pointer = event.pointer,
_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;
GestureBinding.instance!.pointerRouter
.addRoute(pointer, route, transform);
2022-02-16 23:08:23 +08:00
}
}
void stopTrackingPointer(PointerRoute route) {
if (_isTrackingPointer) {
_isTrackingPointer = false;
2022-02-17 15:22:14 +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-02-17 15:22:14 +08:00
_CountdownZoned({required Duration duration}) : assert(duration != null) {
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,
GestureTapDownCallback? onDoubleTapDown,
2022-02-17 15:22:14 +08:00
GestureDoubleTapCallback? onDoubleTap,
GestureLongPressDownCallback? onLongPressDown,
GestureLongPressCallback? onLongPress,
2022-02-17 15:22:14 +08:00
GestureDragStartCallback? onHoldDragStart,
GestureDragUpdateCallback? onHoldDragUpdate,
GestureDragCancelCallback? onHoldDragCancel,
GestureTapDownCallback? onDoubleFinerTap,
GestureDragStartCallback? onOneFingerPanStart,
GestureDragUpdateCallback? onOneFingerPanUpdate,
GestureDragEndCallback? onOneFingerPanEnd,
2022-02-17 15:22:14 +08:00
GestureScaleUpdateCallback? onTwoFingerScaleUpdate,
GestureScaleEndCallback? onTwoFingerScaleEnd,
GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate,
GestureDragUpdateCallback? onTwoFingerVerticalDragUpdate,
GestureDragStartCallback? onTwoFingerPanStart,
GestureDragUpdateCallback? onTwoFingerPanUpdate,
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) {
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(),
(instance) => {
instance
..onHoldDragStart = onHoldDragStart
..onHoldDragUpdate = onHoldDragUpdate
..onHoldDragCancel = onHoldDragCancel
}),
DoubleFinerTapGestureRecognizer: GestureRecognizerFactoryWithHandlers<
DoubleFinerTapGestureRecognizer>(
() => DoubleFinerTapGestureRecognizer(), (instance) {
instance.onDoubleFinerTap = onDoubleFinerTap;
}),
CustomTouchGestureRecognizer:
GestureRecognizerFactoryWithHandlers<CustomTouchGestureRecognizer>(
() => CustomTouchGestureRecognizer(), (instance) {
instance
..onOneFingerPanStart = onOneFingerPanStart
..onOneFingerPanUpdate = onOneFingerPanUpdate
..onOneFingerPanEnd = onOneFingerPanEnd
2022-02-16 23:08:23 +08:00
..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate
2022-02-17 15:22:14 +08:00
..onTwoFingerScaleEnd = onTwoFingerScaleEnd
2022-02-16 23:08:23 +08:00
..onTwoFingerHorizontalDragUpdate = onTwoFingerHorizontalDragUpdate
..onTwoFingerVerticalDragUpdate = onTwoFingerVerticalDragUpdate
..onTwoFingerPanStart = onTwoFingerPanStart
..onTwoFingerPanUpdate = onTwoFingerPanUpdate;
2022-02-16 23:08:23 +08:00
})
});
}