Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 83 additions & 109 deletions lib/web_ui/lib/src/engine/pointer_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class _ButtonSanitizer {
return _htmlButtonsToFlutterButtons(buttons);
}

List<_SanitizedDetails> sanitizeDownEvent({
_SanitizedDetails sanitizeDownEvent({
@required int button,
@required int buttons,
}) {
Expand All @@ -319,72 +319,64 @@ class _ButtonSanitizer {
}

_pressedButtons = _inferDownFlutterButtons(button, buttons);
return <_SanitizedDetails>[
_SanitizedDetails(
change: ui.PointerChange.down,
buttons: _pressedButtons,
)
];
return _SanitizedDetails(
change: ui.PointerChange.down,
buttons: _pressedButtons,
);
}

List<_SanitizedDetails> sanitizeMoveEvent({@required int buttons}) {
_SanitizedDetails sanitizeMoveEvent({@required int buttons}) {
final int newPressedButtons = _htmlButtonsToFlutterButtons(buttons);
// This could happen when the context menu is active and the user clicks
// RMB somewhere else. The browser sends a down event with `buttons:0`.
//
// In this case, we keep the old `buttons` value so we don't confuse the
// framework.
if (_pressedButtons != 0 && newPressedButtons == 0) {
return <_SanitizedDetails>[
_SanitizedDetails(
change: ui.PointerChange.move,
buttons: _pressedButtons,
)
];
return _SanitizedDetails(
change: ui.PointerChange.move,
buttons: _pressedButtons,
);
}

// This could happen when the user clicks RMB then moves the mouse quickly.
// The brower sends a move event with `buttons:2` even though there's no
// buttons down yet.
if (_pressedButtons == 0 && newPressedButtons != 0) {
return <_SanitizedDetails>[
_SanitizedDetails(
change: ui.PointerChange.hover,
buttons: _pressedButtons,
)
];
return _SanitizedDetails(
change: ui.PointerChange.hover,
buttons: _pressedButtons,
);
}

_pressedButtons = newPressedButtons;
return <_SanitizedDetails>[
_SanitizedDetails(
change: _pressedButtons == 0
? ui.PointerChange.hover
: ui.PointerChange.move,
buttons: _pressedButtons,
)
];
return _SanitizedDetails(
change: _pressedButtons == 0
? ui.PointerChange.hover
: ui.PointerChange.move,
buttons: _pressedButtons,
);
}

List<_SanitizedDetails> sanitizeUpEvent() {
_SanitizedDetails sanitizeUpEvent() {
// The pointer could have been released by a `pointerout` event, in which
// case `pointerup` should have no effect.
if (_pressedButtons == 0) {
return <_SanitizedDetails>[];
return null;
}
_pressedButtons = 0;
return <_SanitizedDetails>[_SanitizedDetails(
return _SanitizedDetails(
change: ui.PointerChange.up,
buttons: _pressedButtons,
)];
);
}

List<_SanitizedDetails> sanitizeCancelEvent() {
_SanitizedDetails sanitizeCancelEvent() {
_pressedButtons = 0;
return <_SanitizedDetails>[_SanitizedDetails(
return _SanitizedDetails(
change: ui.PointerChange.cancel,
buttons: _pressedButtons,
)];
);
}
}

Expand Down Expand Up @@ -415,13 +407,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
return sanitizer;
}

void _removePointerIfUnhoverable(List<_SanitizedDetails> details, html.PointerEvent event) {
void _removePointerIfUnhoverable(html.PointerEvent event) {
if (event.pointerType == 'touch') {
_sanitizers.remove(event.pointerId);
details.add(_SanitizedDetails(
buttons: 0,
change: ui.PointerChange.remove,
));
}
}

Expand All @@ -437,32 +425,36 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
_addPointerEventListener('pointerdown', (html.PointerEvent event) {
final int device = event.pointerId;
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final List<_SanitizedDetails> detailsList =
final _SanitizedDetails details =
_ensureSanitizer(device).sanitizeDownEvent(
button: event.button,
buttons: event.buttons,
);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
_convertEventsToPointerData(data: pointerData, event: event, details: details);
_callback(pointerData);
});

_addPointerEventListener('pointermove', (html.PointerEvent event) {
final int device = event.pointerId;
final _ButtonSanitizer sanitizer = _ensureSanitizer(device);
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final Iterable<_SanitizedDetails> detailsList = _expandEvents(event).expand(
final Iterable<_SanitizedDetails> detailsList = _expandEvents(event).map(
(html.PointerEvent expandedEvent) => sanitizer.sanitizeMoveEvent(buttons: expandedEvent.buttons),
);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
for (_SanitizedDetails details in detailsList) {
_convertEventsToPointerData(data: pointerData, event: event, details: details);
}
_callback(pointerData);
});

_addPointerEventListener('pointerup', (html.PointerEvent event) {
final int device = event.pointerId;
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final List<_SanitizedDetails> detailsList = _getSanitizer(device).sanitizeUpEvent();
_removePointerIfUnhoverable(detailsList, event);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
final _SanitizedDetails details = _getSanitizer(device).sanitizeUpEvent();
_removePointerIfUnhoverable(event);
if (details != null) {
_convertEventsToPointerData(data: pointerData, event: event, details: details);
}
_callback(pointerData);
});

Expand All @@ -471,9 +463,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
_addPointerEventListener('pointercancel', (html.PointerEvent event) {
final int device = event.pointerId;
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final List<_SanitizedDetails> detailsList = _getSanitizer(device).sanitizeCancelEvent();
_removePointerIfUnhoverable(detailsList, event);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
final _SanitizedDetails details = _getSanitizer(device).sanitizeCancelEvent();
_removePointerIfUnhoverable(event);
_convertEventsToPointerData(data: pointerData, event: event, details: details);
_callback(pointerData);
});

Expand All @@ -490,47 +482,45 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
}

// For each event that is de-coalesced from `event` and described in
// `detailsList`, convert it to pointer data and store in `data`.
// `details`, convert it to pointer data and store in `data`.
void _convertEventsToPointerData({
@required List<ui.PointerData> data,
@required html.PointerEvent event,
@required Iterable<_SanitizedDetails> detailsList,
@required _SanitizedDetails details,
}) {
assert(data != null);
assert(event != null);
assert(detailsList != null);
assert(details != null);
final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType);
// We force `device: _mouseDeviceId` on mouse pointers because Wheel events
// might come before any PointerEvents, and since wheel events don't contain
// pointerId we always assign `device: _mouseDeviceId` to them.
final int device = kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId;
final double tilt = _computeHighestTilt(event);
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp);
for (_SanitizedDetails details in detailsList) {
_pointerDataConverter.convert(
data,
change: details.change,
timeStamp: timeStamp,
kind: kind,
signalKind: ui.PointerSignalKind.none,
device: device,
physicalX: event.client.x * ui.window.devicePixelRatio,
physicalY: event.client.y * ui.window.devicePixelRatio,
buttons: details.buttons,
pressure: event.pressure,
pressureMin: 0.0,
pressureMax: 1.0,
tilt: tilt,
);
}
_pointerDataConverter.convert(
data,
change: details.change,
timeStamp: timeStamp,
kind: kind,
signalKind: ui.PointerSignalKind.none,
device: device,
physicalX: event.client.x * ui.window.devicePixelRatio,
physicalY: event.client.y * ui.window.devicePixelRatio,
buttons: details.buttons,
pressure: event.pressure,
pressureMin: 0.0,
pressureMax: 1.0,
tilt: tilt,
);
}

List<html.PointerEvent> _expandEvents(html.PointerEvent event) {
// For browsers that don't support `getCoalescedEvents`, we fallback to
// using the original event.
if (js_util.hasProperty(event, 'getCoalescedEvents')) {
final List<html.PointerEvent> coalescedEvents =
event.getCoalescedEvents();
event.getCoalescedEvents().cast<html.PointerEvent>();
// Some events don't perform coalescing, so they return an empty list. In
// that case, we also fallback to using the original event.
if (coalescedEvents.isNotEmpty) {
Expand Down Expand Up @@ -639,13 +629,6 @@ class _TouchAdapter extends _BaseAdapter {
pressed: false,
timeStamp: timeStamp,
);
_convertEventToPointerData(
data: pointerData,
change: ui.PointerChange.remove,
touch: touch,
pressed: false,
timeStamp: timeStamp,
);
}
}
_callback(pointerData);
Expand All @@ -665,13 +648,6 @@ class _TouchAdapter extends _BaseAdapter {
pressed: false,
timeStamp: timeStamp,
);
_convertEventToPointerData(
data: pointerData,
change: ui.PointerChange.remove,
touch: touch,
pressed: false,
timeStamp: timeStamp,
);
}
}
_callback(pointerData);
Expand Down Expand Up @@ -742,29 +718,29 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
void setup() {
_addMouseEventListener('mousedown', (html.MouseEvent event) {
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final List<_SanitizedDetails> sanitizedDetails =
final _SanitizedDetails sanitizedDetails =
_sanitizer.sanitizeDownEvent(
button: event.button,
buttons: event.buttons,
);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
_callback(pointerData);
});

_addMouseEventListener('mousemove', (html.MouseEvent event) {
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final List<_SanitizedDetails> sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
final _SanitizedDetails sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons);
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
_callback(pointerData);
});

_addMouseEventListener('mouseup', (html.MouseEvent event) {
final List<ui.PointerData> pointerData = <ui.PointerData>[];
final bool isEndOfDrag = event.buttons == 0;
final List<_SanitizedDetails> sanitizedDetails = isEndOfDrag ?
final _SanitizedDetails sanitizedDetails = isEndOfDrag ?
_sanitizer.sanitizeUpEvent() :
_sanitizer.sanitizeMoveEvent(buttons: event.buttons);
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
_callback(pointerData);
});

Expand All @@ -785,26 +761,24 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
void _convertEventsToPointerData({
@required List<ui.PointerData> data,
@required html.MouseEvent event,
@required Iterable<_SanitizedDetails> detailsList,
@required _SanitizedDetails details,
}) {
assert(data != null);
assert(event != null);
assert(detailsList != null);
for (_SanitizedDetails details in detailsList) {
_pointerDataConverter.convert(
data,
change: details.change,
timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp),
kind: ui.PointerDeviceKind.mouse,
signalKind: ui.PointerSignalKind.none,
device: _mouseDeviceId,
physicalX: event.client.x * ui.window.devicePixelRatio,
physicalY: event.client.y * ui.window.devicePixelRatio,
buttons: details.buttons,
pressure: 1.0,
pressureMin: 0.0,
pressureMax: 1.0,
);
}
assert(details != null);
_pointerDataConverter.convert(
data,
change: details.change,
timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp),
kind: ui.PointerDeviceKind.mouse,
signalKind: ui.PointerSignalKind.none,
device: _mouseDeviceId,
physicalX: event.client.x * ui.window.devicePixelRatio,
physicalY: event.client.y * ui.window.devicePixelRatio,
buttons: details.buttons,
pressure: 1.0,
pressureMin: 0.0,
pressureMax: 1.0,
);
}
}
33 changes: 33 additions & 0 deletions lib/web_ui/lib/src/engine/pointer_converter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,39 @@ class PointerDataConverter {
scrollDeltaY: scrollDeltaY,
)
);
if (kind == ui.PointerDeviceKind.touch) {
// The browser sends a new device ID for each touch gesture. To
// avoid memory leaks, we send a "remove" event when the gesture is
// over (i.e. when "up" or "cancel" is received).
result.add(
_synthesizePointerData(
timeStamp: timeStamp,
change: ui.PointerChange.remove,
kind: kind,
device: device,
physicalX: physicalX,
physicalY: physicalY,
buttons: 0,
obscured: obscured,
pressure: 0.0,
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,
)
);
_pointers.remove(device);
}
break;
case ui.PointerChange.remove:
assert(_pointers.containsKey(device));
Expand Down
Loading