28
28
static constexpr int32_t kMousePointerDeviceId = 0 ;
29
29
static constexpr int32_t kPointerPanZoomDeviceId = 1 ;
30
30
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
+
31
36
/* *
32
37
* State tracking for mouse events, to adapt between the events coming from the system and the
33
38
* events that the embedding API expects.
91
96
bool rotate_gesture_active = false ;
92
97
93
98
/* *
94
- * System scroll inertia is currently sending us events .
99
+ * Time of last scroll momentum event .
95
100
*/
96
- bool system_scroll_inertia_active = false ;
101
+ NSTimeInterval last_scroll_momentum_changed_time = 0 ;
97
102
98
103
/* *
99
104
* Resets all gesture state to default values.
@@ -521,11 +526,11 @@ - (void)dispatchGestureEvent:(nonnull NSEvent*)event {
521
526
} else if (event.phase == NSEventPhaseNone && event.momentumPhase == NSEventPhaseNone) {
522
527
[self dispatchMouseEvent: event phase: kHover ];
523
528
} 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 ;
529
534
}
530
535
// Skip momentum update events, the framework will generate scroll momentum.
531
536
NSAssert (event.momentumPhase != NSEventPhaseNone,
@@ -549,6 +554,8 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
549
554
_mouseState.rotate_gesture_active ;
550
555
if (event.type == NSEventTypeScrollWheel) {
551
556
_mouseState.pan_gesture_active = true ;
557
+ // Ensure scroll inertia cancel event is not sent afterwards.
558
+ _mouseState.last_scroll_momentum_changed_time = 0 ;
552
559
} else if (event.type == NSEventTypeMagnify) {
553
560
_mouseState.scale_gesture_active = true ;
554
561
} else if (event.type == NSEventTypeRotate) {
@@ -841,8 +848,9 @@ - (void)swipeWithEvent:(NSEvent*)event {
841
848
- (void )touchesBeganWithEvent : (NSEvent *)event {
842
849
NSTouch * touch = event.allTouches .anyObject ;
843
850
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.
846
854
// A scroll inertia cancel message should be sent to the framework.
847
855
NSPoint locationInView = [self .flutterView convertPoint: event.locationInWindow fromView: nil ];
848
856
NSPoint locationInBackingCoordinates =
@@ -858,6 +866,8 @@ - (void)touchesBeganWithEvent:(NSEvent*)event {
858
866
};
859
867
860
868
[_engine sendPointerEvent: flutterEvent];
869
+ // Ensure no further scroll inertia cancel event will be sent.
870
+ _mouseState.last_scroll_momentum_changed_time = 0 ;
861
871
}
862
872
}
863
873
}
0 commit comments