diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 995b75e9011b8..9034e8deaa6c1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -643,8 +643,23 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase { pixelsPerLine = 40.0; } double scaleFactor = self.flutterView.layer.contentsScale; - flutterEvent.scroll_delta_x = -event.scrollingDeltaX * pixelsPerLine * scaleFactor; - flutterEvent.scroll_delta_y = -event.scrollingDeltaY * pixelsPerLine * scaleFactor; + // When mouse input is received while shift is pressed (regardless of + // any other pressed keys), Mac automatically flips the axis. Other + // platforms do not do this, so we flip it back to normalize the input + // received by the framework. The keyboard+mouse-scroll mechanism is exposed + // in the ScrollBehavior of the framework so developers can customize the + // behavior. + // At time of change, Apple does not expose any other type of API or signal + // that the X/Y axes have been flipped. + double scaledDeltaX = -event.scrollingDeltaX * pixelsPerLine * scaleFactor; + double scaledDeltaY = -event.scrollingDeltaY * pixelsPerLine * scaleFactor; + if (event.modifierFlags & NSShiftKeyMask) { + flutterEvent.scroll_delta_x = scaledDeltaY; + flutterEvent.scroll_delta_y = scaledDeltaX; + } else { + flutterEvent.scroll_delta_x = scaledDeltaX; + flutterEvent.scroll_delta_y = scaledDeltaY; + } } [_engine sendPointerEvent:flutterEvent]; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index bc7c18739043b..903065731a31a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -629,6 +629,27 @@ - (bool)testTrackpadGesturesAreSentToFramework { EXPECT_EQ(last_event.scroll_delta_x, -40 * viewController.flutterView.layer.contentsScale); EXPECT_EQ(last_event.scroll_delta_y, -80 * viewController.flutterView.layer.contentsScale); + // A discrete scroll event should use the PointerSignal system, and flip the + // direction when shift is pressed. + CGEventRef cgEventDiscreteShift = + CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0); + CGEventSetType(cgEventDiscreteShift, kCGEventScrollWheel); + CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift); + CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventIsContinuous, 0); + CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis2, + 0); // scroll_delta_x + CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis1, + 2); // scroll_delta_y + + called = false; + [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventDiscreteShift]]; + EXPECT_TRUE(called); + EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScroll); + // pixelsPerLine is 40.0, direction is reversed and axes have been flipped back. + EXPECT_FLOAT_EQ(last_event.scroll_delta_x, 0.0 * viewController.flutterView.layer.contentsScale); + EXPECT_FLOAT_EQ(last_event.scroll_delta_y, + -80.0 * viewController.flutterView.layer.contentsScale); + // Test for scale events. // Start gesture. called = false;