-
Notifications
You must be signed in to change notification settings - Fork 6k
Web trackpad pan #36346
Web trackpad pan #36346
Changes from 12 commits
8f04c00
ad18dee
77c8347
34f091b
41a7f2e
c5b987e
50c21a0
0f2400e
31d04af
85a0a58
426cac0
a07c922
48b93bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -263,6 +263,8 @@ abstract class _BaseAdapter { | |
| final _PointerDataCallback _callback; | ||
| final PointerDataConverter _pointerDataConverter; | ||
| final KeyboardConverter _keyboardConverter; | ||
| DomWheelEvent? _lastWheelEvent; | ||
| bool? _lastWheelEventWasTrackpad; | ||
|
|
||
| /// Each subclass is expected to override this method to attach its own event | ||
| /// listeners and convert events into pointer events. | ||
|
|
@@ -333,13 +335,83 @@ abstract class _BaseAdapter { | |
| mixin _WheelEventListenerMixin on _BaseAdapter { | ||
| static double? _defaultScrollLineHeight; | ||
|
|
||
| bool _isAcceleratedMouseWheelDelta(num delta, num? wheelDelta) { | ||
| // On macOS, scrolling using a mouse wheel by default uses an acceleration | ||
| // curve, so delta values ramp up and are not at fixed multiples of 120. | ||
| // But in this case, the wheelDelta properties of the event still keep | ||
| // their original values. | ||
| // For all events without this acceleration curve applied, the wheelDelta | ||
| // values are by convention three times greater than the delta values and with | ||
| // the opposite sign. | ||
| if (wheelDelta == null) { | ||
| return false; | ||
| } | ||
| // Account for observed issues with integer truncation by allowing +-1px error. | ||
| return (wheelDelta - (-3 * delta)).abs() > 1; | ||
| } | ||
|
|
||
| bool _isTrackpadEvent(DomWheelEvent event, DomWheelEvent? lastEvent, bool? lastEventWasTrackpad) { | ||
|
||
| // This function relies on deprecated and non-standard implementation | ||
| // details. Useful reference material can be found below. | ||
| // | ||
| // https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event.cc | ||
| // https://source.chromium.org/chromium/chromium/src/+/main:ui/events/cocoa/events_mac.mm | ||
| // https://github.com/WebKit/WebKit/blob/main/Source/WebCore/platform/mac/PlatformEventFactoryMac.mm | ||
| // https://searchfox.org/mozilla-central/source/dom/events/WheelEvent.h | ||
| // https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel | ||
| if (browserEngine == BrowserEngine.firefox) { | ||
| // Firefox has restricted the wheelDelta properties, they do not provide | ||
| // enough information to accurately disambiguate trackpad events from mouse | ||
| // wheel events. | ||
| return false; | ||
| } | ||
| if (_isAcceleratedMouseWheelDelta(event.deltaX, event.wheelDeltaX) || | ||
| _isAcceleratedMouseWheelDelta(event.deltaY, event.wheelDeltaY)) { | ||
| return false; | ||
| } | ||
| if (((event.deltaX % 120 == 0) && (event.deltaY % 120 == 0)) || | ||
| (((event.wheelDeltaX ?? 1) % 120 == 0) && ((event.wheelDeltaY ?? 1) % 120) == 0)) { | ||
| // While not in any formal web standard, `blink` and `webkit` browsers use | ||
| // a delta of 120 to represent one mouse wheel turn. If both dimensions of | ||
|
Comment on lines
+374
to
+375
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| // the delta are divisible by 120, this event is probably from a mouse. | ||
| // Checking if wheelDeltaX and wheelDeltaY are both divisible by 120 | ||
| // catches any macOS accelerated mouse wheel deltas which by random chance | ||
| // are not caught by _isAcceleratedMouseWheelDelta. | ||
| final num deltaXChange = (event.deltaX - (lastEvent?.deltaX ?? 0)).abs(); | ||
| final num deltaYChange = (event.deltaY - (lastEvent?.deltaY ?? 0)).abs(); | ||
| if ((lastEvent == null) || | ||
| (deltaXChange == 0 && deltaYChange == 0) || | ||
| !(deltaXChange < 20 && deltaYChange < 20)) { | ||
| // A trackpad event might by chance have a delta of exactly 120, so | ||
| // make sure this event does not have a similar delta to the previous | ||
| // one before calling it a mouse event. | ||
| if (event.timeStamp != null && lastEvent?.timeStamp != null) { | ||
| // If the event has a large delta to the previous event, check if | ||
| // it was preceded within 50 milliseconds by a trackpad event. This | ||
| // handles unlucky 120-delta trackpad events during rapid movement. | ||
| final num diffMs = event.timeStamp! - lastEvent!.timeStamp!; | ||
| if (diffMs < 50 && (lastEventWasTrackpad ?? false)) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| List<ui.PointerData> _convertWheelEventToPointerData( | ||
| DomWheelEvent event | ||
| ) { | ||
| const int domDeltaPixel = 0x00; | ||
| const int domDeltaLine = 0x01; | ||
| const int domDeltaPage = 0x02; | ||
|
|
||
| ui.PointerDeviceKind kind = ui.PointerDeviceKind.mouse; | ||
| if (_isTrackpadEvent(event, _lastWheelEvent, _lastWheelEventWasTrackpad)) { | ||
| kind = ui.PointerDeviceKind.trackpad; | ||
| } | ||
|
|
||
| // Flutter only supports pixel scroll delta. Convert deltaMode values | ||
| // to pixels. | ||
| double deltaX = event.deltaX; | ||
|
|
@@ -371,7 +443,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { | |
| data, | ||
| change: ui.PointerChange.hover, | ||
| timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp!), | ||
| kind: ui.PointerDeviceKind.mouse, | ||
| kind: kind, | ||
| signalKind: ui.PointerSignalKind.scroll, | ||
| device: _mouseDeviceId, | ||
| physicalX: event.clientX * ui.window.devicePixelRatio, | ||
|
|
@@ -382,6 +454,8 @@ mixin _WheelEventListenerMixin on _BaseAdapter { | |
| scrollDeltaX: deltaX, | ||
| scrollDeltaY: deltaY, | ||
| ); | ||
| _lastWheelEvent = event; | ||
| _lastWheelEventWasTrackpad = kind == ui.PointerDeviceKind.trackpad; | ||
| return data; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: What do you think about making this non-nullable and initializing it to false? Currently it probably represents reality more accurately as-is, because a value of
nullmeans that there was no last wheel event. However, that null state is not used anywhere.Up to you what you think is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have it this way as in some previous iterations the null state did matter.