blob: 8e8fecdb8028c6db0cfbda4204b4f454ebab1cdc [file] [log] [blame]
// 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.
import 'dart:ui' as ui show PointerData, PointerChange;
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;
}
Offset lastPosition;
@override
String toString() {
return '_PointerState(pointer: $pointer, down: $down, lastPosition: $lastPosition)';
}
}
/// 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 {
PointerEventConverter._();
// Map from platform pointer identifiers to PointerEvent pointer identifiers.
static final Map<int, _PointerState> _pointers = <int, _PointerState>{};
static _PointerState _ensureStateForPointer(ui.PointerData datum, Offset position) {
return _pointers.putIfAbsent(
datum.device,
() => new _PointerState(position)
);
}
/// 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 (ui.PointerData datum in data) {
final Offset position = new Offset(datum.physicalX, datum.physicalY) / devicePixelRatio;
final Duration timeStamp = datum.timeStamp;
final PointerDeviceKind kind = datum.kind;
assert(datum.change != null);
switch (datum.change) {
case ui.PointerChange.add:
assert(!_pointers.containsKey(datum.device));
final _PointerState state = _ensureStateForPointer(datum, position);
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
);
break;
case ui.PointerChange.hover:
final bool alreadyAdded = _pointers.containsKey(datum.device);
final _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
);
}
final 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);
final _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.
final 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,
synthesized: true,
);
state.lastPosition = position;
}
state.startNewPointer();
state.setDown();
yield new PointerDownEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position,
buttons: datum.buttons,
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;
case ui.PointerChange.move:
// 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
assert(_pointers.containsKey(datum.device));
final _PointerState state = _pointers[datum.device];
assert(state.down);
final Offset offset = position - state.lastPosition;
state.lastPosition = position;
yield new PointerMoveEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position,
delta: offset,
buttons: datum.buttons,
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;
case ui.PointerChange.up:
case ui.PointerChange.cancel:
assert(_pointers.containsKey(datum.device));
final _PointerState state = _pointers[datum.device];
assert(state.down);
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.
final Offset offset = position - state.lastPosition;
state.lastPosition = position;
yield new PointerMoveEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position,
delta: offset,
buttons: datum.buttons,
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,
synthesized: true,
);
state.lastPosition = position;
}
assert(position == state.lastPosition);
state.setUp();
if (datum.change == ui.PointerChange.up) {
yield new PointerUpEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position,
buttons: datum.buttons,
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,
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
);
}
break;
case ui.PointerChange.remove:
assert(_pointers.containsKey(datum.device));
final _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);
yield new PointerRemovedEvent(
timeStamp: timeStamp,
kind: kind,
device: datum.device,
obscured: datum.obscured,
pressureMin: datum.pressureMin,
pressureMax: datum.pressureMax,
distanceMax: datum.distanceMax,
radiusMin: datum.radiusMin,
radiusMax: datum.radiusMax
);
break;
}
}
}
}