diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm index ac901423ee1b6..76a1e08557004 100644 --- a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm @@ -39,6 +39,14 @@ @interface FLEViewController () */ @property(nonatomic) NSTrackingArea* trackingArea; +/** + * Whether or not a kAdd event has been sent for the mouse (or sent again since + * the last kRemove was sent if tracking is enabled). Used to determine whether + * to send an Add event before sending an incoming mouse event, since Flutter + * expects a pointers to be added before events are sent for them. + */ +@property(nonatomic) BOOL mouseCurrentlyAdded; + /** * Updates |trackingArea| for the current tracking settings, creating it with * the correct mode if tracking is enabled, or removing it if not. @@ -416,16 +424,54 @@ - (void)handlePlatformMessage:(const FlutterPlatformMessage*)message { } - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase { + // If a pointer added event hasn't been sent, synthesize one using this event for the basic + // information. + if (!_mouseCurrentlyAdded && phase != kAdd) { + // Only the values extracted for use in flutterEvent below matter, the rest are dummy values. + NSEvent* addEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseEntered + location:event.locationInWindow + modifierFlags:0 + timestamp:event.timestamp + windowNumber:event.windowNumber + context:nil + eventNumber:0 + trackingNumber:0 + userData:NULL]; + [self dispatchMouseEvent:addEvent phase:kAdd]; + } + NSPoint locationInView = [self.view convertPoint:event.locationInWindow fromView:nil]; NSPoint locationInBackingCoordinates = [self.view convertPointToBacking:locationInView]; - const FlutterPointerEvent flutterEvent = { + FlutterPointerEvent flutterEvent = { .struct_size = sizeof(flutterEvent), .phase = phase, .x = locationInBackingCoordinates.x, .y = -locationInBackingCoordinates.y, // convertPointToBacking makes this negative. .timestamp = static_cast(event.timestamp * NSEC_PER_MSEC), }; + + if (event.type == NSEventTypeScrollWheel) { + flutterEvent.signal_kind = kFlutterPointerSignalKindScroll; + + double pixelsPerLine = 1.0; + if (!event.hasPreciseScrollingDeltas) { + CGEventSourceRef source = CGEventCreateSourceFromEvent(event.CGEvent); + pixelsPerLine = CGEventSourceGetPixelsPerLine(source); + if (source) { + CFRelease(source); + } + } + double scaleFactor = self.view.layer.contentsScale; + flutterEvent.scroll_delta_x = event.scrollingDeltaX * pixelsPerLine * scaleFactor; + flutterEvent.scroll_delta_y = -event.scrollingDeltaY * pixelsPerLine * scaleFactor; + } FlutterEngineSendPointerEvent(_engine, &flutterEvent, 1); + + if (phase == kAdd) { + _mouseCurrentlyAdded = YES; + } else if (phase == kRemove) { + _mouseCurrentlyAdded = NO; + } } - (void)dispatchKeyEvent:(NSEvent*)event ofType:(NSString*)type { @@ -539,4 +585,10 @@ - (void)mouseMoved:(NSEvent*)event { [self dispatchMouseEvent:event phase:kHover]; } +- (void)scrollWheel:(NSEvent*)event { + // TODO: Add gesture-based (trackpad) scroll support once it's supported by the engine rather + // than always using kHover. + [self dispatchMouseEvent:event phase:kHover]; +} + @end