mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
add pointer data santizing in flutter web engine (flutter/engine#14082)
This commit is contained in:
parent
b47d7181da
commit
ffeff2cdf3
@ -417,6 +417,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_converter.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/recording_canvas.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/render_vertices.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart
|
||||
|
||||
@ -62,6 +62,7 @@ part 'engine/picture.dart';
|
||||
part 'engine/platform_views.dart';
|
||||
part 'engine/plugins.dart';
|
||||
part 'engine/pointer_binding.dart';
|
||||
part 'engine/pointer_converter.dart';
|
||||
part 'engine/recording_canvas.dart';
|
||||
part 'engine/render_vertices.dart';
|
||||
part 'engine/rrect_renderer.dart';
|
||||
|
||||
@ -15,23 +15,17 @@ class PointerBinding {
|
||||
static PointerBinding get instance => _instance;
|
||||
static PointerBinding _instance;
|
||||
|
||||
// Set of pointerIds that are added before routing hover and mouse wheel
|
||||
// events.
|
||||
//
|
||||
// The device needs to send a one time PointerChange.add before hover and
|
||||
// wheel events.
|
||||
Set<int> _activePointerIds = <int>{};
|
||||
|
||||
PointerBinding(this.domRenderer) {
|
||||
if (_instance == null) {
|
||||
_instance = this;
|
||||
_pointerDataConverter = PointerDataConverter();
|
||||
_detector = const PointerSupportDetector();
|
||||
_adapter = _createAdapter();
|
||||
}
|
||||
assert(() {
|
||||
registerHotRestartListener(() {
|
||||
_adapter?.clearListeners();
|
||||
_activePointerIds.clear();
|
||||
_pointerDataConverter?.clearPointerState();
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
@ -40,7 +34,7 @@ class PointerBinding {
|
||||
final DomRenderer domRenderer;
|
||||
PointerSupportDetector _detector;
|
||||
BaseAdapter _adapter;
|
||||
|
||||
PointerDataConverter _pointerDataConverter;
|
||||
/// Should be used in tests to define custom detection of pointer support.
|
||||
///
|
||||
/// ```dart
|
||||
@ -62,22 +56,22 @@ class PointerBinding {
|
||||
newDetector ??= const PointerSupportDetector();
|
||||
// When changing the detector, we need to swap the adapter.
|
||||
if (newDetector != _detector) {
|
||||
_activePointerIds.clear();
|
||||
_detector = newDetector;
|
||||
_adapter?.clearListeners();
|
||||
_adapter = _createAdapter();
|
||||
_pointerDataConverter?.clearPointerState();
|
||||
}
|
||||
}
|
||||
|
||||
BaseAdapter _createAdapter() {
|
||||
if (_detector.hasPointerEvents) {
|
||||
return PointerAdapter(_onPointerData, domRenderer);
|
||||
return PointerAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||
}
|
||||
if (_detector.hasTouchEvents) {
|
||||
return TouchAdapter(_onPointerData, domRenderer);
|
||||
return TouchAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||
}
|
||||
if (_detector.hasMouseEvents) {
|
||||
return MouseAdapter(_onPointerData, domRenderer);
|
||||
return MouseAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -123,11 +117,19 @@ class _PressedButton {
|
||||
|
||||
/// Common functionality that's shared among adapters.
|
||||
abstract class BaseAdapter {
|
||||
static final Map<String, html.EventListener> _listeners =
|
||||
<String, html.EventListener>{};
|
||||
BaseAdapter(this._callback, this.domRenderer, this._pointerDataConverter) {
|
||||
_setup();
|
||||
}
|
||||
|
||||
/// Listeners that are registered through dart to js api.
|
||||
static final Map<String, html.EventListener> _listeners =
|
||||
<String, html.EventListener>{};
|
||||
/// Listeners that are registered through native javascript api.
|
||||
static final Map<String, html.EventListener> _nativeListeners =
|
||||
<String, html.EventListener>{};
|
||||
final DomRenderer domRenderer;
|
||||
PointerDataCallback _callback;
|
||||
PointerDataConverter _pointerDataConverter;
|
||||
|
||||
// A set of the buttons that are currently being pressed.
|
||||
Set<_PressedButton> _pressedButtons = Set<_PressedButton>();
|
||||
@ -144,10 +146,6 @@ abstract class BaseAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
BaseAdapter(this._callback, this.domRenderer) {
|
||||
_setup();
|
||||
}
|
||||
|
||||
/// Each subclass is expected to override this method to attach its own event
|
||||
/// listeners and convert events into pointer events.
|
||||
void _setup();
|
||||
@ -156,9 +154,21 @@ abstract class BaseAdapter {
|
||||
void clearListeners() {
|
||||
final html.Element glassPane = domRenderer.glassPaneElement;
|
||||
_listeners.forEach((String eventName, html.EventListener listener) {
|
||||
glassPane.removeEventListener(eventName, listener, true);
|
||||
glassPane.removeEventListener(eventName, listener, true);
|
||||
});
|
||||
// For native listener, we will need to remove it through native javascript
|
||||
// api.
|
||||
_nativeListeners.forEach((String eventName, html.EventListener listener) {
|
||||
js_util.callMethod(
|
||||
domRenderer.glassPaneElement,
|
||||
'removeEventListener', <dynamic>[
|
||||
'wheel',
|
||||
listener,
|
||||
]
|
||||
);
|
||||
});
|
||||
_listeners.clear();
|
||||
_nativeListeners.clear();
|
||||
}
|
||||
|
||||
void _addEventListener(String eventName, html.EventListener handler) {
|
||||
@ -177,6 +187,75 @@ abstract class BaseAdapter {
|
||||
domRenderer.glassPaneElement
|
||||
.addEventListener(eventName, loggedHandler, true);
|
||||
}
|
||||
|
||||
/// Converts a floating number timestamp (in milliseconds) to a [Duration] by
|
||||
/// splitting it into two integer components: milliseconds + microseconds.
|
||||
Duration _eventTimeStampToDuration(num milliseconds) {
|
||||
final int ms = milliseconds.toInt();
|
||||
final int micro =
|
||||
((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt();
|
||||
return Duration(milliseconds: ms, microseconds: micro);
|
||||
}
|
||||
|
||||
List<ui.PointerData> _convertWheelEventToPointerData(
|
||||
html.WheelEvent event,
|
||||
) {
|
||||
const int domDeltaPixel = 0x00;
|
||||
const int domDeltaLine = 0x01;
|
||||
const int domDeltaPage = 0x02;
|
||||
|
||||
// Flutter only supports pixel scroll delta. Convert deltaMode values
|
||||
// to pixels.
|
||||
double deltaX = event.deltaX;
|
||||
double deltaY = event.deltaY;
|
||||
switch (event.deltaMode) {
|
||||
case domDeltaLine:
|
||||
deltaX *= 32.0;
|
||||
deltaY *= 32.0;
|
||||
break;
|
||||
case domDeltaPage:
|
||||
deltaX *= ui.window.physicalSize.width;
|
||||
deltaY *= ui.window.physicalSize.height;
|
||||
break;
|
||||
case domDeltaPixel:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: ui.PointerChange.hover,
|
||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||
kind: ui.PointerDeviceKind.mouse,
|
||||
signalKind: ui.PointerSignalKind.scroll,
|
||||
device: _mouseDeviceId,
|
||||
physicalX: event.client.x * ui.window.devicePixelRatio,
|
||||
physicalY: event.client.y * ui.window.devicePixelRatio,
|
||||
buttons: event.buttons,
|
||||
pressure: 1.0,
|
||||
pressureMin: 0.0,
|
||||
pressureMax: 1.0,
|
||||
scrollDeltaX: deltaX,
|
||||
scrollDeltaY: deltaY,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
void _addWheelEventListener(html.EventListener handler) {
|
||||
final dynamic eventOptions = js_util.newObject();
|
||||
final html.EventListener jsHandler = js.allowInterop((html.Event event) => handler(event));
|
||||
_nativeListeners['wheel'] = jsHandler;
|
||||
js_util.setProperty(eventOptions, 'passive', false);
|
||||
js_util.callMethod(
|
||||
domRenderer.glassPaneElement,
|
||||
'addEventListener', <dynamic>[
|
||||
'wheel',
|
||||
jsHandler,
|
||||
eventOptions
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const int _kPrimaryMouseButton = 0x1;
|
||||
@ -207,16 +286,17 @@ int _deviceFromHtmlEvent(event) {
|
||||
|
||||
/// Adapter class to be used with browsers that support native pointer events.
|
||||
class PointerAdapter extends BaseAdapter {
|
||||
PointerAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
||||
: super(callback, domRenderer);
|
||||
PointerAdapter(
|
||||
PointerDataCallback callback,
|
||||
DomRenderer domRenderer,
|
||||
PointerDataConverter _pointerDataConverter
|
||||
) : super(callback, domRenderer, _pointerDataConverter);
|
||||
|
||||
@override
|
||||
void _setup() {
|
||||
_addEventListener('pointerdown', (html.Event event) {
|
||||
final int pointerButton = _pointerButtonFromHtmlEvent(event);
|
||||
final int device = _deviceFromHtmlEvent(event);
|
||||
// The pointerdown event will cause an 'add' event on the framework side.
|
||||
PointerBinding._instance._activePointerIds.add(device);
|
||||
if (_isButtonDown(device, pointerButton)) {
|
||||
// TODO(flutter_web): Remove this temporary fix for right click
|
||||
// on web platform once context guesture is implemented.
|
||||
@ -239,13 +319,6 @@ class PointerAdapter extends BaseAdapter {
|
||||
? ui.PointerChange.move
|
||||
: ui.PointerChange.hover,
|
||||
pointerEvent);
|
||||
_ensureMouseDeviceAdded(
|
||||
data,
|
||||
pointerEvent.client.x,
|
||||
pointerEvent.client.y,
|
||||
pointerEvent.buttons,
|
||||
pointerEvent.timeStamp,
|
||||
pointerEvent.pointerId);
|
||||
_callback(data);
|
||||
});
|
||||
|
||||
@ -270,7 +343,8 @@ class PointerAdapter extends BaseAdapter {
|
||||
_callback(_convertEventToPointerData(ui.PointerChange.cancel, event));
|
||||
});
|
||||
|
||||
_addWheelEventListener((html.WheelEvent event) {
|
||||
_addWheelEventListener((html.Event event) {
|
||||
assert(event is html.WheelEvent);
|
||||
if (_debugLogPointerEvents) {
|
||||
print(event.type);
|
||||
}
|
||||
@ -289,7 +363,8 @@ class PointerAdapter extends BaseAdapter {
|
||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||
for (int i = 0; i < allEvents.length; i++) {
|
||||
final html.PointerEvent event = allEvents[i];
|
||||
data.add(ui.PointerData(
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: change,
|
||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||
kind: _pointerTypeToDeviceKind(event.pointerType),
|
||||
@ -301,7 +376,7 @@ class PointerAdapter extends BaseAdapter {
|
||||
pressureMin: 0.0,
|
||||
pressureMax: 1.0,
|
||||
tilt: _computeHighestTilt(event),
|
||||
));
|
||||
);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@ -343,8 +418,11 @@ class PointerAdapter extends BaseAdapter {
|
||||
|
||||
/// Adapter to be used with browsers that support touch events.
|
||||
class TouchAdapter extends BaseAdapter {
|
||||
TouchAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
||||
: super(callback, domRenderer);
|
||||
TouchAdapter(
|
||||
PointerDataCallback callback,
|
||||
DomRenderer domRenderer,
|
||||
PointerDataConverter _pointerDataConverter
|
||||
) : super(callback, domRenderer, _pointerDataConverter);
|
||||
|
||||
@override
|
||||
void _setup() {
|
||||
@ -381,11 +459,12 @@ class TouchAdapter extends BaseAdapter {
|
||||
html.TouchEvent event,
|
||||
) {
|
||||
final html.TouchList touches = event.changedTouches;
|
||||
final List<ui.PointerData> data = List<ui.PointerData>(touches.length);
|
||||
final List<ui.PointerData> data = List<ui.PointerData>();
|
||||
final int len = touches.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
final html.Touch touch = touches[i];
|
||||
data[i] = ui.PointerData(
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: change,
|
||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||
kind: ui.PointerDeviceKind.touch,
|
||||
@ -408,8 +487,11 @@ const int _mouseDeviceId = -1;
|
||||
|
||||
/// Adapter to be used with browsers that support mouse events.
|
||||
class MouseAdapter extends BaseAdapter {
|
||||
MouseAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
||||
: super(callback, domRenderer);
|
||||
MouseAdapter(
|
||||
PointerDataCallback callback,
|
||||
DomRenderer domRenderer,
|
||||
PointerDataConverter _pointerDataConverter
|
||||
) : super(callback, domRenderer, _pointerDataConverter);
|
||||
|
||||
@override
|
||||
void _setup() {
|
||||
@ -442,7 +524,8 @@ class MouseAdapter extends BaseAdapter {
|
||||
_callback(_convertEventToPointerData(ui.PointerChange.up, event));
|
||||
});
|
||||
|
||||
_addWheelEventListener((html.WheelEvent event) {
|
||||
_addWheelEventListener((html.Event event) {
|
||||
assert(event is html.WheelEvent);
|
||||
if (_debugLogPointerEvents) {
|
||||
print(event.type);
|
||||
}
|
||||
@ -455,16 +538,9 @@ class MouseAdapter extends BaseAdapter {
|
||||
ui.PointerChange change,
|
||||
html.MouseEvent event,
|
||||
) {
|
||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||
// The mousedown event will cause an 'add' event on the framework side.
|
||||
if (event.type == 'mousedown') {
|
||||
PointerBinding._instance._activePointerIds.add(_mouseDeviceId);
|
||||
}
|
||||
if (event.type == 'mousemove') {
|
||||
_ensureMouseDeviceAdded(data, event.client.x, event.client.y,
|
||||
event.buttons, event.timeStamp, _mouseDeviceId);
|
||||
}
|
||||
data.add(ui.PointerData(
|
||||
List<ui.PointerData> data = <ui.PointerData>[];
|
||||
_pointerDataConverter.convert(
|
||||
data,
|
||||
change: change,
|
||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||
kind: ui.PointerDeviceKind.mouse,
|
||||
@ -476,101 +552,7 @@ class MouseAdapter extends BaseAdapter {
|
||||
pressure: 1.0,
|
||||
pressureMin: 0.0,
|
||||
pressureMax: 1.0,
|
||||
));
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a floating number timestamp (in milliseconds) to a [Duration] by
|
||||
/// splitting it into two integer components: milliseconds + microseconds.
|
||||
Duration _eventTimeStampToDuration(num milliseconds) {
|
||||
final int ms = milliseconds.toInt();
|
||||
final int micro =
|
||||
((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt();
|
||||
return Duration(milliseconds: ms, microseconds: micro);
|
||||
}
|
||||
|
||||
void _ensureMouseDeviceAdded(List<ui.PointerData> data, double clientX,
|
||||
double clientY, int buttons, double timeStamp, int deviceId) {
|
||||
if (PointerBinding.instance._activePointerIds.contains(deviceId)) {
|
||||
return;
|
||||
}
|
||||
PointerBinding.instance._activePointerIds.add(deviceId);
|
||||
// Only send [PointerChange.add] the first time.
|
||||
data.insert(
|
||||
0,
|
||||
ui.PointerData(
|
||||
change: ui.PointerChange.add,
|
||||
timeStamp: _eventTimeStampToDuration(timeStamp),
|
||||
kind: ui.PointerDeviceKind.mouse,
|
||||
// In order for Flutter to actually add this pointer, we need to set the
|
||||
// signal to none.
|
||||
signalKind: ui.PointerSignalKind.none,
|
||||
device: deviceId,
|
||||
physicalX: clientX * ui.window.devicePixelRatio,
|
||||
physicalY: clientY * ui.window.devicePixelRatio,
|
||||
buttons: buttons,
|
||||
pressure: 1.0,
|
||||
pressureMin: 0.0,
|
||||
pressureMax: 1.0,
|
||||
scrollDeltaX: 0,
|
||||
scrollDeltaY: 0,
|
||||
));
|
||||
}
|
||||
|
||||
List<ui.PointerData> _convertWheelEventToPointerData(
|
||||
html.WheelEvent event,
|
||||
) {
|
||||
const int domDeltaPixel = 0x00;
|
||||
const int domDeltaLine = 0x01;
|
||||
const int domDeltaPage = 0x02;
|
||||
|
||||
// Flutter only supports pixel scroll delta. Convert deltaMode values
|
||||
// to pixels.
|
||||
double deltaX = event.deltaX;
|
||||
double deltaY = event.deltaY;
|
||||
switch (event.deltaMode) {
|
||||
case domDeltaLine:
|
||||
deltaX *= 32.0;
|
||||
deltaY *= 32.0;
|
||||
break;
|
||||
case domDeltaPage:
|
||||
deltaX *= ui.window.physicalSize.width;
|
||||
deltaY *= ui.window.physicalSize.height;
|
||||
break;
|
||||
case domDeltaPixel:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||
_ensureMouseDeviceAdded(data, event.client.x, event.client.y, event.buttons,
|
||||
event.timeStamp, _mouseDeviceId);
|
||||
data.add(ui.PointerData(
|
||||
change: ui.PointerChange.hover,
|
||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||
kind: ui.PointerDeviceKind.mouse,
|
||||
signalKind: ui.PointerSignalKind.scroll,
|
||||
device: _mouseDeviceId,
|
||||
physicalX: event.client.x * ui.window.devicePixelRatio,
|
||||
physicalY: event.client.y * ui.window.devicePixelRatio,
|
||||
buttons: event.buttons,
|
||||
pressure: 1.0,
|
||||
pressureMin: 0.0,
|
||||
pressureMax: 1.0,
|
||||
scrollDeltaX: deltaX,
|
||||
scrollDeltaY: deltaY,
|
||||
));
|
||||
return data;
|
||||
}
|
||||
|
||||
void _addWheelEventListener(void listener(html.WheelEvent e)) {
|
||||
final dynamic eventOptions = js_util.newObject();
|
||||
js_util.setProperty(eventOptions, 'passive', false);
|
||||
js_util.callMethod(PointerBinding.instance.domRenderer.glassPaneElement,
|
||||
'addEventListener', <dynamic>[
|
||||
'wheel',
|
||||
js.allowInterop((html.WheelEvent event) => listener(event)),
|
||||
eventOptions
|
||||
]);
|
||||
}
|
||||
|
||||
@ -0,0 +1,640 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
part of engine;
|
||||
|
||||
class _PointerState {
|
||||
_PointerState(this.x, this.y);
|
||||
|
||||
/// The identifier used in framework hit test.
|
||||
int get pointer => _pointer;
|
||||
int _pointer;
|
||||
static int _pointerCount = 0;
|
||||
void startNewPointer() {
|
||||
_pointerCount += 1;
|
||||
_pointer = _pointerCount;
|
||||
}
|
||||
|
||||
bool down = false;
|
||||
|
||||
double x;
|
||||
double y;
|
||||
}
|
||||
|
||||
/// Converter to convert web pointer data into a form that framework can
|
||||
/// understand.
|
||||
///
|
||||
/// This converter calculates pointer location delta and pointer identifier for
|
||||
/// each pointer. Both are required by framework to correctly trigger gesture
|
||||
/// activity. It also attempts to sanitize pointer data input sequence by always
|
||||
/// synthesizing an add pointer data prior to hover or down if it the pointer is
|
||||
/// not previously added.
|
||||
///
|
||||
/// For example:
|
||||
/// before:
|
||||
/// hover -> down -> move -> up
|
||||
/// after:
|
||||
/// add(synthesize) -> hover -> down -> move -> up
|
||||
///
|
||||
/// before:
|
||||
/// down -> move -> up
|
||||
/// after:
|
||||
/// add(synthesize) -> down -> move -> up
|
||||
class PointerDataConverter {
|
||||
PointerDataConverter();
|
||||
|
||||
// Map from browser pointer identifiers to PointerEvent pointer identifiers.
|
||||
final Map<int, _PointerState> _pointers = <int, _PointerState>{};
|
||||
|
||||
/// Clears the existing pointer states.
|
||||
///
|
||||
/// This method is invoked during hot reload to make sure we have a clean
|
||||
/// converter after hot reload.
|
||||
void clearPointerState() {
|
||||
_pointers.clear();
|
||||
_PointerState._pointerCount = 0;
|
||||
}
|
||||
|
||||
_PointerState _ensureStateForPointer(int device, double x, double y) {
|
||||
return _pointers.putIfAbsent(
|
||||
device,
|
||||
() => _PointerState(x, y),
|
||||
);
|
||||
}
|
||||
|
||||
ui.PointerData _generateCompletePointerData({
|
||||
Duration timeStamp,
|
||||
ui.PointerChange change,
|
||||
ui.PointerDeviceKind kind,
|
||||
ui.PointerSignalKind signalKind,
|
||||
int device,
|
||||
double physicalX,
|
||||
double physicalY,
|
||||
int buttons,
|
||||
bool obscured,
|
||||
double pressure,
|
||||
double pressureMin,
|
||||
double pressureMax,
|
||||
double distance,
|
||||
double distanceMax,
|
||||
double size,
|
||||
double radiusMajor,
|
||||
double radiusMinor,
|
||||
double radiusMin,
|
||||
double radiusMax,
|
||||
double orientation,
|
||||
double tilt,
|
||||
int platformData,
|
||||
double scrollDeltaX,
|
||||
double scrollDeltaY,
|
||||
}) {
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
final double deltaX = physicalX - state.x;
|
||||
final double deltaY = physicalY - state.y;
|
||||
state.x = physicalX;
|
||||
state.y = physicalY;
|
||||
return ui.PointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
pointerIdentifier: state.pointer ?? 0,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
physicalDeltaX: deltaX,
|
||||
physicalDeltaY: deltaY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
);
|
||||
}
|
||||
|
||||
bool _locationHasChanged(int device, double physicalX, double physicalY) {
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
return state.x != physicalX || state.y != physicalY;
|
||||
}
|
||||
|
||||
ui.PointerData _synthesizePointerData({
|
||||
Duration timeStamp,
|
||||
ui.PointerChange change,
|
||||
ui.PointerDeviceKind kind,
|
||||
int device,
|
||||
double physicalX,
|
||||
double physicalY,
|
||||
int buttons,
|
||||
bool obscured,
|
||||
double pressure,
|
||||
double pressureMin,
|
||||
double pressureMax,
|
||||
double distance,
|
||||
double distanceMax,
|
||||
double size,
|
||||
double radiusMajor,
|
||||
double radiusMinor,
|
||||
double radiusMin,
|
||||
double radiusMax,
|
||||
double orientation,
|
||||
double tilt,
|
||||
int platformData,
|
||||
double scrollDeltaX,
|
||||
double scrollDeltaY,
|
||||
}) {
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
final double deltaX = physicalX - state.x;
|
||||
final double deltaY = physicalY - state.y;
|
||||
state.x = physicalX;
|
||||
state.y = physicalY;
|
||||
return ui.PointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
// All the pointer data except scroll should not have a signal kind, and
|
||||
// there is no use case for synthetic scroll event. We should be
|
||||
// safe to default it to ui.PointerSignalKind.none.
|
||||
signalKind: ui.PointerSignalKind.none,
|
||||
device: device,
|
||||
pointerIdentifier: state.pointer ?? 0,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
physicalDeltaX: deltaX,
|
||||
physicalDeltaY: deltaY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
synthesized: true,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts the given html pointer event metrics into a sequence of framework-compatible
|
||||
/// pointer data and stores it into [result]
|
||||
void convert(
|
||||
List<ui.PointerData> result, {
|
||||
Duration timeStamp = Duration.zero,
|
||||
ui.PointerChange change = ui.PointerChange.cancel,
|
||||
ui.PointerDeviceKind kind = ui.PointerDeviceKind.touch,
|
||||
ui.PointerSignalKind signalKind,
|
||||
int device = 0,
|
||||
double physicalX = 0.0,
|
||||
double physicalY = 0.0,
|
||||
int buttons = 0,
|
||||
bool obscured = false,
|
||||
double pressure = 0.0,
|
||||
double pressureMin = 0.0,
|
||||
double pressureMax = 0.0,
|
||||
double distance = 0.0,
|
||||
double distanceMax = 0.0,
|
||||
double size = 0.0,
|
||||
double radiusMajor = 0.0,
|
||||
double radiusMinor = 0.0,
|
||||
double radiusMin = 0.0,
|
||||
double radiusMax = 0.0,
|
||||
double orientation = 0.0,
|
||||
double tilt = 0.0,
|
||||
int platformData = 0,
|
||||
double scrollDeltaX = 0.0,
|
||||
double scrollDeltaY = 0.0,
|
||||
}) {
|
||||
assert(change != null);
|
||||
if (signalKind == null ||
|
||||
signalKind == ui.PointerSignalKind.none) {
|
||||
switch (change) {
|
||||
case ui.PointerChange.add:
|
||||
assert(!_pointers.containsKey(device));
|
||||
_ensureStateForPointer(device, physicalX, physicalY);
|
||||
assert(!_locationHasChanged(device, physicalX, physicalY));
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.hover:
|
||||
final bool alreadyAdded = _pointers.containsKey(device);
|
||||
final _PointerState state = _ensureStateForPointer(
|
||||
device, physicalX, physicalY);
|
||||
assert(!state.down);
|
||||
if (!alreadyAdded) {
|
||||
// Synthesizes an add pointer data.
|
||||
result.add(
|
||||
_synthesizePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: ui.PointerChange.add,
|
||||
kind: kind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
}
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.down:
|
||||
final bool alreadyAdded = _pointers.containsKey(device);
|
||||
final _PointerState state = _ensureStateForPointer(
|
||||
device, physicalX, physicalY);
|
||||
assert(!state.down);
|
||||
if (!alreadyAdded) {
|
||||
// Synthesizes an add pointer data.
|
||||
result.add(
|
||||
_synthesizePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: ui.PointerChange.add,
|
||||
kind: kind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
}
|
||||
assert(!_locationHasChanged(device, physicalX, physicalY));
|
||||
state.startNewPointer();
|
||||
state.down = true;
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.move:
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
assert(state.down);
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.up:
|
||||
case ui.PointerChange.cancel:
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
assert(state.down);
|
||||
assert(!_locationHasChanged(device, physicalX, physicalY));
|
||||
state.down = false;
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.remove:
|
||||
assert(_pointers.containsKey(device));
|
||||
final _PointerState state = _pointers[device];
|
||||
assert(!state.down);
|
||||
assert(!_locationHasChanged(device, physicalX, physicalY));
|
||||
_pointers.remove(device);
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (signalKind) {
|
||||
case ui.PointerSignalKind.scroll:
|
||||
final bool alreadyAdded = _pointers.containsKey(device);
|
||||
final _PointerState state = _ensureStateForPointer(
|
||||
device, physicalX, physicalY);
|
||||
if (!alreadyAdded) {
|
||||
// Synthesizes an add pointer data.
|
||||
result.add(
|
||||
_synthesizePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: ui.PointerChange.add,
|
||||
kind: kind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
}
|
||||
if (_locationHasChanged(device, physicalX, physicalY)) {
|
||||
// Synthesize a hover/move of the pointer to the scroll location
|
||||
// before sending the scroll event, if necessary, so that clients
|
||||
// don't have to worry about native ordering of hover and scroll
|
||||
// events.
|
||||
if (state.down) {
|
||||
result.add(
|
||||
_synthesizePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: ui.PointerChange.move,
|
||||
kind: kind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
result.add(
|
||||
_synthesizePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: ui.PointerChange.hover,
|
||||
kind: kind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
result.add(
|
||||
_generateCompletePointerData(
|
||||
timeStamp: timeStamp,
|
||||
change: change,
|
||||
kind: kind,
|
||||
signalKind: signalKind,
|
||||
device: device,
|
||||
physicalX: physicalX,
|
||||
physicalY: physicalY,
|
||||
buttons: buttons,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
size: size,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt,
|
||||
platformData: platformData,
|
||||
scrollDeltaX: scrollDeltaX,
|
||||
scrollDeltaY: scrollDeltaY,
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ui.PointerSignalKind.none:
|
||||
assert(false); // This branch should already have 'none' filtered out.
|
||||
break;
|
||||
case ui.PointerSignalKind.unknown:
|
||||
// Ignore unknown signals.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,11 @@ void main() {
|
||||
}));
|
||||
|
||||
expect(packets, hasLength(3));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.down));
|
||||
// An add will be synthesized.
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets[0].data[0].synthesized, equals(true));
|
||||
expect(packets[0].data[1].change, equals(ui.PointerChange.down));
|
||||
expect(packets[1].data[0].change, equals(ui.PointerChange.up));
|
||||
expect(packets[2].data[0].change, equals(ui.PointerChange.down));
|
||||
});
|
||||
@ -78,10 +82,20 @@ void main() {
|
||||
}));
|
||||
|
||||
expect(packets, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.down));
|
||||
// An add will be synthesized.
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets[0].data[0].synthesized, equals(true));
|
||||
expect(packets[0].data[0].device, equals(1));
|
||||
expect(packets[1].data[0].change, equals(ui.PointerChange.down));
|
||||
expect(packets[0].data[1].change, equals(ui.PointerChange.down));
|
||||
expect(packets[0].data[1].device, equals(1));
|
||||
// An add will be synthesized.
|
||||
expect(packets[1].data, hasLength(2));
|
||||
expect(packets[1].data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets[1].data[0].synthesized, equals(true));
|
||||
expect(packets[1].data[0].device, equals(2));
|
||||
expect(packets[1].data[1].change, equals(ui.PointerChange.down));
|
||||
expect(packets[1].data[1].device, equals(2));
|
||||
});
|
||||
|
||||
test('creates an add event if the first pointer activity is a hover', () {
|
||||
@ -99,10 +113,11 @@ void main() {
|
||||
expect(packets.single.data, hasLength(2));
|
||||
|
||||
expect(packets.single.data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets.single.data[0].synthesized, equals(true));
|
||||
expect(packets.single.data[1].change, equals(ui.PointerChange.hover));
|
||||
});
|
||||
|
||||
test('does not create an add event if got a pointerdown', () {
|
||||
test('does create an add event if got a pointerdown', () {
|
||||
List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[];
|
||||
ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {
|
||||
packets.add(packet);
|
||||
@ -114,9 +129,247 @@ void main() {
|
||||
}));
|
||||
|
||||
expect(packets, hasLength(1));
|
||||
expect(packets.single.data, hasLength(1));
|
||||
expect(packets.single.data, hasLength(2));
|
||||
|
||||
expect(packets.single.data[0].change, equals(ui.PointerChange.down));
|
||||
expect(packets.single.data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets.single.data[1].change, equals(ui.PointerChange.down));
|
||||
});
|
||||
|
||||
test('does calculate delta and pointer identifier correctly', () {
|
||||
List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[];
|
||||
ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointermove', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 10.0,
|
||||
'clientY': 10.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointermove', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 20.0,
|
||||
'clientY': 20.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointerdown', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 20.0,
|
||||
'clientY': 20.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointermove', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 40.0,
|
||||
'clientY': 30.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointerup', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 40.0,
|
||||
'clientY': 30.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointermove', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 20.0,
|
||||
'clientY': 10.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointerdown', {
|
||||
'pointerId': 1,
|
||||
'button': 1,
|
||||
'clientX': 20.0,
|
||||
'clientY': 10.0,
|
||||
}));
|
||||
|
||||
expect(packets, hasLength(7));
|
||||
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets[0].data[0].pointerIdentifier, equals(0));
|
||||
expect(packets[0].data[0].synthesized, equals(true));
|
||||
expect(packets[0].data[0].physicalX, equals(10.0));
|
||||
expect(packets[0].data[0].physicalY, equals(10.0));
|
||||
expect(packets[0].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
|
||||
|
||||
expect(packets[0].data[1].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[0].data[1].pointerIdentifier, equals(0));
|
||||
expect(packets[0].data[1].synthesized, equals(false));
|
||||
expect(packets[0].data[1].physicalX, equals(10.0));
|
||||
expect(packets[0].data[1].physicalY, equals(10.0));
|
||||
expect(packets[0].data[1].physicalDeltaX, equals(0.0));
|
||||
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
|
||||
|
||||
expect(packets[1].data, hasLength(1));
|
||||
expect(packets[1].data[0].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[1].data[0].pointerIdentifier, equals(0));
|
||||
expect(packets[1].data[0].synthesized, equals(false));
|
||||
expect(packets[1].data[0].physicalX, equals(20.0));
|
||||
expect(packets[1].data[0].physicalY, equals(20.0));
|
||||
expect(packets[1].data[0].physicalDeltaX, equals(10.0));
|
||||
expect(packets[1].data[0].physicalDeltaY, equals(10.0));
|
||||
|
||||
expect(packets[2].data, hasLength(1));
|
||||
expect(packets[2].data[0].change, equals(ui.PointerChange.down));
|
||||
expect(packets[2].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[2].data[0].synthesized, equals(false));
|
||||
expect(packets[2].data[0].physicalX, equals(20.0));
|
||||
expect(packets[2].data[0].physicalY, equals(20.0));
|
||||
expect(packets[2].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[2].data[0].physicalDeltaY, equals(0.0));
|
||||
|
||||
expect(packets[3].data, hasLength(1));
|
||||
expect(packets[3].data[0].change, equals(ui.PointerChange.move));
|
||||
expect(packets[3].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[3].data[0].synthesized, equals(false));
|
||||
expect(packets[3].data[0].physicalX, equals(40.0));
|
||||
expect(packets[3].data[0].physicalY, equals(30.0));
|
||||
expect(packets[3].data[0].physicalDeltaX, equals(20.0));
|
||||
expect(packets[3].data[0].physicalDeltaY, equals(10.0));
|
||||
|
||||
expect(packets[4].data, hasLength(1));
|
||||
expect(packets[4].data[0].change, equals(ui.PointerChange.up));
|
||||
expect(packets[4].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[4].data[0].synthesized, equals(false));
|
||||
expect(packets[4].data[0].physicalX, equals(40.0));
|
||||
expect(packets[4].data[0].physicalY, equals(30.0));
|
||||
expect(packets[4].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[4].data[0].physicalDeltaY, equals(0.0));
|
||||
|
||||
expect(packets[5].data, hasLength(1));
|
||||
expect(packets[5].data[0].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[5].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[5].data[0].synthesized, equals(false));
|
||||
expect(packets[5].data[0].physicalX, equals(20.0));
|
||||
expect(packets[5].data[0].physicalY, equals(10.0));
|
||||
expect(packets[5].data[0].physicalDeltaX, equals(-20.0));
|
||||
expect(packets[5].data[0].physicalDeltaY, equals(-20.0));
|
||||
|
||||
expect(packets[6].data, hasLength(1));
|
||||
expect(packets[6].data[0].change, equals(ui.PointerChange.down));
|
||||
expect(packets[6].data[0].pointerIdentifier, equals(2));
|
||||
expect(packets[6].data[0].synthesized, equals(false));
|
||||
expect(packets[6].data[0].physicalX, equals(20.0));
|
||||
expect(packets[6].data[0].physicalY, equals(10.0));
|
||||
expect(packets[6].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[6].data[0].physicalDeltaY, equals(0.0));
|
||||
});
|
||||
|
||||
test('does synthesize add or hover or more for scroll', () {
|
||||
List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[];
|
||||
ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {
|
||||
packets.add(packet);
|
||||
};
|
||||
|
||||
glassPane.dispatchEvent(html.WheelEvent('wheel',
|
||||
button: 1,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
deltaX: 10,
|
||||
deltaY: 10,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(html.WheelEvent('wheel',
|
||||
button: 1,
|
||||
clientX: 20,
|
||||
clientY: 50,
|
||||
deltaX: 10,
|
||||
deltaY: 10,
|
||||
));
|
||||
|
||||
glassPane.dispatchEvent(html.PointerEvent('pointerdown', {
|
||||
'pointerId': -1,
|
||||
'button': 1,
|
||||
'clientX': 20.0,
|
||||
'clientY': 50.0,
|
||||
}));
|
||||
|
||||
glassPane.dispatchEvent(html.WheelEvent('wheel',
|
||||
button: 1,
|
||||
clientX: 30,
|
||||
clientY: 60,
|
||||
deltaX: 10,
|
||||
deltaY: 10,
|
||||
));
|
||||
|
||||
expect(packets, hasLength(4));
|
||||
|
||||
// An add will be synthesized.
|
||||
expect(packets[0].data, hasLength(2));
|
||||
expect(packets[0].data[0].change, equals(ui.PointerChange.add));
|
||||
expect(packets[0].data[0].pointerIdentifier, equals(0));
|
||||
expect(packets[0].data[0].synthesized, equals(true));
|
||||
expect(packets[0].data[0].physicalX, equals(10.0));
|
||||
expect(packets[0].data[0].physicalY, equals(10.0));
|
||||
expect(packets[0].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[0].data[0].physicalDeltaY, equals(0.0));
|
||||
|
||||
expect(packets[0].data[1].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll));
|
||||
expect(packets[0].data[1].pointerIdentifier, equals(0));
|
||||
expect(packets[0].data[1].synthesized, equals(false));
|
||||
expect(packets[0].data[1].physicalX, equals(10.0));
|
||||
expect(packets[0].data[1].physicalY, equals(10.0));
|
||||
expect(packets[0].data[1].physicalDeltaX, equals(0.0));
|
||||
expect(packets[0].data[1].physicalDeltaY, equals(0.0));
|
||||
|
||||
// A hover will be synthesized.
|
||||
expect(packets[1].data, hasLength(2));
|
||||
expect(packets[1].data[0].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[1].data[0].pointerIdentifier, equals(0));
|
||||
expect(packets[1].data[0].synthesized, equals(true));
|
||||
expect(packets[1].data[0].physicalX, equals(20.0));
|
||||
expect(packets[1].data[0].physicalY, equals(50.0));
|
||||
expect(packets[1].data[0].physicalDeltaX, equals(10.0));
|
||||
expect(packets[1].data[0].physicalDeltaY, equals(40.0));
|
||||
|
||||
expect(packets[1].data[1].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll));
|
||||
expect(packets[1].data[1].pointerIdentifier, equals(0));
|
||||
expect(packets[1].data[1].synthesized, equals(false));
|
||||
expect(packets[1].data[1].physicalX, equals(20.0));
|
||||
expect(packets[1].data[1].physicalY, equals(50.0));
|
||||
expect(packets[1].data[1].physicalDeltaX, equals(0.0));
|
||||
expect(packets[1].data[1].physicalDeltaY, equals(0.0));
|
||||
|
||||
// No synthetic pointer data for down event.
|
||||
expect(packets[2].data, hasLength(1));
|
||||
expect(packets[2].data[0].change, equals(ui.PointerChange.down));
|
||||
expect(packets[2].data[0].signalKind, equals(null));
|
||||
expect(packets[2].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[2].data[0].synthesized, equals(false));
|
||||
expect(packets[2].data[0].physicalX, equals(20.0));
|
||||
expect(packets[2].data[0].physicalY, equals(50.0));
|
||||
expect(packets[2].data[0].physicalDeltaX, equals(0.0));
|
||||
expect(packets[2].data[0].physicalDeltaY, equals(0.0));
|
||||
|
||||
// A move will be synthesized instead of hover because the button is currently down.
|
||||
expect(packets[3].data, hasLength(2));
|
||||
expect(packets[3].data[0].change, equals(ui.PointerChange.move));
|
||||
expect(packets[3].data[0].pointerIdentifier, equals(1));
|
||||
expect(packets[3].data[0].synthesized, equals(true));
|
||||
expect(packets[3].data[0].physicalX, equals(30.0));
|
||||
expect(packets[3].data[0].physicalY, equals(60.0));
|
||||
expect(packets[3].data[0].physicalDeltaX, equals(10.0));
|
||||
expect(packets[3].data[0].physicalDeltaY, equals(10.0));
|
||||
|
||||
expect(packets[3].data[1].change, equals(ui.PointerChange.hover));
|
||||
expect(packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll));
|
||||
expect(packets[3].data[1].pointerIdentifier, equals(1));
|
||||
expect(packets[3].data[1].synthesized, equals(false));
|
||||
expect(packets[3].data[1].physicalX, equals(30.0));
|
||||
expect(packets[3].data[1].physicalY, equals(60.0));
|
||||
expect(packets[3].data[1].physicalDeltaX, equals(0.0));
|
||||
expect(packets[3].data[1].physicalDeltaY, equals(0.0));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user