drag.dart 5.02 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:ui' as ui;
6

7 8 9
import 'arena.dart';
import 'recognizer.dart';
import 'constants.dart';
10
import 'events.dart';
11

12
enum DragState {
13 14 15 16 17
  ready,
  possible,
  accepted
}

18
typedef void GestureDragStartCallback();
19
typedef void GestureDragUpdateCallback(double delta);
20
typedef void GestureDragEndCallback(ui.Offset velocity);
21

22
typedef void GesturePanStartCallback();
23 24
typedef void GesturePanUpdateCallback(ui.Offset delta);
typedef void GesturePanEndCallback(ui.Offset velocity);
25

26
typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
27

28
int _eventTime(PointerInputEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds
29

30
bool _isFlingGesture(ui.GestureVelocity velocity) {
31 32 33 34 35 36
  double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y;
  return velocity.isValid &&
    velocitySquared > kMinFlingVelocity * kMinFlingVelocity &&
    velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity;
}

37 38
abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecognizer {
  _DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd })
39 40
    : super(router: router);

41
  GestureDragStartCallback onStart;
42
  _GesturePolymorphicUpdateCallback<T> onUpdate;
43
  GestureDragEndCallback onEnd;
44

45 46
  DragState _state = DragState.ready;
  T _pendingDragDelta;
47

48
  T get _initialPendingDragDelta;
49
  T _getDragDelta(PointerInputEvent event);
50
  bool get _hasSufficientPendingDragDeltaToAccept;
51

52
  final ui.VelocityTracker _velocityTracker = new ui.VelocityTracker();
53

54
  void addPointer(PointerInputEvent event) {
55
    startTrackingPointer(event.pointer);
56 57 58
    if (_state == DragState.ready) {
      _state = DragState.possible;
      _pendingDragDelta = _initialPendingDragDelta;
59 60 61
    }
  }

62
  void handleEvent(PointerInputEvent event) {
63
    assert(_state != DragState.ready);
64
    if (event.type == 'pointermove') {
65
      _velocityTracker.addPosition(_eventTime(event), event.pointer, event.x, event.y);
66 67
      T delta = _getDragDelta(event);
      if (_state == DragState.accepted) {
68 69
        if (onUpdate != null)
          onUpdate(delta);
70
      } else {
71 72
        _pendingDragDelta += delta;
        if (_hasSufficientPendingDragDeltaToAccept)
73 74 75 76 77 78 79
          resolve(GestureDisposition.accepted);
      }
    }
    stopTrackingIfPointerNoLongerDown(event);
  }

  void acceptGesture(int pointer) {
80 81 82
    if (_state != DragState.accepted) {
      _state = DragState.accepted;
      T delta = _pendingDragDelta;
83
      _pendingDragDelta = _initialPendingDragDelta;
84 85
      if (onStart != null)
        onStart();
86
      if (delta != _initialPendingDragDelta && onUpdate != null)
87
        onUpdate(delta);
88 89 90
    }
  }

91
  void didStopTrackingLastPointer(int pointer) {
92
    if (_state == DragState.possible) {
93
      resolve(GestureDisposition.rejected);
94
      _state = DragState.ready;
95 96
      return;
    }
97 98
    bool wasAccepted = (_state == DragState.accepted);
    _state = DragState.ready;
99
    if (wasAccepted && onEnd != null) {
100 101
      ui.GestureVelocity gestureVelocity = _velocityTracker.getVelocity(pointer);
      ui.Offset velocity = ui.Offset.zero;
102
      if (_isFlingGesture(gestureVelocity))
103
        velocity = new ui.Offset(gestureVelocity.x, gestureVelocity.y);
104 105 106 107 108 109 110 111
      onEnd(velocity);
    }
    _velocityTracker.reset();
  }

  void dispose() {
    _velocityTracker.reset();
    super.dispose();
112
  }
113 114
}

115 116
class VerticalDragGestureRecognizer extends _DragGestureRecognizer<double> {
  VerticalDragGestureRecognizer({
117
    PointerRouter router,
118 119 120
    GestureDragStartCallback onStart,
    GestureDragUpdateCallback onUpdate,
    GestureDragEndCallback onEnd
121
  }) : super(router: router, onStart: onStart, onUpdate: onUpdate, onEnd: onEnd);
122

123
  double get _initialPendingDragDelta => 0.0;
124
  double _getDragDelta(PointerInputEvent event) => event.dy;
125
  bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop;
126 127
}

128 129
class HorizontalDragGestureRecognizer extends _DragGestureRecognizer<double> {
  HorizontalDragGestureRecognizer({
130
    PointerRouter router,
131 132 133
    GestureDragStartCallback onStart,
    GestureDragUpdateCallback onUpdate,
    GestureDragEndCallback onEnd
134 135
  }) : super(router: router, onStart: onStart, onUpdate: onUpdate, onEnd: onEnd);

136
  double get _initialPendingDragDelta => 0.0;
137
  double _getDragDelta(PointerInputEvent event) => event.dx;
138
  bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop;
139 140
}

141
class PanGestureRecognizer extends _DragGestureRecognizer<ui.Offset> {
142 143 144 145 146 147 148
  PanGestureRecognizer({
    PointerRouter router,
    GesturePanStartCallback onStart,
    GesturePanUpdateCallback onUpdate,
    GesturePanEndCallback onEnd
  }) : super(router: router, onStart: onStart, onUpdate: onUpdate, onEnd: onEnd);

149
  ui.Offset get _initialPendingDragDelta => ui.Offset.zero;
150
  ui.Offset _getDragDelta(PointerInputEvent event) => new ui.Offset(event.dx, event.dy);
151
  bool get _hasSufficientPendingDragDeltaToAccept {
152
    return _pendingDragDelta.distance > kPanSlop;
153
  }
154
}