multidrag.dart 9.77 KB
Newer Older
1 2 3 4 5 6 7 8
// 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:async';
import 'dart:ui' show Point, Offset;

import 'arena.dart';
9
import 'binding.dart';
10 11 12 13 14 15 16 17 18
import 'constants.dart';
import 'events.dart';
import 'recognizer.dart';
import 'velocity_tracker.dart';

typedef Drag GestureMultiDragStartCallback(Point position);

class Drag {
  void move(Offset offset) { }
19
  void end(Velocity velocity) { }
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
  void cancel() { }
}

abstract class MultiDragPointerState {
  MultiDragPointerState(this.initialPosition);

  final Point initialPosition;

  final VelocityTracker _velocityTracker = new VelocityTracker();
  Drag _client;

  Offset get pendingDelta => _pendingDelta;
  Offset _pendingDelta = Offset.zero;

  GestureArenaEntry _arenaEntry;
  void _setArenaEntry(GestureArenaEntry entry) {
    assert(_arenaEntry == null);
    assert(pendingDelta != null);
    assert(_client == null);
    _arenaEntry = entry;
  }

  void resolve(GestureDisposition disposition) {
    _arenaEntry.resolve(disposition);
  }

  void _move(PointerMoveEvent event) {
    assert(_arenaEntry != null);
    _velocityTracker.addPosition(event.timeStamp, event.position);
    if (_client != null) {
      assert(pendingDelta == null);
      _client.move(event.delta);
    } else {
      assert(pendingDelta != null);
      _pendingDelta += event.delta;
      checkForResolutionAfterMove();
    }
    return null;
  }

  /// Override this to call resolve() if the drag should be accepted or rejected.
  /// This is called when a pointer movement is received, but only if the gesture
  /// has not yet been resolved.
  void checkForResolutionAfterMove() { }

  /// Called when the gesture was accepted.
66 67 68 69 70 71 72 73 74
  ///
  /// Either immediately or at some future point before the gesture is disposed,
  /// call starter(), passing it initialPosition, to start the drag.
  void accepted(GestureMultiDragStartCallback starter);

  /// Called when the gesture was rejected.
  ///
  /// [dispose()] will be called immediately following this.
  void rejected() {
75 76
    assert(_arenaEntry != null);
    assert(_client == null);
77
    assert(pendingDelta != null);
78
    _pendingDelta = null;
79
    _arenaEntry = null;
80 81
  }

82
  void _startDrag(Drag client) {
83 84
    assert(_arenaEntry != null);
    assert(_client == null);
85
    assert(client != null);
86
    assert(pendingDelta != null);
87 88
    _client = client;
    _client.move(pendingDelta);
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    _pendingDelta = null;
  }

  void _up() {
    assert(_arenaEntry != null);
    if (_client != null) {
      assert(pendingDelta == null);
      _client.end(_velocityTracker.getVelocity());
      _client = null;
    } else {
      assert(pendingDelta != null);
      _pendingDelta = null;
    }
    _arenaEntry = null;
  }

  void _cancel() {
    assert(_arenaEntry != null);
    if (_client != null) {
      assert(pendingDelta == null);
      _client.cancel();
      _client = null;
    } else {
      assert(pendingDelta != null);
      _pendingDelta = null;
    }
    _arenaEntry = null;
  }

118 119 120
  void dispose() {
    assert(() { _pendingDelta = null; return true; });
  }
121 122 123 124 125 126 127
}

abstract class MultiDragGestureRecognizer<T extends MultiDragPointerState> extends GestureRecognizer {
  GestureMultiDragStartCallback onStart;

  Map<int, T> _pointers = <int, T>{};

128
  @override
129 130 131 132 133 134 135
  void addPointer(PointerDownEvent event) {
    assert(_pointers != null);
    assert(event.pointer != null);
    assert(event.position != null);
    assert(!_pointers.containsKey(event.pointer));
    T state = createNewPointerState(event);
    _pointers[event.pointer] = state;
136 137
    Gesturer.instance.pointerRouter.addRoute(event.pointer, handleEvent);
    state._setArenaEntry(Gesturer.instance.gestureArena.add(event.pointer, this));
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  }

  T createNewPointerState(PointerDownEvent event);

  void handleEvent(PointerEvent event) {
    assert(_pointers != null);
    assert(event.pointer != null);
    assert(event.timeStamp != null);
    assert(event.position != null);
    assert(_pointers.containsKey(event.pointer));
    T state = _pointers[event.pointer];
    if (event is PointerMoveEvent) {
      state._move(event);
    } else if (event is PointerUpEvent) {
      assert(event.delta == Offset.zero);
      state._up();
      _removeState(event.pointer);
    } else if (event is PointerCancelEvent) {
      assert(event.delta == Offset.zero);
      state._cancel();
      _removeState(event.pointer);
    } else if (event is! PointerDownEvent) {
      // we get the PointerDownEvent that resulted in our addPointer gettig called since we
      // add ourselves to the pointer router then (before the pointer router has heard of
      // the event).
      assert(false);
    }
  }

167
  @override
168 169 170 171
  void acceptGesture(int pointer) {
    assert(_pointers != null);
    T state = _pointers[pointer];
    assert(state != null);
172 173 174 175 176 177 178 179
    state.accepted((Point initialPosition) => _startDrag(initialPosition, pointer));
  }

