// Copyright 2015 The Chromium 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'; /// 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. typedef GestureLongPressCallback = void Function(); /// Callback signature for [LongPressGestureRecognizer.onLongPressUp]. /// /// Called when a pointer stops contacting the screen after a long press /// gesture was detected. 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. 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. 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. typedef GestureLongPressEndCallback = void Function(LongPressEndDetails details); /// 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 }) : assert(globalPosition != null); /// The global position at which the pointer contacted the screen. final Offset globalPosition; } /// 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, this.offsetFromOrigin = Offset.zero, }) : assert(globalPosition != null), assert(offsetFromOrigin != null); /// The global position of the pointer when it triggered this update. final Offset globalPosition; /// 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; } /// 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 }) : assert(globalPosition != null); /// The global position at which the pointer lifted from the screen. final Offset globalPosition; } /// 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. 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. LongPressGestureRecognizer({ double postAcceptSlopTolerance, PointerDeviceKind kind, Object debugOwner, }) : super( deadline: kLongPressTimeout, postAcceptSlopTolerance: postAcceptSlopTolerance, kind: kind, debugOwner: debugOwner, ); bool _longPressAccepted = false; Offset _longPressOrigin; /// Called when a long press gesture has been recognized. /// /// See also: /// /// * [onLongPressStart], which has the same timing but has data for the /// press location. GestureLongPressCallback onLongPress; /// Callback for long press start with gesture location. /// /// See also: /// /// * [onLongPress], which has the same timing but without the location data. GestureLongPressStartCallback onLongPressStart; /// Callback for moving the gesture after the lang press is recognized. GestureLongPressMoveUpdateCallback onLongPressMoveUpdate; /// Called when the pointer stops contacting the screen after the long-press. /// /// See also: /// /// * [onLongPressEnd], which has the same timing but has data for the up /// gesture location. GestureLongPressUpCallback onLongPressUp; /// Callback for long press end with gesture location. /// /// See also: /// /// * [onLongPressUp], which has the same timing but without the location data. GestureLongPressEndCallback onLongPressEnd; @override void didExceedDeadline() { resolve(GestureDisposition.accepted); _longPressAccepted = true; super.acceptGesture(primaryPointer); if (onLongPress != null) { invokeCallback('onLongPress', onLongPress); } if (onLongPressStart != null) { invokeCallback('onLongPressStart', () { onLongPressStart(LongPressStartDetails( globalPosition: _longPressOrigin, )); }); } } @override void handlePrimaryPointer(PointerEvent event) { if (event is PointerUpEvent) { if (_longPressAccepted == true) { if (onLongPressUp != null) { invokeCallback('onLongPressUp', onLongPressUp); } if (onLongPressEnd != null) { invokeCallback('onLongPressEnd', () { onLongPressEnd(LongPressEndDetails( globalPosition: event.position, )); }); } _longPressAccepted = false; } else { resolve(GestureDisposition.rejected); } } else if (event is PointerDownEvent || event is PointerCancelEvent) { // The first touch. _longPressAccepted = false; _longPressOrigin = event.position; } else if (event is PointerMoveEvent && _longPressAccepted && onLongPressMoveUpdate != null) { invokeCallback('onLongPressMoveUpdate', () { onLongPressMoveUpdate(LongPressMoveUpdateDetails( globalPosition: event.position, offsetFromOrigin: event.position - _longPressOrigin, )); }); } } @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'; }