| // Copyright 2014 The Flutter 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:ui' as ui show PointerData, PointerChange, PointerSignalKind; |
| |
| import 'events.dart'; |
| |
| // 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) { |
| case PointerDeviceKind.mouse: |
| return buttons; |
| case PointerDeviceKind.touch: |
| case PointerDeviceKind.stylus: |
| case PointerDeviceKind.invertedStylus: |
| return buttons | kPrimaryButton; |
| default: |
| // 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; |
| } |
| } |
| |
| /// Converts from engine pointer data to framework pointer events. |
| /// |
| /// This takes [PointerDataPacket] objects, as received from the engine via |
| /// [dart:ui.Window.onPointerDataPacket], and converts them to [PointerEvent] |
| /// objects. |
| class PointerEventConverter { |
| // This class is not meant to be instatiated or extended; this constructor |
| // prevents instantiation and extension. |
| // ignore: unused_element |
| PointerEventConverter._(); |
| |
| /// Expand the given packet of pointer data into a sequence of framework |
| /// pointer events. |
| /// |
| /// 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. |
| static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* { |
| for (final ui.PointerData datum in data) { |
| 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; |
| assert(datum.change != null); |
| if (datum.signalKind == null || datum.signalKind == ui.PointerSignalKind.none) { |
| switch (datum.change) { |
| case ui.PointerChange.add: |
| yield 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: radiusMin, |
| radiusMax: radiusMax, |
| orientation: datum.orientation, |
| tilt: datum.tilt, |
| ); |
| break; |
| case ui.PointerChange.hover: |
| yield PointerHoverEvent( |
| 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, |
| ); |
| break; |
| case ui.PointerChange.down: |
| yield PointerDownEvent( |
| 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, |
| ); |
| break; |
| case ui.PointerChange.move: |
| yield PointerMoveEvent( |
| 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, |
| ); |
| break; |
| case ui.PointerChange.up: |
| yield PointerUpEvent( |
| 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, |
| ); |
| break; |
| case ui.PointerChange.cancel: |
| yield PointerCancelEvent( |
| 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, |
| ); |
| break; |
| case ui.PointerChange.remove: |
| yield PointerRemovedEvent( |
| 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, |
| ); |
| break; |
| } |
| } else { |
| switch (datum.signalKind) { |
| case ui.PointerSignalKind.scroll: |
| final Offset scrollDelta = |
| Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio; |
| yield PointerScrollEvent( |
| timeStamp: timeStamp, |
| kind: kind, |
| device: datum.device, |
| position: position, |
| scrollDelta: scrollDelta, |
| ); |
| break; |
| case ui.PointerSignalKind.none: |
| assert(false); // This branch should already have 'none' filtered out. |
| break; |
| case ui.PointerSignalKind.unknown: |
| // Ignore unknown signals. |
| break; |
| } |
| } |
| } |
| } |
| |
| static double _toLogicalPixels(double physicalPixels, double devicePixelRatio) => |
| physicalPixels == null ? null : physicalPixels / devicePixelRatio; |
| } |