2828static constexpr int32_t kMousePointerDeviceId = 0 ;
2929static constexpr int32_t kPointerPanZoomDeviceId = 1 ;
3030
31+ // A trackpad touch following inertial scrolling should cause an inertia cancel
32+ // event to be issued. Use a window of 50 milliseconds after the scroll to account
33+ // for delays in event propagation observed in macOS Ventura.
34+ static constexpr double kTrackpadTouchInertiaCancelWindowMs = 0.050 ;
35+
3136/* *
3237 * State tracking for mouse events, to adapt between the events coming from the system and the
3338 * events that the embedding API expects.
9196 bool rotate_gesture_active = false ;
9297
9398 /* *
94- * System scroll inertia is currently sending us events .
99+ * Time of last scroll momentum event .
95100 */
96- bool system_scroll_inertia_active = false ;
101+ NSTimeInterval last_scroll_momentum_changed_time = 0 ;
97102
98103 /* *
99104 * Resets all gesture state to default values.
@@ -521,11 +526,11 @@ - (void)dispatchGestureEvent:(nonnull NSEvent*)event {
521526 } else if (event.phase == NSEventPhaseNone && event.momentumPhase == NSEventPhaseNone) {
522527 [self dispatchMouseEvent: event phase: kHover ];
523528 } else {
524- if ( event. momentumPhase == NSEventPhaseBegan) {
525- _mouseState. system_scroll_inertia_active = true ;
526- } else if (event. momentumPhase == NSEventPhaseEnded ||
527- event.momentumPhase == NSEventPhaseCancelled ) {
528- _mouseState.system_scroll_inertia_active = false ;
529+ // Waiting until the first momentum change event is a workaround for an issue where
530+ // touchesBegan: is called unexpectedly while in low power mode within the interval between
531+ // momentum start and the first momentum change.
532+ if ( event.momentumPhase == NSEventPhaseChanged ) {
533+ _mouseState.last_scroll_momentum_changed_time = event. timestamp ;
529534 }
530535 // Skip momentum update events, the framework will generate scroll momentum.
531536 NSAssert (event.momentumPhase != NSEventPhaseNone,
@@ -549,6 +554,8 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
549554 _mouseState.rotate_gesture_active ;
550555 if (event.type == NSEventTypeScrollWheel) {
551556 _mouseState.pan_gesture_active = true ;
557+ // Ensure scroll inertia cancel event is not sent afterwards.
558+ _mouseState.last_scroll_momentum_changed_time = 0 ;
552559 } else if (event.type == NSEventTypeMagnify) {
553560 _mouseState.scale_gesture_active = true ;
554561 } else if (event.type == NSEventTypeRotate) {
@@ -841,8 +848,9 @@ - (void)swipeWithEvent:(NSEvent*)event {
841848- (void )touchesBeganWithEvent : (NSEvent *)event {
842849 NSTouch * touch = event.allTouches .anyObject ;
843850 if (touch != nil ) {
844- if (_mouseState.system_scroll_inertia_active ) {
845- // The trackpad has been touched and a scroll gesture is still sending inertia events.
851+ if ((event.timestamp - _mouseState.last_scroll_momentum_changed_time ) <
852+ kTrackpadTouchInertiaCancelWindowMs ) {
853+ // The trackpad has been touched following a scroll momentum event.
846854 // A scroll inertia cancel message should be sent to the framework.
847855 NSPoint locationInView = [self .flutterView convertPoint: event.locationInWindow fromView: nil ];
848856 NSPoint locationInBackingCoordinates =
@@ -858,6 +866,8 @@ - (void)touchesBeganWithEvent:(NSEvent*)event {
858866 };
859867
860868 [_engine sendPointerEvent: flutterEvent];
869+ // Ensure no further scroll inertia cancel event will be sent.
870+ _mouseState.last_scroll_momentum_changed_time = 0 ;
861871 }
862872 }
863873}
0 commit comments