Move TapAndDragGestureRecognizer code under gestures (#119508)
diff --git a/packages/flutter/lib/gestures.dart b/packages/flutter/lib/gestures.dart index be2f30d..8a12689 100644 --- a/packages/flutter/lib/gestures.dart +++ b/packages/flutter/lib/gestures.dart
@@ -30,5 +30,6 @@ export 'src/gestures/resampler.dart'; export 'src/gestures/scale.dart'; export 'src/gestures/tap.dart'; +export 'src/gestures/tap_and_drag.dart'; export 'src/gestures/team.dart'; export 'src/gestures/velocity_tracker.dart';
diff --git a/packages/flutter/lib/src/widgets/tap_and_drag_gestures.dart b/packages/flutter/lib/src/gestures/tap_and_drag.dart similarity index 94% rename from packages/flutter/lib/src/widgets/tap_and_drag_gestures.dart rename to packages/flutter/lib/src/gestures/tap_and_drag.dart index 51a8435..907173c 100644 --- a/packages/flutter/lib/src/widgets/tap_and_drag_gestures.dart +++ b/packages/flutter/lib/src/gestures/tap_and_drag.dart
@@ -5,11 +5,13 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/services.dart' show HardwareKeyboard, LogicalKeyboardKey; -import 'framework.dart'; -import 'gesture_detector.dart'; +import 'constants.dart'; +import 'events.dart'; +import 'monodrag.dart'; +import 'recognizer.dart'; +import 'scale.dart'; +import 'tap.dart'; // Examples can assume: // void setState(VoidCallback fn) { } @@ -75,14 +77,13 @@ class TapDragDownDetails with Diagnosticable { /// Creates details for a [GestureTapDragDownCallback]. /// - /// The [globalPosition], [localPosition], [consecutiveTapCount], and - /// [keysPressedOnDown] arguments must be provided and must not be null. + /// The [globalPosition], [localPosition], and [consecutiveTapCount] + /// arguments must be provided and must not be null. TapDragDownDetails({ required this.globalPosition, required this.localPosition, this.kind, required this.consecutiveTapCount, - required this.keysPressedOnDown, }); /// The global position at which the pointer contacted the screen. @@ -98,9 +99,6 @@ /// the number in the series this tap is. final int consecutiveTapCount; - /// The keys that were pressed when the most recent [PointerDownEvent] occurred. - final Set<LogicalKeyboardKey> keysPressedOnDown; - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -108,7 +106,6 @@ properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition)); properties.add(DiagnosticsProperty<PointerDeviceKind?>('kind', kind)); properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount)); - properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keysPressedOnDown', keysPressedOnDown)); } } @@ -134,14 +131,13 @@ class TapDragUpDetails with Diagnosticable { /// Creates details for a [GestureTapDragUpCallback]. /// - /// The [kind], [globalPosition], [localPosition], [consecutiveTapCount], and - /// [keysPressedOnDown] arguments must be provided and must not be null. + /// The [kind], [globalPosition], [localPosition], and [consecutiveTapCount] + /// arguments must be provided and must not be null. TapDragUpDetails({ required this.kind, required this.globalPosition, required this.localPosition, required this.consecutiveTapCount, - required this.keysPressedOnDown, }); /// The global position at which the pointer contacted the screen. @@ -157,9 +153,6 @@ /// the number in the series this tap is. final int consecutiveTapCount; - /// The keys that were pressed when the most recent [PointerDownEvent] occurred. - final Set<LogicalKeyboardKey> keysPressedOnDown; - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -167,7 +160,6 @@ properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition)); properties.add(DiagnosticsProperty<PointerDeviceKind?>('kind', kind)); properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount)); - properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keysPressedOnDown', keysPressedOnDown)); } } @@ -193,15 +185,14 @@ class TapDragStartDetails with Diagnosticable { /// Creates details for a [GestureTapDragStartCallback]. /// - /// The [globalPosition], [localPosition], [consecutiveTapCount], and - /// [keysPressedOnDown] arguments must be provided and must not be null. + /// The [globalPosition], [localPosition], and [consecutiveTapCount] + /// arguments must be provided and must not be null. TapDragStartDetails({ this.sourceTimeStamp, required this.globalPosition, required this.localPosition, this.kind, required this.consecutiveTapCount, - required this.keysPressedOnDown, }); /// Recorded timestamp of the source pointer event that triggered the drag @@ -229,9 +220,6 @@ /// the number in the series this tap is. final int consecutiveTapCount; - /// The keys that were pressed when the most recent [PointerDownEvent] occurred. - final Set<LogicalKeyboardKey> keysPressedOnDown; - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -240,7 +228,6 @@ properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition)); properties.add(DiagnosticsProperty<PointerDeviceKind?>('kind', kind)); properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount)); - properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keysPressedOnDown', keysPressedOnDown)); } } @@ -272,8 +259,7 @@ /// coordinates of [delta] and the other coordinate must be zero. /// /// The [globalPosition], [localPosition], [offsetFromOrigin], [localOffsetFromOrigin], - /// [consecutiveTapCount], and [keysPressedOnDown] arguments must be provided and must - /// not be null. + /// and [consecutiveTapCount] arguments must be provided and must not be null. TapDragUpdateDetails({ this.sourceTimeStamp, this.delta = Offset.zero, @@ -284,7 +270,6 @@ required this.offsetFromOrigin, required this.localOffsetFromOrigin, required this.consecutiveTapCount, - required this.keysPressedOnDown, }) : assert( primaryDelta == null || (primaryDelta == delta.dx && delta.dy == 0.0) @@ -357,9 +342,6 @@ /// the number in the series this tap is. final int consecutiveTapCount; - /// The keys that were pressed when the most recent [PointerDownEvent] occurred. - final Set<LogicalKeyboardKey> keysPressedOnDown; - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -372,7 +354,6 @@ properties.add(DiagnosticsProperty<Offset>('offsetFromOrigin', offsetFromOrigin)); properties.add(DiagnosticsProperty<Offset>('localOffsetFromOrigin', localOffsetFromOrigin)); properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount)); - properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keysPressedOnDown', keysPressedOnDown)); } } @@ -400,13 +381,11 @@ /// /// The [velocity] argument must not be null. /// - /// The [consecutiveTapCount], and [keysPressedOnDown] arguments must - /// be provided and must not be null. + /// The [consecutiveTapCount] argument must be provided and must not be null. TapDragEndDetails({ this.velocity = Velocity.zero, this.primaryVelocity, required this.consecutiveTapCount, - required this.keysPressedOnDown, }) : assert( primaryVelocity == null || primaryVelocity == velocity.pixelsPerSecond.dx @@ -434,16 +413,12 @@ /// the number in the series this tap is. final int consecutiveTapCount; - /// The keys that were pressed when the most recent [PointerDownEvent] occurred. - final Set<LogicalKeyboardKey> keysPressedOnDown; - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<Velocity>('velocity', velocity)); properties.add(DiagnosticsProperty<double?>('primaryVelocity', primaryVelocity)); properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount)); - properties.add(DiagnosticsProperty<Set<LogicalKeyboardKey>>('keysPressedOnDown', keysPressedOnDown)); } } @@ -506,15 +481,6 @@ // this value will be set to `0`, and a new series will begin. int get consecutiveTapCount => _consecutiveTapCount; - // The set of [LogicalKeyboardKey]s pressed when the most recent [PointerDownEvent] - // was tracked in [addAllowedPointer]. - // - // This value defaults to an empty set. - // - // When the timer between two taps elapses, the recognizer loses the arena, the gesture is cancelled - // or the recognizer is disposed of then this value is reset. - Set<LogicalKeyboardKey> get keysPressedOnDown => _keysPressedOnDown ?? <LogicalKeyboardKey>{}; - // The upper limit for the [consecutiveTapCount]. When this limit is reached // all tap related state is reset and a new tap series is tracked. // @@ -525,7 +491,6 @@ PointerDownEvent? _down; PointerUpEvent? _up; int _consecutiveTapCount = 0; - Set<LogicalKeyboardKey>? _keysPressedOnDown; OffsetPair? _originPosition; int? _previousButtons; @@ -534,6 +499,21 @@ Timer? _consecutiveTapTimer; Offset? _lastTapOffset; + /// {@template flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackStart} + /// Callback used to indicate that a tap tracking has started upon + /// a [PointerDownEvent]. + /// {@endtemplate} + VoidCallback? onTapTrackStart; + + /// {@template flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackReset} + /// Callback used to indicate that a tap tracking has been reset which + /// happens on the next [PointerDownEvent] after the timer between two taps + /// elapses, the recognizer loses the arena, the gesture is cancelled or + /// the recognizer is disposed of. + /// {@endtemplate} + + VoidCallback? onTapTrackReset; + // When tracking a tap, the [consecutiveTapCount] is incremented if the given tap // falls under the tolerance specifications and reset to 1 if not. @override @@ -595,10 +575,10 @@ void _trackTap(PointerDownEvent event) { _down = event; - _keysPressedOnDown = HardwareKeyboard.instance.logicalKeysPressed; _previousButtons = event.buttons; _lastTapOffset = event.position; _originPosition = OffsetPair(local: event.localPosition, global: event.position); + onTapTrackStart?.call(); } bool _hasSameButton(int buttons) { @@ -653,9 +633,9 @@ _originPosition = null; _lastTapOffset = null; _consecutiveTapCount = 0; - _keysPressedOnDown = null; _down = null; _up = null; + onTapTrackReset?.call(); } } @@ -1204,7 +1184,6 @@ localPosition: event.localPosition, kind: getKindForPointer(event.pointer), consecutiveTapCount: consecutiveTapCount, - keysPressedOnDown: keysPressedOnDown, ); if (onTapDown != null) { @@ -1224,7 +1203,6 @@ globalPosition: event.position, localPosition: event.localPosition, consecutiveTapCount: consecutiveTapCount, - keysPressedOnDown: keysPressedOnDown, ); if (onTapUp != null) { @@ -1245,7 +1223,6 @@ localPosition: _initialPosition.local, kind: getKindForPointer(event.pointer), consecutiveTapCount: consecutiveTapCount, - keysPressedOnDown: keysPressedOnDown, ); invokeCallback<void>('onDragStart', () => onDragStart!(details)); @@ -1267,7 +1244,6 @@ offsetFromOrigin: globalPosition - _initialPosition.global, localOffsetFromOrigin: localPosition - _initialPosition.local, consecutiveTapCount: consecutiveTapCount, - keysPressedOnDown: keysPressedOnDown, ); if (dragUpdateThrottleFrequency != null) { @@ -1293,7 +1269,6 @@ TapDragEndDetails( primaryVelocity: 0.0, consecutiveTapCount: consecutiveTapCount, - keysPressedOnDown: keysPressedOnDown, ); if (onDragEnd != null) {
diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index a447e6e..8d45921 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -24,7 +24,6 @@ import 'magnifier.dart'; import 'overlay.dart'; import 'scrollable.dart'; -import 'tap_and_drag_gestures.dart'; import 'tap_region.dart'; import 'ticker_provider.dart'; import 'transitions.dart'; @@ -1993,11 +1992,6 @@ && targetSelection.end >= textPosition.offset; } - /// Returns true if shift left or right is contained in the given set. - static bool _containsShift(Set<LogicalKeyboardKey> keysPressed) { - return keysPressed.any(<LogicalKeyboardKey>{ LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftRight }.contains); - } - // Expand the selection to the given global position. // // Either base or extent will be moved to the last tapped position, whichever @@ -2074,6 +2068,10 @@ @protected RenderEditable get renderEditable => editableText.renderEditable; + /// Whether the Shift key was pressed when the most recent [PointerDownEvent] + /// was tracked by the [BaseTapAndDragGestureRecognizer]. + bool _isShiftPressed = false; + /// The viewport offset pixels of any [Scrollable] containing the /// [RenderEditable] at the last drag start. double _dragStartScrollOffset = 0.0; @@ -2113,6 +2111,30 @@ // focused, the cursor moves to the long press position. bool _longPressStartedWithoutFocus = false; + /// Handler for [TextSelectionGestureDetector.onTapTrackStart]. + /// + /// See also: + /// + /// * [TextSelectionGestureDetector.onTapTrackStart], which triggers this + /// callback. + @protected + void onTapTrackStart() { + _isShiftPressed = HardwareKeyboard.instance.logicalKeysPressed + .intersection(<LogicalKeyboardKey>{LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftRight}) + .isNotEmpty; + } + + /// Handler for [TextSelectionGestureDetector.onTapTrackReset]. + /// + /// See also: + /// + /// * [TextSelectionGestureDetector.onTapTrackReset], which triggers this + /// callback. + @protected + void onTapTrackReset() { + _isShiftPressed = false; + } + /// Handler for [TextSelectionGestureDetector.onTapDown]. /// /// By default, it forwards the tap to [RenderEditable.handleTapDown] and sets @@ -2145,11 +2167,9 @@ || kind == PointerDeviceKind.touch || kind == PointerDeviceKind.stylus; - // Handle shift + click selection if needed. - final bool isShiftPressed = _containsShift(details.keysPressedOnDown); // It is impossible to extend the selection when the shift key is pressed, if the // renderEditable.selection is invalid. - final bool isShiftPressedValid = isShiftPressed && renderEditable.selection?.baseOffset != null; + final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null; switch (defaultTargetPlatform) { case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -2246,11 +2266,9 @@ @protected void onSingleTapUp(TapDragUpDetails details) { if (delegate.selectionEnabled) { - // Handle shift + click selection if needed. - final bool isShiftPressed = _containsShift(details.keysPressedOnDown); // It is impossible to extend the selection when the shift key is pressed, if the // renderEditable.selection is invalid. - final bool isShiftPressedValid = isShiftPressed && renderEditable.selection?.baseOffset != null; + final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null; switch (defaultTargetPlatform) { case TargetPlatform.linux: case TargetPlatform.macOS: @@ -2641,9 +2659,7 @@ return; } - final bool isShiftPressed = _containsShift(details.keysPressedOnDown); - - if (isShiftPressed && renderEditable.selection != null && renderEditable.selection!.isValid) { + if (_isShiftPressed && renderEditable.selection != null && renderEditable.selection!.isValid) { switch (defaultTargetPlatform) { case TargetPlatform.iOS: case TargetPlatform.macOS: @@ -2730,9 +2746,7 @@ return; } - final bool isShiftPressed = _containsShift(details.keysPressedOnDown); - - if (!isShiftPressed) { + if (!_isShiftPressed) { // Adjust the drag start offset for possible viewport offset changes. final Offset editableOffset = renderEditable.maxLines == 1 ? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0) @@ -2931,14 +2945,13 @@ /// callback. @protected void onDragSelectionEnd(TapDragEndDetails details) { - final bool isShiftPressed = _containsShift(details.keysPressedOnDown); _dragBeganOnPreviousSelection = null; if (_shouldShowSelectionToolbar && _TextSelectionGestureDetectorState._getEffectiveConsecutiveTapCount(details.consecutiveTapCount) == 2) { editableText.showToolbar(); } - if (isShiftPressed) { + if (_isShiftPressed) { _dragStartSelection = null; } @@ -2956,6 +2969,8 @@ }) { return TextSelectionGestureDetector( key: key, + onTapTrackStart: onTapTrackStart, + onTapTrackReset: onTapTrackReset, onTapDown: onTapDown, onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null, onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null, @@ -2996,6 +3011,8 @@ /// The [child] parameter must not be null. const TextSelectionGestureDetector({ super.key, + this.onTapTrackStart, + this.onTapTrackReset, this.onTapDown, this.onForcePressStart, this.onForcePressEnd, @@ -3015,6 +3032,12 @@ required this.child, }); + /// {@macro flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackStart} + final VoidCallback? onTapTrackStart; + + /// {@macro flutter.gestures.selectionrecognizers.BaseTapAndDragGestureRecognizer.onTapTrackReset} + final VoidCallback? onTapTrackReset; + /// Called for every tap down including every tap down that's part of a /// double click or a long press, except touches that include enough movement /// to not qualify as taps (e.g. pans and flings). @@ -3125,6 +3148,14 @@ } } + void _handleTapTrackStart() { + widget.onTapTrackStart?.call(); + } + + void _handleTapTrackReset() { + widget.onTapTrackReset?.call(); + } + // The down handler is force-run on success of a single tap and optimistically // run before a long press success. void _handleTapDown(TapDragDownDetails details) { @@ -3231,6 +3262,8 @@ // Text selection should start from the position of the first pointer // down event. ..dragStartBehavior = DragStartBehavior.down + ..onTapTrackStart = _handleTapTrackStart + ..onTapTrackReset = _handleTapTrackReset ..onTapDown = _handleTapDown ..onDragStart = _handleDragStart ..onDragUpdate = _handleDragUpdate @@ -3249,6 +3282,8 @@ // Text selection should start from the position of the first pointer // down event. ..dragStartBehavior = DragStartBehavior.down + ..onTapTrackStart = _handleTapTrackStart + ..onTapTrackReset = _handleTapTrackReset ..onTapDown = _handleTapDown ..onDragStart = _handleDragStart ..onDragUpdate = _handleDragUpdate
diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index 092cd82..539d6aa 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart
@@ -140,7 +140,6 @@ export 'src/widgets/spell_check.dart'; export 'src/widgets/status_transitions.dart'; export 'src/widgets/table.dart'; -export 'src/widgets/tap_and_drag_gestures.dart'; export 'src/widgets/tap_region.dart'; export 'src/widgets/text.dart'; export 'src/widgets/text_editing_intents.dart';
diff --git a/packages/flutter/test/widgets/tap_and_drag_gestures_test.dart b/packages/flutter/test/gestures/tap_and_drag_test.dart similarity index 99% rename from packages/flutter/test/widgets/tap_and_drag_gestures_test.dart rename to packages/flutter/test/gestures/tap_and_drag_test.dart index 33027be..152341b 100644 --- a/packages/flutter/test/widgets/tap_and_drag_gestures_test.dart +++ b/packages/flutter/test/gestures/tap_and_drag_test.dart
@@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import '../gestures/gesture_tester.dart';
diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart index 41fa392..67d49cb 100644 --- a/packages/flutter/test/widgets/text_selection_test.dart +++ b/packages/flutter/test/widgets/text_selection_test.dart
@@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart' show ValueListenable, defaultTargetPlatform; -import 'package:flutter/gestures.dart' show PointerDeviceKind, kSecondaryButton; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart';