From 95a3120b40e1fc7fd322ac0a326eefbc8297bd7b Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 22 Feb 2025 22:28:53 -0800 Subject: [PATCH] Add better scroll wheel support and option to disable acceleration macOS applies a acceleration to the mouse scroll wheel which is often frustrating to use. In particular, a single click on the scroll wheel generates a fractional scroll, which doesn't do anything in MacVim as it scrolls by line. Fix this by forcing each scroll event to scroll at least one line (this is configurable to be higher, or 0 which means use the old behavior). Also add an option to simply turn off acceleration and scroll a fixed number of lines. Also, fix horizontal scrolling using mouse wheel. In macOS, the way to scroll horizontally using a normal wheel is to hold Shift key and scroll, and the OS will convert that to a horizontal scroll event. However, we were sending the shift modifier to Vim as well, which interprets it as which is not what we want (this scrolls a whole page). We manually remove the shift modifier when we detect this. It does mean there's functionally no way to send shift-scroll wheel events to Vim, but it is ok, as macOS generally works this way and it's consistent with how native GUI apps work. --- runtime/doc/gui_mac.txt | 15 ++++++--- runtime/doc/tags | 3 ++ src/MacVim/Base.lproj/Preferences.xib | 47 ++++++++++++++++++++++++--- src/MacVim/MMAppController.m | 3 ++ src/MacVim/MMTextViewHelper.m | 44 ++++++++++++++++++++----- src/MacVim/Miscellaneous.h | 5 ++- src/MacVim/Miscellaneous.m | 3 ++ 7 files changed, 102 insertions(+), 18 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index 619c88a314..283d22dd29 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -283,8 +283,6 @@ as general information regarding macOS user defaults. Here is a list of relevant dictionary entries: KEY VALUE ~ -*MMAllowForceClickLookUp* use Force click for data lookup instead of - [bool] *MMCellWidthMultiplier* width of a normal glyph in em units [float] *MMCmdLineAlignBottom* Pin command-line to bottom of MacVim [bool] *MMDialogsTrackPwd* open/save dialogs track the Vim pwd [bool] @@ -307,7 +305,6 @@ KEY VALUE ~ *MMTitlebarAppearsTransparent* enable a transparent titlebar [bool] *MMAppearanceModeSelection* dark mode selection (|macvim-dark-mode|)[bool] *MMRendererClipToRow* clip tall characters to the row they are on [bool] -*MMScrollOneDirectionOnly* scroll along one axis only when using trackpad [bool] *MMSmoothResize* allow smooth resizing of MacVim window [bool] *MMShareFindPboard* share search text to Find Pasteboard [bool] *MMTextInsetBottom* text area offset in pixels [int] @@ -316,12 +313,22 @@ KEY VALUE ~ *MMTextInsetTop* text area offset in pixels [int] *MMTexturedWindow* use brushed metal window (Tiger only) [bool] *MMTranslateCtrlClick* interpret ctrl-click as right-click [bool] -*MMUseMouseTime* use mousetime to detect multiple clicks [bool] *MMVerticalSplit* files open in vertical splits [bool] *MMZoomBoth* zoom button maximizes both directions [bool] *MMUpdaterPrereleaseChannel* opt-in to pre-release software update [bool] *MMShowWhatsNewOnStartup* show "What's New" after updating to new version [bool] +Mouse / Trackpad ~ +*MMScrollOneDirectionOnly* scroll along one axis only when using trackpad [bool] +*MMAllowForceClickLookUp* use Force click for data lookup instead of + [bool] +*MMMouseWheelDisableAcceleration* disable OS scroll wheel acceleration [bool] +*MMMouseWheelNumLines* how many lines to scroll when scroll wheel + acceleration is turned off [int] +*MMMouseWheelMinLines* min number of lines to scroll per scroll wheel + click when acceleration is on [int] +*MMUseMouseTime* use mousetime to detect multiple clicks [bool] + Tabs ~ *MMTabColorsMode* use default/auto/colorscheme for tab colors [int] *MMWindowUseTabBackgroundColor* use tabs background fill color as window color [bool] diff --git a/runtime/doc/tags b/runtime/doc/tags index 6009d63700..a651266968 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5654,6 +5654,9 @@ MMFullScreenFadeTime gui_mac.txt /*MMFullScreenFadeTime* MMLoginShell gui_mac.txt /*MMLoginShell* MMLoginShellArgument gui_mac.txt /*MMLoginShellArgument* MMLoginShellCommand gui_mac.txt /*MMLoginShellCommand* +MMMouseWheelDisableAcceleration gui_mac.txt /*MMMouseWheelDisableAcceleration* +MMMouseWheelMinLines gui_mac.txt /*MMMouseWheelMinLines* +MMMouseWheelNumLines gui_mac.txt /*MMMouseWheelNumLines* MMNativeFullScreen gui_mac.txt /*MMNativeFullScreen* MMNoFontSubstitution gui_mac.txt /*MMNoFontSubstitution* MMNoTitleBarWindow gui_mac.txt /*MMNoTitleBarWindow* diff --git a/src/MacVim/Base.lproj/Preferences.xib b/src/MacVim/Base.lproj/Preferences.xib index 6d70bccb9f..c5ad95715a 100644 --- a/src/MacVim/Base.lproj/Preferences.xib +++ b/src/MacVim/Base.lproj/Preferences.xib @@ -573,11 +573,11 @@ - + - + @@ -604,11 +604,11 @@ - + - + @@ -617,7 +617,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 393ddd502e..596dd4da4c 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -217,6 +217,9 @@ + (void)registerDefaults [NSNumber numberWithBool:YES], MMUseInlineImKey, #endif // INCLUDE_OLD_IM_CODE [NSNumber numberWithBool:NO], MMSuppressTerminationAlertKey, + [NSNumber numberWithBool:NO], MMMouseWheelDisableAccelerationKey, + [NSNumber numberWithInt:1], MMMouseWheelMinLinesKey, + [NSNumber numberWithInt:3], MMMouseWheelNumLinesKey, [NSNumber numberWithBool:YES], MMNativeFullScreenKey, [NSNumber numberWithDouble:0.0], MMFullScreenFadeTimeKey, [NSNumber numberWithBool:NO], MMNonNativeFullScreenShowMenuKey, diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index c854e95b87..533a1b40c6 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -312,15 +312,15 @@ - (void)doCommandBySelector:(SEL)sel else interpretKeyEventsSwallowedKey = NO; } -- (void)scrollWheel:(NSEvent *)event +- (void)calcScrollWheelDeltas:(NSEvent *)event x:(float *)x y:(float *)y eventFlags:(NSEventModifierFlags*)eventFlags { float dx = 0; float dy = 0; -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if ([event hasPreciseScrollingDeltas]) { const NSEventPhase phase = event.phase; - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; CGFloat eventScrollingDeltaX = event.scrollingDeltaX; CGFloat eventScrollingDeltaY = event.scrollingDeltaY; @@ -366,12 +366,40 @@ - (void)scrollWheel:(NSEvent *)event scrollingDeltaY = 0; dx = [event scrollingDeltaX]; dy = [event scrollingDeltaY]; + + if ([ud boolForKey:MMMouseWheelDisableAccelerationKey]) { + // Use scroll wheel as discrete steps as macOS acceleration often + // leads to unintuitive results. + NSInteger numLines = [ud integerForKey:MMMouseWheelNumLinesKey]; + dx = dx == 0 ? 0 : (dx > 0 ? numLines : -numLines); + dy = dy == 0 ? 0 : (dy > 0 ? numLines : -numLines); + } else { + // Use the default OS acceleration, but we still want to round up + // the delta to at least 1 (configurable) as otherwise it's + // frustrating to use as MacVim fails to scroll even one line. + NSInteger minScrollLines = [ud integerForKey:MMMouseWheelMinLinesKey]; + dx = dx == 0 ? 0 : (fabs(dx) > minScrollLines ? dx : (dx > 0 ? minScrollLines : -minScrollLines)); + dy = dy == 0 ? 0 : (fabs(dy) > minScrollLines ? dy : (dy > 0 ? minScrollLines : -minScrollLines)); + } + + if ((*eventFlags & NSEventModifierFlagShift) && dx != 0 && dy == 0) { + // A shift-scroll in macOS converts a normal Y to X-axis scroll. + // There is no proper API to query this so this is just done by + // heuristics. We need to remove the shift flag, or Vim would see + // this as a which is not what we want. + *eventFlags = *eventFlags & (~NSEventModifierFlagShift); + } } -#else - dx = [event deltaX]; - dy = [event deltaY]; -#endif + *x = dx; + *y = dy; +} + +- (void)scrollWheel:(NSEvent *)event +{ + NSEventModifierFlags eventFlags = event.modifierFlags; + float dx = 0, dy = 0; + [self calcScrollWheelDeltas:event x:&dx y:&dy eventFlags:&eventFlags]; if (dx == 0 && dy == 0) return; @@ -386,7 +414,7 @@ - (void)scrollWheel:(NSEvent *)event int row, col; NSPoint pt = [textView convertPoint:[event locationInWindow] fromView:nil]; if ([textView convertPoint:pt toRow:&row column:&col]) { - unsigned flags = (unsigned)[event modifierFlags]; + unsigned flags = (unsigned)eventFlags; NSMutableData *data = [NSMutableData data]; [data appendBytes:&row length:sizeof(int)]; diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index d36d2f1f4a..0216de6f21 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -61,6 +61,9 @@ extern NSString *MMUseInlineImKey; extern NSString *MMSuppressTerminationAlertKey; extern NSString *MMNativeFullScreenKey; extern NSString *MMUseMouseTimeKey; +extern NSString *MMMouseWheelDisableAccelerationKey; ///< Disable native macOS mouse scroll wheel acceleration +extern NSString *MMMouseWheelMinLinesKey; ///< Minimum scroll delta for mouse scroll wheel, only when acceleration is on +extern NSString *MMMouseWheelNumLinesKey; ///< Number of lines to scroll using mouse scroll wheel, only when acceleration is off extern NSString *MMFullScreenFadeTimeKey; extern NSString *MMNonNativeFullScreenShowMenuKey; extern NSString *MMNonNativeFullScreenSafeAreaBehaviorKey; @@ -69,7 +72,7 @@ extern NSString *MMCmdLineAlignBottomKey; extern NSString *MMRendererClipToRowKey; extern NSString *MMAllowForceClickLookUpKey; extern NSString *MMUpdaterPrereleaseChannelKey; -extern NSString *MMLastUsedBundleVersionKey; // The last used version of MacVim before this launch +extern NSString *MMLastUsedBundleVersionKey; ///< The last used version of MacVim before this launch extern NSString *MMShowWhatsNewOnStartupKey; extern NSString *MMScrollOneDirectionOnlyKey; diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index 17e9304238..3618db398e 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -57,6 +57,9 @@ NSString *MMSuppressTerminationAlertKey = @"MMSuppressTerminationAlert"; NSString *MMNativeFullScreenKey = @"MMNativeFullScreen"; NSString *MMUseMouseTimeKey = @"MMUseMouseTime"; +NSString *MMMouseWheelDisableAccelerationKey = @"MMMouseWheelDisbleAcceleration"; +NSString *MMMouseWheelMinLinesKey = @"MMMouseWheelMinLines"; +NSString *MMMouseWheelNumLinesKey = @"MMMouseWheelNumLines"; NSString *MMFullScreenFadeTimeKey = @"MMFullScreenFadeTime"; NSString *MMNonNativeFullScreenShowMenuKey = @"MMNonNativeFullScreenShowMenu"; NSString *MMNonNativeFullScreenSafeAreaBehaviorKey = @"MMNonNativeFullScreenSafeAreaBehavior";