  Drag _startDrag(Point initialPosition, int pointer) {
    assert(_pointers != null);
    T state = _pointers[pointer];
    assert(state != null);
    assert(state._pendingDelta != null);
180 181
    Drag drag;
    if (onStart != null)
182
      drag = onStart(initialPosition);
183
    if (drag != null) {
184
      state._startDrag(drag);
185 186 187
    } else {
      _removeState(pointer);
    }
188
    return drag;
189 190
  }

191
  @override
192 193 194 195 196 197 198 199 200 201 202 203 204
  void rejectGesture(int pointer) {
    assert(_pointers != null);
    if (_pointers.containsKey(pointer)) {
      T state = _pointers[pointer];
      assert(state != null);
      state.rejected();
      _removeState(pointer);
    } // else we already preemptively forgot about it (e.g. we got an up event)
  }

  void _removeState(int pointer) {
    assert(_pointers != null);
    assert(_pointers.containsKey(pointer));
205
    Gesturer.instance.pointerRouter.removeRoute(pointer, handleEvent);
206 207 208 209
    _pointers[pointer].dispose();
    _pointers.remove(pointer);
  }

210
  @override
211 212 213 214 215 216 217 218 219 220 221 222 223
  void dispose() {
    for (int pointer in _pointers.keys)
      _removeState(pointer);
    _pointers = null;
    super.dispose();
  }

}


class _ImmediatePointerState extends MultiDragPointerState {
  _ImmediatePointerState(Point initialPosition) : super(initialPosition);

224
  @override
225 226 227 228 229
  void checkForResolutionAfterMove() {
    assert(pendingDelta != null);
    if (pendingDelta.distance > kTouchSlop)
      resolve(GestureDisposition.accepted);
  }
230

231
  @override
232 233 234
  void accepted(GestureMultiDragStartCallback starter) {
    starter(initialPosition);
  }
235 236 237
}

class ImmediateMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_ImmediatePointerState> {
238
  @override
239 240 241
  _ImmediatePointerState createNewPointerState(PointerDownEvent event) {
    return new _ImmediatePointerState(event.position);
  }
242

243
  @override
244
  String toStringShort() => 'multidrag';
245 246
}

247 248 249 250

class _HorizontalPointerState extends MultiDragPointerState {
  _HorizontalPointerState(Point initialPosition) : super(initialPosition);

251
  @override
252 253 254 255 256 257
  void checkForResolutionAfterMove() {
    assert(pendingDelta != null);
    if (pendingDelta.dx.abs() > kTouchSlop)
      resolve(GestureDisposition.accepted);
  }

258
  @override
259 260 261 262 263 264
  void accepted(GestureMultiDragStartCallback starter) {
    starter(initialPosition);
  }
}

class HorizontalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_HorizontalPointerState> {
265
  @override
266 267 268
  _HorizontalPointerState createNewPointerState(PointerDownEvent event) {
    return new _HorizontalPointerState(event.position);
  }
269

270
  @override
271
  String toStringShort() => 'horizontal multidrag';
272 273 274 275 276 277
}


class _VerticalPointerState extends MultiDragPointerState {
  _VerticalPointerState(Point initialPosition) : super(initialPosition);

278
  @override
279 280 281 282 283 284
  void checkForResolutionAfterMove() {
    assert(pendingDelta != null);
    if (pendingDelta.dy.abs() > kTouchSlop)
      resolve(GestureDisposition.accepted);
  }

285
  @override
286 287 288 289 290 291
  void accepted(GestureMultiDragStartCallback starter) {
    starter(initialPosition);
  }
}

class VerticalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_VerticalPointerState> {
292
  @override
293 294 295
  _VerticalPointerState createNewPointerState(PointerDownEvent event) {
    return new _VerticalPointerState(event.position);
  }
296

297
  @override
298
  String toStringShort() => 'vertical multidrag';
299 300 301
}


302 303 304 305 306 307 308
class _DelayedPointerState extends MultiDragPointerState {
  _DelayedPointerState(Point initialPosition, Duration delay) : super(initialPosition) {
    assert(delay != null);
    _timer = new Timer(delay, _delayPassed);
  }

  Timer _timer;
309
  GestureMultiDragStartCallback _starter;
310 311 312 313 314 315

  void _delayPassed() {
    assert(_timer != null);
    assert(pendingDelta != null);
    assert(pendingDelta.distance <= kTouchSlop);
    _timer = null;
316 317 318 319 320 321 322
    if (_starter != null) {
      _starter(initialPosition);
      _starter = null;
    } else {
      resolve(GestureDisposition.accepted);
    }
    assert(_starter == null);
323 324
  }

325
  @override
326 327 328 329 330 331
  void accepted(GestureMultiDragStartCallback starter) {
    assert(_starter == null);
    if (_timer == null)
      starter(initialPosition);
    else
      _starter = starter;
332 333
  }

334
  @override
335 336 337 338 339 340 341
  void checkForResolutionAfterMove() {
    assert(_timer != null);
    assert(pendingDelta != null);
    if (pendingDelta.distance > kTouchSlop)
      resolve(GestureDisposition.rejected);
  }

342
  @override
343 344 345 346 347 348 349 350 351 352
  void dispose() {
    _timer?.cancel();
    _timer = null;
    super.dispose();
  }
}

class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_DelayedPointerState> {
  DelayedMultiDragGestureRecognizer({
    Duration delay: kLongPressTimeout
353
  }) : _delay = delay {
354 355 356 357 358 359 360 361 362 363
    assert(delay != null);
  }

  Duration get delay => _delay;
  Duration _delay;
  void set delay(Duration value) {
    assert(value != null);
    _delay = value;
  }

364
  @override
365 366 367
  _DelayedPointerState createNewPointerState(PointerDownEvent event) {
    return new _DelayedPointerState(event.position, _delay);
  }
368

369
  @override
370
  String toStringShort() => 'long multidrag';
371
}