"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/gestures/long_press.dart" (13 Nov 2020, 20720 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 
    6 import 'arena.dart';
    7 import 'constants.dart';
    8 import 'events.dart';
    9 import 'recognizer.dart';
   10 import 'velocity_tracker.dart';
   11 
   12 /// Callback signature for [LongPressGestureRecognizer.onLongPress].
   13 ///
   14 /// Called when a pointer has remained in contact with the screen at the
   15 /// same location for a long period of time.
   16 typedef GestureLongPressCallback = void Function();
   17 
   18 /// Callback signature for [LongPressGestureRecognizer.onLongPressUp].
   19 ///
   20 /// Called when a pointer stops contacting the screen after a long press
   21 /// gesture was detected.
   22 typedef GestureLongPressUpCallback = void Function();
   23 
   24 /// Callback signature for [LongPressGestureRecognizer.onLongPressStart].
   25 ///
   26 /// Called when a pointer has remained in contact with the screen at the
   27 /// same location for a long period of time. Also reports the long press down
   28 /// position.
   29 typedef GestureLongPressStartCallback = void Function(LongPressStartDetails details);
   30 
   31 /// Callback signature for [LongPressGestureRecognizer.onLongPressMoveUpdate].
   32 ///
   33 /// Called when a pointer is moving after being held in contact at the same
   34 /// location for a long period of time. Reports the new position and its offset
   35 /// from the original down position.
   36 typedef GestureLongPressMoveUpdateCallback = void Function(LongPressMoveUpdateDetails details);
   37 
   38 /// Callback signature for [LongPressGestureRecognizer.onLongPressEnd].
   39 ///
   40 /// Called when a pointer stops contacting the screen after a long press
   41 /// gesture was detected. Also reports the position where the pointer stopped
   42 /// contacting the screen.
   43 typedef GestureLongPressEndCallback = void Function(LongPressEndDetails details);
   44 
   45 /// Details for callbacks that use [GestureLongPressStartCallback].
   46 ///
   47 /// See also:
   48 ///
   49 ///  * [LongPressGestureRecognizer.onLongPressStart], which uses [GestureLongPressStartCallback].
   50 ///  * [LongPressMoveUpdateDetails], the details for [GestureLongPressMoveUpdateCallback]
   51 ///  * [LongPressEndDetails], the details for [GestureLongPressEndCallback].
   52 class LongPressStartDetails {
   53   /// Creates the details for a [GestureLongPressStartCallback].
   54   ///
   55   /// The [globalPosition] argument must not be null.
   56   const LongPressStartDetails({
   57     this.globalPosition = Offset.zero,
   58     Offset? localPosition,
   59   }) : assert(globalPosition != null),
   60        localPosition = localPosition ?? globalPosition;
   61 
   62   /// The global position at which the pointer contacted the screen.
   63   final Offset globalPosition;
   64 
   65   /// The local position at which the pointer contacted the screen.
   66   final Offset localPosition;
   67 }
   68 
   69 /// Details for callbacks that use [GestureLongPressMoveUpdateCallback].
   70 ///
   71 /// See also:
   72 ///
   73 ///  * [LongPressGestureRecognizer.onLongPressMoveUpdate], which uses [GestureLongPressMoveUpdateCallback].
   74 ///  * [LongPressEndDetails], the details for [GestureLongPressEndCallback]
   75 ///  * [LongPressStartDetails], the details for [GestureLongPressStartCallback].
   76 class LongPressMoveUpdateDetails {
   77   /// Creates the details for a [GestureLongPressMoveUpdateCallback].
   78   ///
   79   /// The [globalPosition] and [offsetFromOrigin] arguments must not be null.
   80   const LongPressMoveUpdateDetails({
   81     this.globalPosition = Offset.zero,
   82     Offset? localPosition,
   83     this.offsetFromOrigin = Offset.zero,
   84     Offset? localOffsetFromOrigin,
   85   }) : assert(globalPosition != null),
   86        assert(offsetFromOrigin != null),
   87        localPosition = localPosition ?? globalPosition,
   88        localOffsetFromOrigin = localOffsetFromOrigin ?? offsetFromOrigin;
   89 
   90   /// The global position of the pointer when it triggered this update.
   91   final Offset globalPosition;
   92 
   93   /// The local position of the pointer when it triggered this update.
   94   final Offset localPosition;
   95 
   96   /// A delta offset from the point where the long press drag initially contacted
   97   /// the screen to the point where the pointer is currently located (the
   98   /// present [globalPosition]) when this callback is triggered.
   99   final Offset offsetFromOrigin;
  100 
  101   /// A local delta offset from the point where the long press drag initially contacted
  102   /// the screen to the point where the pointer is currently located (the
  103   /// present [localPosition]) when this callback is triggered.
  104   final Offset localOffsetFromOrigin;
  105 }
  106 
  107 /// Details for callbacks that use [GestureLongPressEndCallback].
  108 ///
  109 /// See also:
  110 ///
  111 ///  * [LongPressGestureRecognizer.onLongPressEnd], which uses [GestureLongPressEndCallback].
  112 ///  * [LongPressMoveUpdateDetails], the details for [GestureLongPressMoveUpdateCallback]
  113 ///  * [LongPressStartDetails], the details for [GestureLongPressStartCallback].
  114 class LongPressEndDetails {
  115   /// Creates the details for a [GestureLongPressEndCallback].
  116   ///
  117   /// The [globalPosition] argument must not be null.
  118   const LongPressEndDetails({
  119     this.globalPosition = Offset.zero,
  120     Offset? localPosition,
  121     this.velocity = Velocity.zero,
  122   }) : assert(globalPosition != null),
  123        localPosition = localPosition ?? globalPosition;
  124 
  125   /// The global position at which the pointer lifted from the screen.
  126   final Offset globalPosition;
  127 
  128   /// The local position at which the pointer contacted the screen.
  129   final Offset localPosition;
  130 
  131   /// The pointer's velocity when it stopped contacting the screen.
  132   ///
  133   /// Defaults to zero if not specified in the constructor.
  134   final Velocity velocity;
  135 }
  136 
  137 /// Recognizes when the user has pressed down at the same location for a long
  138 /// period of time.
  139 ///
  140 /// The gesture must not deviate in position from its touch down point for 500ms
  141 /// until it's recognized. Once the gesture is accepted, the finger can be
  142 /// moved, triggering [onLongPressMoveUpdate] callbacks, unless the
  143 /// [postAcceptSlopTolerance] constructor argument is specified.
  144 ///
  145 /// [LongPressGestureRecognizer] may compete on pointer events of
  146 /// [kPrimaryButton], [kSecondaryButton], and/or [kTertiaryButton] if at least
  147 /// one corresponding callback is non-null. If it has no callbacks, it is a no-op.
  148 class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
  149   /// Creates a long-press gesture recognizer.
  150   ///
  151   /// Consider assigning the [onLongPressStart] callback after creating this
  152   /// object.
  153   ///
  154   /// The [postAcceptSlopTolerance] argument can be used to specify a maximum
  155   /// allowed distance for the gesture to deviate from the starting point once
  156   /// the long press has triggered. If the gesture deviates past that point,
  157   /// subsequent callbacks ([onLongPressMoveUpdate], [onLongPressUp],
  158   /// [onLongPressEnd]) will stop. Defaults to null, which means the gesture
  159   /// can be moved without limit once the long press is accepted.
  160   ///
  161   /// The [duration] argument can be used to overwrite the default duration
  162   /// after which the long press will be recognized.
  163   LongPressGestureRecognizer({
  164     Duration? duration,
  165     double? postAcceptSlopTolerance,
  166     PointerDeviceKind? kind,
  167     Object? debugOwner,
  168   }) : super(
  169           deadline: duration ?? kLongPressTimeout,
  170           postAcceptSlopTolerance: postAcceptSlopTolerance,
  171           kind: kind,
  172           debugOwner: debugOwner,
  173         );
  174 
  175   bool _longPressAccepted = false;
  176   OffsetPair? _longPressOrigin;
  177   // The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
  178   // different set of buttons, the gesture is canceled.
  179   int? _initialButtons;
  180 
  181   /// Called when a long press gesture by a primary button has been recognized.
  182   ///
  183   /// See also:
  184   ///
  185   ///  * [kPrimaryButton], the button this callback responds to.
  186   ///  * [onLongPressStart], which has the same timing but has data for the
  187   ///    press location.
  188   GestureLongPressCallback? onLongPress;
  189 
  190   /// Called when a long press gesture by a primary button has been recognized.
  191   ///
  192   /// See also:
  193   ///
  194   ///  * [kPrimaryButton], the button this callback responds to.
  195   ///  * [onLongPress], which has the same timing but without details.
  196   ///  * [LongPressStartDetails], which is passed as an argument to this callback.
  197   GestureLongPressStartCallback? onLongPressStart;
  198 
  199   /// Called when moving after the long press by a primary button is recognized.
  200   ///
  201   /// See also:
  202   ///
  203   ///  * [kPrimaryButton], the button this callback responds to.
  204   ///  * [LongPressMoveUpdateDetails], which is passed as an argument to this
  205   ///    callback.
  206   GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate;
  207 
  208   /// Called when the pointer stops contacting the screen after a long-press
  209   /// by a primary button.
  210   ///
  211   /// See also:
  212   ///
  213   ///  * [kPrimaryButton], the button this callback responds to.
  214   ///  * [onLongPressEnd], which has the same timing but has data for the up
  215   ///    gesture location.
  216   GestureLongPressUpCallback? onLongPressUp;
  217 
  218   /// Called when the pointer stops contacting the screen after a long-press
  219   /// by a primary button.
  220   ///
  221   /// See also:
  222   ///
  223   ///  * [kPrimaryButton], the button this callback responds to.
  224   ///  * [onLongPressUp], which has the same timing, but without details.
  225   ///  * [LongPressEndDetails], which is passed as an argument to this
  226   ///    callback.
  227   GestureLongPressEndCallback? onLongPressEnd;
  228 
  229   /// Called when a long press gesture by a secondary button has been
  230   /// recognized.
  231   ///
  232   /// See also:
  233   ///
  234   ///  * [kSecondaryButton], the button this callback responds to.
  235   ///  * [onSecondaryLongPressStart], which has the same timing but has data for
  236   ///    the press location.
  237   GestureLongPressCallback? onSecondaryLongPress;
  238 
  239   /// Called when a long press gesture by a secondary button has been recognized.
  240   ///
  241   /// See also:
  242   ///
  243   ///  * [kSecondaryButton], the button this callback responds to.
  244   ///  * [onSecondaryLongPress], which has the same timing but without details.
  245   ///  * [LongPressStartDetails], which is passed as an argument to this
  246   ///    callback.
  247   GestureLongPressStartCallback? onSecondaryLongPressStart;
  248 
  249   /// Called when moving after the long press by a secondary button is
  250   /// recognized.
  251   ///
  252   /// See also:
  253   ///
  254   ///  * [kSecondaryButton], the button this callback responds to.
  255   ///  * [LongPressMoveUpdateDetails], which is passed as an argument to this
  256   ///    callback.
  257   GestureLongPressMoveUpdateCallback? onSecondaryLongPressMoveUpdate;
  258 
  259   /// Called when the pointer stops contacting the screen after a long-press by
  260   /// a secondary button.
  261   ///
  262   /// See also:
  263   ///
  264   ///  * [kSecondaryButton], the button this callback responds to.
  265   ///  * [onSecondaryLongPressEnd], which has the same timing but has data for
  266   ///    the up gesture location.
  267   GestureLongPressUpCallback? onSecondaryLongPressUp;
  268 
  269   /// Called when the pointer stops contacting the screen after a long-press by
  270   /// a secondary button.
  271   ///
  272   /// See also:
  273   ///
  274   ///  * [kSecondaryButton], the button this callback responds to.
  275   ///  * [onSecondaryLongPressUp], which has the same timing, but without
  276   ///    details.
  277   ///  * [LongPressEndDetails], which is passed as an argument to this callback.
  278   GestureLongPressEndCallback? onSecondaryLongPressEnd;
  279 
  280   /// Called when a long press gesture by a tertiary button has been
  281   /// recognized.
  282   ///
  283   /// See also:
  284   ///
  285   ///  * [kTertiaryButton], the button this callback responds to.
  286   ///  * [onTertiaryLongPressStart], which has the same timing but has data for
  287   ///    the press location.
  288   GestureLongPressCallback? onTertiaryLongPress;
  289 
  290   /// Called when a long press gesture by a tertiary button has been recognized.
  291   ///
  292   /// See also:
  293   ///
  294   ///  * [kTertiaryButton], the button this callback responds to.
  295   ///  * [onTertiaryLongPress], which has the same timing but without details.
  296   ///  * [LongPressStartDetails], which is passed as an argument to this
  297   ///    callback.
  298   GestureLongPressStartCallback? onTertiaryLongPressStart;
  299 
  300   /// Called when moving after the long press by a tertiary button is
  301   /// recognized.
  302   ///
  303   /// See also:
  304   ///
  305   ///  * [kTertiaryButton], the button this callback responds to.
  306   ///  * [LongPressMoveUpdateDetails], which is passed as an argument to this
  307   ///    callback.
  308   GestureLongPressMoveUpdateCallback? onTertiaryLongPressMoveUpdate;
  309 
  310   /// Called when the pointer stops contacting the screen after a long-press by
  311   /// a tertiary button.
  312   ///
  313   /// See also:
  314   ///
  315   ///  * [kTertiaryButton], the button this callback responds to.
  316   ///  * [onTertiaryLongPressEnd], which has the same timing but has data for
  317   ///    the up gesture location.
  318   GestureLongPressUpCallback? onTertiaryLongPressUp;
  319 
  320   /// Called when the pointer stops contacting the screen after a long-press by
  321   /// a tertiary button.
  322   ///
  323   /// See also:
  324   ///
  325   ///  * [kTertiaryButton], the button this callback responds to.
  326   ///  * [onTertiaryLongPressUp], which has the same timing, but without
  327   ///    details.
  328   ///  * [LongPressEndDetails], which is passed as an argument to this callback.
  329   GestureLongPressEndCallback? onTertiaryLongPressEnd;
  330 
  331   VelocityTracker? _velocityTracker;
  332 
  333   @override
  334   bool isPointerAllowed(PointerDownEvent event) {
  335     switch (event.buttons) {
  336       case kPrimaryButton:
  337         if (onLongPressStart == null &&
  338             onLongPress == null &&
  339             onLongPressMoveUpdate == null &&
  340             onLongPressEnd == null &&
  341             onLongPressUp == null)
  342           return false;
  343         break;
  344       case kSecondaryButton:
  345         if (onSecondaryLongPressStart == null &&
  346             onSecondaryLongPress == null &&
  347             onSecondaryLongPressMoveUpdate == null &&
  348             onSecondaryLongPressEnd == null &&
  349             onSecondaryLongPressUp == null)
  350           return false;
  351         break;
  352       case kTertiaryButton:
  353         if (onTertiaryLongPressStart == null &&
  354             onTertiaryLongPress == null &&
  355             onTertiaryLongPressMoveUpdate == null &&
  356             onTertiaryLongPressEnd == null &&
  357             onTertiaryLongPressUp == null)
  358           return false;
  359         break;
  360       default:
  361         return false;
  362     }
  363     return super.isPointerAllowed(event);
  364   }
  365 
  366   @override
  367   void didExceedDeadline() {
  368     // Exceeding the deadline puts the gesture in the accepted state.
  369     resolve(GestureDisposition.accepted);
  370     _longPressAccepted = true;
  371     super.acceptGesture(primaryPointer!);
  372     _checkLongPressStart();
  373   }
  374 
  375   @override
  376   void handlePrimaryPointer(PointerEvent event) {
  377     if (!event.synthesized) {
  378       if (event is PointerDownEvent) {
  379         _velocityTracker = VelocityTracker.withKind(event.kind);
  380         _velocityTracker!.addPosition(event.timeStamp, event.localPosition);
  381       }
  382       if (event is PointerMoveEvent) {
  383         assert(_velocityTracker != null);
  384         _velocityTracker!.addPosition(event.timeStamp, event.localPosition);
  385       }
  386     }
  387 
  388     if (event is PointerUpEvent) {
  389       if (_longPressAccepted == true) {
  390         _checkLongPressEnd(event);
  391       } else {
  392         // Pointer is lifted before timeout.
  393         resolve(GestureDisposition.rejected);
  394       }
  395       _reset();
  396     } else if (event is PointerCancelEvent) {
  397       _reset();
  398     } else if (event is PointerDownEvent) {
  399       // The first touch.
  400       _longPressOrigin = OffsetPair.fromEventPosition(event);
  401       _initialButtons = event.buttons;
  402     } else if (event is PointerMoveEvent) {
  403       if (event.buttons != _initialButtons) {
  404         resolve(GestureDisposition.rejected);
  405         stopTrackingPointer(primaryPointer!);
  406       } else if (_longPressAccepted) {
  407         _checkLongPressMoveUpdate(event);
  408       }
  409     }
  410   }
  411 
  412   void _checkLongPressStart() {
  413     switch (_initialButtons) {
  414       case kPrimaryButton:
  415         if (onLongPressStart != null) {
  416           final LongPressStartDetails details = LongPressStartDetails(
  417             globalPosition: _longPressOrigin!.global,
  418             localPosition: _longPressOrigin!.local,
  419           );
  420           invokeCallback<void>('onLongPressStart', () => onLongPressStart!(details));
  421         }
  422         if (onLongPress != null) {
  423           invokeCallback<void>('onLongPress', onLongPress!);
  424         }
  425         break;
  426       case kSecondaryButton:
  427         if (onSecondaryLongPressStart != null) {
  428           final LongPressStartDetails details = LongPressStartDetails(
  429             globalPosition: _longPressOrigin!.global,
  430             localPosition: _longPressOrigin!.local,
  431           );
  432           invokeCallback<void>(
  433               'onSecondaryLongPressStart', () => onSecondaryLongPressStart!(details));
  434         }
  435         if (onSecondaryLongPress != null) {
  436           invokeCallback<void>('onSecondaryLongPress', onSecondaryLongPress!);
  437         }
  438         break;
  439       case kTertiaryButton:
  440         if (onTertiaryLongPressStart != null) {
  441           final LongPressStartDetails details = LongPressStartDetails(
  442             globalPosition: _longPressOrigin!.global,
  443             localPosition: _longPressOrigin!.local,
  444           );
  445           invokeCallback<void>(
  446               'onTertiaryLongPressStart', () => onTertiaryLongPressStart!(details));
  447         }
  448         if (onTertiaryLongPress != null) {
  449           invokeCallback<void>('onTertiaryLongPress', onTertiaryLongPress!);
  450         }
  451         break;
  452       default:
  453         assert(false, 'Unhandled button $_initialButtons');
  454     }
  455   }
  456 
  457   void _checkLongPressMoveUpdate(PointerEvent event) {
  458     final LongPressMoveUpdateDetails details = LongPressMoveUpdateDetails(
  459       globalPosition: event.position,
  460       localPosition: event.localPosition,
  461       offsetFromOrigin: event.position - _longPressOrigin!.global,
  462       localOffsetFromOrigin: event.localPosition - _longPressOrigin!.local,
  463     );
  464     switch (_initialButtons) {
  465       case kPrimaryButton:
  466         if (onLongPressMoveUpdate != null) {
  467           invokeCallback<void>('onLongPressMoveUpdate',
  468             () => onLongPressMoveUpdate!(details));
  469         }
  470         break;
  471       case kSecondaryButton:
  472         if (onSecondaryLongPressMoveUpdate != null) {
  473           invokeCallback<void>('onSecondaryLongPressMoveUpdate',
  474             () => onSecondaryLongPressMoveUpdate!(details));
  475         }
  476         break;
  477       case kTertiaryButton:
  478         if (onTertiaryLongPressMoveUpdate != null) {
  479           invokeCallback<void>('onTertiaryLongPressMoveUpdate',
  480                   () => onTertiaryLongPressMoveUpdate!(details));
  481         }
  482         break;
  483       default:
  484         assert(false, 'Unhandled button $_initialButtons');
  485     }
  486   }
  487 
  488   void _checkLongPressEnd(PointerEvent event) {
  489     final VelocityEstimate? estimate = _velocityTracker!.getVelocityEstimate();
  490     final Velocity velocity = estimate == null
  491         ? Velocity.zero
  492         : Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
  493     final LongPressEndDetails details = LongPressEndDetails(
  494       globalPosition: event.position,
  495       localPosition: event.localPosition,
  496       velocity: velocity,
  497     );
  498 
  499     _velocityTracker = null;
  500     switch (_initialButtons) {
  501       case kPrimaryButton:
  502         if (onLongPressEnd != null) {
  503           invokeCallback<void>('onLongPressEnd', () => onLongPressEnd!(details));
  504         }
  505         if (onLongPressUp != null) {
  506           invokeCallback<void>('onLongPressUp', onLongPressUp!);
  507         }
  508         break;
  509       case kSecondaryButton:
  510         if (onSecondaryLongPressEnd != null) {
  511           invokeCallback<void>('onSecondaryLongPressEnd', () => onSecondaryLongPressEnd!(details));
  512         }
  513         if (onSecondaryLongPressUp != null) {
  514           invokeCallback<void>('onSecondaryLongPressUp', onSecondaryLongPressUp!);
  515         }
  516         break;
  517       case kTertiaryButton:
  518         if (onTertiaryLongPressEnd != null) {
  519           invokeCallback<void>('onTertiaryLongPressEnd', () => onTertiaryLongPressEnd!(details));
  520         }
  521         if (onTertiaryLongPressUp != null) {
  522           invokeCallback<void>('onTertiaryLongPressUp', onTertiaryLongPressUp!);
  523         }
  524         break;
  525       default:
  526         assert(false, 'Unhandled button $_initialButtons');
  527     }
  528   }
  529 
  530   void _reset() {
  531     _longPressAccepted = false;
  532     _longPressOrigin = null;
  533     _initialButtons = null;
  534     _velocityTracker = null;
  535   }
  536 
  537   @override
  538   void resolve(GestureDisposition disposition) {
  539     if (_longPressAccepted && disposition == GestureDisposition.rejected) {
  540       // This can happen if the gesture has been canceled. For example when
  541       // the buttons have changed.
  542       _reset();
  543     }
  544     super.resolve(disposition);
  545   }
  546 
  547   @override
  548   void acceptGesture(int pointer) {
  549     // Winning the arena isn't important here since it may happen from a sweep.
  550     // Explicitly exceeding the deadline puts the gesture in accepted state.
  551   }
  552 
  553   @override
  554   String get debugDescription => 'long press';
  555 }