Unverified Commit e4e9dde4 authored by Tomasz Gucio's avatar Tomasz Gucio Committed by GitHub

Move TapAndDragGestureRecognizer code under gestures (#119508)

parent 3213588c
......@@ -30,5 +30,6 @@ export 'src/gestures/recognizer.dart';
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';
......@@ -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 @@ typedef GestureTapDragDownCallback = void Function(TapDragDownDetails details);
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 @@ class TapDragDownDetails with Diagnosticable {
/// 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 @@ class TapDragDownDetails with Diagnosticable {
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 @@ typedef GestureTapDragUpCallback = void Function(TapDragUpDetails details);
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 @@ class TapDragUpDetails with Diagnosticable {
/// 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 @@ class TapDragUpDetails with Diagnosticable {
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 @@ typedef GestureTapDragStartCallback = void Function(TapDragStartDetails details)
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 @@ class TapDragStartDetails with Diagnosticable {
/// 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 @@ class TapDragStartDetails with Diagnosticable {
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 @@ class TapDragUpdateDetails with Diagnosticable {
/// 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 @@ class TapDragUpdateDetails with Diagnosticable {
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 @@ class TapDragUpdateDetails with Diagnosticable {
/// 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 @@ class TapDragUpdateDetails with Diagnosticable {
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 @@ class TapDragEndDetails with Diagnosticable {
///
/// 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 @@ class TapDragEndDetails with Diagnosticable {
/// 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 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
// 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 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
PointerDownEvent? _down;
PointerUpEvent? _up;
int _consecutiveTapCount = 0;
Set<LogicalKeyboardKey>? _keysPressedOnDown;
OffsetPair? _originPosition;
int? _previousButtons;
......@@ -534,6 +499,21 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
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 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
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 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
_originPosition = null;
_lastTapOffset = null;
_consecutiveTapCount = 0;
_keysPressedOnDown = null;
_down = null;
_up = null;
onTapTrackReset?.call();
}
}
......@@ -1204,7 +1184,6 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
localPosition: event.localPosition,
kind: getKindForPointer(event.pointer),
consecutiveTapCount: consecutiveTapCount,
keysPressedOnDown: keysPressedOnDown,
);
if (onTapDown != null) {
......@@ -1224,7 +1203,6 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
globalPosition: event.position,
localPosition: event.localPosition,
consecutiveTapCount: consecutiveTapCount,
keysPressedOnDown: keysPressedOnDown,
);
if (onTapUp != null) {
......@@ -1245,7 +1223,6 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
localPosition: _initialPosition.local,
kind: getKindForPointer(event.pointer),
consecutiveTapCount: consecutiveTapCount,
keysPressedOnDown: keysPressedOnDown,
);
invokeCallback<void>('onDragStart', () => onDragStart!(details));
......@@ -1267,7 +1244,6 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
offsetFromOrigin: globalPosition - _initialPosition.global,
localOffsetFromOrigin: localPosition - _initialPosition.local,
consecutiveTapCount: consecutiveTapCount,
keysPressedOnDown: keysPressedOnDown,
);
if (dragUpdateThrottleFrequency != null) {
......@@ -1293,7 +1269,6 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
TapDragEndDetails(
primaryVelocity: 0.0,
consecutiveTapCount: consecutiveTapCount,
keysPressedOnDown: keysPressedOnDown,
);
if (onDragEnd != null) {
......
......@@ -24,7 +24,6 @@ import 'gesture_detector.dart';
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 @@ class TextSelectionGestureDetectorBuilder {
&& 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 @@ class TextSelectionGestureDetectorBuilder {
@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 @@ class TextSelectionGestureDetectorBuilder {
// 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 @@ class TextSelectionGestureDetectorBuilder {
|| 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 @@ class TextSelectionGestureDetectorBuilder {
@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 @@ class TextSelectionGestureDetectorBuilder {
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 @@ class TextSelectionGestureDetectorBuilder {
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 @@ class TextSelectionGestureDetectorBuilder {
/// 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 @@ class TextSelectionGestureDetectorBuilder {
}) {
return TextSelectionGestureDetector(
key: key,
onTapTrackStart: onTapTrackStart,
onTapTrackReset: onTapTrackReset,
onTapDown: onTapDown,
onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null,
onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null,
......@@ -2996,6 +3011,8 @@ class TextSelectionGestureDetector extends StatefulWidget {
/// 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 @@ class TextSelectionGestureDetector extends StatefulWidget {
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 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
}
}
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 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
// 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 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
// 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
......
......@@ -140,7 +140,6 @@ export 'src/widgets/spacer.dart';
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';
......
......@@ -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';
......
......@@ -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';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment