diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart index f759b0cd3e..4e1f5d9a75 100644 --- a/packages/flutter/lib/src/cupertino/text_field.dart +++ b/packages/flutter/lib/src/cupertino/text_field.dart @@ -478,25 +478,8 @@ class _CupertinoTextFieldState extends State with AutomaticK _requestKeyboard(); } - void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) { - _renderEditable.selectPositionAt( - from: details.globalPosition, - cause: SelectionChangedCause.longPress, - ); - } - - void _handleSingleLongTapDragUpdate(GestureLongPressDragUpdateDetails details) { - _renderEditable.selectPositionAt( - from: details.globalPosition, - cause: SelectionChangedCause.longPress, - ); - } - - void _handleSingleLongTapUp(GestureLongPressDragUpDetails details) { - _renderEditable.selectPositionAt( - from: details.globalPosition, - cause: SelectionChangedCause.longPress - ); + void _handleSingleLongTapDown() { + _renderEditable.selectPosition(cause: SelectionChangedCause.longPress); _editableTextKey.currentState.showToolbar(); } @@ -505,12 +488,6 @@ class _CupertinoTextFieldState extends State with AutomaticK _editableTextKey.currentState.showToolbar(); } - void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { - if (cause == SelectionChangedCause.longPress) { - _editableTextKey.currentState?.bringIntoView(selection.base); - } - } - @override bool get wantKeepAlive => _controller?.text?.isNotEmpty == true; @@ -664,7 +641,6 @@ class _CupertinoTextFieldState extends State with AutomaticK selectionColor: _kSelectionHighlightColor, selectionControls: cupertinoTextSelectionControls, onChanged: widget.onChanged, - onSelectionChanged: _handleSelectionChanged, onEditingComplete: widget.onEditingComplete, onSubmitted: widget.onSubmitted, inputFormatters: formatters, @@ -705,9 +681,7 @@ class _CupertinoTextFieldState extends State with AutomaticK onForcePressStart: _handleForcePressStarted, onForcePressEnd: _handleForcePressEnded, onSingleTapUp: _handleSingleTapUp, - onSingleLongTapStart: _handleSingleLongTapStart, - onSingleLongTapDragUpdate: _handleSingleLongTapDragUpdate, - onSingleLongTapUp: _handleSingleLongTapUp, + onSingleLongTapDown: _handleSingleLongTapDown, onDoubleTapDown: _handleDoubleTapDown, behavior: HitTestBehavior.translucent, child: _addTextDependentAttachments(paddedEditable, textStyle), diff --git a/packages/flutter/lib/src/gestures/long_press.dart b/packages/flutter/lib/src/gestures/long_press.dart index 7fa6921054..f9f723989a 100644 --- a/packages/flutter/lib/src/gestures/long_press.dart +++ b/packages/flutter/lib/src/gestures/long_press.dart @@ -11,117 +11,9 @@ import 'recognizer.dart'; /// same location for a long period of time. typedef GestureLongPressCallback = void Function(); -/// Signature for when a pointer stops contacting the screen after a long press -/// gesture was detected. +/// Signature for when a pointer stops contacting the screen after a long press gesture was detected. typedef GestureLongPressUpCallback = void Function(); -/// Signature from a [LongPressDragGestureRecognizer] when a pointer has remained -/// in contact with the screen at the same location for a long period of time. -typedef GestureLongPressDragStartCallback = void Function(GestureLongPressDragStartDetails details); - -/// Signature from a [LongPressDragGestureRecognizer] when a pointer is moving -/// after being held in contact at the same location for a long period of time. -typedef GestureLongPressDragUpdateCallback = void Function(GestureLongPressDragUpdateDetails details); - -/// Signature from a [LongPressDragGestureRecognizer] after a pointer stops -/// contacting the screen. -/// -/// The contact stop position may be different from the contact start position. -typedef GestureLongPressDragUpCallback = void Function(GestureLongPressDragUpDetails details); - -/// Details for callbacks that use [GestureLongPressDragStartCallback]. -/// -/// See also: -/// -/// * [LongPressDragGestureRecognizer.onLongPressStart], which uses [GestureLongPressDragStartCallback]. -/// * [GestureLongPressDragUpdateDetails], the details for [GestureLongPressDragUpdateCallback] -/// * [GestureLongPressDragUpDetails], the details for [GestureLongPressDragUpCallback]. -class GestureLongPressDragStartDetails { - /// Creates the details for a [GestureLongPressDragStartCallback]. - /// - /// The [globalPosition] argument must not be null. - const GestureLongPressDragStartDetails({ this.sourceTimeStamp, this.globalPosition = Offset.zero }) - : assert(globalPosition != null); - - /// Recorded timestamp of the source pointer event that triggered the press - /// event. - /// - /// Could be null if triggered by proxied events such as accessibility. - /// - /// See also: - /// - /// * [PointerEvent.synthesized] for details on synthesized pointer events. - final Duration sourceTimeStamp; - - /// The global position at which the pointer contacted the screen. - final Offset globalPosition; -} - -/// Details for callbacks that use [GestureLongPressDragUpdateCallback]. -/// -/// See also: -/// -/// * [LongPressDragGestureRecognizer.onLongPressDragUpdate], which uses [GestureLongPressDragUpdateCallback]. -/// * [GestureLongPressDragUpDetails], the details for [GestureLongPressDragUpCallback] -/// * [GestureLongPressDragStartDetails], the details for [GestureLongPressDragStartCallback]. -class GestureLongPressDragUpdateDetails { - /// Creates the details for a [GestureLongPressDragUpdateCallback]. - /// - /// The [globalPosition] and [offsetFromOrigin] arguments must not be null. - const GestureLongPressDragUpdateDetails({ - this.sourceTimeStamp, - this.globalPosition = Offset.zero, - this.offsetFromOrigin = Offset.zero, - }) : assert(globalPosition != null), - assert(offsetFromOrigin != null); - - /// Recorded timestamp of the source pointer event that triggered the press - /// event. - /// - /// Could be null if triggered by proxied events such as accessibility. - /// - /// See also: - /// - /// * [PointerEvent.synthesized] for details on synthesized pointer events. - final Duration sourceTimeStamp; - - /// 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 when - /// this callback is triggered. - final Offset offsetFromOrigin; -} - -/// Details for callbacks that use [GestureLongPressDragUpCallback]. -/// -/// See also: -/// -/// * [LongPressDragGestureRecognizer.onLongPressUp], which uses [GestureLongPressDragUpCallback]. -/// * [GestureLongPressDragUpdateDetails], the details for [GestureLongPressDragUpdateCallback] -/// * [GestureLongPressDragStartDetails], the details for [GestureLongPressDragStartCallback]. -class GestureLongPressDragUpDetails { - /// Creates the details for a [GestureLongPressDragUpCallback]. - /// - /// The [globalPosition] argument must not be null. - const GestureLongPressDragUpDetails({ this.sourceTimeStamp, this.globalPosition = Offset.zero }) - : assert(globalPosition != null); - - /// Recorded timestamp of the source pointer event that triggered the press - /// event. - /// - /// Could be null if triggered by proxied events such as accessibility. - /// - /// See also: - /// - /// * [PointerEvent.synthesized] for details on synthesized pointer events. - final Duration sourceTimeStamp; - - /// 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. class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer { @@ -136,8 +28,7 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer { /// Called when a long press gesture has been recognized. GestureLongPressCallback onLongPress; - /// Called when the pointer stops contacting the screen after the long-press - /// gesture has been recognized. + /// Called when the pointer stops contacting the screen after the long-press gesture has been recognized. GestureLongPressUpCallback onLongPressUp; @override @@ -167,107 +58,3 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer { @override String get debugDescription => 'long press'; } - -/// Recognizes long presses that can be subsequently dragged around. -/// -/// Similar to a [LongPressGestureRecognizer] where a press has to be held down -/// at the same location for a long period of time. However drag events that -/// occur after the long-press hold threshold has past will not cancel the -/// gesture. The [onLongPressDragUpdate] callback will be called until an up -/// event occurs. -/// -/// See also: -/// -/// * [LongPressGestureRecognizer], which cancels its gesture if a drag event -/// occurs at any point during the long-press. -class LongPressDragGestureRecognizer extends PrimaryPointerGestureRecognizer { - /// Creates a long-press-drag gesture recognizer. - /// - /// Consider assigning the [onLongPressStart], [onLongPressDragUpdate] and - /// the [onLongPressUp] callbacks after creating this object. - LongPressDragGestureRecognizer({ Object debugOwner }) : super( - deadline: kLongPressTimeout, - // Since it's a drag gesture, no travel distance will cause it to get - // rejected after the long-press is accepted. - postAcceptSlopTolerance: null, - debugOwner: debugOwner, - ); - - bool _longPressAccepted = false; - - Offset _longPressOrigin; - - Duration _longPressStartTimestamp; - - /// Called when a long press gesture has been recognized. - GestureLongPressDragStartCallback onLongPressStart; - - /// Called as the primary pointer is dragged after the long press. - GestureLongPressDragUpdateCallback onLongPressDragUpdate; - - /// Called when the pointer stops contacting the screen after the - /// long-press gesture has been recognized. - GestureLongPressDragUpCallback onLongPressUp; - - @override - void didExceedDeadline() { - resolve(GestureDisposition.accepted); - _longPressAccepted = true; - super.acceptGesture(primaryPointer); - if (onLongPressStart != null) { - invokeCallback('onLongPressStart', () { - onLongPressStart(GestureLongPressDragStartDetails( - sourceTimeStamp: _longPressStartTimestamp, - globalPosition: _longPressOrigin, - )); - }); - } - } - - @override - void handlePrimaryPointer(PointerEvent event) { - if (event is PointerUpEvent) { - if (_longPressAccepted == true && onLongPressUp != null) { - _longPressAccepted = false; - invokeCallback('onLongPressUp', () { - onLongPressUp(GestureLongPressDragUpDetails( - sourceTimeStamp: event.timeStamp, - globalPosition: event.position, - )); - }); - } else { - resolve(GestureDisposition.rejected); - } - } else if (event is PointerDownEvent) { - // The first touch. - _longPressAccepted = false; - _longPressStartTimestamp = event.timeStamp; - _longPressOrigin = event.position; - } else if (event is PointerMoveEvent && _longPressAccepted && onLongPressDragUpdate != null) { - invokeCallback('onLongPressDrag', () { - onLongPressDragUpdate(GestureLongPressDragUpdateDetails( - sourceTimeStamp: event.timeStamp, - 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 - void didStopTrackingLastPointer(int pointer) { - _longPressAccepted = false; - _longPressOrigin = null; - _longPressStartTimestamp = null; - super.didStopTrackingLastPointer(pointer); - } - - @override - String get debugDescription => 'long press drag'; -} diff --git a/packages/flutter/lib/src/gestures/recognizer.dart b/packages/flutter/lib/src/gestures/recognizer.dart index 71b4beb99d..d5ddbd619e 100644 --- a/packages/flutter/lib/src/gestures/recognizer.dart +++ b/packages/flutter/lib/src/gestures/recognizer.dart @@ -262,10 +262,10 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer { /// The possible states of a [PrimaryPointerGestureRecognizer]. /// /// The recognizer advances from [ready] to [possible] when starts tracking a -/// primary pointer. The recognizer moves to [accepted] when resolved to win the -/// gesture arena. It may then move to [defunct] from [accepted] or go to -/// [defunct] directly when rejected. Once the recognizer has stopped tracking -/// any remaining pointers, the recognizer returns to [ready]. +/// primary pointer. When the primary pointer is resolve (either accepted or +/// or rejected), the recognizers advances to [defunct]. Once the recognizer +/// has stopped tracking any remaining pointers, the recognizer returns to +/// [ready]. enum GestureRecognizerState { /// The recognizer is ready to start recognizing a gesture. ready, @@ -275,9 +275,6 @@ enum GestureRecognizerState { /// been accepted definitively. possible, - /// The gesture has been definitively accepted by the recognizer. - accepted, - /// Further pointer events cannot cause this recognizer to recognize the /// gesture until the recognizer returns to the [ready] state (typically when /// all the pointers the recognizer is tracking are removed from the screen). @@ -287,43 +284,18 @@ enum GestureRecognizerState { /// A base class for gesture recognizers that track a single primary pointer. /// /// Gestures based on this class will reject the gesture if the primary pointer -/// travels beyond [preAcceptSlopTolerance] pixels from the original contact -/// point before the gesture is accepted or beyond [postAcceptSlopTolerance] -/// from where the pointer was after the gesture was accepted. +/// travels beyond [kTouchSlop] pixels from the original contact point. abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecognizer { /// Initializes the [deadline] field during construction of subclasses. PrimaryPointerGestureRecognizer({ this.deadline, - this.preAcceptSlopTolerance = kTouchSlop, - this.postAcceptSlopTolerance = kTouchSlop, Object debugOwner, - }) : assert( - preAcceptSlopTolerance == null || preAcceptSlopTolerance >= 0, - 'The preAcceptSlopTolerance must be positive or null', - ), - assert( - postAcceptSlopTolerance == null || postAcceptSlopTolerance >= 0, - 'The postAcceptSlopTolerance must be positive or null', - ), - super(debugOwner: debugOwner); + }) : super(debugOwner: debugOwner); /// If non-null, the recognizer will call [didExceedDeadline] after this /// amount of time has elapsed since starting to track the primary pointer. final Duration deadline; - /// The maximum distance in logical pixels the gesture is allowed to drift - /// from the initial touch down position before the gesture is accepted. - /// - /// Drifting past the allowed slop amount causes the gesture to be rejected. - final double preAcceptSlopTolerance; - - /// The maximum distance in logical pixels the gesture is allowed to drift - /// after the gesture has been accepted. - /// - /// Drifting past the allowed slop amount causes the gesture to be rejected, - /// even after being accepted. - final double postAcceptSlopTolerance; - /// The current state of the recognizer. /// /// See [GestureRecognizerState] for a description of the states. @@ -352,17 +324,9 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni @override void handleEvent(PointerEvent event) { assert(state != GestureRecognizerState.ready); - if (event.pointer == primaryPointer) { - final bool isPreAcceptSlopPastTolerance = - state == GestureRecognizerState.possible && - preAcceptSlopTolerance != null && - _getDistance(event) > preAcceptSlopTolerance; - final bool isPostAcceptSlopPastTolerance = - state == GestureRecognizerState.accepted && - postAcceptSlopTolerance != null && - _getDistance(event) > postAcceptSlopTolerance; - - if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) { + if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) { + // TODO(abarth): Maybe factor the slop handling out into a separate class? + if (event is PointerMoveEvent && _getDistance(event) > kTouchSlop) { resolve(GestureDisposition.rejected); stopTrackingPointer(primaryPointer); } else { @@ -384,20 +348,9 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni assert(deadline == null); } - @override - void acceptGesture(int pointer) { - // Ignore state 'ready' here because that would happen if this recognizer - // won by a sweep. - if (pointer == primaryPointer && state == GestureRecognizerState.possible) { - state = GestureRecognizerState.accepted; - } - } - @override void rejectGesture(int pointer) { - if (pointer == primaryPointer - && (state == GestureRecognizerState.possible || - state == GestureRecognizerState.accepted)) { + if (pointer == primaryPointer && state == GestureRecognizerState.possible) { _stopTimer(); state = GestureRecognizerState.defunct; } diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 6f01f6bf18..6619290aef 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -566,15 +566,6 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi _editableTextKey.currentState?.requestKeyboard(); } - void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { - // iOS cursor doesn't move via a selection handle. The scroll happens - // directly from new text selection changes. - if (Theme.of(context).platform == TargetPlatform.iOS - && cause == SelectionChangedCause.longPress) { - _editableTextKey.currentState?.bringIntoView(selection.base); - } - } - InteractiveInkFeature _createInkFeature(TapDownDetails details) { final MaterialInkController inkController = Material.of(context); final ThemeData themeData = Theme.of(context); @@ -648,14 +639,11 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi _cancelCurrentSplash(); } - void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) { + void _handleSingleLongTapDown() { if (widget.selectionEnabled) { switch (Theme.of(context).platform) { case TargetPlatform.iOS: - _renderEditable.selectPositionAt( - from: details.globalPosition, - cause: SelectionChangedCause.longPress, - ); + _renderEditable.selectPosition(cause: SelectionChangedCause.longPress); break; case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -663,35 +651,11 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi Feedback.forLongPress(context); break; } + _editableTextKey.currentState.showToolbar(); } _confirmCurrentSplash(); } - void _handleSingleLongTapDragUpdate(GestureLongPressDragUpdateDetails details) { - if (widget.selectionEnabled) { - switch (Theme.of(context).platform) { - case TargetPlatform.iOS: - _renderEditable.selectPositionAt( - from: details.globalPosition, - cause: SelectionChangedCause.longPress, - ); - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - _renderEditable.selectWordsInRange( - from: details.globalPosition - details.offsetFromOrigin, - to: details.globalPosition, - cause: SelectionChangedCause.longPress, - ); - break; - } - } - } - - void _handleSingleLongTapUp(GestureLongPressDragUpDetails details) { - _editableTextKey.currentState.showToolbar(); - } - void _handleDoubleTapDown(TapDownDetails details) { if (widget.selectionEnabled) { _renderEditable.selectWord(cause: SelectionChangedCause.doubleTap); @@ -810,7 +774,6 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi selectionColor: themeData.textSelectionColor, selectionControls: widget.selectionEnabled ? textSelectionControls : null, onChanged: widget.onChanged, - onSelectionChanged: _handleSelectionChanged, onEditingComplete: widget.onEditingComplete, onSubmitted: widget.onSubmitted, inputFormatters: formatters, @@ -859,9 +822,7 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi onForcePressStart: forcePressEnabled ? _handleForcePressStarted : null, onSingleTapUp: _handleSingleTapUp, onSingleTapCancel: _handleSingleTapCancel, - onSingleLongTapStart: _handleSingleLongTapStart, - onSingleLongTapDragUpdate: _handleSingleLongTapDragUpdate, - onSingleLongTapUp: _handleSingleLongTapUp, + onSingleLongTapDown: _handleSingleLongTapDown, onDoubleTapDown: _handleDoubleTapDown, behavior: HitTestBehavior.translucent, child: child, diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index c7633ab863..08a8f9b1fc 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -1191,7 +1191,7 @@ class RenderEditable extends RenderBox { /// When [ignorePointer] is true, an ancestor widget must respond to tap /// down events by calling this method. void handleTapDown(TapDownDetails details) { - _lastTapDownPosition = details.globalPosition - _paintOffset; + _lastTapDownPosition = details.globalPosition + -_paintOffset; } void _handleTapDown(TapDownDetails details) { assert(!ignorePointer); @@ -1247,27 +1247,12 @@ class RenderEditable extends RenderBox { /// programmatically manipulate its `value` or `selection` directly. /// {@endtemplate} void selectPosition({@required SelectionChangedCause cause}) { - selectPositionAt(from: _lastTapDownPosition, cause: cause); - } - - /// Select text between the global positions [from] and [to]. - void selectPositionAt({@required Offset from, Offset to, @required SelectionChangedCause cause}) { assert(cause != null); _layoutText(constraints.maxWidth); + assert(_lastTapDownPosition != null); if (onSelectionChanged != null) { - final TextPosition fromPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset)); - final TextPosition toPosition = to == null - ? null - : _textPainter.getPositionForOffset(globalToLocal(to - _paintOffset)); - onSelectionChanged( - TextSelection( - baseOffset: fromPosition.offset, - extentOffset: toPosition?.offset ?? fromPosition.offset, - affinity: fromPosition.affinity, - ), - this, - cause, - ); + final TextPosition position = _textPainter.getPositionForOffset(globalToLocal(_lastTapDownPosition)); + onSelectionChanged(TextSelection.fromPosition(position), this, cause); } } @@ -1288,10 +1273,10 @@ class RenderEditable extends RenderBox { assert(cause != null); _layoutText(constraints.maxWidth); if (onSelectionChanged != null) { - final TextPosition firstPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset)); + final TextPosition firstPosition = _textPainter.getPositionForOffset(globalToLocal(from + -_paintOffset)); final TextSelection firstWord = _selectWordAtOffset(firstPosition); final TextSelection lastWord = to == null ? - firstWord : _selectWordAtOffset(_textPainter.getPositionForOffset(globalToLocal(to - _paintOffset))); + firstWord : _selectWordAtOffset(_textPainter.getPositionForOffset(globalToLocal(to + -_paintOffset))); onSelectionChanged( TextSelection( diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 8c064a0e2e..1c50ee1c4b 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -19,10 +19,6 @@ export 'package:flutter/gestures.dart' show GestureTapCallback, GestureTapCancelCallback, GestureLongPressCallback, - GestureLongPressUpCallback, - GestureLongPressDragStartCallback, - GestureLongPressDragUpdateCallback, - GestureLongPressDragUpCallback, GestureDragDownCallback, GestureDragStartCallback, GestureDragUpdateCallback, @@ -35,9 +31,6 @@ export 'package:flutter/gestures.dart' show GestureForcePressPeakCallback, GestureForcePressEndCallback, GestureForcePressUpdateCallback, - GestureLongPressDragStartDetails, - GestureLongPressDragUpdateDetails, - GestureLongPressDragUpDetails, ScaleStartDetails, ScaleUpdateDetails, ScaleEndDetails, @@ -157,10 +150,6 @@ class GestureDetector extends StatelessWidget { /// because a combination of a horizontal and vertical drag is a pan. Simply /// use the pan callbacks instead. /// - /// Long press and long press drag callbacks cannot be used simultaneously - /// since they overlap. A long press cannot be subsequently dragged while a - /// long press drag can be dragged after a long press. - /// /// By default, gesture detectors contribute semantic information to the tree /// that is used by assistive technology. GestureDetector({ @@ -173,9 +162,6 @@ class GestureDetector extends StatelessWidget { this.onDoubleTap, this.onLongPress, this.onLongPressUp, - this.onLongPressDragStart, - this.onLongPressDragUpdate, - this.onLongPressDragUp, this.onVerticalDragDown, this.onVerticalDragStart, this.onVerticalDragUpdate, @@ -208,8 +194,6 @@ class GestureDetector extends StatelessWidget { final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null; final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null; final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null; - final bool haveLongPress = onLongPress != null || onLongPressUp != null; - final bool haveLongPressDrag = onLongPressDragStart != null || onLongPressDragUpdate != null || onLongPressDragUp != null; if (havePan || haveScale) { if (havePan && haveScale) { throw FlutterError( @@ -226,15 +210,6 @@ class GestureDetector extends StatelessWidget { ); } } - if (haveLongPress && haveLongPressDrag) { - throw FlutterError( - 'Incorrect GestureDetector arguments.\n' - 'Having both a long press and a long press drag recognizer is ' - 'redundant as the long press drag is a superset of long press. ' - 'Except long press drag allows for drags after the long press is ' - 'triggered.' - ); - } return true; }()), super(key: key); @@ -283,37 +258,11 @@ class GestureDetector extends StatelessWidget { /// A pointer has remained in contact with the screen at the same location for /// a long period of time. - /// - /// The long press drag callbacks [onLongPressDragStart], [onLongPressDragUpdate] - /// and [onLongPressDragUp] cannot be set while this callback is set. final GestureLongPressCallback onLongPress; /// A pointer that has triggered a long-press has stopped contacting the screen. - /// - /// The long press drag callbacks [onLongPressDragStart], [onLongPressDragUpdate] - /// and [onLongPressDragUp] cannot be set while this callback is set. final GestureLongPressUpCallback onLongPressUp; - /// A pointer has remained in contact with the screen at the same location for - /// a long period of time and can subsequently be dragged. - /// - /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot - /// be set while this callback is set. - final GestureLongPressDragStartCallback onLongPressDragStart; - - /// A pointer has been drag-moved after a long press. - /// - /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot - /// be set while this callback is set. - final GestureLongPressDragUpdateCallback onLongPressDragUpdate; - - /// A pointer that has triggered a long press has stopped contacting the screen - /// regardless of whether the pointer is dragged after the long press. - /// - /// The non-drag long press callbacks [onLongPress] and [onLongPressUp] cannot - /// be set while this callback is set. - final GestureLongPressDragUpCallback onLongPressDragUp; - /// A pointer has contacted the screen and might begin to move vertically. final GestureDragDownCallback onVerticalDragDown; @@ -473,7 +422,7 @@ class GestureDetector extends StatelessWidget { ); } - if (onLongPress != null || onLongPressUp != null) { + if (onLongPress != null || onLongPressUp !=null) { gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => LongPressGestureRecognizer(debugOwner: this), (LongPressGestureRecognizer instance) { @@ -484,18 +433,6 @@ class GestureDetector extends StatelessWidget { ); } - if (onLongPressDragStart != null || onLongPressDragUpdate != null || onLongPressDragUp != null) { - gestures[LongPressDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => LongPressDragGestureRecognizer(debugOwner: this), - (LongPressDragGestureRecognizer instance) { - instance - ..onLongPressStart = onLongPressDragStart - ..onLongPressDragUpdate = onLongPressDragUpdate - ..onLongPressUp = onLongPressDragUp; - }, - ); - } - if (onVerticalDragDown != null || onVerticalDragStart != null || onVerticalDragUpdate != null || diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 02b2b9525e..0351213261 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -617,9 +617,7 @@ class TextSelectionGestureDetector extends StatefulWidget { this.onForcePressEnd, this.onSingleTapUp, this.onSingleTapCancel, - this.onSingleLongTapStart, - this.onSingleLongTapDragUpdate, - this.onSingleLongTapUp, + this.onSingleLongTapDown, this.onDoubleTapDown, this.behavior, @required this.child, @@ -653,13 +651,7 @@ class TextSelectionGestureDetector extends StatefulWidget { /// Called for a single long tap that's sustained for longer than /// [kLongPressTimeout] but not necessarily lifted. Not called for a /// double-tap-hold, which calls [onDoubleTapDown] instead. - final GestureLongPressDragStartCallback onSingleLongTapStart; - - /// Called after [onSingleLongTapStart] when the pointer is dragged. - final GestureLongPressDragUpdateCallback onSingleLongTapDragUpdate; - - /// Called after [onSingleLongTapStart] when the pointer is lifted. - final GestureLongPressDragUpCallback onSingleLongTapUp; + final GestureLongPressCallback onSingleLongTapDown; /// Called after a momentary hold or a short tap that is close in space and /// time (within [kDoubleTapTimeout]) to a previous short tap. @@ -743,21 +735,9 @@ class _TextSelectionGestureDetectorState extends State( - find.byElementPredicate((Element element) => element.renderObject is RenderEditable) - ); - - List lastCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 66), // Last character's position. - ); - - expect(lastCharEndpoint.length, 1); - // Just testing the test and making sure that the last character is off - // the right side of the screen. - expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(1094.73486328125)); - - final Offset textfieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); - - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(300, 5)); - await tester.pump(const Duration(milliseconds: 500)); - - expect( - controller.selection, - const TextSelection.collapsed(offset: 18, affinity: TextAffinity.upstream), - ); - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.moveBy(const Offset(600, 0)); - // To the edge of the screen basically. - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 54, affinity: TextAffinity.upstream), - ); - // Keep moving out. - await gesture.moveBy(const Offset(1, 0)); - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 61, affinity: TextAffinity.upstream), - ); - await gesture.moveBy(const Offset(1, 0)); - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), - ); // We're at the edge now. - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.up(); - await tester.pump(); - - // The selection isn't affected by the gesture lift. - expect( - controller.selection, - const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), - ); - // The toolbar now shows up. - expect(find.byType(CupertinoButton), findsNWidgets(2)); - - lastCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 66), // Last character's position. - ); - - expect(lastCharEndpoint.length, 1); - // The last character is now on screen. - expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(786.73486328125)); - - final List firstCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 0), // First character's position. - ); - expect(firstCharEndpoint.length, 1); - // The first character is now offscreen to the left. - expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-308.20499999821186)); - }); - testWidgets( 'long tap after a double tap select is not affected', (WidgetTester tester) async { diff --git a/packages/flutter/test/gestures/long_press_test.dart b/packages/flutter/test/gestures/long_press_test.dart index c0a6244363..10c6c4eb22 100644 --- a/packages/flutter/test/gestures/long_press_test.dart +++ b/packages/flutter/test/gestures/long_press_test.dart @@ -9,275 +9,153 @@ import 'gesture_tester.dart'; const PointerDownEvent down = PointerDownEvent( pointer: 5, - position: Offset(10, 10), + position: Offset(10.0, 10.0) ); const PointerUpEvent up = PointerUpEvent( pointer: 5, - position: Offset(11, 9), -); - -const PointerMoveEvent move = PointerMoveEvent( - pointer: 5, - position: Offset(100, 200), + position: Offset(11.0, 9.0) ); void main() { setUp(ensureGestureBinding); - group('Long press', () { - LongPressGestureRecognizer longPress; - bool longPressDown; - bool longPressUp; + testGesture('Should recognize long press', (GestureTester tester) { + final LongPressGestureRecognizer longPress = LongPressGestureRecognizer(); - setUp(() { - longPress = LongPressGestureRecognizer(); - longPressDown = false; - longPress.onLongPress = () { - longPressDown = true; - }; - longPressUp = false; - longPress.onLongPressUp = () { - longPressUp = true; - }; - }); + bool longPressRecognized = false; + longPress.onLongPress = () { + longPressRecognized = true; + }; - testGesture('Should recognize long press', (GestureTester tester) { - longPress.addPointer(down); - tester.closeArena(5); - expect(longPressDown, isFalse); - tester.route(down); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 700)); - expect(longPressDown, isTrue); + longPress.addPointer(down); + tester.closeArena(5); + expect(longPressRecognized, isFalse); + tester.route(down); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 300)); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 700)); + expect(longPressRecognized, isTrue); - longPress.dispose(); - }); - - testGesture('Up cancels long press', (GestureTester tester) { - longPress.addPointer(down); - tester.closeArena(5); - expect(longPressDown, isFalse); - tester.route(down); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressDown, isFalse); - tester.route(up); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - expect(longPressDown, isFalse); - - longPress.dispose(); - }); - - testGesture('Moving before accept cancels', (GestureTester tester) { - longPress.addPointer(down); - tester.closeArena(5); - expect(longPressDown, isFalse); - tester.route(down); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressDown, isFalse); - tester.route(move); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - tester.route(up); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressDown, isFalse); - expect(longPressUp, isFalse); - - longPress.dispose(); - }); - - testGesture('Moving after accept cancels', (GestureTester tester) { - longPress.addPointer(down); - tester.closeArena(5); - expect(longPressDown, isFalse); - tester.route(down); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - expect(longPressDown, isTrue); - tester.route(move); - tester.route(up); - tester.async.elapse(const Duration(milliseconds: 300)); - // longPressDown is still true since it already happened but up never - // happens because it's been canceled since. - expect(longPressDown, isTrue); - expect(longPressUp, isFalse); - - longPress.dispose(); - }); - - testGesture('Should recognize both tap down and long press', (GestureTester tester) { - final TapGestureRecognizer tap = TapGestureRecognizer(); - - bool tapDownRecognized = false; - tap.onTapDown = (_) { - tapDownRecognized = true; - }; - - tap.addPointer(down); - longPress.addPointer(down); - tester.closeArena(5); - expect(tapDownRecognized, isFalse); - expect(longPressDown, isFalse); - tester.route(down); - expect(tapDownRecognized, isFalse); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(tapDownRecognized, isTrue); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 700)); - expect(tapDownRecognized, isTrue); - expect(longPressDown, isTrue); - - tap.dispose(); - longPress.dispose(); - }); - - testGesture('Drag start delayed by microtask', (GestureTester tester) { - final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer(); - - bool isDangerousStack = false; - - bool dragStartRecognized = false; - drag.onStart = (DragStartDetails details) { - expect(isDangerousStack, isFalse); - dragStartRecognized = true; - }; - - drag.addPointer(down); - longPress.addPointer(down); - tester.closeArena(5); - expect(dragStartRecognized, isFalse); - expect(longPressDown, isFalse); - tester.route(down); - expect(dragStartRecognized, isFalse); - expect(longPressDown, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(dragStartRecognized, isFalse); - expect(longPressDown, isFalse); - isDangerousStack = true; - longPress.dispose(); - isDangerousStack = false; - expect(dragStartRecognized, isFalse); - expect(longPressDown, isFalse); - tester.async.flushMicrotasks(); - expect(dragStartRecognized, isTrue); - expect(longPressDown, isFalse); - drag.dispose(); - }); - - testGesture('Should recognize long press up', (GestureTester tester) { - bool longPressUpRecognized = false; - longPress.onLongPressUp = () { - longPressUpRecognized = true; - }; - - longPress.addPointer(down); - tester.closeArena(5); - expect(longPressUpRecognized, isFalse); - tester.route(down); // kLongPressTimeout = 500; - expect(longPressUpRecognized, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressUpRecognized, isFalse); - tester.async.elapse(const Duration(milliseconds: 700)); - tester.route(up); - expect(longPressUpRecognized, isTrue); - - longPress.dispose(); - }); + longPress.dispose(); }); - group('long press drag', () { - LongPressDragGestureRecognizer longPressDrag; - bool longPressStart; - bool longPressUp; - Offset longPressDragUpdate; + testGesture('Up cancels long press', (GestureTester tester) { + final LongPressGestureRecognizer longPress = LongPressGestureRecognizer(); - setUp(() { - longPressDrag = LongPressDragGestureRecognizer(); - longPressStart = false; - longPressDrag.onLongPressStart = (GestureLongPressDragStartDetails details) { - longPressStart = true; - }; - longPressUp = false; - longPressDrag.onLongPressUp = (GestureLongPressDragUpDetails details) { - longPressUp = true; - }; - longPressDragUpdate = null; - longPressDrag.onLongPressDragUpdate = (GestureLongPressDragUpdateDetails details) { - longPressDragUpdate = details.globalPosition; - }; - }); + bool longPressRecognized = false; + longPress.onLongPress = () { + longPressRecognized = true; + }; - testGesture('Should recognize long press down', (GestureTester tester) { - longPressDrag.addPointer(down); - tester.closeArena(5); - expect(longPressStart, isFalse); - tester.route(down); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(milliseconds: 700)); - expect(longPressStart, isTrue); + longPress.addPointer(down); + tester.closeArena(5); + expect(longPressRecognized, isFalse); + tester.route(down); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 300)); + expect(longPressRecognized, isFalse); + tester.route(up); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(seconds: 1)); + expect(longPressRecognized, isFalse); - longPressDrag.dispose(); - }); + longPress.dispose(); + }); - testGesture('Short up cancels long press', (GestureTester tester) { - longPressDrag.addPointer(down); - tester.closeArena(5); - expect(longPressStart, isFalse); - tester.route(down); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressStart, isFalse); - tester.route(up); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - expect(longPressStart, isFalse); + testGesture('Should recognize both tap down and long press', (GestureTester tester) { + final LongPressGestureRecognizer longPress = LongPressGestureRecognizer(); + final TapGestureRecognizer tap = TapGestureRecognizer(); - longPressDrag.dispose(); - }); + bool tapDownRecognized = false; + tap.onTapDown = (_) { + tapDownRecognized = true; + }; - testGesture('Moving before accept cancels', (GestureTester tester) { - longPressDrag.addPointer(down); - tester.closeArena(5); - expect(longPressStart, isFalse); - tester.route(down); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressStart, isFalse); - tester.route(move); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - tester.route(up); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressStart, isFalse); - expect(longPressUp, isFalse); + bool longPressRecognized = false; + longPress.onLongPress = () { + longPressRecognized = true; + }; - longPressDrag.dispose(); - }); + tap.addPointer(down); + longPress.addPointer(down); + tester.closeArena(5); + expect(tapDownRecognized, isFalse); + expect(longPressRecognized, isFalse); + tester.route(down); + expect(tapDownRecognized, isFalse); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 300)); + expect(tapDownRecognized, isTrue); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 700)); + expect(tapDownRecognized, isTrue); + expect(longPressRecognized, isTrue); - testGesture('Moving after accept does not cancel', (GestureTester tester) { - longPressDrag.addPointer(down); - tester.closeArena(5); - expect(longPressStart, isFalse); - tester.route(down); - expect(longPressStart, isFalse); - tester.async.elapse(const Duration(seconds: 1)); - expect(longPressStart, isTrue); - tester.route(move); - expect(longPressDragUpdate, const Offset(100, 200)); - tester.route(up); - tester.async.elapse(const Duration(milliseconds: 300)); - expect(longPressStart, isTrue); - expect(longPressUp, isTrue); + tap.dispose(); + longPress.dispose(); + }); - longPressDrag.dispose(); - }); + testGesture('Drag start delayed by microtask', (GestureTester tester) { + final LongPressGestureRecognizer longPress = LongPressGestureRecognizer(); + final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer(); + + bool isDangerousStack = false; + + bool dragStartRecognized = false; + drag.onStart = (DragStartDetails details) { + expect(isDangerousStack, isFalse); + dragStartRecognized = true; + }; + + bool longPressRecognized = false; + longPress.onLongPress = () { + expect(isDangerousStack, isFalse); + longPressRecognized = true; + }; + + drag.addPointer(down); + longPress.addPointer(down); + tester.closeArena(5); + expect(dragStartRecognized, isFalse); + expect(longPressRecognized, isFalse); + tester.route(down); + expect(dragStartRecognized, isFalse); + expect(longPressRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 300)); + expect(dragStartRecognized, isFalse); + expect(longPressRecognized, isFalse); + isDangerousStack = true; + longPress.dispose(); + isDangerousStack = false; + expect(dragStartRecognized, isFalse); + expect(longPressRecognized, isFalse); + tester.async.flushMicrotasks(); + expect(dragStartRecognized, isTrue); + expect(longPressRecognized, isFalse); + drag.dispose(); + }); + + testGesture('Should recognize long press up', (GestureTester tester) { + final LongPressGestureRecognizer longPress = LongPressGestureRecognizer(); + + bool longPressUpRecognized = false; + longPress.onLongPressUp = () { + longPressUpRecognized = true; + }; + + longPress.addPointer(down); + tester.closeArena(5); + expect(longPressUpRecognized, isFalse); + tester.route(down); // kLongPressTimeout = 500; + expect(longPressUpRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 300)); + expect(longPressUpRecognized, isFalse); + tester.async.elapse(const Duration(milliseconds: 700)); + tester.route(up); + expect(longPressUpRecognized, isTrue); + + longPress.dispose(); }); } diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 3bdc359060..c9e23cdb94 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -4143,7 +4143,7 @@ void main() { ); testWidgets( - 'long press tap cannot initiate a double tap (iOS)', + 'long press tap is not a double tap (iOS)', (WidgetTester tester) async { final TextEditingController controller = TextEditingController( text: 'Atwater Peel Sherbrooke Bonaventure', @@ -4181,161 +4181,6 @@ void main() { }, ); - testWidgets( - 'long press drag moves the cursor under the drag and shows toolbar on lift (iOS)', - (WidgetTester tester) async { - final TextEditingController controller = TextEditingController( - text: 'Atwater Peel Sherbrooke Bonaventure', - ); - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(platform: TargetPlatform.iOS), - home: Material( - child: Center( - child: TextField( - controller: controller, - ), - ), - ), - ), - ); - - final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); - - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(50.0, 5.0)); - await tester.pump(const Duration(milliseconds: 500)); - - // Long press on iOS shows collapsed selection cursor. - expect( - controller.selection, - const TextSelection.collapsed(offset: 3, affinity: TextAffinity.downstream), - ); - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.moveBy(const Offset(50, 0)); - await tester.pump(); - - // The selection position is now moved with the drag. - expect( - controller.selection, - const TextSelection.collapsed(offset: 6, affinity: TextAffinity.downstream), - ); - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.moveBy(const Offset(50, 0)); - await tester.pump(); - - // The selection position is now moved with the drag. - expect( - controller.selection, - const TextSelection.collapsed(offset: 9, affinity: TextAffinity.downstream), - ); - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.up(); - await tester.pump(); - - // The selection isn't affected by the gesture lift. - expect( - controller.selection, - const TextSelection.collapsed(offset: 9, affinity: TextAffinity.downstream), - ); - // The toolbar now shows up. - expect(find.byType(CupertinoButton), findsNWidgets(2)); - }, - ); - - testWidgets('long press drag can edge scroll (iOS)', (WidgetTester tester) async { - final TextEditingController controller = TextEditingController( - text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges', - ); - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(platform: TargetPlatform.iOS), - home: Material( - child: Center( - child: TextField( - controller: controller, - maxLines: 1, - ), - ), - ), - ), - ); - - final RenderEditable renderEditable = findRenderEditable(tester); - - List lastCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 66), // Last character's position. - ); - - expect(lastCharEndpoint.length, 1); - // Just testing the test and making sure that the last character is off - // the right side of the screen. - expect(lastCharEndpoint[0].point.dx, 1056); - - final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); - - final TestGesture gesture = - await tester.startGesture(textfieldStart + const Offset(300, 5)); - await tester.pump(const Duration(milliseconds: 500)); - - expect( - controller.selection, - const TextSelection.collapsed(offset: 19, affinity: TextAffinity.upstream), - ); - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.moveBy(const Offset(600, 0)); - // To the edge of the screen basically. - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 56, affinity: TextAffinity.downstream), - ); - // Keep moving out. - await gesture.moveBy(const Offset(1, 0)); - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 62, affinity: TextAffinity.downstream), - ); - await gesture.moveBy(const Offset(1, 0)); - await tester.pump(); - expect( - controller.selection, - const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), - ); // We're at the edge now. - expect(find.byType(CupertinoButton), findsNothing); - - await gesture.up(); - await tester.pump(); - - // The selection isn't affected by the gesture lift. - expect( - controller.selection, - const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream), - ); - // The toolbar now shows up. - expect(find.byType(CupertinoButton), findsNWidgets(2)); - - lastCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 66), // Last character's position. - ); - - expect(lastCharEndpoint.length, 1); - // The last character is now on screen. - expect(lastCharEndpoint[0].point.dx, moreOrLessEquals(798.6666870117188)); - - final List firstCharEndpoint = renderEditable.getEndpointsForSelection( - const TextSelection.collapsed(offset: 0), // First character's position. - ); - expect(firstCharEndpoint.length, 1); - // The first character is now offscreen to the left. - expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-257.33331298828125)); - }); - testWidgets( 'long tap after a double tap select is not affected (iOS)', (WidgetTester tester) async { diff --git a/packages/flutter/test/widgets/gesture_detector_test.dart b/packages/flutter/test/widgets/gesture_detector_test.dart index fc94a913db..922126bf9a 100644 --- a/packages/flutter/test/widgets/gesture_detector_test.dart +++ b/packages/flutter/test/widgets/gesture_detector_test.dart @@ -486,19 +486,4 @@ void main() { expect(horizontalDragStart, 1); expect(forcePressStart, 0); }); - - testWidgets('Cannot set both a long press and a long press drag callback', (WidgetTester tester) async { - try { - GestureDetector( - onLongPress: () {}, - onLongPressDragUpdate: (GestureLongPressDragUpdateDetails details) {}, - child: Container( - color: const Color(0xFF00FF00), - ), - ); - throw 'setting long press and long press drag should throw'; - } on FlutterError catch (_) { - // Should throw. - } - }); } diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart index 937d5d9bce..084c1ba121 100644 --- a/packages/flutter/test/widgets/text_selection_test.dart +++ b/packages/flutter/test/widgets/text_selection_test.dart @@ -9,7 +9,7 @@ void main() { int tapCount; int singleTapUpCount; int singleTapCancelCount; - int singleLongTapStartCount; + int singleLongTapDownCount; int doubleTapDownCount; int forcePressStartCount; int forcePressEndCount; @@ -17,7 +17,7 @@ void main() { void _handleTapDown(TapDownDetails details) { tapCount++; } void _handleSingleTapUp(TapUpDetails details) { singleTapUpCount++; } void _handleSingleTapCancel() { singleTapCancelCount++; } - void _handleSingleLongTapStart(GestureLongPressDragStartDetails details) { singleLongTapStartCount++; } + void _handleSingleLongTapDown() { singleLongTapDownCount++; } void _handleDoubleTapDown(TapDownDetails details) { doubleTapDownCount++; } void _handleForcePressStart(ForcePressDetails details) { forcePressStartCount++; } void _handleForcePressEnd(ForcePressDetails details) { forcePressEndCount++; } @@ -26,7 +26,7 @@ void main() { tapCount = 0; singleTapUpCount = 0; singleTapCancelCount = 0; - singleLongTapStartCount = 0; + singleLongTapDownCount = 0; doubleTapDownCount = 0; forcePressStartCount = 0; forcePressEndCount = 0; @@ -39,7 +39,7 @@ void main() { onTapDown: _handleTapDown, onSingleTapUp: _handleSingleTapUp, onSingleTapCancel: _handleSingleTapCancel, - onSingleLongTapStart: _handleSingleLongTapStart, + onSingleLongTapDown: _handleSingleLongTapDown, onDoubleTapDown: _handleDoubleTapDown, onForcePressStart: _handleForcePressStart, onForcePressEnd: _handleForcePressEnd, @@ -106,7 +106,7 @@ void main() { expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 1); // The double tap down hold supersedes the single tap down. - expect(singleLongTapStartCount, 0); + expect(singleLongTapDownCount, 0); await gesture.up(); // Nothing else happens on up. @@ -114,7 +114,7 @@ void main() { expect(tapCount, 2); expect(singleTapCancelCount, 0); expect(doubleTapDownCount, 1); - expect(singleLongTapStartCount, 0); + expect(singleLongTapDownCount, 0); }); testWidgets('a very quick swipe is just a canceled tap', (WidgetTester tester) async { @@ -127,7 +127,7 @@ void main() { expect(tapCount, 0); expect(singleTapCancelCount, 1); expect(doubleTapDownCount, 0); - expect(singleLongTapStartCount, 0); + expect(singleLongTapDownCount, 0); await gesture.up(); // Nothing else happens on up. @@ -135,7 +135,7 @@ void main() { expect(tapCount, 0); expect(singleTapCancelCount, 1); expect(doubleTapDownCount, 0); - expect(singleLongTapStartCount, 0); + expect(singleLongTapDownCount, 0); }); testWidgets('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async { @@ -148,7 +148,7 @@ void main() { expect(tapCount, 1); expect(singleTapCancelCount, 1); expect(doubleTapDownCount, 0); - expect(singleLongTapStartCount, 0); + expect(singleLongTapDownCount, 0); }); testWidgets('a force press intiates a force press', (WidgetTester tester) async {