blob: fa6c324f844f1bae07a12886457390663d745816 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'arena.dart';
import 'constants.dart';
import 'events.dart';
import 'recognizer.dart';
import 'velocity_tracker.dart';
/// Callback signature for [LongPressGestureRecognizer.onLongPressDown].
///
/// Called when a pointer that might cause a long-press has contacted the
/// screen. The position at which the pointer contacted the screen is available
/// in the `details`.
///
/// See also:
///
/// * [GestureDetector.onLongPressDown], which matches this signature.
/// * [GestureLongPressStartCallback], the signature that gets called when the
/// pointer has been in contact with the screen long enough to be considered
/// a long-press.
typedef GestureLongPressDownCallback = void Function(LongPressDownDetails details);
/// Callback signature for [LongPressGestureRecognizer.onLongPressCancel].
///
/// Called when the pointer that previously triggered a
/// [GestureLongPressDownCallback] will not end up causing a long-press.
///
/// See also:
///
/// * [GestureDetector.onLongPressCancel], which matches this signature.
typedef GestureLongPressCancelCallback = void Function();
/// Callback signature for [LongPressGestureRecognizer.onLongPress].
///
/// Called when a pointer has remained in contact with the screen at the
/// same location for a long period of time.
///
/// See also:
///
/// * [GestureDetector.onLongPress], which matches this signature.
/// * [GestureLongPressStartCallback], which is the same signature but with
/// details of where the long press occurred.
typedef GestureLongPressCallback = void Function();
/// Callback signature for [LongPressGestureRecognizer.onLongPressUp].
///
/// Called when a pointer stops contacting the screen after a long press
/// gesture was detected.
///
/// See also:
///
/// * [GestureDetector.onLongPressUp], which matches this signature.
typedef GestureLongPressUpCallback = void Function();
/// Callback signature for [LongPressGestureRecognizer.onLongPressStart].
///
/// Called when a pointer has remained in contact with the screen at the
/// same location for a long period of time. Also reports the long press down
/// position.
///
/// See also:
///
/// * [GestureDetector.onLongPressStart], which matches this signature.
/// * [GestureLongPressCallback], which is the same signature without the
/// details.
typedef GestureLongPressStartCallback = void Function(LongPressStartDetails details);
/// Callback signature for [LongPressGestureRecognizer.onLongPressMoveUpdate].
///
/// Called when a pointer is moving after being held in contact at the same
/// location for a long period of time. Reports the new position and its offset
/// from the original down position.
///
/// See also:
///
/// * [GestureDetector.onLongPressMoveUpdate], which matches this signature.
typedef GestureLongPressMoveUpdateCallback = void Function(LongPressMoveUpdateDetails details);
/// Callback signature for [LongPressGestureRecognizer.onLongPressEnd].
///
/// Called when a pointer stops contacting the screen after a long press
/// gesture was detected. Also reports the position where the pointer stopped
/// contacting the screen.
///
/// See also:
///
/// * [GestureDetector.onLongPressEnd], which matches this signature.
typedef GestureLongPressEndCallback = void Function(LongPressEndDetails details);
/// Details for callbacks that use [GestureLongPressDownCallback].
///
/// See also:
///
/// * [LongPressGestureRecognizer.onLongPressDown], whose callback passes
/// these details.
/// * [LongPressGestureRecognizer.onSecondaryLongPressDown], whose callback
/// passes these details.
/// * [LongPressGestureRecognizer.onTertiaryLongPressDown], whose callback
/// passes these details.
class LongPressDownDetails {
/// Creates the details for a [GestureLongPressDownCallback].
///
/// The `globalPosition` argument must not be null.
///
/// If the `localPosition` argument is not specified, it will default to the
/// global position.
const LongPressDownDetails({
this.globalPosition = Offset.zero,
Offset? localPosition,
this.kind,
}) : assert(globalPosition != null),
localPosition = localPosition ?? globalPosition;
/// The global position at which the pointer contacted the screen.
final Offset globalPosition;
/// The kind of the device that initiated the event.
final PointerDeviceKind? kind;
/// The local position at which the pointer contacted the screen.
final Offset localPosition;
}
/// Details for callbacks that use [GestureLongPressStartCallback].
///
/// See also:
///
/// * [LongPressGestureRecognizer.onLongPressStart], which uses [GestureLongPressStartCallback].
/// * [LongPressMoveUpdateDetails], the details for [GestureLongPressMoveUpdateCallback]
/// * [LongPressEndDetails], the details for [GestureLongPressEndCallback].
class LongPressStartDetails {
/// Creates the details for a [GestureLongPressStartCallback].
///
/// The [globalPosition] argument must not be null.
const LongPressStartDetails({
this.globalPosition = Offset.zero,
Offset? localPosition,
}) : assert(globalPosition != null),
localPosition = localPosition ?? globalPosition;
/// The global position at which the pointer initially contacted the screen.
final Offset globalPosition;
/// The local position at which the pointer initially contacted the screen.
final Offset localPosition;
}
/// Details for callbacks that use [GestureLongPressMoveUpdateCallback].
///
/// See also:
///
/// * [LongPressGestureRecognizer.onLongPressMoveUpdate], which uses [GestureLongPressMoveUpdateCallback].
/// * [LongPressEndDetails], the details for [GestureLongPressEndCallback]
/// * [LongPressStartDetails], the details for [GestureLongPressStartCallback].
class LongPressMoveUpdateDetails {
/// Creates the details for a [GestureLongPressMoveUpdateCallback].
///
/// The [globalPosition] and [offsetFromOrigin] arguments must not be null.
const LongPressMoveUpdateDetails({
this.globalPosition = Offset.zero,
Offset? localPosition,
this.offsetFromOrigin = Offset.zero,
Offset? localOffsetFromOrigin,
}) : assert(globalPosition != null),
assert(offsetFromOrigin != null),
localPosition = localPosition ?? globalPosition,
localOffsetFromOrigin = localOffsetFromOrigin ?? offsetFromOrigin;
/// The global position of the pointer when it triggered this update.
final Offset globalPosition;
/// The local position of the pointer when it triggered this update.
final Offset localPosition;
/// A delta offset from the point where the long press drag initially contacted
/// the screen to the point where the pointer is currently located (the
/// present [globalPosition]) when this callback is triggered.
final Offset offsetFromOrigin;
/// A local delta offset from the point where the long press drag initially contacted
/// the screen to the point where the pointer is currently located (the
/// present [localPosition]) when this callback is triggered.
final Offset localOffsetFromOrigin;
}
/// Details for callbacks that use [GestureLongPressEndCallback].
///
/// See also:
///
/// * [LongPressGestureRecognizer.onLongPressEnd], which uses [GestureLongPressEndCallback].
/// * [LongPressMoveUpdateDetails], the details for [GestureLongPressMoveUpdateCallback].
/// * [LongPressStartDetails], the details for [GestureLongPressStartCallback].
class LongPressEndDetails {
/// Creates the details for a [GestureLongPressEndCallback].
///
/// The [globalPosition] argument must not be null.
const LongPressEndDetails({
this.globalPosition = Offset.zero,
Offset? localPosition,
this.velocity = Velocity.zero,
}) : assert(globalPosition != null),
localPosition = localPosition ?? globalPosition;
/// The global position at which the pointer lifted from the screen.
final Offset globalPosition;
/// The local position at which the pointer contacted the screen.
final Offset localPosition;
/// The pointer's velocity when it stopped contacting the screen.
///
/// Defaults to zero if not specified in the constructor.
final Velocity velocity;
}
/// Recognizes when the user has pressed down at the same location for a long
/// period of time.
///
/// The gesture must not deviate in position from its touch down point for 500ms
/// until it's recognized. Once the gesture is accepted, the finger can be
/// moved, triggering [onLongPressMoveUpdate] callbacks, unless the
/// [postAcceptSlopTolerance] constructor argument is specified.
///
/// [LongPressGestureRecognizer] may compete on pointer events of
/// [kPrimaryButton], [kSecondaryButton], and/or [kTertiaryButton] if at least
/// one corresponding callback is non-null. If it has no callbacks, it is a no-op.
class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
/// Creates a long-press gesture recognizer.
///
/// Consider assigning the [onLongPressStart] callback after creating this
/// object.
///
/// The [postAcceptSlopTolerance] argument can be used to specify a maximum
/// allowed distance for the gesture to deviate from the starting point once
/// the long press has triggered. If the gesture deviates past that point,
/// subsequent callbacks ([onLongPressMoveUpdate], [onLongPressUp],
/// [onLongPressEnd]) will stop. Defaults to null, which means the gesture
/// can be moved without limit once the long press is accepted.
///
/// The [duration] argument can be used to overwrite the default duration
/// after which the long press will be recognized.
///
/// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
LongPressGestureRecognizer({
Duration? duration,
double? postAcceptSlopTolerance,
@Deprecated(
'Migrate to supportedDevices. '
'This feature was deprecated after v2.3.0-1.0.pre.',
)
PointerDeviceKind? kind,
Set<PointerDeviceKind>? supportedDevices,
Object? debugOwner,
}) : super(
deadline: duration ?? kLongPressTimeout,
postAcceptSlopTolerance: postAcceptSlopTolerance,
kind: kind,
supportedDevices: supportedDevices,
debugOwner: debugOwner,
);
bool _longPressAccepted = false;
OffsetPair? _longPressOrigin;
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
// different set of buttons, the gesture is canceled.
int? _initialButtons;
/// Called when a pointer has contacted the screen at a particular location
/// with a primary button, which might be the start of a long-press.
///
/// This triggers after the pointer down event.
///
/// If this recognizer doesn't win the arena, [onLongPressCancel] is called
/// next. Otherwise, [onLongPressStart] is called next.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
/// * [LongPressDownDetails], which is passed as an argument to this callback.
/// * [GestureDetector.onLongPressDown], which exposes this callback in a widget.
GestureLongPressDownCallback? onLongPressDown;
/// Called when a pointer that previously triggered [onLongPressDown] will
/// not end up causing a long-press.
///
/// This triggers once the gesture loses the arena if [onLongPressDown] has
/// previously been triggered.
///
/// If this recognizer wins the arena, [onLongPressStart] and [onLongPress]
/// are called instead.
///
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
/// been exceeded, this callback will not be called, since the gesture will
/// have already won the arena at that point.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
GestureLongPressCancelCallback? onLongPressCancel;
/// Called when a long press gesture by a primary button has been recognized.
///
/// This is equivalent to (and is called immediately after) [onLongPressStart].
/// The only difference between the two is that this callback does not
/// contain details of the position at which the pointer initially contacted
/// the screen.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
GestureLongPressCallback? onLongPress;
/// Called when a long press gesture by a primary button has been recognized.
///
/// This is equivalent to (and is called immediately before) [onLongPress].
/// The only difference between the two is that this callback contains
/// details of the position at which the pointer initially contacted the
/// screen, whereas [onLongPress] does not.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [LongPressStartDetails], which is passed as an argument to this callback.
GestureLongPressStartCallback? onLongPressStart;
/// Called when moving after the long press by a primary button is recognized.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [LongPressMoveUpdateDetails], which is passed as an argument to this
/// callback.
GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate;
/// Called when the pointer stops contacting the screen after a long-press
/// by a primary button.
///
/// This is equivalent to (and is called immediately after) [onLongPressEnd].
/// The only difference between the two is that this callback does not
/// contain details of the state of the pointer when it stopped contacting
/// the screen.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
GestureLongPressUpCallback? onLongPressUp;
/// Called when the pointer stops contacting the screen after a long-press
/// by a primary button.
///
/// This is equivalent to (and is called immediately before) [onLongPressUp].
/// The only difference between the two is that this callback contains
/// details of the state of the pointer when it stopped contacting the
/// screen, whereas [onLongPressUp] does not.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [LongPressEndDetails], which is passed as an argument to this
/// callback.
GestureLongPressEndCallback? onLongPressEnd;
/// Called when a pointer has contacted the screen at a particular location
/// with a secondary button, which might be the start of a long-press.
///
/// This triggers after the pointer down event.
///
/// If this recognizer doesn't win the arena, [onSecondaryLongPressCancel] is
/// called next. Otherwise, [onSecondaryLongPressStart] is called next.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
/// * [onLongPressDown], a similar callback but for a primary button.
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
/// * [LongPressDownDetails], which is passed as an argument to this callback.
/// * [GestureDetector.onSecondaryLongPressDown], which exposes this callback
/// in a widget.
GestureLongPressDownCallback? onSecondaryLongPressDown;
/// Called when a pointer that previously triggered [onSecondaryLongPressDown]
/// will not end up causing a long-press.
///
/// This triggers once the gesture loses the arena if
/// [onSecondaryLongPressDown] has previously been triggered.
///
/// If this recognizer wins the arena, [onSecondaryLongPressStart] and
/// [onSecondaryLongPress] are called instead.
///
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
/// been exceeded, this callback will not be called, since the gesture will
/// have already won the arena at that point.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
GestureLongPressCancelCallback? onSecondaryLongPressCancel;
/// Called when a long press gesture by a secondary button has been
/// recognized.
///
/// This is equivalent to (and is called immediately after)
/// [onSecondaryLongPressStart]. The only difference between the two is that
/// this callback does not contain details of the position at which the
/// pointer initially contacted the screen.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
GestureLongPressCallback? onSecondaryLongPress;
/// Called when a long press gesture by a secondary button has been recognized.
///
/// This is equivalent to (and is called immediately before)
/// [onSecondaryLongPress]. The only difference between the two is that this
/// callback contains details of the position at which the pointer initially
/// contacted the screen, whereas [onSecondaryLongPress] does not.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
/// * [LongPressStartDetails], which is passed as an argument to this
/// callback.
GestureLongPressStartCallback? onSecondaryLongPressStart;
/// Called when moving after the long press by a secondary button is
/// recognized.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
/// * [LongPressMoveUpdateDetails], which is passed as an argument to this
/// callback.
GestureLongPressMoveUpdateCallback? onSecondaryLongPressMoveUpdate;
/// Called when the pointer stops contacting the screen after a long-press by
/// a secondary button.
///
/// This is equivalent to (and is called immediately after)
/// [onSecondaryLongPressEnd]. The only difference between the two is that
/// this callback does not contain details of the state of the pointer when
/// it stopped contacting the screen.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
GestureLongPressUpCallback? onSecondaryLongPressUp;
/// Called when the pointer stops contacting the screen after a long-press by
/// a secondary button.
///
/// This is equivalent to (and is called immediately before)
/// [onSecondaryLongPressUp]. The only difference between the two is that
/// this callback contains details of the state of the pointer when it
/// stopped contacting the screen, whereas [onSecondaryLongPressUp] does not.
///
/// See also:
///
/// * [kSecondaryButton], the button this callback responds to.
/// * [LongPressEndDetails], which is passed as an argument to this callback.
GestureLongPressEndCallback? onSecondaryLongPressEnd;
/// Called when a pointer has contacted the screen at a particular location
/// with a tertiary button, which might be the start of a long-press.
///
/// This triggers after the pointer down event.
///
/// If this recognizer doesn't win the arena, [onTertiaryLongPressCancel] is
/// called next. Otherwise, [onTertiaryLongPressStart] is called next.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
/// * [onLongPressDown], a similar callback but for a primary button.
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
/// * [LongPressDownDetails], which is passed as an argument to this callback.
/// * [GestureDetector.onTertiaryLongPressDown], which exposes this callback
/// in a widget.
GestureLongPressDownCallback? onTertiaryLongPressDown;
/// Called when a pointer that previously triggered [onTertiaryLongPressDown]
/// will not end up causing a long-press.
///
/// This triggers once the gesture loses the arena if
/// [onTertiaryLongPressDown] has previously been triggered.
///
/// If this recognizer wins the arena, [onTertiaryLongPressStart] and
/// [onTertiaryLongPress] are called instead.
///
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
/// been exceeded, this callback will not be called, since the gesture will
/// have already won the arena at that point.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
GestureLongPressCancelCallback? onTertiaryLongPressCancel;
/// Called when a long press gesture by a tertiary button has been
/// recognized.
///
/// This is equivalent to (and is called immediately after)
/// [onTertiaryLongPressStart]. The only difference between the two is that
/// this callback does not contain details of the position at which the
/// pointer initially contacted the screen.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
GestureLongPressCallback? onTertiaryLongPress;
/// Called when a long press gesture by a tertiary button has been recognized.
///
/// This is equivalent to (and is called immediately before)
/// [onTertiaryLongPress]. The only difference between the two is that this
/// callback contains details of the position at which the pointer initially
/// contacted the screen, whereas [onTertiaryLongPress] does not.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
/// * [LongPressStartDetails], which is passed as an argument to this
/// callback.
GestureLongPressStartCallback? onTertiaryLongPressStart;
/// Called when moving after the long press by a tertiary button is
/// recognized.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
/// * [LongPressMoveUpdateDetails], which is passed as an argument to this
/// callback.
GestureLongPressMoveUpdateCallback? onTertiaryLongPressMoveUpdate;
/// Called when the pointer stops contacting the screen after a long-press by
/// a tertiary button.
///
/// This is equivalent to (and is called immediately after)
/// [onTertiaryLongPressEnd]. The only difference between the two is that
/// this callback does not contain details of the state of the pointer when
/// it stopped contacting the screen.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
GestureLongPressUpCallback? onTertiaryLongPressUp;
/// Called when the pointer stops contacting the screen after a long-press by
/// a tertiary button.
///
/// This is equivalent to (and is called immediately before)
/// [onTertiaryLongPressUp]. The only difference between the two is that
/// this callback contains details of the state of the pointer when it
/// stopped contacting the screen, whereas [onTertiaryLongPressUp] does not.
///
/// See also:
///
/// * [kTertiaryButton], the button this callback responds to.
/// * [LongPressEndDetails], which is passed as an argument to this callback.
GestureLongPressEndCallback? onTertiaryLongPressEnd;
VelocityTracker? _velocityTracker;
@override
bool isPointerAllowed(PointerDownEvent event) {
switch (event.buttons) {
case kPrimaryButton:
if (onLongPressDown == null &&
onLongPressCancel == null &&
onLongPressStart == null &&
onLongPress == null &&
onLongPressMoveUpdate == null &&
onLongPressEnd == null &&
onLongPressUp == null)
return false;
break;
case kSecondaryButton:
if (onSecondaryLongPressDown == null &&
onSecondaryLongPressCancel == null &&
onSecondaryLongPressStart == null &&
onSecondaryLongPress == null &&
onSecondaryLongPressMoveUpdate == null &&
onSecondaryLongPressEnd == null &&
onSecondaryLongPressUp == null)
return false;
break;
case kTertiaryButton:
if (onTertiaryLongPressDown == null &&
onTertiaryLongPressCancel == null &&
onTertiaryLongPressStart == null &&
onTertiaryLongPress == null &&
onTertiaryLongPressMoveUpdate == null &&
onTertiaryLongPressEnd == null &&
onTertiaryLongPressUp == null)
return false;
break;
default:
return false;
}
return super.isPointerAllowed(event);
}
@override
void didExceedDeadline() {
// Exceeding the deadline puts the gesture in the accepted state.
resolve(GestureDisposition.accepted);
_longPressAccepted = true;
super.acceptGesture(primaryPointer!);
_checkLongPressStart();
}
@override
void handlePrimaryPointer(PointerEvent event) {
if (!event.synthesized) {
if (event is PointerDownEvent) {
_velocityTracker = VelocityTracker.withKind(event.kind);
_velocityTracker!.addPosition(event.timeStamp, event.localPosition);
}
if (event is PointerMoveEvent) {
assert(_velocityTracker != null);
_velocityTracker!.addPosition(event.timeStamp, event.localPosition);
}
}
if (event is PointerUpEvent) {
if (_longPressAccepted == true) {
_checkLongPressEnd(event);
} else {
// Pointer is lifted before timeout.
resolve(GestureDisposition.rejected);
}
_reset();
} else if (event is PointerCancelEvent) {
_checkLongPressCancel();
_reset();
} else if (event is PointerDownEvent) {
// The first touch.
_longPressOrigin = OffsetPair.fromEventPosition(event);
_initialButtons = event.buttons;
_checkLongPressDown(event);
} else if (event is PointerMoveEvent) {
if (event.buttons != _initialButtons) {
resolve(GestureDisposition.rejected);
stopTrackingPointer(primaryPointer!);
} else if (_longPressAccepted) {
_checkLongPressMoveUpdate(event);
}
}
}
void _checkLongPressDown(PointerDownEvent event) {
assert(_longPressOrigin != null);
final LongPressDownDetails details = LongPressDownDetails(
globalPosition: _longPressOrigin!.global,
localPosition: _longPressOrigin!.local,
kind: getKindForPointer(event.pointer),
);
switch (_initialButtons) {
case kPrimaryButton:
if (onLongPressDown != null)
invokeCallback<void>('onLongPressDown', () => onLongPressDown!(details));
break;
case kSecondaryButton:
if (onSecondaryLongPressDown != null)
invokeCallback<void>('onSecondaryLongPressDown', () => onSecondaryLongPressDown!(details));
break;
case kTertiaryButton:
if (onTertiaryLongPressDown != null)
invokeCallback<void>('onTertiaryLongPressDown', () => onTertiaryLongPressDown!(details));
break;
default:
assert(false, 'Unhandled button $_initialButtons');
}
}
void _checkLongPressCancel() {
if (state == GestureRecognizerState.possible) {
switch (_initialButtons) {
case kPrimaryButton:
if (onLongPressCancel != null)
invokeCallback<void>('onLongPressCancel', onLongPressCancel!);
break;
case kSecondaryButton:
if (onSecondaryLongPressCancel != null)
invokeCallback<void>('onSecondaryLongPressCancel', onSecondaryLongPressCancel!);
break;
case kTertiaryButton:
if (onTertiaryLongPressCancel != null)
invokeCallback<void>('onTertiaryLongPressCancel', onTertiaryLongPressCancel!);
break;
default:
assert(false, 'Unhandled button $_initialButtons');
}
}
}
void _checkLongPressStart() {
switch (_initialButtons) {
case kPrimaryButton:
if (onLongPressStart != null) {
final LongPressStartDetails details = LongPressStartDetails(
globalPosition: _longPressOrigin!.global,
localPosition: _longPressOrigin!.local,
);
invokeCallback<void>('onLongPressStart', () => onLongPressStart!(details));
}
if (onLongPress != null) {
invokeCallback<void>('onLongPress', onLongPress!);
}
break;
case kSecondaryButton:
if (onSecondaryLongPressStart != null) {
final LongPressStartDetails details = LongPressStartDetails(
globalPosition: _longPressOrigin!.global,
localPosition: _longPressOrigin!.local,
);
invokeCallback<void>('onSecondaryLongPressStart', () => onSecondaryLongPressStart!(details));
}
if (onSecondaryLongPress != null) {
invokeCallback<void>('onSecondaryLongPress', onSecondaryLongPress!);
}
break;
case kTertiaryButton:
if (onTertiaryLongPressStart != null) {
final LongPressStartDetails details = LongPressStartDetails(
globalPosition: _longPressOrigin!.global,
localPosition: _longPressOrigin!.local,
);
invokeCallback<void>('onTertiaryLongPressStart', () => onTertiaryLongPressStart!(details));
}
if (onTertiaryLongPress != null) {
invokeCallback<void>('onTertiaryLongPress', onTertiaryLongPress!);
}
break;
default:
assert(false, 'Unhandled button $_initialButtons');
}
}
void _checkLongPressMoveUpdate(PointerEvent event) {
final LongPressMoveUpdateDetails details = LongPressMoveUpdateDetails(
globalPosition: event.position,
localPosition: event.localPosition,
offsetFromOrigin: event.position - _longPressOrigin!.global,
localOffsetFromOrigin: event.localPosition - _longPressOrigin!.local,
);
switch (_initialButtons) {
case kPrimaryButton:
if (onLongPressMoveUpdate != null) {
invokeCallback<void>('onLongPressMoveUpdate', () => onLongPressMoveUpdate!(details));
}
break;
case kSecondaryButton:
if (onSecondaryLongPressMoveUpdate != null) {
invokeCallback<void>('onSecondaryLongPressMoveUpdate', () => onSecondaryLongPressMoveUpdate!(details));
}
break;
case kTertiaryButton:
if (onTertiaryLongPressMoveUpdate != null) {
invokeCallback<void>('onTertiaryLongPressMoveUpdate', () => onTertiaryLongPressMoveUpdate!(details));
}
break;
default:
assert(false, 'Unhandled button $_initialButtons');
}
}
void _checkLongPressEnd(PointerEvent event) {
final VelocityEstimate? estimate = _velocityTracker!.getVelocityEstimate();
final Velocity velocity = estimate == null
? Velocity.zero
: Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
final LongPressEndDetails details = LongPressEndDetails(
globalPosition: event.position,
localPosition: event.localPosition,
velocity: velocity,
);
_velocityTracker = null;
switch (_initialButtons) {
case kPrimaryButton:
if (onLongPressEnd != null) {
invokeCallback<void>('onLongPressEnd', () => onLongPressEnd!(details));
}
if (onLongPressUp != null) {
invokeCallback<void>('onLongPressUp', onLongPressUp!);
}
break;
case kSecondaryButton:
if (onSecondaryLongPressEnd != null) {
invokeCallback<void>('onSecondaryLongPressEnd', () => onSecondaryLongPressEnd!(details));
}
if (onSecondaryLongPressUp != null) {
invokeCallback<void>('onSecondaryLongPressUp', onSecondaryLongPressUp!);
}
break;
case kTertiaryButton:
if (onTertiaryLongPressEnd != null) {
invokeCallback<void>('onTertiaryLongPressEnd', () => onTertiaryLongPressEnd!(details));
}
if (onTertiaryLongPressUp != null) {
invokeCallback<void>('onTertiaryLongPressUp', onTertiaryLongPressUp!);
}
break;
default:
assert(false, 'Unhandled button $_initialButtons');
}
}
void _reset() {
_longPressAccepted = false;
_longPressOrigin = null;
_initialButtons = null;
_velocityTracker = null;
}
@override
void resolve(GestureDisposition disposition) {
if (disposition == GestureDisposition.rejected) {
if (_longPressAccepted) {
// This can happen if the gesture has been canceled. For example when
// the buttons have changed.
_reset();
} else {
_checkLongPressCancel();
}
}
super.resolve(disposition);
}
@override
void acceptGesture(int pointer) {
// Winning the arena isn't important here since it may happen from a sweep.
// Explicitly exceeding the deadline puts the gesture in accepted state.
}
@override
String get debugDescription => 'long press';
}