converter.dart 13.4 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Ian Hickson's avatar
Ian Hickson committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5

6
import 'dart:ui' as ui show PointerChange, PointerData, PointerSignalKind;
Ian Hickson's avatar
Ian Hickson committed
7 8 9

import 'events.dart';

10 11 12 13
export 'dart:ui' show PointerData;

export 'events.dart' show PointerEvent;

14 15 16 17 18 19 20
// Add `kPrimaryButton` to [buttons] when a pointer of certain devices is down.
//
// TODO(tongmu): This patch is supposed to be done by embedders. Patching it
// in framework is a workaround before [PointerEventConverter] is moved to embedders.
// https://github.com/flutter/flutter/issues/30454
int _synthesiseDownButtons(int buttons, PointerDeviceKind kind) {
  switch (kind) {
21
    case PointerDeviceKind.mouse:
22
    case PointerDeviceKind.trackpad:
23
      return buttons;
24 25 26
    case PointerDeviceKind.touch:
    case PointerDeviceKind.stylus:
    case PointerDeviceKind.invertedStylus:
27
      return buttons == 0 ? kPrimaryButton : buttons;
28
    case PointerDeviceKind.unknown:
29 30 31
      // We have no information about the device but we know we never want
      // buttons to be 0 when the pointer is down.
      return buttons == 0 ? kPrimaryButton : buttons;
32 33 34
  }
}

35 36 37 38 39 40 41 42 43 44 45 46
/// Signature for a callback that returns the device pixel ratio of a
/// [FlutterView] identified by the provided `viewId`.
///
/// Returns null if no view with the provided ID exists.
///
/// Used by [PointerEventConverter.expand].
///
/// See also:
///
///  * [FlutterView.devicePixelRatio] for an explanation of device pixel ratio.
typedef DevicePixelRatioGetter = double? Function(int viewId);

47
/// Converts from engine pointer data to framework pointer events.
48 49
///
/// This takes [PointerDataPacket] objects, as received from the engine via
50 51
/// [dart:ui.PlatformDispatcher.onPointerDataPacket], and converts them to
/// [PointerEvent] objects.
52
abstract final class PointerEventConverter {
53 54
  /// Expand the given packet of pointer data into a sequence of framework
  /// pointer events.
55
  ///
56 57 58 59
  /// The `devicePixelRatioForView` is used to obtain the device pixel ratio for
  /// the view a particular event occurred in to convert its data from physical
  /// coordinates to logical pixels. See the discussion at [PointerEvent] for
  /// more details on the [PointerEvent] coordinate space.
60
  static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, DevicePixelRatioGetter devicePixelRatioForView) {
61 62
    return data
        .where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown)
