converter.dart 12.9 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;
32 33 34 35 36

  @override
  String toString() {
    return '_PointerState(pointer: $pointer, down: $down, lastPosition: $lastPosition)';
  }
Ian Hickson's avatar
Ian Hickson committed
37 38
}

39
/// Converts from engine pointer data to framework pointer events.
40 41 42 43
///
/// 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
44
class PointerEventConverter {
45 46
  PointerEventConverter._();

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

50
  static _PointerState _ensureStateForPointer(ui.PointerData datum, Offset position) {
51 52 53 54 55 56
    return _pointers.putIfAbsent(
      datum.device,
      () => new _PointerState(position)
    );
  }

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