converter.dart 12.8 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

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;
  }

31
  Offset lastPosition;
Ian Hickson's avatar
Ian Hickson committed
32 33
}

34
/// Converts from engine pointer data to framework pointer events.
35 36 37 38
///
/// This takes [PointerDataPacket] objects, as received from the engine via
/// [dart:ui.Window.onPointerDataPacket], and converts them to [PointerEvent]
/// objects.
Ian Hickson's avatar
Ian Hickson committed
39
class PointerEventConverter {
40 41
  PointerEventConverter._();

Ian Hickson's avatar
Ian Hickson committed
42
  // Map from platform pointer identifiers to PointerEvent pointer identifiers.
43
  static final Map<int, _PointerState> _pointers = <int, _PointerState>{};
Ian Hickson's avatar
Ian Hickson committed
44

45
  static _PointerState _ensureStateForPointer(ui.PointerData datum, Offset position) {
46 47 48 49 50 51
    return _pointers.putIfAbsent(
      datum.device,
      () => new _PointerState(position)
    );
  }

52
  /// Expand the given packet of pointer data into a sequence of framework pointer events.
53 54 55 56 57
  ///
  /// The `devicePixelRatio` argument (usually given the value from
  /// [dart:ui.Window.devicePixelRatio]) is used to convert the incoming data
  /// from physical coordinates to logical pixels. See the discussion at
  /// [PointerEvent] for more details on the [PointerEvent] coordinate space.
58 59
  static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* {
    for (ui.PointerData datum in data) {
60
      final Offset position = new Offset(datum.physicalX, datum.physicalY) / devicePixelRatio;
61 62
      final Duration timeStamp = datum.timeStamp;
      final PointerDeviceKind kind = datum.kind;
63
      assert(datum.change != null);
64
      switch (datum.change) {
65 66
        case ui.PointerChange.add:
          assert(!_pointers.containsKey(datum.device));
67
          final _PointerState state = _ensureStateForPointer(datum, position);
Ian Hickson's avatar
Ian Hickson committed
68 69 70 71
          assert(state.lastPosition == position);
          yield new PointerAddedEvent(
            timeStamp: timeStamp,
            kind: kind,
72
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
73 74 75 76 77 78 79 80 81 82 83
            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
          );
84 85 86
          break;
        case ui.PointerChange.hover:
          final bool alreadyAdded = _pointers.containsKey(datum.device);
87
          final _PointerState state = _ensureStateForPointer(datum, position);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
          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
            );
          }
107
          final Offset offset = position - state.lastPosition;
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
          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);
132
          final _PointerState state = _ensureStateForPointer(datum, position);
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
          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.
156
            final Offset offset = position - state.lastPosition;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
            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,
175 176
              tilt: datum.tilt,
              synthesized: true,
177 178 179 180 181
            );
            state.lastPosition = position;
          }
          state.startNewPointer();
          state.setDown();
Ian Hickson's avatar
Ian Hickson committed
182 183 184 185
          yield new PointerDownEvent(
            timeStamp: timeStamp,
            pointer: state.pointer,
            kind: kind,
186
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
187
            position: position,
188
            buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
189 190 191 192 193 194 195 196 197 198 199 200 201
            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;
202
        case ui.PointerChange.move:
Ian Hickson's avatar
Ian Hickson committed
203 204 205
          // 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
206
          assert(_pointers.containsKey(datum.device));
207
          final _PointerState state = _pointers[datum.device];
Ian Hickson's avatar
Ian Hickson committed
208
          assert(state.down);
209
          final Offset offset = position - state.lastPosition;
Ian Hickson's avatar
Ian Hickson committed
210 211 212 213 214
          state.lastPosition = position;
          yield new PointerMoveEvent(
            timeStamp: timeStamp,
            pointer: state.pointer,
            kind: kind,
215
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
216 217
            position: position,
            delta: offset,
218
            buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
219 220 221 222 223 224 225 226 227 228 229 230 231
            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;
232 233
        case ui.PointerChange.up:
        case ui.PointerChange.cancel:
234
          assert(_pointers.containsKey(datum.device));
235
          final _PointerState state = _pointers[datum.device];
Ian Hickson's avatar
Ian Hickson committed
236
          assert(state.down);
237 238 239 240 241 242
          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.
243
            final Offset offset = position - state.lastPosition;
244 245 246 247 248
            state.lastPosition = position;
            yield new PointerMoveEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
249
              device: datum.device,
250 251
              position: position,
              delta: offset,
252
              buttons: datum.buttons,
253 254 255 256 257 258 259 260 261 262
              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,
263 264
              tilt: datum.tilt,
              synthesized: true,
265 266 267
            );
            state.lastPosition = position;
          }
Ian Hickson's avatar
Ian Hickson committed
268 269
          assert(position == state.lastPosition);
          state.setUp();
270
          if (datum.change == ui.PointerChange.up) {
Ian Hickson's avatar
Ian Hickson committed
271 272 273 274
            yield new PointerUpEvent(
              timeStamp: timeStamp,
              pointer: state.pointer,
              kind: kind,
275
              device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
276
              position: position,
277
              buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291
              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,
292
              device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
293
              position: position,
294
              buttons: datum.buttons,
Ian Hickson's avatar
Ian Hickson committed
295 296 297 298 299 300 301 302 303 304 305
              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
            );
          }
306 307
          break;
        case ui.PointerChange.remove:
308
          assert(_pointers.containsKey(datum.device));
309
          final _PointerState state = _pointers[datum.device];
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
          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
330 331 332
          yield new PointerRemovedEvent(
            timeStamp: timeStamp,
            kind: kind,
333
            device: datum.device,
Ian Hickson's avatar
Ian Hickson committed
334 335 336 337 338 339 340 341 342 343 344 345
            obscured: datum.obscured,
            pressureMin: datum.pressureMin,
            pressureMax: datum.pressureMax,
            distanceMax: datum.distanceMax,
            radiusMin: datum.radiusMin,
            radiusMax: datum.radiusMax
          );
          break;
      }
    }
  }
}