mirror of
https://github.com/encounter/engine.git
synced 2026-07-10 03:18:43 -07:00
add pointer data santizing in flutter web engine (#14082)
This commit is contained in:
@@ -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/platform_views.dart
|
||||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.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_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/recording_canvas.dart
|
||||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/render_vertices.dart
|
FILE: ../../../flutter/lib/web_ui/lib/src/engine/render_vertices.dart
|
||||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.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/platform_views.dart';
|
||||||
part 'engine/plugins.dart';
|
part 'engine/plugins.dart';
|
||||||
part 'engine/pointer_binding.dart';
|
part 'engine/pointer_binding.dart';
|
||||||
|
part 'engine/pointer_converter.dart';
|
||||||
part 'engine/recording_canvas.dart';
|
part 'engine/recording_canvas.dart';
|
||||||
part 'engine/render_vertices.dart';
|
part 'engine/render_vertices.dart';
|
||||||
part 'engine/rrect_renderer.dart';
|
part 'engine/rrect_renderer.dart';
|
||||||
|
|||||||
@@ -15,23 +15,17 @@ class PointerBinding {
|
|||||||
static PointerBinding get instance => _instance;
|
static PointerBinding get instance => _instance;
|
||||||
static PointerBinding _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) {
|
PointerBinding(this.domRenderer) {
|
||||||
if (_instance == null) {
|
if (_instance == null) {
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
_pointerDataConverter = PointerDataConverter();
|
||||||
_detector = const PointerSupportDetector();
|
_detector = const PointerSupportDetector();
|
||||||
_adapter = _createAdapter();
|
_adapter = _createAdapter();
|
||||||
}
|
}
|
||||||
assert(() {
|
assert(() {
|
||||||
registerHotRestartListener(() {
|
registerHotRestartListener(() {
|
||||||
_adapter?.clearListeners();
|
_adapter?.clearListeners();
|
||||||
_activePointerIds.clear();
|
_pointerDataConverter?.clearPointerState();
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
@@ -40,7 +34,7 @@ class PointerBinding {
|
|||||||
final DomRenderer domRenderer;
|
final DomRenderer domRenderer;
|
||||||
PointerSupportDetector _detector;
|
PointerSupportDetector _detector;
|
||||||
BaseAdapter _adapter;
|
BaseAdapter _adapter;
|
||||||
|
PointerDataConverter _pointerDataConverter;
|
||||||
/// Should be used in tests to define custom detection of pointer support.
|
/// Should be used in tests to define custom detection of pointer support.
|
||||||
///
|
///
|
||||||
/// ```dart
|
/// ```dart
|
||||||
@@ -62,22 +56,22 @@ class PointerBinding {
|
|||||||
newDetector ??= const PointerSupportDetector();
|
newDetector ??= const PointerSupportDetector();
|
||||||
// When changing the detector, we need to swap the adapter.
|
// When changing the detector, we need to swap the adapter.
|
||||||
if (newDetector != _detector) {
|
if (newDetector != _detector) {
|
||||||
_activePointerIds.clear();
|
|
||||||
_detector = newDetector;
|
_detector = newDetector;
|
||||||
_adapter?.clearListeners();
|
_adapter?.clearListeners();
|
||||||
_adapter = _createAdapter();
|
_adapter = _createAdapter();
|
||||||
|
_pointerDataConverter?.clearPointerState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseAdapter _createAdapter() {
|
BaseAdapter _createAdapter() {
|
||||||
if (_detector.hasPointerEvents) {
|
if (_detector.hasPointerEvents) {
|
||||||
return PointerAdapter(_onPointerData, domRenderer);
|
return PointerAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||||
}
|
}
|
||||||
if (_detector.hasTouchEvents) {
|
if (_detector.hasTouchEvents) {
|
||||||
return TouchAdapter(_onPointerData, domRenderer);
|
return TouchAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||||
}
|
}
|
||||||
if (_detector.hasMouseEvents) {
|
if (_detector.hasMouseEvents) {
|
||||||
return MouseAdapter(_onPointerData, domRenderer);
|
return MouseAdapter(_onPointerData, domRenderer, _pointerDataConverter);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -123,11 +117,19 @@ class _PressedButton {
|
|||||||
|
|
||||||
/// Common functionality that's shared among adapters.
|
/// Common functionality that's shared among adapters.
|
||||||
abstract class BaseAdapter {
|
abstract class BaseAdapter {
|
||||||
static final Map<String, html.EventListener> _listeners =
|
BaseAdapter(this._callback, this.domRenderer, this._pointerDataConverter) {
|
||||||
<String, html.EventListener>{};
|
_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;
|
final DomRenderer domRenderer;
|
||||||
PointerDataCallback _callback;
|
PointerDataCallback _callback;
|
||||||
|
PointerDataConverter _pointerDataConverter;
|
||||||
|
|
||||||
// A set of the buttons that are currently being pressed.
|
// A set of the buttons that are currently being pressed.
|
||||||
Set<_PressedButton> _pressedButtons = Set<_PressedButton>();
|
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
|
/// Each subclass is expected to override this method to attach its own event
|
||||||
/// listeners and convert events into pointer events.
|
/// listeners and convert events into pointer events.
|
||||||
void _setup();
|
void _setup();
|
||||||
@@ -156,9 +154,21 @@ abstract class BaseAdapter {
|
|||||||
void clearListeners() {
|
void clearListeners() {
|
||||||
final html.Element glassPane = domRenderer.glassPaneElement;
|
final html.Element glassPane = domRenderer.glassPaneElement;
|
||||||
_listeners.forEach((String eventName, html.EventListener listener) {
|
_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();
|
_listeners.clear();
|
||||||
|
_nativeListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addEventListener(String eventName, html.EventListener handler) {
|
void _addEventListener(String eventName, html.EventListener handler) {
|
||||||
@@ -177,6 +187,75 @@ abstract class BaseAdapter {
|
|||||||
domRenderer.glassPaneElement
|
domRenderer.glassPaneElement
|
||||||
.addEventListener(eventName, loggedHandler, true);
|
.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;
|
const int _kPrimaryMouseButton = 0x1;
|
||||||
@@ -207,16 +286,17 @@ int _deviceFromHtmlEvent(event) {
|
|||||||
|
|
||||||
/// Adapter class to be used with browsers that support native pointer events.
|
/// Adapter class to be used with browsers that support native pointer events.
|
||||||
class PointerAdapter extends BaseAdapter {
|
class PointerAdapter extends BaseAdapter {
|
||||||
PointerAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
PointerAdapter(
|
||||||
: super(callback, domRenderer);
|
PointerDataCallback callback,
|
||||||
|
DomRenderer domRenderer,
|
||||||
|
PointerDataConverter _pointerDataConverter
|
||||||
|
) : super(callback, domRenderer, _pointerDataConverter);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void _setup() {
|
void _setup() {
|
||||||
_addEventListener('pointerdown', (html.Event event) {
|
_addEventListener('pointerdown', (html.Event event) {
|
||||||
final int pointerButton = _pointerButtonFromHtmlEvent(event);
|
final int pointerButton = _pointerButtonFromHtmlEvent(event);
|
||||||
final int device = _deviceFromHtmlEvent(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)) {
|
if (_isButtonDown(device, pointerButton)) {
|
||||||
// TODO(flutter_web): Remove this temporary fix for right click
|
// TODO(flutter_web): Remove this temporary fix for right click
|
||||||
// on web platform once context guesture is implemented.
|
// on web platform once context guesture is implemented.
|
||||||
@@ -239,13 +319,6 @@ class PointerAdapter extends BaseAdapter {
|
|||||||
? ui.PointerChange.move
|
? ui.PointerChange.move
|
||||||
: ui.PointerChange.hover,
|
: ui.PointerChange.hover,
|
||||||
pointerEvent);
|
pointerEvent);
|
||||||
_ensureMouseDeviceAdded(
|
|
||||||
data,
|
|
||||||
pointerEvent.client.x,
|
|
||||||
pointerEvent.client.y,
|
|
||||||
pointerEvent.buttons,
|
|
||||||
pointerEvent.timeStamp,
|
|
||||||
pointerEvent.pointerId);
|
|
||||||
_callback(data);
|
_callback(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -270,7 +343,8 @@ class PointerAdapter extends BaseAdapter {
|
|||||||
_callback(_convertEventToPointerData(ui.PointerChange.cancel, event));
|
_callback(_convertEventToPointerData(ui.PointerChange.cancel, event));
|
||||||
});
|
});
|
||||||
|
|
||||||
_addWheelEventListener((html.WheelEvent event) {
|
_addWheelEventListener((html.Event event) {
|
||||||
|
assert(event is html.WheelEvent);
|
||||||
if (_debugLogPointerEvents) {
|
if (_debugLogPointerEvents) {
|
||||||
print(event.type);
|
print(event.type);
|
||||||
}
|
}
|
||||||
@@ -289,7 +363,8 @@ class PointerAdapter extends BaseAdapter {
|
|||||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
final List<ui.PointerData> data = <ui.PointerData>[];
|
||||||
for (int i = 0; i < allEvents.length; i++) {
|
for (int i = 0; i < allEvents.length; i++) {
|
||||||
final html.PointerEvent event = allEvents[i];
|
final html.PointerEvent event = allEvents[i];
|
||||||
data.add(ui.PointerData(
|
_pointerDataConverter.convert(
|
||||||
|
data,
|
||||||
change: change,
|
change: change,
|
||||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||||
kind: _pointerTypeToDeviceKind(event.pointerType),
|
kind: _pointerTypeToDeviceKind(event.pointerType),
|
||||||
@@ -301,7 +376,7 @@ class PointerAdapter extends BaseAdapter {
|
|||||||
pressureMin: 0.0,
|
pressureMin: 0.0,
|
||||||
pressureMax: 1.0,
|
pressureMax: 1.0,
|
||||||
tilt: _computeHighestTilt(event),
|
tilt: _computeHighestTilt(event),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -343,8 +418,11 @@ class PointerAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
/// Adapter to be used with browsers that support touch events.
|
/// Adapter to be used with browsers that support touch events.
|
||||||
class TouchAdapter extends BaseAdapter {
|
class TouchAdapter extends BaseAdapter {
|
||||||
TouchAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
TouchAdapter(
|
||||||
: super(callback, domRenderer);
|
PointerDataCallback callback,
|
||||||
|
DomRenderer domRenderer,
|
||||||
|
PointerDataConverter _pointerDataConverter
|
||||||
|
) : super(callback, domRenderer, _pointerDataConverter);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void _setup() {
|
void _setup() {
|
||||||
@@ -381,11 +459,12 @@ class TouchAdapter extends BaseAdapter {
|
|||||||
html.TouchEvent event,
|
html.TouchEvent event,
|
||||||
) {
|
) {
|
||||||
final html.TouchList touches = event.changedTouches;
|
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;
|
final int len = touches.length;
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
final html.Touch touch = touches[i];
|
final html.Touch touch = touches[i];
|
||||||
data[i] = ui.PointerData(
|
_pointerDataConverter.convert(
|
||||||
|
data,
|
||||||
change: change,
|
change: change,
|
||||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||||
kind: ui.PointerDeviceKind.touch,
|
kind: ui.PointerDeviceKind.touch,
|
||||||
@@ -408,8 +487,11 @@ const int _mouseDeviceId = -1;
|
|||||||
|
|
||||||
/// Adapter to be used with browsers that support mouse events.
|
/// Adapter to be used with browsers that support mouse events.
|
||||||
class MouseAdapter extends BaseAdapter {
|
class MouseAdapter extends BaseAdapter {
|
||||||
MouseAdapter(PointerDataCallback callback, DomRenderer domRenderer)
|
MouseAdapter(
|
||||||
: super(callback, domRenderer);
|
PointerDataCallback callback,
|
||||||
|
DomRenderer domRenderer,
|
||||||
|
PointerDataConverter _pointerDataConverter
|
||||||
|
) : super(callback, domRenderer, _pointerDataConverter);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void _setup() {
|
void _setup() {
|
||||||
@@ -442,7 +524,8 @@ class MouseAdapter extends BaseAdapter {
|
|||||||
_callback(_convertEventToPointerData(ui.PointerChange.up, event));
|
_callback(_convertEventToPointerData(ui.PointerChange.up, event));
|
||||||
});
|
});
|
||||||
|
|
||||||
_addWheelEventListener((html.WheelEvent event) {
|
_addWheelEventListener((html.Event event) {
|
||||||
|
assert(event is html.WheelEvent);
|
||||||
if (_debugLogPointerEvents) {
|
if (_debugLogPointerEvents) {
|
||||||
print(event.type);
|
print(event.type);
|
||||||
}
|
}
|
||||||
@@ -455,16 +538,9 @@ class MouseAdapter extends BaseAdapter {
|
|||||||
ui.PointerChange change,
|
ui.PointerChange change,
|
||||||
html.MouseEvent event,
|
html.MouseEvent event,
|
||||||
) {
|
) {
|
||||||
final List<ui.PointerData> data = <ui.PointerData>[];
|
List<ui.PointerData> data = <ui.PointerData>[];
|
||||||
// The mousedown event will cause an 'add' event on the framework side.
|
_pointerDataConverter.convert(
|
||||||
if (event.type == 'mousedown') {
|
data,
|
||||||
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(
|
|
||||||
change: change,
|
change: change,
|
||||||
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
timeStamp: _eventTimeStampToDuration(event.timeStamp),
|
||||||
kind: ui.PointerDeviceKind.mouse,
|
kind: ui.PointerDeviceKind.mouse,
|
||||||
@@ -476,101 +552,7 @@ class MouseAdapter extends BaseAdapter {
|
|||||||
pressure: 1.0,
|
pressure: 1.0,
|
||||||
pressureMin: 0.0,
|
pressureMin: 0.0,
|
||||||
pressureMax: 1.0,
|
pressureMax: 1.0,
|
||||||
));
|
);
|
||||||
return data;
|
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
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,11 @@ void main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
expect(packets, hasLength(3));
|
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[1].data[0].change, equals(ui.PointerChange.up));
|
||||||
expect(packets[2].data[0].change, equals(ui.PointerChange.down));
|
expect(packets[2].data[0].change, equals(ui.PointerChange.down));
|
||||||
});
|
});
|
||||||
@@ -78,10 +82,20 @@ void main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
expect(packets, hasLength(2));
|
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[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[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', () {
|
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, hasLength(2));
|
||||||
|
|
||||||
expect(packets.single.data[0].change, equals(ui.PointerChange.add));
|
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));
|
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>[];
|
List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[];
|
||||||
ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {
|
ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) {
|
||||||
packets.add(packet);
|
packets.add(packet);
|
||||||
@@ -114,9 +129,247 @@ void main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
expect(packets, hasLength(1));
|
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));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user