recognizer.dart 4.18 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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:sky' as sky;

import 'package:sky/gestures/arena.dart';
import 'package:sky/gestures/constants.dart';
Adam Barth's avatar
Adam Barth committed
10
import 'package:sky/gestures/pointer_router.dart';
11

Adam Barth's avatar
Adam Barth committed
12
export 'package:sky/gestures/pointer_router.dart' show PointerRouter;
13

14 15 16 17 18 19 20 21 22 23 24 25
abstract class GestureRecognizer extends GestureArenaMember {
  GestureRecognizer({ PointerRouter router }) : _router = router;

  PointerRouter _router;

  final List<GestureArenaEntry> _entries = new List<GestureArenaEntry>();
  final Set<int> _trackedPointers = new Set<int>();

  /// The primary entry point for users of this class.
  void addPointer(sky.PointerEvent event);

  void handleEvent(sky.PointerEvent event);
26 27
  void acceptGesture(int pointer) { }
  void rejectGesture(int pointer) { }
28
  void didStopTrackingLastPointer(int pointer);
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

  void resolve(GestureDisposition disposition) {
    List<GestureArenaEntry> localEntries = new List.from(_entries);
    _entries.clear();
    for (GestureArenaEntry entry in localEntries)
      entry.resolve(disposition);
  }

  void dispose() {
    resolve(GestureDisposition.rejected);
    for (int pointer in _trackedPointers)
      _router.removeRoute(pointer, handleEvent);
    _trackedPointers.clear();
    assert(_entries.isEmpty);
    _router = null;
  }

  void startTrackingPointer(int pointer) {
    _router.addRoute(pointer, handleEvent);
    _trackedPointers.add(pointer);
    _entries.add(GestureArena.instance.add(pointer, this));
  }

  void stopTrackingPointer(int pointer) {
    _router.removeRoute(pointer, handleEvent);
    _trackedPointers.remove(pointer);
    if (_trackedPointers.isEmpty)
56
      didStopTrackingLastPointer(pointer);
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 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 118 119 120
  }

  void stopTrackingIfPointerNoLongerDown(sky.PointerEvent event) {
    if (event.type == 'pointerup' || event.type == 'pointercancel')
      stopTrackingPointer(event.pointer);
  }

}

enum GestureRecognizerState {
  ready,
  possible,
  defunct
}

sky.Point _getPoint(sky.PointerEvent event) {
  return new sky.Point(event.x, event.y);
}

abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer {
  PrimaryPointerGestureRecognizer({ PointerRouter router, this.deadline })
    : super(router: router);

  final Duration deadline;

  GestureRecognizerState state = GestureRecognizerState.ready;
  int primaryPointer;
  sky.Point initialPosition;
  Timer _timer;

  void addPointer(sky.PointerEvent event) {
    startTrackingPointer(event.pointer);
    if (state == GestureRecognizerState.ready) {
      state = GestureRecognizerState.possible;
      primaryPointer = event.pointer;
      initialPosition = _getPoint(event);
      if (deadline != null)
        _timer = new Timer(deadline, didExceedDeadline);
    }
  }

  void handleEvent(sky.PointerEvent event) {
    assert(state != GestureRecognizerState.ready);
    if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
      // TODO(abarth): Maybe factor the slop handling out into a separate class?
      if (event.type == 'pointermove' && _getDistance(event) > kTouchSlop)
        resolve(GestureDisposition.rejected);
      else
        handlePrimaryPointer(event);
    }
    stopTrackingIfPointerNoLongerDown(event);
  }

  /// Override to provide behavior for the primary pointer when the gesture is still possible.
  void handlePrimaryPointer(sky.PointerEvent event);

  /// Override to be notified with [deadline] is exceeded.
  ///
  /// You must override this function if you supply a [deadline].
  void didExceedDeadline() {
    assert(deadline == null);
  }

  void rejectGesture(int pointer) {
121 122
    if (pointer == primaryPointer) {
      _stopTimer();
123
      state = GestureRecognizerState.defunct;
124
    }
125 126
  }

127
  void didStopTrackingLastPointer(int pointer) {
128
    _stopTimer();
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    state = GestureRecognizerState.ready;
  }

  void dispose() {
    _stopTimer();
    super.dispose();
  }

  void _stopTimer() {
    if (_timer != null) {
      _timer.cancel();
      _timer = null;
    }
  }

  double _getDistance(sky.PointerEvent event) {
    sky.Offset offset = _getPoint(event) - initialPosition;
    return offset.distance;
  }

}