converter.dart 12.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
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 show PointerData, PointerChange;
Ian Hickson's avatar
Ian Hickson committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

import 'events.dart';

class _PointerState {
  _PointerState(this.lastPosition);

  int get pointer => _pointer; // The identifier used in PointerEvent objects.
  int _pointer;
  static int _pointerCount = 0;
  void startNewPointer() {
    _pointerCount += 1;
    _pointer = _pointerCount;
  }

  bool get down => _down;
  bool _down = false;
  void setDown() {
    assert(!_down);
    _down = true;
  }
  void setUp() {
    assert(_down);
    _down = false;
  }

  Point lastPosition;
}

34
/// Converts from engine pointer data to framework pointer events.
Ian Hickson's avatar
Ian Hickson committed
35 36 37 38
class PointerEventConverter {
  // Map from platform pointer identifiers to PointerEvent pointer identifiers.
  static Map<int, _PointerState> _pointers = <int, _PointerState>{};

39 40 41 42 43 44 45
  static _PointerState _ensureStateForPointer(ui.PointerData datum, Point position) {
    return _pointers.putIfAbsent(
      datum.device,
      () => new _PointerState(position)
    );
  }

46
  /// Expand the given packet of pointer data into a sequence of framework pointer events.
47 48 49 50 51
  static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* {
    for (ui.PointerData datum in data) {
      final Point position = new Point(datum.physicalX, datum.physicalY) / devicePixelRatio;
      final Duration timeStamp = datum.timeStamp;
      final PointerDeviceKind kind = datum.kind;
52
      assert(datum.change != null);
53
      switch (datum.change) {
54 55 56
        case ui.PointerChange.add:
          assert(!_pointers.containsKey(datum.device));
          _PointerState state = _ensureStateForPointer(datum, position);
Ian Hickson's avatar
Ian Hickson committed
57 58 59 60
          assert(state.lastPosition == position);
          yield new PointerAddedEvent(
            timeStamp: timeStamp,
            kind: kind,
61
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
62 63 64 65 66 67 68 69 70 71 72
            position: position,
            obscured: datum.obscured,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distance: datum.distance,
            distanceMax: datum.distanceMax,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax,
            orientation: datum.orientation,
            tilt: datum.tilt
          );
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 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 167 168 169
          break;
        case ui.PointerChange.hover:
          final bool alreadyAdded = _pointers.containsKey(datum.device);
          _PointerState state = _ensureStateForPointer(datum, position);
          assert(!state.down);
          if (!alreadyAdded) {
            assert(state.lastPosition == position);
            yield new PointerAddedEvent(
              timeStamp: timeStamp,
              kind: kind,
              device: datum.device,
              position: position,
              obscured: datum.obscured,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
          }
          Offset offset = position - state.lastPosition;
          state.lastPosition = position;
          yield new PointerHoverEvent(
            timeStamp: timeStamp,
            kind: kind,
            device: datum.device,
            position: position,
            delta: offset,
            buttons: datum.buttons,
            obscured: datum.obscured,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distance: datum.distance,
            distanceMax: datum.distanceMax,
            radiusMajor: datum.radiusMajor,
            radiusMinor: datum.radiusMajor,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax,
            orientation: datum.orientation,
            tilt: datum.tilt
          );
          state.lastPosition = position;
          break;
        case ui.PointerChange.down:
          final bool alreadyAdded = _pointers.containsKey(datum.device);
          _PointerState state = _ensureStateForPointer(datum, position);
          assert(!state.down);
          if (!alreadyAdded) {
            assert(state.lastPosition == position);
            yield new PointerAddedEvent(
              timeStamp: timeStamp,
              kind: kind,
              device: datum.device,
              position: position,
              obscured: datum.obscured,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
          }
          if (state.lastPosition != position) {
            // Not all sources of pointer packets respect the invariant that
            // they hover the pointer to the down location before sending the
            // down event. We restore the invariant here for our clients.
            Offset offset = position - state.lastPosition;
            state.lastPosition = position;
            yield new PointerHoverEvent(
              timeStamp: timeStamp,
              kind: kind,
              device: datum.device,
              position: position,
              delta: offset,
              buttons: datum.buttons,
              obscured: datum.obscured,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMajor: datum.radiusMajor,
              radiusMinor: datum.radiusMajor,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
            state.lastPosition = position;
          }
          state.startNewPointer();
          state.setDown();
Ian Hickson's avatar
Ian Hickson committed
170 171 172 173
          yield new PointerDownEvent(
            timeStamp: timeStamp,
            pointer: state.pointer,
            kind: kind,
174
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
175
            position: position,
176
            buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
177 178 179 180 181 182 183 184 185 186 187 188 189
            obscured: datum.obscured,
            pressure: datum.pressure,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distanceMax: datum.distanceMax,
            radiusMajor: datum.radiusMajor,
            radiusMinor: datum.radiusMajor,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax,
            orientation: datum.orientation,
            tilt: datum.tilt
          );
          break;
190
        case ui.PointerChange.move:
Ian Hickson's avatar
Ian Hickson committed
191 192 193
          // If the service starts supporting hover pointers, then it must also
          // start sending us ADDED and REMOVED data points.
          // See also: https://github.com/flutter/flutter/issues/720
194 195
          assert(_pointers.containsKey(datum.device));
          _PointerState state = _pointers[datum.device];
Ian Hickson's avatar
Ian Hickson committed
196 197 198 199 200 201 202
          assert(state.down);
          Offset offset = position - state.lastPosition;
          state.lastPosition = position;
          yield new PointerMoveEvent(
            timeStamp: timeStamp,
            pointer: state.pointer,
            kind: kind,
203
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
204 205
            position: position,
            delta: offset,
206
            buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
207 208 209 210 211 212 213 214 215 216 217 218 219
            obscured: datum.obscured,
            pressure: datum.pressure,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distanceMax: datum.distanceMax,
            radiusMajor: datum.radiusMajor,
            radiusMinor: datum.radiusMajor,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax,
            orientation: datum.orientation,
            tilt: datum.tilt
          );
          break;
220 221
        case ui.PointerChange.up:
        case ui.PointerChange.cancel:
222 223
          assert(_pointers.containsKey(datum.device));
          _PointerState state = _pointers[datum.device];
Ian Hickson's avatar
Ian Hickson committed
224
          assert(state.down);
225 226 227 228 229 230 231 232 233 234 235 236
          if (position != state.lastPosition) {
            // Not all sources of pointer packets respect the invariant that
            // they move the pointer to the up location before sending the up
            // event. For example, in the iOS simulator, of you drag outside the
            // window, you'll get a stream of pointers that violates that
            // invariant. We restore the invariant here for our clients.
            Offset offset = position - state.lastPosition;
            state.lastPosition = position;
            yield new PointerMoveEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
237
              device: datum.device,
238 239
              position: position,
              delta: offset,
240
              buttons: datum.buttons,
241 242 243 244 245 246 247 248 249 250 251 252 253 254
              obscured: datum.obscured,
              pressure: datum.pressure,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distanceMax: datum.distanceMax,
              radiusMajor: datum.radiusMajor,
              radiusMinor: datum.radiusMajor,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
            state.lastPosition = position;
          }
Ian Hickson's avatar
Ian Hickson committed
255 256
          assert(position == state.lastPosition);
          state.setUp();
257
          if (datum.change == ui.PointerChange.up) {
Ian Hickson's avatar
Ian Hickson committed
258 259 260 261
            yield new PointerUpEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
262
              device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
263
              position: position,
264
              buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278
              obscured: datum.obscured,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
          } else {
            yield new PointerCancelEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
279
              device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
280
              position: position,
281
              buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
282 283 284 285 286 287 288 289 290 291 292
              obscured: datum.obscured,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
          }
293 294
          break;
        case ui.PointerChange.remove:
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
          assert(_pointers.containsKey(datum.device));
          _PointerState state = _pointers[datum.device];
          if (state.down) {
            yield new PointerCancelEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
              device: datum.device,
              position: position,
              buttons: datum.buttons,
              obscured: datum.obscured,
              pressureMin: datum.pressureMin,
              pressureMax: datum.pressureMax,
              distance: datum.distance,
              distanceMax: datum.distanceMax,
              radiusMin: datum.radiusMin,
              radiusMax: datum.radiusMax,
              orientation: datum.orientation,
              tilt: datum.tilt
            );
          }
          _pointers.remove(datum.device);
Ian Hickson's avatar
Ian Hickson committed
317 318 319
          yield new PointerRemovedEvent(
            timeStamp: timeStamp,
            kind: kind,
320
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
321 322 323 324 325 326 327 328 329 330 331 332
            obscured: datum.obscured,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distanceMax: datum.distanceMax,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax
          );
          break;
      }
    }
  }
}