63
        .map<PointerEvent?>((ui.PointerData datum) {
64 65 66 67 68
          final double? devicePixelRatio = devicePixelRatioForView(datum.viewId);
          if (devicePixelRatio == null) {
            // View doesn't exist anymore.
            return null;
          }
69 70 71 72 73 74 75 76 77 78 79 80 81
          final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio;
          final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio;
          final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio);
          final double radiusMajor = _toLogicalPixels(datum.radiusMajor, devicePixelRatio);
          final double radiusMin = _toLogicalPixels(datum.radiusMin, devicePixelRatio);
          final double radiusMax = _toLogicalPixels(datum.radiusMax, devicePixelRatio);
          final Duration timeStamp = datum.timeStamp;
          final PointerDeviceKind kind = datum.kind;
          switch (datum.signalKind ?? ui.PointerSignalKind.none) {
            case ui.PointerSignalKind.none:
              switch (datum.change) {
                case ui.PointerChange.add:
                  return PointerAddedEvent(
82
                    viewId: datum.viewId,
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
                    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: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.hover:
                  return PointerHoverEvent(
100
                    viewId: datum.viewId,
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
                    timeStamp: timeStamp,
                    kind: kind,
                    device: datum.device,
                    position: position,
                    delta: delta,
                    buttons: datum.buttons,
                    obscured: datum.obscured,
                    pressureMin: datum.pressureMin,
                    pressureMax: datum.pressureMax,
                    distance: datum.distance,
                    distanceMax: datum.distanceMax,
                    size: datum.size,
                    radiusMajor: radiusMajor,
                    radiusMinor: radiusMinor,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    synthesized: datum.synthesized,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.down:
                  return PointerDownEvent(
124
                    viewId: datum.viewId,
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    kind: kind,
                    device: datum.device,
                    position: position,
                    buttons: _synthesiseDownButtons(datum.buttons, kind),
                    obscured: datum.obscured,
                    pressure: datum.pressure,
                    pressureMin: datum.pressureMin,
                    pressureMax: datum.pressureMax,
                    distanceMax: datum.distanceMax,
                    size: datum.size,
                    radiusMajor: radiusMajor,
                    radiusMinor: radiusMinor,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.move:
                  return PointerMoveEvent(
147
                    viewId: datum.viewId,
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    kind: kind,
                    device: datum.device,
                    position: position,
                    delta: delta,
                    buttons: _synthesiseDownButtons(datum.buttons, kind),
                    obscured: datum.obscured,
                    pressure: datum.pressure,
                    pressureMin: datum.pressureMin,
                    pressureMax: datum.pressureMax,
                    distanceMax: datum.distanceMax,
                    size: datum.size,
                    radiusMajor: radiusMajor,
                    radiusMinor: radiusMinor,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    platformData: datum.platformData,
                    synthesized: datum.synthesized,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.up:
                  return PointerUpEvent(
173
                    viewId: datum.viewId,
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    kind: kind,
                    device: datum.device,
                    position: position,
                    buttons: datum.buttons,
                    obscured: datum.obscured,
                    pressure: datum.pressure,
                    pressureMin: datum.pressureMin,
                    pressureMax: datum.pressureMax,
                    distance: datum.distance,
                    distanceMax: datum.distanceMax,
                    size: datum.size,
                    radiusMajor: radiusMajor,
                    radiusMinor: radiusMinor,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.cancel:
                  return PointerCancelEvent(
197
                    viewId: datum.viewId,
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    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,
                    size: datum.size,
                    radiusMajor: radiusMajor,
                    radiusMinor: radiusMinor,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    orientation: datum.orientation,
                    tilt: datum.tilt,
                    embedderId: datum.embedderId,
                  );
                case ui.PointerChange.remove:
                  return PointerRemovedEvent(
220
                    viewId: datum.viewId,
221 222 223 224 225 226 227 228 229 230 231 232
                    timeStamp: timeStamp,
                    kind: kind,
                    device: datum.device,
                    position: position,
                    obscured: datum.obscured,
                    pressureMin: datum.pressureMin,
                    pressureMax: datum.pressureMax,
                    distanceMax: datum.distanceMax,
                    radiusMin: radiusMin,
                    radiusMax: radiusMax,
                    embedderId: datum.embedderId,
                  );
233 234
                case ui.PointerChange.panZoomStart:
                  return PointerPanZoomStartEvent(
235
                    viewId: datum.viewId,
236 237 238 239 240 241 242 243 244 245 246 247 248
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    device: datum.device,
                    position: position,
                    embedderId: datum.embedderId,
                    synthesized: datum.synthesized,
                  );
                case ui.PointerChange.panZoomUpdate:
                  final Offset pan =
                      Offset(datum.panX, datum.panY) / devicePixelRatio;
                  final Offset panDelta =
                      Offset(datum.panDeltaX, datum.panDeltaY) / devicePixelRatio;
                  return PointerPanZoomUpdateEvent(
249
                    viewId: datum.viewId,
250 251 252 253 254 255 256 257 258 259 260 261 262
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    device: datum.device,
                    position: position,
                    pan: pan,
                    panDelta: panDelta,
                    scale: datum.scale,
                    rotation: datum.rotation,
                    embedderId: datum.embedderId,
                    synthesized: datum.synthesized,
                  );
                case ui.PointerChange.panZoomEnd:
                  return PointerPanZoomEndEvent(
263
                    viewId: datum.viewId,
264 265 266 267 268 269 270
                    timeStamp: timeStamp,
                    pointer: datum.pointerIdentifier,
                    device: datum.device,
                    position: position,
                    embedderId: datum.embedderId,
                    synthesized: datum.synthesized,
                  );
271 272
              }
            case ui.PointerSignalKind.scroll:
273 274 275
              if (!datum.scrollDeltaX.isFinite || !datum.scrollDeltaY.isFinite || devicePixelRatio <= 0) {
                return null;
              }
276 277 278
              final Offset scrollDelta =
                  Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio;
              return PointerScrollEvent(
279
                viewId: datum.viewId,
280 281 282 283 284 285 286
                timeStamp: timeStamp,
                kind: kind,
                device: datum.device,
                position: position,
                scrollDelta: scrollDelta,
                embedderId: datum.embedderId,
              );
287 288
            case ui.PointerSignalKind.scrollInertiaCancel:
              return PointerScrollInertiaCancelEvent(
289
                viewId: datum.viewId,
290 291 292 293 294 295
                timeStamp: timeStamp,
                kind: kind,
                device: datum.device,
                position: position,
                embedderId: datum.embedderId,
              );
296 297
            case ui.PointerSignalKind.scale:
              return PointerScaleEvent(
298
                viewId: datum.viewId,
299 300 301 302 303 304 305
                timeStamp: timeStamp,
                kind: kind,
                device: datum.device,
                position: position,
                embedderId: datum.embedderId,
                scale: datum.scale,
              );
306 307 308
            case ui.PointerSignalKind.unknown:
              throw StateError('Unreachable');
          }
309
        }).whereType<PointerEvent>();
Ian Hickson's avatar
Ian Hickson committed
310
  }
311

312
  static double _toLogicalPixels(double physicalPixels, double devicePixelRatio) => physicalPixels / devicePixelRatio;
Ian Hickson's avatar
Ian Hickson committed
313
}