mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
Reorganise the drag related gesture files. (#9487)
Move the Drag*Details classes into drag_details.dart. Move the one-gesture drag recognizers into monodrag.dart. Move Drag into drag.dart. Adjust the comments to claim that Drag is used by other things than MultiDragGestureRecognizer. (Right now this is a lie but it will hopefully be true soon.)
This commit is contained in:
parent
31b1964a23
commit
a591628cab
|
@ -12,10 +12,12 @@ export 'src/gestures/binding.dart';
|
|||
export 'src/gestures/constants.dart';
|
||||
export 'src/gestures/converter.dart';
|
||||
export 'src/gestures/drag.dart';
|
||||
export 'src/gestures/drag_details.dart';
|
||||
export 'src/gestures/events.dart';
|
||||
export 'src/gestures/hit_test.dart';
|
||||
export 'src/gestures/long_press.dart';
|
||||
export 'src/gestures/lsq_solver.dart';
|
||||
export 'src/gestures/monodrag.dart';
|
||||
export 'src/gestures/multidrag.dart';
|
||||
export 'src/gestures/multitap.dart';
|
||||
export 'src/gestures/pointer_router.dart';
|
||||
|
|
|
@ -2,470 +2,28 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'drag_details.dart';
|
||||
|
||||
import 'arena.dart';
|
||||
import 'constants.dart';
|
||||
import 'events.dart';
|
||||
import 'recognizer.dart';
|
||||
import 'velocity_tracker.dart';
|
||||
/// Interface for objects that receive updates about drags.
|
||||
///
|
||||
/// This interface is used in various ways. For example,
|
||||
/// [MultiDragGestureRecognizer] uses it to update its clients when it
|
||||
/// recognizes a gesture. Similarly, the scrolling infrastructure in the widgets
|
||||
/// library uses it to notify the [DragScrollActivity] when the user drags the
|
||||
/// scrollable.
|
||||
abstract class Drag {
|
||||
/// The pointer has moved.
|
||||
void update(DragUpdateDetails details) { }
|
||||
|
||||
enum _DragState {
|
||||
ready,
|
||||
possible,
|
||||
accepted,
|
||||
}
|
||||
|
||||
/// Details object for callbacks that use [GestureDragDownCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onDown], which uses [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragDownDetails {
|
||||
/// Creates details for a [GestureDragDownCallback].
|
||||
///
|
||||
/// The [globalPosition] argument must not be null.
|
||||
DragDownDetails({ this.globalPosition: Offset.zero }) {
|
||||
assert(globalPosition != null);
|
||||
}
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
///
|
||||
/// Defaults to the origin if not specified in the constructor.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($globalPosition)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer has contacted the screen and might begin to
|
||||
/// move.
|
||||
///
|
||||
/// The `details` object provides the position of the touch.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onDown].
|
||||
typedef void GestureDragDownCallback(DragDownDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragStartCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onStart], which uses [GestureDragStartCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragStartDetails {
|
||||
/// Creates details for a [GestureDragStartCallback].
|
||||
///
|
||||
/// The [globalPosition] argument must not be null.
|
||||
DragStartDetails({ this.globalPosition: Offset.zero }) {
|
||||
assert(globalPosition != null);
|
||||
}
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
///
|
||||
/// Defaults to the origin if not specified in the constructor.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($globalPosition)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer has contacted the screen and has begun to move.
|
||||
///
|
||||
/// The `details` object provides the position of the touch when it first
|
||||
/// touched the surface.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onStart].
|
||||
typedef void GestureDragStartCallback(DragStartDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragUpdateCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onUpdate], which uses [GestureDragUpdateCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragUpdateDetails {
|
||||
/// Creates details for a [DragUpdateDetails].
|
||||
///
|
||||
/// The [delta] argument must not be null.
|
||||
///
|
||||
/// If [primaryDelta] is non-null, then its value must match one of the
|
||||
/// coordinates of [delta] and the other coordinate must be zero.
|
||||
///
|
||||
/// The [globalPosition] argument must be provided and must not be null.
|
||||
DragUpdateDetails({
|
||||
this.delta: Offset.zero,
|
||||
this.primaryDelta,
|
||||
@required this.globalPosition
|
||||
}) {
|
||||
assert(primaryDelta == null
|
||||
|| (primaryDelta == delta.dx && delta.dy == 0.0)
|
||||
|| (primaryDelta == delta.dy && delta.dx == 0.0));
|
||||
}
|
||||
|
||||
/// The amount the pointer has moved since the previous update.
|
||||
///
|
||||
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
|
||||
/// a horizontal or vertical drag), then this offset contains only the delta
|
||||
/// in that direction (i.e., the coordinate in the other direction is zero).
|
||||
///
|
||||
/// Defaults to zero if not specified in the constructor.
|
||||
final Offset delta;
|
||||
|
||||
/// The amount the pointer has moved along the primary axis since the previous
|
||||
/// update.
|
||||
///
|
||||
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
|
||||
/// a horizontal or vertical drag), then this value contains the component of
|
||||
/// [delta] along the primary axis (e.g., horizontal or vertical,
|
||||
/// respectively). Otherwise, if the [GestureDragUpdateCallback] is for a
|
||||
/// two-dimensional drag (e.g., a pan), then this value is null.
|
||||
///
|
||||
/// Defaults to null if not specified in the constructor.
|
||||
final double primaryDelta;
|
||||
|
||||
/// The pointer's global position when it triggered this update.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($delta)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer that is in contact with the screen and moving
|
||||
/// has moved again.
|
||||
///
|
||||
/// The `details` object provides the position of the touch and the distance it
|
||||
/// has travelled since the last update.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onUpdate].
|
||||
typedef void GestureDragUpdateCallback(DragUpdateDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragEndCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onEnd], which uses [GestureDragEndCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
class DragEndDetails {
|
||||
/// Creates details for a [GestureDragEndCallback].
|
||||
///
|
||||
/// The [velocity] argument must not be null.
|
||||
DragEndDetails({
|
||||
this.velocity: Velocity.zero,
|
||||
this.primaryVelocity,
|
||||
}) {
|
||||
assert(velocity != null);
|
||||
assert(primaryVelocity == null
|
||||
|| primaryVelocity == velocity.pixelsPerSecond.dx
|
||||
|| primaryVelocity == velocity.pixelsPerSecond.dy);
|
||||
}
|
||||
|
||||
/// The velocity the pointer was moving when it stopped contacting the screen.
|
||||
///
|
||||
/// Defaults to zero if not specified in the constructor.
|
||||
final Velocity velocity;
|
||||
|
||||
/// The velocity the pointer was moving along the primary axis when it stopped
|
||||
/// contacting the screen, in logical pixels per second.
|
||||
///
|
||||
/// If the [GestureDragEndCallback] is for a one-dimensional drag (e.g., a
|
||||
/// horizontal or vertical drag), then this value contains the component of
|
||||
/// [velocity] along the primary axis (e.g., horizontal or vertical,
|
||||
/// respectively). Otherwise, if the [GestureDragEndCallback] is for a
|
||||
/// two-dimensional drag (e.g., a pan), then this value is null.
|
||||
///
|
||||
/// Defaults to null if not specified in the constructor.
|
||||
final double primaryVelocity;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($velocity)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer that was previously in contact with the screen
|
||||
/// and moving is no longer in contact with the screen.
|
||||
///
|
||||
/// The velocity at which the pointer was moving when it stopped contacting
|
||||
/// the screen is available in the `details`.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onEnd].
|
||||
typedef void GestureDragEndCallback(DragEndDetails details);
|
||||
|
||||
/// Signature for when the pointer that previously triggered a
|
||||
/// [GestureDragDownCallback] did not complete.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onCancel].
|
||||
typedef void GestureDragCancelCallback();
|
||||
|
||||
/// Recognizes movement.
|
||||
///
|
||||
/// In contrast to [MultiDragGestureRecognizer], [DragGestureRecognizer]
|
||||
/// recognizes a single gesture sequence for all the pointers it watches, which
|
||||
/// means that the recognizer has at most one drag sequence active at any given
|
||||
/// time regardless of how many pointers are in contact with the screen.
|
||||
///
|
||||
/// [DragGestureRecognizer] is not intended to be used directly. Instead,
|
||||
/// consider using one of its subclasses to recognize specific types for drag
|
||||
/// gestures.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [HorizontalDragGestureRecognizer]
|
||||
/// * [VerticalDragGestureRecognizer]
|
||||
/// * [PanGestureRecognizer]
|
||||
abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||
/// A pointer has contacted the screen and might begin to move.
|
||||
///
|
||||
/// The position of the pointer is provided in the callback's `details`
|
||||
/// argument, which is a [DragDownDetails] object.
|
||||
GestureDragDownCallback onDown;
|
||||
|
||||
/// A pointer has contacted the screen and has begun to move.
|
||||
///
|
||||
/// The position of the pointer is provided in the callback's `details`
|
||||
/// argument, which is a [DragStartDetails] object.
|
||||
GestureDragStartCallback onStart;
|
||||
|
||||
/// A pointer that is in contact with the screen and moving has moved again.
|
||||
///
|
||||
/// The distance travelled by the pointer since the last update is provided in
|
||||
/// the callback's `details` argument, which is a [DragUpdateDetails] object.
|
||||
GestureDragUpdateCallback onUpdate;
|
||||
|
||||
/// A pointer that was previously in contact with the screen and moving is no
|
||||
/// longer in contact with the screen and was moving at a specific velocity
|
||||
/// when it stopped contacting the screen.
|
||||
///
|
||||
/// The velocity is provided in the callback's `details` argument, which is a
|
||||
/// [DragEndDetails] object.
|
||||
GestureDragEndCallback onEnd;
|
||||
|
||||
/// The pointer that previously triggered [onDown] did not complete.
|
||||
GestureDragCancelCallback onCancel;
|
||||
|
||||
/// The minimum distance an input pointer drag must have moved to
|
||||
/// to be considered a fling gesture.
|
||||
///
|
||||
/// This value is typically compared with the distance traveled along the
|
||||
/// scrolling axis. If null then [kTouchSlop] is used.
|
||||
double minFlingDistance;
|
||||
|
||||
/// The minimum velocity for an input pointer drag to be considered fling.
|
||||
///
|
||||
/// This value is typically compared with the magnitude of fling gesture's
|
||||
/// velocity along the scrolling axis. If null then [kMinFlingVelocity]
|
||||
/// is used.
|
||||
double minFlingVelocity;
|
||||
|
||||
/// Fling velocity magnitudes will be clamped to this value.
|
||||
///
|
||||
/// If null then [kMaxFlingVelocity] is used.
|
||||
double maxFlingVelocity;
|
||||
|
||||
_DragState _state = _DragState.ready;
|
||||
Offset _initialPosition;
|
||||
Offset _pendingDragOffset;
|
||||
|
||||
bool _isFlingGesture(VelocityEstimate estimate);
|
||||
Offset _getDeltaForDetails(Offset delta);
|
||||
double _getPrimaryValueFromOffset(Offset value);
|
||||
bool get _hasSufficientPendingDragDeltaToAccept;
|
||||
|
||||
final Map<int, VelocityTracker> _velocityTrackers = <int, VelocityTracker>{};
|
||||
|
||||
@override
|
||||
void addPointer(PointerEvent event) {
|
||||
startTrackingPointer(event.pointer);
|
||||
_velocityTrackers[event.pointer] = new VelocityTracker();
|
||||
if (_state == _DragState.ready) {
|
||||
_state = _DragState.possible;
|
||||
_initialPosition = event.position;
|
||||
_pendingDragOffset = Offset.zero;
|
||||
if (onDown != null)
|
||||
invokeCallback<Null>('onDown', () => onDown(new DragDownDetails(globalPosition: _initialPosition))); // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event) {
|
||||
assert(_state != _DragState.ready);
|
||||
if (event is PointerMoveEvent) {
|
||||
final VelocityTracker tracker = _velocityTrackers[event.pointer];
|
||||
assert(tracker != null);
|
||||
tracker.addPosition(event.timeStamp, event.position);
|
||||
final Offset delta = event.delta;
|
||||
if (_state == _DragState.accepted) {
|
||||
if (onUpdate != null) {
|
||||
invokeCallback<Null>('onUpdate', () => onUpdate(new DragUpdateDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
delta: _getDeltaForDetails(delta),
|
||||
primaryDelta: _getPrimaryValueFromOffset(delta),
|
||||
globalPosition: event.position,
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
_pendingDragOffset += delta;
|
||||
if (_hasSufficientPendingDragDeltaToAccept)
|
||||
resolve(GestureDisposition.accepted);
|
||||
}
|
||||
}
|
||||
stopTrackingIfPointerNoLongerDown(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void acceptGesture(int pointer) {
|
||||
if (_state != _DragState.accepted) {
|
||||
_state = _DragState.accepted;
|
||||
final Offset delta = _pendingDragOffset;
|
||||
_pendingDragOffset = Offset.zero;
|
||||
if (onStart != null) {
|
||||
invokeCallback<Null>('onStart', () => onStart(new DragStartDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
globalPosition: _initialPosition,
|
||||
)));
|
||||
}
|
||||
if (delta != Offset.zero && onUpdate != null) {
|
||||
invokeCallback<Null>('onUpdate', () => onUpdate(new DragUpdateDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
delta: _getDeltaForDetails(delta),
|
||||
primaryDelta: _getPrimaryValueFromOffset(delta),
|
||||
globalPosition: _initialPosition,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void rejectGesture(int pointer) {
|
||||
stopTrackingPointer(pointer);
|
||||
}
|
||||
|
||||
@override
|
||||
void didStopTrackingLastPointer(int pointer) {
|
||||
if (_state == _DragState.possible) {
|
||||
resolve(GestureDisposition.rejected);
|
||||
_state = _DragState.ready;
|
||||
if (onCancel != null)
|
||||
invokeCallback<Null>('onCancel', onCancel); // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
return;
|
||||
}
|
||||
final bool wasAccepted = (_state == _DragState.accepted);
|
||||
_state = _DragState.ready;
|
||||
if (wasAccepted && onEnd != null) {
|
||||
final VelocityTracker tracker = _velocityTrackers[pointer];
|
||||
assert(tracker != null);
|
||||
|
||||
final VelocityEstimate estimate = tracker.getVelocityEstimate();
|
||||
if (estimate != null && _isFlingGesture(estimate)) {
|
||||
final Velocity velocity = new Velocity(pixelsPerSecond: estimate.pixelsPerSecond)
|
||||
.clampMagnitude(minFlingVelocity ?? kMinFlingVelocity, maxFlingVelocity ?? kMaxFlingVelocity);
|
||||
invokeCallback<Null>('onEnd', () => onEnd(new DragEndDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
velocity: velocity,
|
||||
primaryVelocity: _getPrimaryValueFromOffset(velocity.pixelsPerSecond),
|
||||
)));
|
||||
} else {
|
||||
invokeCallback<Null>('onEnd', () => onEnd(new DragEndDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
velocity: Velocity.zero,
|
||||
primaryVelocity: 0.0,
|
||||
)));
|
||||
}
|
||||
}
|
||||
_velocityTrackers.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_velocityTrackers.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes movement in the vertical direction.
|
||||
///
|
||||
/// Used for vertical scrolling.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [VerticalMultiDragGestureRecognizer]
|
||||
class VerticalDragGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.dy.abs() > minVelocity && estimate.offset.dy.abs() > minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragOffset.dy.abs() > kTouchSlop;
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => new Offset(0.0, delta.dy);
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => value.dy;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'vertical drag';
|
||||
}
|
||||
|
||||
/// Recognizes movement in the horizontal direction.
|
||||
///
|
||||
/// Used for horizontal scrolling.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [HorizontalMultiDragGestureRecognizer]
|
||||
class HorizontalDragGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.dx.abs() > minVelocity && estimate.offset.dx.abs() > minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragOffset.dx.abs() > kTouchSlop;
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => new Offset(delta.dx, 0.0);
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => value.dx;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'horizontal drag';
|
||||
}
|
||||
|
||||
/// Recognizes movement both horizontally and vertically.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ImmediateMultiDragGestureRecognizer]
|
||||
/// * [DelayedMultiDragGestureRecognizer]
|
||||
class PanGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.distanceSquared > minVelocity * minVelocity &&
|
||||
estimate.offset.distanceSquared > minDistance * minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept {
|
||||
return _pendingDragOffset.distance > kPanSlop;
|
||||
}
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => delta;
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => null;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'pan';
|
||||
/// The pointer is no longer in contact with the screen.
|
||||
///
|
||||
/// The velocity at which the pointer was moving when it stopped contacting
|
||||
/// the screen is available in the `details`.
|
||||
void end(DragEndDetails details) { }
|
||||
|
||||
/// The input from the pointer is no longer directed towards this receiver.
|
||||
///
|
||||
/// For example, the user might have been interrupted by a system-modal dialog
|
||||
/// in the middle of the drag.
|
||||
void cancel() { }
|
||||
}
|
||||
|
|
182
packages/flutter/lib/src/gestures/drag_details.dart
Normal file
182
packages/flutter/lib/src/gestures/drag_details.dart
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' show Offset;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'velocity_tracker.dart';
|
||||
|
||||
/// Details object for callbacks that use [GestureDragDownCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onDown], which uses [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragDownDetails {
|
||||
/// Creates details for a [GestureDragDownCallback].
|
||||
///
|
||||
/// The [globalPosition] argument must not be null.
|
||||
DragDownDetails({ this.globalPosition: Offset.zero }) {
|
||||
assert(globalPosition != null);
|
||||
}
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
///
|
||||
/// Defaults to the origin if not specified in the constructor.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($globalPosition)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer has contacted the screen and might begin to
|
||||
/// move.
|
||||
///
|
||||
/// The `details` object provides the position of the touch.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onDown].
|
||||
typedef void GestureDragDownCallback(DragDownDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragStartCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onStart], which uses [GestureDragStartCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragStartDetails {
|
||||
/// Creates details for a [GestureDragStartCallback].
|
||||
///
|
||||
/// The [globalPosition] argument must not be null.
|
||||
DragStartDetails({ this.globalPosition: Offset.zero }) {
|
||||
assert(globalPosition != null);
|
||||
}
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
///
|
||||
/// Defaults to the origin if not specified in the constructor.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($globalPosition)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer has contacted the screen and has begun to move.
|
||||
///
|
||||
/// The `details` object provides the position of the touch when it first
|
||||
/// touched the surface.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onStart].
|
||||
typedef void GestureDragStartCallback(DragStartDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragUpdateCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onUpdate], which uses [GestureDragUpdateCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragEndDetails], the details for [GestureDragEndCallback].
|
||||
class DragUpdateDetails {
|
||||
/// Creates details for a [DragUpdateDetails].
|
||||
///
|
||||
/// The [delta] argument must not be null.
|
||||
///
|
||||
/// If [primaryDelta] is non-null, then its value must match one of the
|
||||
/// coordinates of [delta] and the other coordinate must be zero.
|
||||
///
|
||||
/// The [globalPosition] argument must be provided and must not be null.
|
||||
DragUpdateDetails({
|
||||
this.delta: Offset.zero,
|
||||
this.primaryDelta,
|
||||
@required this.globalPosition
|
||||
}) {
|
||||
assert(primaryDelta == null
|
||||
|| (primaryDelta == delta.dx && delta.dy == 0.0)
|
||||
|| (primaryDelta == delta.dy && delta.dx == 0.0));
|
||||
}
|
||||
|
||||
/// The amount the pointer has moved since the previous update.
|
||||
///
|
||||
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
|
||||
/// a horizontal or vertical drag), then this offset contains only the delta
|
||||
/// in that direction (i.e., the coordinate in the other direction is zero).
|
||||
///
|
||||
/// Defaults to zero if not specified in the constructor.
|
||||
final Offset delta;
|
||||
|
||||
/// The amount the pointer has moved along the primary axis since the previous
|
||||
/// update.
|
||||
///
|
||||
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
|
||||
/// a horizontal or vertical drag), then this value contains the component of
|
||||
/// [delta] along the primary axis (e.g., horizontal or vertical,
|
||||
/// respectively). Otherwise, if the [GestureDragUpdateCallback] is for a
|
||||
/// two-dimensional drag (e.g., a pan), then this value is null.
|
||||
///
|
||||
/// Defaults to null if not specified in the constructor.
|
||||
final double primaryDelta;
|
||||
|
||||
/// The pointer's global position when it triggered this update.
|
||||
final Offset globalPosition;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($delta)';
|
||||
}
|
||||
|
||||
/// Signature for when a pointer that is in contact with the screen and moving
|
||||
/// has moved again.
|
||||
///
|
||||
/// The `details` object provides the position of the touch and the distance it
|
||||
/// has travelled since the last update.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onUpdate].
|
||||
typedef void GestureDragUpdateCallback(DragUpdateDetails details);
|
||||
|
||||
/// Details object for callbacks that use [GestureDragEndCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DragGestureRecognizer.onEnd], which uses [GestureDragEndCallback].
|
||||
/// * [DragDownDetails], the details for [GestureDragDownCallback].
|
||||
/// * [DragStartDetails], the details for [GestureDragStartCallback].
|
||||
/// * [DragUpdateDetails], the details for [GestureDragUpdateCallback].
|
||||
class DragEndDetails {
|
||||
/// Creates details for a [GestureDragEndCallback].
|
||||
///
|
||||
/// The [velocity] argument must not be null.
|
||||
DragEndDetails({
|
||||
this.velocity: Velocity.zero,
|
||||
this.primaryVelocity,
|
||||
}) {
|
||||
assert(velocity != null);
|
||||
assert(primaryVelocity == null
|
||||
|| primaryVelocity == velocity.pixelsPerSecond.dx
|
||||
|| primaryVelocity == velocity.pixelsPerSecond.dy);
|
||||
}
|
||||
|
||||
/// The velocity the pointer was moving when it stopped contacting the screen.
|
||||
///
|
||||
/// Defaults to zero if not specified in the constructor.
|
||||
final Velocity velocity;
|
||||
|
||||
/// The velocity the pointer was moving along the primary axis when it stopped
|
||||
/// contacting the screen, in logical pixels per second.
|
||||
///
|
||||
/// If the [GestureDragEndCallback] is for a one-dimensional drag (e.g., a
|
||||
/// horizontal or vertical drag), then this value contains the component of
|
||||
/// [velocity] along the primary axis (e.g., horizontal or vertical,
|
||||
/// respectively). Otherwise, if the [GestureDragEndCallback] is for a
|
||||
/// two-dimensional drag (e.g., a pan), then this value is null.
|
||||
///
|
||||
/// Defaults to null if not specified in the constructor.
|
||||
final double primaryVelocity;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($velocity)';
|
||||
}
|
297
packages/flutter/lib/src/gestures/monodrag.dart
Normal file
297
packages/flutter/lib/src/gestures/monodrag.dart
Normal file
|
@ -0,0 +1,297 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'arena.dart';
|
||||
import 'constants.dart';
|
||||
import 'drag_details.dart';
|
||||
import 'events.dart';
|
||||
import 'recognizer.dart';
|
||||
import 'velocity_tracker.dart';
|
||||
|
||||
enum _DragState {
|
||||
ready,
|
||||
possible,
|
||||
accepted,
|
||||
}
|
||||
|
||||
/// Signature for when a pointer that was previously in contact with the screen
|
||||
/// and moving is no longer in contact with the screen.
|
||||
///
|
||||
/// The velocity at which the pointer was moving when it stopped contacting
|
||||
/// the screen is available in the `details`.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onEnd].
|
||||
typedef void GestureDragEndCallback(DragEndDetails details);
|
||||
|
||||
/// Signature for when the pointer that previously triggered a
|
||||
/// [GestureDragDownCallback] did not complete.
|
||||
///
|
||||
/// See [DragGestureRecognizer.onCancel].
|
||||
typedef void GestureDragCancelCallback();
|
||||
|
||||
/// Recognizes movement.
|
||||
///
|
||||
/// In contrast to [MultiDragGestureRecognizer], [DragGestureRecognizer]
|
||||
/// recognizes a single gesture sequence for all the pointers it watches, which
|
||||
/// means that the recognizer has at most one drag sequence active at any given
|
||||
/// time regardless of how many pointers are in contact with the screen.
|
||||
///
|
||||
/// [DragGestureRecognizer] is not intended to be used directly. Instead,
|
||||
/// consider using one of its subclasses to recognize specific types for drag
|
||||
/// gestures.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [HorizontalDragGestureRecognizer]
|
||||
/// * [VerticalDragGestureRecognizer]
|
||||
/// * [PanGestureRecognizer]
|
||||
abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
|
||||
/// A pointer has contacted the screen and might begin to move.
|
||||
///
|
||||
/// The position of the pointer is provided in the callback's `details`
|
||||
/// argument, which is a [DragDownDetails] object.
|
||||
GestureDragDownCallback onDown;
|
||||
|
||||
/// A pointer has contacted the screen and has begun to move.
|
||||
///
|
||||
/// The position of the pointer is provided in the callback's `details`
|
||||
/// argument, which is a [DragStartDetails] object.
|
||||
GestureDragStartCallback onStart;
|
||||
|
||||
/// A pointer that is in contact with the screen and moving has moved again.
|
||||
///
|
||||
/// The distance travelled by the pointer since the last update is provided in
|
||||
/// the callback's `details` argument, which is a [DragUpdateDetails] object.
|
||||
GestureDragUpdateCallback onUpdate;
|
||||
|
||||
/// A pointer that was previously in contact with the screen and moving is no
|
||||
/// longer in contact with the screen and was moving at a specific velocity
|
||||
/// when it stopped contacting the screen.
|
||||
///
|
||||
/// The velocity is provided in the callback's `details` argument, which is a
|
||||
/// [DragEndDetails] object.
|
||||
GestureDragEndCallback onEnd;
|
||||
|
||||
/// The pointer that previously triggered [onDown] did not complete.
|
||||
GestureDragCancelCallback onCancel;
|
||||
|
||||
/// The minimum distance an input pointer drag must have moved to
|
||||
/// to be considered a fling gesture.
|
||||
///
|
||||
/// This value is typically compared with the distance traveled along the
|
||||
/// scrolling axis. If null then [kTouchSlop] is used.
|
||||
double minFlingDistance;
|
||||
|
||||
/// The minimum velocity for an input pointer drag to be considered fling.
|
||||
///
|
||||
/// This value is typically compared with the magnitude of fling gesture's
|
||||
/// velocity along the scrolling axis. If null then [kMinFlingVelocity]
|
||||
/// is used.
|
||||
double minFlingVelocity;
|
||||
|
||||
/// Fling velocity magnitudes will be clamped to this value.
|
||||
///
|
||||
/// If null then [kMaxFlingVelocity] is used.
|
||||
double maxFlingVelocity;
|
||||
|
||||
_DragState _state = _DragState.ready;
|
||||
Offset _initialPosition;
|
||||
Offset _pendingDragOffset;
|
||||
|
||||
bool _isFlingGesture(VelocityEstimate estimate);
|
||||
Offset _getDeltaForDetails(Offset delta);
|
||||
double _getPrimaryValueFromOffset(Offset value);
|
||||
bool get _hasSufficientPendingDragDeltaToAccept;
|
||||
|
||||
final Map<int, VelocityTracker> _velocityTrackers = <int, VelocityTracker>{};
|
||||
|
||||
@override
|
||||
void addPointer(PointerEvent event) {
|
||||
startTrackingPointer(event.pointer);
|
||||
_velocityTrackers[event.pointer] = new VelocityTracker();
|
||||
if (_state == _DragState.ready) {
|
||||
_state = _DragState.possible;
|
||||
_initialPosition = event.position;
|
||||
_pendingDragOffset = Offset.zero;
|
||||
if (onDown != null)
|
||||
invokeCallback<Null>('onDown', () => onDown(new DragDownDetails(globalPosition: _initialPosition))); // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event) {
|
||||
assert(_state != _DragState.ready);
|
||||
if (event is PointerMoveEvent) {
|
||||
final VelocityTracker tracker = _velocityTrackers[event.pointer];
|
||||
assert(tracker != null);
|
||||
tracker.addPosition(event.timeStamp, event.position);
|
||||
final Offset delta = event.delta;
|
||||
if (_state == _DragState.accepted) {
|
||||
if (onUpdate != null) {
|
||||
invokeCallback<Null>('onUpdate', () => onUpdate(new DragUpdateDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
delta: _getDeltaForDetails(delta),
|
||||
primaryDelta: _getPrimaryValueFromOffset(delta),
|
||||
globalPosition: event.position,
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
_pendingDragOffset += delta;
|
||||
if (_hasSufficientPendingDragDeltaToAccept)
|
||||
resolve(GestureDisposition.accepted);
|
||||
}
|
||||
}
|
||||
stopTrackingIfPointerNoLongerDown(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void acceptGesture(int pointer) {
|
||||
if (_state != _DragState.accepted) {
|
||||
_state = _DragState.accepted;
|
||||
final Offset delta = _pendingDragOffset;
|
||||
_pendingDragOffset = Offset.zero;
|
||||
if (onStart != null) {
|
||||
invokeCallback<Null>('onStart', () => onStart(new DragStartDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
globalPosition: _initialPosition,
|
||||
)));
|
||||
}
|
||||
if (delta != Offset.zero && onUpdate != null) {
|
||||
invokeCallback<Null>('onUpdate', () => onUpdate(new DragUpdateDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
delta: _getDeltaForDetails(delta),
|
||||
primaryDelta: _getPrimaryValueFromOffset(delta),
|
||||
globalPosition: _initialPosition,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void rejectGesture(int pointer) {
|
||||
stopTrackingPointer(pointer);
|
||||
}
|
||||
|
||||
@override
|
||||
void didStopTrackingLastPointer(int pointer) {
|
||||
if (_state == _DragState.possible) {
|
||||
resolve(GestureDisposition.rejected);
|
||||
_state = _DragState.ready;
|
||||
if (onCancel != null)
|
||||
invokeCallback<Null>('onCancel', onCancel); // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
return;
|
||||
}
|
||||
final bool wasAccepted = (_state == _DragState.accepted);
|
||||
_state = _DragState.ready;
|
||||
if (wasAccepted && onEnd != null) {
|
||||
final VelocityTracker tracker = _velocityTrackers[pointer];
|
||||
assert(tracker != null);
|
||||
|
||||
final VelocityEstimate estimate = tracker.getVelocityEstimate();
|
||||
if (estimate != null && _isFlingGesture(estimate)) {
|
||||
final Velocity velocity = new Velocity(pixelsPerSecond: estimate.pixelsPerSecond)
|
||||
.clampMagnitude(minFlingVelocity ?? kMinFlingVelocity, maxFlingVelocity ?? kMaxFlingVelocity);
|
||||
invokeCallback<Null>('onEnd', () => onEnd(new DragEndDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
velocity: velocity,
|
||||
primaryVelocity: _getPrimaryValueFromOffset(velocity.pixelsPerSecond),
|
||||
)));
|
||||
} else {
|
||||
invokeCallback<Null>('onEnd', () => onEnd(new DragEndDetails( // ignore: STRONG_MODE_INVALID_CAST_FUNCTION_EXPR, https://github.com/dart-lang/sdk/issues/27504
|
||||
velocity: Velocity.zero,
|
||||
primaryVelocity: 0.0,
|
||||
)));
|
||||
}
|
||||
}
|
||||
_velocityTrackers.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_velocityTrackers.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes movement in the vertical direction.
|
||||
///
|
||||
/// Used for vertical scrolling.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [VerticalMultiDragGestureRecognizer]
|
||||
class VerticalDragGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.dy.abs() > minVelocity && estimate.offset.dy.abs() > minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragOffset.dy.abs() > kTouchSlop;
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => new Offset(0.0, delta.dy);
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => value.dy;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'vertical drag';
|
||||
}
|
||||
|
||||
/// Recognizes movement in the horizontal direction.
|
||||
///
|
||||
/// Used for horizontal scrolling.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [HorizontalMultiDragGestureRecognizer]
|
||||
class HorizontalDragGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.dx.abs() > minVelocity && estimate.offset.dx.abs() > minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragOffset.dx.abs() > kTouchSlop;
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => new Offset(delta.dx, 0.0);
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => value.dx;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'horizontal drag';
|
||||
}
|
||||
|
||||
/// Recognizes movement both horizontally and vertically.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ImmediateMultiDragGestureRecognizer]
|
||||
/// * [DelayedMultiDragGestureRecognizer]
|
||||
class PanGestureRecognizer extends DragGestureRecognizer {
|
||||
@override
|
||||
bool _isFlingGesture(VelocityEstimate estimate) {
|
||||
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
|
||||
final double minDistance = minFlingDistance ?? kTouchSlop;
|
||||
return estimate.pixelsPerSecond.distanceSquared > minVelocity * minVelocity &&
|
||||
estimate.offset.distanceSquared > minDistance * minDistance;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get _hasSufficientPendingDragDeltaToAccept {
|
||||
return _pendingDragOffset.distance > kPanSlop;
|
||||
}
|
||||
|
||||
@override
|
||||
Offset _getDeltaForDetails(Offset delta) => delta;
|
||||
|
||||
@override
|
||||
double _getPrimaryValueFromOffset(Offset value) => null;
|
||||
|
||||
@override
|
||||
String toStringShort() => 'pan';
|
||||
}
|
|
@ -11,6 +11,7 @@ import 'arena.dart';
|
|||
import 'binding.dart';
|
||||
import 'constants.dart';
|
||||
import 'drag.dart';
|
||||
import 'drag_details.dart';
|
||||
import 'events.dart';
|
||||
import 'recognizer.dart';
|
||||
import 'velocity_tracker.dart';
|
||||
|
@ -18,24 +19,6 @@ import 'velocity_tracker.dart';
|
|||
/// Signature for when [MultiDragGestureRecognizer] recognizes the start of a drag gesture.
|
||||
typedef Drag GestureMultiDragStartCallback(Offset position);
|
||||
|
||||
/// Interface for receiving updates about drags from a [MultiDragGestureRecognizer].
|
||||
abstract class Drag {
|
||||
/// The pointer has moved.
|
||||
void update(DragUpdateDetails details) { }
|
||||
|
||||
/// The pointer is no longer in contact with the screen.
|
||||
///
|
||||
/// The velocity at which the pointer was moving when it stopped contacting
|
||||
/// the screen is available in the `details`.
|
||||
void end(DragEndDetails details) { }
|
||||
|
||||
/// The input from the pointer is no longer directed towards this receiver.
|
||||
///
|
||||
/// For example, the user might have been interrupted by a system-modal dialog
|
||||
/// in the middle of the drag.
|
||||
void cancel() { }
|
||||
}
|
||||
|
||||
/// Per-pointer state for a [MultiDragGestureRecognizer].
|
||||
///
|
||||
/// A [MultiDragGestureRecognizer] tracks each pointer separately. The state for
|
||||
|
|
Loading…
Reference in a new issue