From 82ec6d034ca343f0c451d9c9e960feab909afb82 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Fri, 31 Jan 2025 16:58:48 -0800 Subject: [PATCH] MMTabline: Add Vim colorscheme / window use tab fill color support Tabs can now use colors defined by the Vim colorscheme under the TabLine/TabLineFill/TabLineSel highlight groups. There are now 3 coloring modes available: default, automatic (the default mode that generates matching colors using foreground/background colors), and Vim colorscheme. The new mode looks quite nice in some colorschemes and allows user customization, but for some existing ones it doesn't quite look right as they were designed for non-GUI tabs, which is why it's not the default. MacVim window can now also use the tabline's fill color as the window background color, which allows the title bar to show as a cohesive whole when transparent title bar is set. This creates a nice look especially when the colorscheme is designed for this. Other tab coloring changes: - Tabs now support high contrast mode (macOS accessibility setting) now. The default colors will use higher contrast colors, and in all modes, the tabs will be drawn with an outline similar to other system native UI to aid visual differentiation. - Tabs will also show a dimmed text color when the window has lost focus, similar to other native title bar / tool bar UI elements. - Fixed default colors to look a little better. Previously the fill color and unselected tab background colors were the same. --- runtime/doc/gui_mac.txt | 13 +- runtime/doc/tags | 3 +- src/MacVim/Base.lproj/Preferences.xib | 72 ++++++++--- src/MacVim/MMAppController.m | 5 +- src/MacVim/MMBackend.h | 1 + src/MacVim/MMBackend.m | 11 ++ src/MacVim/MMTabline/MMTab.m | 22 ++-- src/MacVim/MMTabline/MMTabline.h | 18 ++- src/MacVim/MMTabline/MMTabline.m | 164 +++++++++++++++++++------- src/MacVim/MMVimController.m | 20 ++++ src/MacVim/MMVimView.h | 3 + src/MacVim/MMVimView.m | 73 +++++++++++- src/MacVim/MMWindowController.h | 5 +- src/MacVim/MMWindowController.m | 65 +++++++++- src/MacVim/MacVim.h | 1 + src/MacVim/Miscellaneous.h | 15 ++- src/MacVim/Miscellaneous.m | 12 +- src/MacVim/gui_macvim.m | 44 ++++++- src/highlight.c | 6 + src/proto/gui_macvim.pro | 1 + 20 files changed, 461 insertions(+), 93 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index df2644f4e2..619c88a314 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -310,11 +310,6 @@ KEY VALUE ~ *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] -*MMShowAddTabButton* enable "add tab" button on tabline [bool] -*MMShowTabScrollButtons* enable tab scroll buttons on tabline [bool] -*MMTabMinWidth* minimum width of a tab [int] -*MMTabOptimumWidth* default width of a tab [int] -*MMDefaultTablineColors* use default colors instead of colorscheme for tabs [bool] *MMTextInsetBottom* text area offset in pixels [int] *MMTextInsetLeft* text area offset in pixels [int] *MMTextInsetRight* text area offset in pixels [int] @@ -327,6 +322,14 @@ KEY VALUE ~ *MMUpdaterPrereleaseChannel* opt-in to pre-release software update [bool] *MMShowWhatsNewOnStartup* show "What's New" after updating to new version [bool] +Tabs ~ +*MMTabColorsMode* use default/auto/colorscheme for tab colors [int] +*MMWindowUseTabBackgroundColor* use tabs background fill color as window color [bool] +*MMShowAddTabButton* enable "add tab" button on tabline [bool] +*MMShowTabScrollButtons* enable tab scroll buttons on tabline [bool] +*MMTabMinWidth* minimum width of a tab [int] +*MMTabOptimumWidth* default width of a tab [int] + As an example, if you have more than one mouse button and would wish to free up Ctrl-click so you can bind it to something else, then the appropriate command is: > diff --git a/runtime/doc/tags b/runtime/doc/tags index d8b5f59472..b55d15bddf 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5645,7 +5645,6 @@ MMAllowForceClickLookUp gui_mac.txt /*MMAllowForceClickLookUp* MMAppearanceModeSelection gui_mac.txt /*MMAppearanceModeSelection* MMCellWidthMultiplier gui_mac.txt /*MMCellWidthMultiplier* MMCmdLineAlignBottom gui_mac.txt /*MMCmdLineAlignBottom* -MMDefaultTablineColors gui_mac.txt /*MMDefaultTablineColors* MMDialogsTrackPwd gui_mac.txt /*MMDialogsTrackPwd* MMDisableLaunchAnimation gui_mac.txt /*MMDisableLaunchAnimation* MMDisableTablineAnimation gui_mac.txt /*MMDisableTablineAnimation* @@ -5666,6 +5665,7 @@ MMShowAddTabButton gui_mac.txt /*MMShowAddTabButton* MMShowTabScrollButtons gui_mac.txt /*MMShowTabScrollButtons* MMShowWhatsNewOnStartup gui_mac.txt /*MMShowWhatsNewOnStartup* MMSmoothResize gui_mac.txt /*MMSmoothResize* +MMTabColorsMode gui_mac.txt /*MMTabColorsMode* MMTabMinWidth gui_mac.txt /*MMTabMinWidth* MMTabOptimumWidth gui_mac.txt /*MMTabOptimumWidth* MMTextInsetBottom gui_mac.txt /*MMTextInsetBottom* @@ -5678,6 +5678,7 @@ MMTranslateCtrlClick gui_mac.txt /*MMTranslateCtrlClick* MMUpdaterPrereleaseChannel gui_mac.txt /*MMUpdaterPrereleaseChannel* MMUseMouseTime gui_mac.txt /*MMUseMouseTime* MMVerticalSplit gui_mac.txt /*MMVerticalSplit* +MMWindowUseTabBackgroundColor gui_mac.txt /*MMWindowUseTabBackgroundColor* MMZoomBoth gui_mac.txt /*MMZoomBoth* MS-DOS os_msdos.txt /*MS-DOS* MS-Windows os_win32.txt /*MS-Windows* diff --git a/src/MacVim/Base.lproj/Preferences.xib b/src/MacVim/Base.lproj/Preferences.xib index 1aa2e8057d..893ae01353 100644 --- a/src/MacVim/Base.lproj/Preferences.xib +++ b/src/MacVim/Base.lproj/Preferences.xib @@ -280,11 +280,11 @@ - + - + @@ -345,11 +345,11 @@ - + - + @@ -358,7 +358,7 @@ + - - - - - - - - - @@ -535,7 +569,7 @@ - + @@ -622,7 +656,7 @@ - + diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 1dd4ac5a4e..89d347609e 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -178,6 +178,9 @@ + (void)registerDefaults [NSNumber numberWithInt:210], MMTabOptimumWidthKey, [NSNumber numberWithBool:YES], MMShowAddTabButtonKey, [NSNumber numberWithBool:NO], MMShowTabScrollButtonsKey, + [NSNumber numberWithInt:MMTabColorsModeAutomatic], + MMTabColorsModeKey, + [NSNumber numberWithBool:NO], MMWindowUseTabBackgroundColorKey, [NSNumber numberWithInt:2], MMTextInsetLeftKey, [NSNumber numberWithInt:1], MMTextInsetRightKey, [NSNumber numberWithInt:1], MMTextInsetTopKey, @@ -1238,7 +1241,7 @@ - (void)refreshAllAppearances - (void)refreshAllTabProperties { for (MMVimController *vc in vimControllers) { - [vc.windowController.vimView refreshTabProperties]; + [vc.windowController refreshTabProperties]; } } diff --git a/src/MacVim/MMBackend.h b/src/MacVim/MMBackend.h index 6a730a0f3f..e8271f2ded 100644 --- a/src/MacVim/MMBackend.h +++ b/src/MacVim/MMBackend.h @@ -62,6 +62,7 @@ - (void)setBackgroundColor:(int)color; - (void)setForegroundColor:(int)color; - (void)setSpecialColor:(int)color; +- (void)setTablineColors:(int[6])colors; - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg; - (NSConnection *)connection; - (NSDictionary *)actionDict; diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 9f38769967..0d35f841a3 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -293,6 +293,17 @@ - (void)setSpecialColor:(int)color specialColor = MM_COLOR(color); } +- (void)setTablineColors:(int[6])colors +{ + unsigned tabColors[6]; + for (int i = 0; i < 6; i++) { + tabColors[i] = MM_COLOR(colors[i]); + } + NSMutableData *data = [NSMutableData data]; + [data appendBytes:&tabColors length:sizeof(tabColors)]; + [self queueMessage:SetTablineColorsMsgID data:data]; +} + - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg { defaultBackgroundColor = MM_COLOR_WITH_TRANSP(bg,p_transp); diff --git a/src/MacVim/MMTabline/MMTab.m b/src/MacVim/MMTabline/MMTab.m index 1b6de407a9..235af717dc 100644 --- a/src/MacVim/MMTabline/MMTab.m +++ b/src/MacVim/MMTabline/MMTab.m @@ -3,9 +3,10 @@ #import "MMTabline.h" #import "MMHoverButton.h" -#import "MacVim.h" // for availability macros +// Only imported for AVAILABLE_MAC_OS +#import "MacVim.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 +#if !defined(MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 typedef NSString * NSAnimatablePropertyKey; #endif @@ -46,7 +47,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect tabline:(MMTabline *)tabline [self addSubview:_closeButton]; _titleLabel = [NSTextField new]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11 +#if defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11 if (AVAILABLE_MAC_OS(10,11)) { _titleLabel.font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize weight:NSFontWeightSemibold]; } else @@ -109,27 +110,29 @@ - (void)setFillColor:(NSColor *)fillColor - (void)setState:(MMTabState)state { + const BOOL hasFocus = (self.window == nil) || [self.window isKeyWindow]; + // Transitions to and from MMTabStateSelected // DO NOT animate so that UX feels snappier. if (state == MMTabStateSelected) { _closeButton.fgColor = _tabline.tablineSelFgColor; - _titleLabel.textColor = _tabline.tablineSelFgColor; + _titleLabel.textColor = hasFocus ? _tabline.tablineSelFgColor : _tabline.tablineUnfocusedSelFgColor; self.fillColor = _tabline.tablineSelBgColor; } else if (state == MMTabStateUnselected) { if (_state == MMTabStateSelected) { _closeButton.fgColor = _tabline.tablineFgColor; - _titleLabel.textColor = _tabline.tablineFgColor; + _titleLabel.textColor = hasFocus ? _tabline.tablineFgColor : _tabline.tablineUnfocusedFgColor; self.fillColor = _tabline.tablineBgColor; } else { _closeButton.animator.fgColor = _tabline.tablineFgColor; - _titleLabel.animator.textColor = _tabline.tablineFgColor; + _titleLabel.animator.textColor = hasFocus ? _tabline.tablineFgColor : _tabline.tablineUnfocusedFgColor; self.animator.fillColor = _tabline.tablineBgColor; } } else { // state == MMTabStateUnselectedHover _closeButton.animator.fgColor = _tabline.tablineSelFgColor; - _titleLabel.animator.textColor = _tabline.tablineSelFgColor; + _titleLabel.animator.textColor = hasFocus ? _tabline.tablineSelFgColor : _tabline.tablineUnfocusedSelFgColor; self.animator.fillColor = self.unselectedHoverColor; } _state = state; @@ -164,6 +167,11 @@ - (void)drawRect:(NSRect)dirtyRect [p transformUsingAffineTransform:transform]; } [p fill]; + NSColor *strokeColor = _tabline.tablineStrokeColor; + if (strokeColor != nil) { + [strokeColor set]; + [p stroke]; + } } @end diff --git a/src/MacVim/MMTabline/MMTabline.h b/src/MacVim/MMTabline/MMTabline.h index a43be7828b..98bef13bdf 100644 --- a/src/MacVim/MMTabline/MMTabline.h +++ b/src/MacVim/MMTabline/MMTabline.h @@ -19,11 +19,18 @@ @property (nonatomic) BOOL useAnimation; @property (nonatomic, readonly) NSInteger numberOfTabs; @property (nonatomic, retain, readonly) MMHoverButton *addTabButton; + @property (nonatomic, retain) NSColor *tablineBgColor; @property (nonatomic, retain) NSColor *tablineFgColor; @property (nonatomic, retain) NSColor *tablineSelBgColor; @property (nonatomic, retain) NSColor *tablineSelFgColor; @property (nonatomic, retain) NSColor *tablineFillFgColor; + +// Derived colors that cannot be set directly +@property (nonatomic, readonly) NSColor *tablineUnfocusedFgColor; +@property (nonatomic, readonly) NSColor *tablineUnfocusedSelFgColor; +@property (nonatomic, readonly) NSColor *tablineStrokeColor; + @property (nonatomic, weak) id delegate; /// Add a tab at the end. It's not selected automatically. @@ -57,7 +64,16 @@ - (void)scrollTabToVisibleAtIndex:(NSInteger)index; - (void)scrollBackwardOneTab; - (void)scrollForwardOneTab; -- (void)setTablineSelBackground:(NSColor *)back foreground:(NSColor *)fore; + +/// Sets the colors used by this tab bar explicitly. Pass nil to use default +/// colors based on the system light/dark modes. +- (void)setColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg + fill:(NSColor *)fill; + +/// Lets the tabline calculate best colors to use based on background and +/// foreground colors of the selected tab. The colors cannot be nil. +- (void)setAutoColorsSelBg:(NSColor *)back fg:(NSColor *)fore; @end diff --git a/src/MacVim/MMTabline/MMTabline.m b/src/MacVim/MMTabline/MMTabline.m index ff90d46d01..dd237ec239 100644 --- a/src/MacVim/MMTabline/MMTabline.m +++ b/src/MacVim/MMTabline/MMTabline.m @@ -1,6 +1,8 @@ #import #import #import "MMTabline.h" + +// Only imported for getCurrentAppearance() #import "Miscellaneous.h" typedef struct TabWidth { @@ -26,9 +28,20 @@ return button; } -static BOOL isDarkMode(NSAppearance *appearance) { - int flags = getCurrentAppearance(appearance); - return (flags == 1 || flags == 3); +static CGFloat calculateBrightness(NSColor *color) { + if (color.colorSpace.colorSpaceModel == NSColorSpaceModelRGB) { + // Calculate brightness according to a formula from + // the W3C that gives brightness as the eye perceives it. Then lighten + // or darken the default colors based on whether brightness is greater + // than 50% to achieve good visual contrast. + // www.w3.org/WAI/ER/WD-AERT/#color-contrast + CGFloat r, g, b; + [color getRed:&r green:&g blue:&b alpha:NULL]; + return r * 0.299 + g * 0.114 + b * 0.587; + } else if (color.colorSpace.colorSpaceModel == NSColorSpaceModelGray) { + return color.whiteComponent; + } + return 1; } @implementation MMTabline @@ -47,6 +60,7 @@ @implementation MMTabline MMHoverButton *_backwardScrollButton; MMHoverButton *_forwardScrollButton; id _scrollWheelEventMonitor; + AppearanceType _appearance; // cached appearance to avoid querying it every time } @synthesize tablineBgColor = _tablineBgColor; @@ -54,6 +68,8 @@ @implementation MMTabline @synthesize tablineSelBgColor = _tablineSelBgColor; @synthesize tablineSelFgColor = _tablineSelFgColor; @synthesize tablineFillFgColor = _tablineFillFgColor; +@synthesize tablineUnfocusedFgColor = _tablineUnfocusedFgColor; +@synthesize tablineUnfocusedSelFgColor = _tablineUnfocusedSelFgColor; - (instancetype)initWithFrame:(NSRect)frameRect { @@ -111,11 +127,15 @@ - (instancetype)initWithFrame:(NSRect)frameRect [self addConstraint:_addTabButtonTrailingConstraint]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didScroll:) name:NSViewBoundsDidChangeNotification object:_scrollView.contentView]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTabStates) name:NSWindowDidBecomeKeyNotification object:self.window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTabStates) name:NSWindowDidResignKeyNotification object:self.window]; if ([self useRightToLeft]) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTabsContainerBoundsForRTL:) name:NSViewFrameDidChangeNotification object:_tabsContainer]; } [self addScrollWheelMonitor]; + + _appearance = getCurrentAppearance(self.effectiveAppearance); } return self; } @@ -135,7 +155,8 @@ - (void)updateLayer - (void)viewDidChangeEffectiveAppearance { - for (MMTab *tab in _tabs) tab.state = tab.state; + _appearance = getCurrentAppearance(self.effectiveAppearance); + [self updateTabStates]; } - (void)viewDidHide @@ -217,39 +238,50 @@ - (void)setShowsTabScrollButtons:(BOOL)showsTabScrollButtons - (NSColor *)tablineBgColor { - return _tablineBgColor ?: isDarkMode(self.effectiveAppearance) - ? [NSColor colorWithWhite:0.2 alpha:1] - : [NSColor colorWithWhite:0.8 alpha:1]; + if (_tablineBgColor != nil) + return _tablineBgColor; + switch (_appearance) { + case AppearanceLight: + default: + return [NSColor colorWithWhite:0.8 alpha:1]; + case AppearanceDark: + return [NSColor colorWithWhite:0.2 alpha:1]; + case AppearanceLightHighContrast: + return [NSColor colorWithWhite:0.7 alpha:1]; + case AppearanceDarkHighContrast: + return [NSColor colorWithWhite:0.15 alpha:1]; + } } - (void)setTablineBgColor:(NSColor *)color { _tablineBgColor = color; - for (MMTab *tab in _tabs) tab.state = tab.state; + [self updateTabStates]; } - (NSColor *)tablineFgColor { - return _tablineFgColor ?: NSColor.disabledControlTextColor; + return _tablineFgColor ?: NSColor.secondaryLabelColor; } - (void)setTablineFgColor:(NSColor *)color { _tablineFgColor = color; - for (MMTab *tab in _tabs) tab.state = tab.state; + _tablineUnfocusedFgColor = nil; + [self updateTabStates]; } - (NSColor *)tablineSelBgColor { - return _tablineSelBgColor ?: isDarkMode(self.effectiveAppearance) - ? [NSColor colorWithWhite:0.4 alpha:1] - : NSColor.whiteColor; + return _tablineSelBgColor ?: (_appearance == AppearanceLight || _appearance == AppearanceLightHighContrast) + ? [NSColor colorWithWhite:0.95 alpha:1] + : [NSColor colorWithWhite:0.4 alpha:1]; } - (void)setTablineSelBgColor:(NSColor *)color { _tablineSelBgColor = color; - for (MMTab *tab in _tabs) tab.state = tab.state; + [self updateTabStates]; } - (NSColor *)tablineSelFgColor @@ -260,17 +292,18 @@ - (NSColor *)tablineSelFgColor - (void)setTablineSelFgColor:(NSColor *)color { _tablineSelFgColor = color; + _tablineUnfocusedSelFgColor = nil; _addTabButton.fgColor = color; _backwardScrollButton.fgColor = color; _forwardScrollButton.fgColor = color; - for (MMTab *tab in _tabs) tab.state = tab.state; + [self updateTabStates]; } - (NSColor *)tablineFillFgColor { - return _tablineFillFgColor ?: isDarkMode(self.effectiveAppearance) - ? [NSColor colorWithWhite:0.2 alpha:1] - : [NSColor colorWithWhite:0.8 alpha:1]; + return _tablineFillFgColor ?: (_appearance == AppearanceLight || _appearance == AppearanceLightHighContrast) + ? [NSColor colorWithWhite:0.9 alpha:1] + : [NSColor colorWithWhite:0.25 alpha:1]; } - (void)setTablineFillFgColor:(NSColor *)color @@ -279,6 +312,30 @@ - (void)setTablineFillFgColor:(NSColor *)color self.needsDisplay = YES; } +- (NSColor *)tablineUnfocusedFgColor +{ + return _tablineUnfocusedFgColor ?: _tablineFgColor ?: NSColor.tertiaryLabelColor; +} + +- (NSColor *)tablineUnfocusedSelFgColor +{ + return _tablineUnfocusedSelFgColor ?: _tablineSelFgColor ?: NSColor.tertiaryLabelColor; +} + +- (NSColor *)tablineStrokeColor +{ + if (_appearance == AppearanceLight || _appearance == AppearanceDark) + return nil; // non-high-contrast modes + + // High-contrast modes. Should stroke to make it easier to read. + NSColor *bgColor = self.tablineBgColor; + CGFloat brightness = calculateBrightness(bgColor); + if (brightness > 0.5) + return NSColor.blackColor; + else + return NSColor.whiteColor; +} + - (NSInteger)addTabAtEnd { return [self addTabAtIndex:(_tabs.count ? _tabs.count : 0)]; @@ -511,43 +568,59 @@ - (MMTab *)tabAtIndex:(NSInteger)index return _tabs[index]; } -- (void)setTablineSelBackground:(NSColor *)back foreground:(NSColor *)fore +- (void)setColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg + fill:(NSColor *)fill { - // Reset to default tabline colors if user doesn't want auto-generated ones. - if ([NSUserDefaults.standardUserDefaults boolForKey:@"MMDefaultTablineColors"]) { - self.tablineBgColor = nil; - self.tablineFgColor = nil; - self.tablineSelBgColor = nil; - self.tablineSelFgColor = nil; - self.tablineFillFgColor = nil; - return; - } + // Don't use the property mutators as we just want to update the states in + // one go at the end. + _tablineSelBgColor = selBg; + _tablineSelFgColor = selFg; + _tablineBgColor = tabBg; + _tablineFgColor = tabFg; + _tablineFillFgColor = fill; + + _tablineUnfocusedFgColor = [_tablineFgColor blendedColorWithFraction:0.4 ofColor:_tablineBgColor]; + _tablineUnfocusedSelFgColor = [_tablineSelFgColor blendedColorWithFraction:0.38 ofColor:_tablineSelBgColor]; + + _addTabButton.fgColor = _tablineSelFgColor; + _backwardScrollButton.fgColor = _tablineSelFgColor; + _forwardScrollButton.fgColor = _tablineSelFgColor; + + [self updateTabStates]; + self.needsDisplay = YES; +} +- (void)setAutoColorsSelBg:(NSColor *)back fg:(NSColor *)fore; +{ // Set the colors for the tabline based on the default background and - // foreground colors. Calculate brightness according to a formula from - // the W3C that gives brightness as the eye perceives it. Then lighten - // or darken the default colors based on whether brightness is greater - // than 50% to achieve good visual contrast. - // www.w3.org/WAI/ER/WD-AERT/#color-contrast - CGFloat r, g, b, brightness; - [back getRed:&r green:&g blue:&b alpha:NULL]; - brightness = r * 0.299 + g * 0.114 + b * 0.587; - - self.tablineSelBgColor = back; + // foreground colors. + const CGFloat brightness = calculateBrightness(back); - self.tablineSelFgColor = (brightness > 0.5) + _tablineSelBgColor = back; + + _tablineSelFgColor = (brightness > 0.5) ? [fore blendedColorWithFraction:0.6 ofColor:NSColor.blackColor] : [fore blendedColorWithFraction:0.6 ofColor:NSColor.whiteColor]; + _addTabButton.fgColor = _tablineSelFgColor; + _backwardScrollButton.fgColor = _tablineSelFgColor; + _forwardScrollButton.fgColor = _tablineSelFgColor; - self.tablineBgColor = (brightness > 0.5) + _tablineBgColor = (brightness > 0.5) ? [back blendedColorWithFraction:0.16 ofColor:NSColor.blackColor] : [back blendedColorWithFraction:0.13 ofColor:NSColor.whiteColor]; - self.tablineFgColor = [self.tablineSelFgColor blendedColorWithFraction:0.5 ofColor:self.tablineBgColor]; + _tablineFgColor = [_tablineSelFgColor blendedColorWithFraction:0.5 ofColor:_tablineBgColor]; + + _tablineUnfocusedFgColor = [_tablineFgColor blendedColorWithFraction:0.4 ofColor:_tablineBgColor]; + _tablineUnfocusedSelFgColor = [_tablineSelFgColor blendedColorWithFraction:0.38 ofColor:_tablineSelBgColor]; - self.tablineFillFgColor = (brightness > 0.5) + _tablineFillFgColor = (brightness > 0.5) ? [back blendedColorWithFraction:0.25 ofColor:NSColor.blackColor] : [back blendedColorWithFraction:0.18 ofColor:NSColor.whiteColor]; + + [self updateTabStates]; + self.needsDisplay = YES; } #pragma mark - Helpers @@ -740,6 +813,11 @@ - (void)fixupLayoutWithAnimation:(BOOL)shouldAnimate [self fixupLayoutWithAnimation:shouldAnimate delayResize:NO]; } +- (void)updateTabStates +{ + for (MMTab *tab in _tabs) tab.state = tab.state; +} + #pragma mark - Right-to-left (RTL) support - (BOOL)useRightToLeft @@ -943,7 +1021,7 @@ - (void)scrollTabToVisibleAtIndex:(NSInteger)index // faster animations. For example, the user might hold down the tab // scrolling buttons (causing them to repeatedly fire) or they might // rapidly click them. -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 +#if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 static NSTimeInterval lastTime = 0; struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index df12bfa0f4..12fa382f12 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -968,6 +968,26 @@ - (void)handleMessage:(int)msgid data:(NSData *)data } } break; + case SetTablineColorsMsgID: + { + const void *bytes = [data bytes]; + unsigned argbTabBg = *((unsigned*)bytes); bytes += sizeof(unsigned); + unsigned argbTabFg = *((unsigned*)bytes); bytes += sizeof(unsigned); + unsigned argbFillBg = *((unsigned*)bytes); bytes += sizeof(unsigned); + unsigned argbFillFg = *((unsigned*)bytes); bytes += sizeof(unsigned); + unsigned argbSelBg = *((unsigned*)bytes); bytes += sizeof(unsigned); + unsigned argbSelFg = *((unsigned*)bytes); bytes += sizeof(unsigned); + + NSColor *tabBg = [NSColor colorWithRgbInt:argbTabBg]; + NSColor *tabFg = [NSColor colorWithRgbInt:argbTabFg]; + NSColor *fillBg = [NSColor colorWithRgbInt:argbFillBg]; + NSColor *fillFg = [NSColor colorWithRgbInt:argbFillFg]; + NSColor *selBg = [NSColor colorWithRgbInt:argbSelBg]; + NSColor *selFg = [NSColor colorWithRgbInt:argbSelFg]; + + [windowController setTablineColorsTabBg:tabBg tabFg:tabFg fillBg:fillBg fillFg:fillFg selBg:selBg selFg:selFg]; + } + break; case SetDefaultColorsMsgID: { const void *bytes = [data bytes]; diff --git a/src/MacVim/MMVimView.h b/src/MacVim/MMVimView.h index d29b75c63c..f1a49009b3 100644 --- a/src/MacVim/MMVimView.h +++ b/src/MacVim/MMVimView.h @@ -60,6 +60,9 @@ - (void)finishPlaceScrollbars; - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore; +- (void)setTablineColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + fillBg:(NSColor *)fillBg fillFg:(NSColor *)fillFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg; - (void)viewWillStartLiveResize; - (void)viewDidEndLiveResize; diff --git a/src/MacVim/MMVimView.m b/src/MacVim/MMVimView.m index 96c6273094..9a7a5a4781 100644 --- a/src/MacVim/MMVimView.m +++ b/src/MacVim/MMVimView.m @@ -35,6 +35,15 @@ MMScrollerTypeBottom }; +typedef enum: NSInteger { + MMTabColorTypeTabBg = 0, + MMTabColorTypeTabFg, + MMTabColorTypeSelBg, + MMTabColorTypeSelFg, + MMTabColorTypeFill, + MMTabColorTypeCount +} MMTabColorType; + // TODO: Move! @interface MMScroller : NSScroller { @@ -72,6 +81,9 @@ - (void)liveResizeDidEnd; @implementation MMVimView +{ + NSColor *tabColors[MMTabColorTypeCount]; +} - (MMVimView *)initWithFrame:(NSRect)frame vimController:(MMVimController *)controller @@ -133,6 +145,9 @@ - (void)dealloc [tabline release]; [scrollbars release]; scrollbars = nil; + for (NSUInteger i = 0; i < MMTabColorTypeCount; i++) + [tabColors[i] release]; + // HACK! The text storage is the principal owner of the text system, but we // keep only a reference to the text view, so release the text storage // first (unless we are using the CoreText renderer). @@ -362,6 +377,7 @@ - (void)refreshTabProperties { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; tabline.showsTabScrollButtons = [ud boolForKey:MMShowTabScrollButtonsKey]; + [self updateTablineColors]; } - (void)createScrollbarWithIdentifier:(int32_t)ident type:(int)type @@ -453,11 +469,36 @@ - (void)finishPlaceScrollbars } } +- (void)updateTablineColors +{ + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + MMTabColorsMode tabColorsMode = [ud integerForKey:MMTabColorsModeKey]; + if (tabColorsMode == MMTabColorsModeDefaultColors) { + [tabline setColorsTabBg:nil + tabFg:nil + selBg:nil + selFg:nil + fill:nil]; + } else if (tabColorsMode == MMTabColorsModeVimColorscheme) { + [tabline setColorsTabBg:tabColors[MMTabColorTypeTabBg] + tabFg:tabColors[MMTabColorTypeTabFg] + selBg:tabColors[MMTabColorTypeSelBg] + selFg:tabColors[MMTabColorTypeSelFg] + fill:tabColors[MMTabColorTypeFill]]; + } else { + // tabColorsMode == MMTabColorsModeAutomatic, but catch-all in case it's + // set to an out-of-range number. + NSColor *back = [[self textView] defaultBackgroundColor]; + NSColor *fore = [[self textView] defaultForegroundColor]; + [tabline setAutoColorsSelBg:back fg:fore]; + } + +} + - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore { [textView setDefaultColorsBackground:back foreground:fore]; - - [tabline setTablineSelBackground:back foreground:fore]; + [self updateTablineColors]; CALayer *backedLayer = [self layer]; if (backedLayer) { @@ -476,6 +517,21 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore [self setNeedsDisplay:YES]; } +- (void)setTablineColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + fillBg:(NSColor *)fillBg fillFg:(NSColor *)fillFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg +{ + for (NSUInteger i = 0; i < MMTabColorTypeCount; i++) + [tabColors[i] release]; + tabColors[MMTabColorTypeTabBg] = [tabBg retain]; + tabColors[MMTabColorTypeTabFg] = [tabFg retain]; + tabColors[MMTabColorTypeSelBg] = [selBg retain]; + tabColors[MMTabColorTypeSelFg] = [selFg retain]; + tabColors[MMTabColorTypeFill] = [fillBg retain]; + (void)fillFg; // We don't use fillFg as we don't draw anything in the empty area + [self updateTablineColors]; +} + // -- MMTablineDelegate ---------------------------------------------- @@ -594,6 +650,19 @@ - (void)setFrame:(NSRect)frame - (void)viewDidChangeEffectiveAppearance { [vimController appearanceChanged:getCurrentAppearance(self.effectiveAppearance)]; + + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if ([ud integerForKey:MMTabColorsModeKey] == MMTabColorsModeDefaultColors && + [ud boolForKey:MMWindowUseTabBackgroundColorKey]) + { + // Tab line default colors depends on system light/dark modes. We will + // need to notify the window as well if it is set up to use the tab bar + // colors. We need to schedule this for later because the tabline's + // effectAppearance gets changed *after* this method is called, so we + // need to delay the refresh or we would get stale data. + MMWindowController *winController = [vimController windowController]; + [winController performSelectorOnMainThread:@selector(refreshTabProperties) withObject:nil waitUntilDone:NO]; + } } @end // MMVimView diff --git a/src/MacVim/MMWindowController.h b/src/MacVim/MMWindowController.h index df932f0e98..544385f8c2 100644 --- a/src/MacVim/MMWindowController.h +++ b/src/MacVim/MMWindowController.h @@ -84,7 +84,10 @@ - (void)setBackgroundOption:(int)dark; - (void)refreshApperanceMode; - (void)updateResizeConstraints:(BOOL)resizeWindow; - +- (void)setTablineColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + fillBg:(NSColor *)fillBg fillFg:(NSColor *)fillFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg; +- (void)refreshTabProperties; - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore; - (void)setFont:(NSFont *)font; - (void)setWideFont:(NSFont *)font; diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index e062796052..a583aa395f 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -71,6 +71,7 @@ #import "MMWindow.h" #import "MMWindowController.h" #import "Miscellaneous.h" +#import "MMTabline/MMTabline.h" // These have to be the same as in option.h @@ -672,11 +673,61 @@ - (void)refreshApperanceMode #endif } +- (void)setTablineColorsTabBg:(NSColor *)tabBg tabFg:(NSColor *)tabFg + fillBg:(NSColor *)fillBg fillFg:(NSColor *)fillFg + selBg:(NSColor *)selBg selFg:(NSColor *)selFg +{ + [vimView setTablineColorsTabBg:tabBg tabFg:tabFg fillBg:fillBg fillFg:fillFg selBg:selBg selFg:selFg]; + + if([[NSUserDefaults standardUserDefaults] boolForKey:MMWindowUseTabBackgroundColorKey]) { + if (!vimView.tabline.hidden) { + [self setWindowColorToTablineColor]; + } + } +} + +- (void)setWindowColorToTablineColor +{ + NSColor *defaultBg = vimView.textView.defaultBackgroundColor; + NSColor *tablineColor = vimView.tabline.tablineFillFgColor; + if (defaultBg.alphaComponent == 1.0) { + [self setWindowBackgroundColor:tablineColor]; + } else { + // Make sure 'transparency' Vim setting is preserved + NSColor *colorWithAlpha = [tablineColor colorWithAlphaComponent:defaultBg.alphaComponent]; + [self setWindowBackgroundColor:colorWithAlpha]; + } +} + +- (void)refreshTabProperties +{ + [vimView refreshTabProperties]; + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if([ud boolForKey:MMWindowUseTabBackgroundColorKey] && !vimView.tabline.hidden) { + [self setWindowColorToTablineColor]; + } else { + [self setWindowBackgroundColor:vimView.textView.defaultBackgroundColor]; + } +} + - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore +{ + [vimView setDefaultColorsBackground:back foreground:fore]; + if([[NSUserDefaults standardUserDefaults] boolForKey:MMWindowUseTabBackgroundColorKey] && + !vimView.tabline.hidden) + { + [self setWindowColorToTablineColor]; + } + else { + [self setWindowBackgroundColor:back]; + } +} + +- (void)setWindowBackgroundColor:(NSColor *)back { // NOTE: This is called when the transparency changes so set the opacity // flag on the window here (should be faster if the window is opaque). - BOOL isOpaque = [back alphaComponent] == 1.0f; + const BOOL isOpaque = [back alphaComponent] == 1.0f; [decoratedWindow setOpaque:isOpaque]; if (fullScreenWindow) [fullScreenWindow setOpaque:isOpaque]; @@ -727,7 +778,7 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore // but if we are toggling the titlebar transparent option, we need to set // the window background color in order the title bar to be tinted correctly. if ([[NSUserDefaults standardUserDefaults] - boolForKey:MMTitlebarAppearsTransparentKey]) { + boolForKey:MMTitlebarAppearsTransparentKey]) { if ([back alphaComponent] != 0) { [decoratedWindow setBackgroundColor:back]; } else { @@ -739,8 +790,6 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore } } } - - [vimView setDefaultColorsBackground:back foreground:fore]; } - (void)setFont:(NSFont *)font @@ -871,6 +920,14 @@ - (void)showTabline:(BOOL)on [vimView showTabline:on]; [self updateTablineSeparator]; shouldMaximizeWindow = YES; + + if([[NSUserDefaults standardUserDefaults] boolForKey:MMWindowUseTabBackgroundColorKey]) { + if (on) { + [self setWindowColorToTablineColor]; + } else { + [self setWindowBackgroundColor:vimView.textView.defaultBackgroundColor]; + } + } } - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index 515ebe8355..aa12b8c575 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -301,6 +301,7 @@ extern const char * const MMVimMsgIDStrings[]; MSG(SetWideFontMsgID) \ MSG(VimShouldCloseMsgID) \ MSG(SetDefaultColorsMsgID) \ + MSG(SetTablineColorsMsgID) \ MSG(ExecuteActionMsgID) \ MSG(DropFilesMsgID) \ MSG(DropStringMsgID) \ diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index 0d297dd094..a247b3f647 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -23,6 +23,8 @@ extern NSString *MMTabMaxWidthKey; extern NSString *MMTabOptimumWidthKey; extern NSString *MMShowAddTabButtonKey; extern NSString *MMShowTabScrollButtonsKey; +extern NSString *MMTabColorsModeKey; +extern NSString *MMWindowUseTabBackgroundColorKey; extern NSString *MMTextInsetLeftKey; extern NSString *MMTextInsetRightKey; extern NSString *MMTextInsetTopKey; @@ -104,6 +106,11 @@ enum MMAppearanceModeSelectionEnum { MMAppearanceModeSelectionBackgroundOption = 3, }; +typedef enum : NSInteger { + MMTabColorsModeDefaultColors = 0, ///< Use default colors based on macOS light/dark modes + MMTabColorsModeAutomatic, ///< Automatically derive tab colors based on foreground/background colors + MMTabColorsModeVimColorscheme, ///< Use Vim colorscheme TabLine/TabLineSel/TabLineFill colors +} MMTabColorsMode; enum { // These values are chosen so that the min text view size is not too small @@ -174,7 +181,13 @@ NSView *showHiddenFilesView(void); NSString *normalizeFilename(NSString *filename); NSArray *normalizeFilenames(NSArray *filenames); -int getCurrentAppearance(NSAppearance *appearance); +typedef enum : int { + AppearanceLight = 0, + AppearanceDark, + AppearanceLightHighContrast, + AppearanceDarkHighContrast, +} AppearanceType; +AppearanceType getCurrentAppearance(NSAppearance *appearance); // Pasteboard helpers NSPasteboardType getPasteboardFilenamesType(void); diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index 0b1e0523e2..17e9304238 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -19,6 +19,8 @@ NSString *MMTabOptimumWidthKey = @"MMTabOptimumWidth"; NSString *MMShowAddTabButtonKey = @"MMShowAddTabButton"; NSString *MMShowTabScrollButtonsKey = @"MMShowTabScrollButtons"; +NSString *MMTabColorsModeKey = @"MMTabColorsMode"; +NSString *MMWindowUseTabBackgroundColorKey = @"MMWindowUseTabBackgroundColor"; NSString *MMTextInsetLeftKey = @"MMTextInsetLeft"; NSString *MMTextInsetRightKey = @"MMTextInsetRight"; NSString *MMTextInsetTopKey = @"MMTextInsetTop"; @@ -305,9 +307,9 @@ - (NSInteger)tag -int +AppearanceType getCurrentAppearance(NSAppearance *appearance){ - int flag = 0; // for macOS 10.13 or earlier always return 0; + int flag = AppearanceLight; // for macOS 10.13 or earlier always return 0; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 if (@available(macOS 10.14, *)) { NSAppearanceName appearanceName = [appearance bestMatchFromAppearancesWithNames: @@ -316,11 +318,11 @@ - (NSInteger)tag , NSAppearanceNameAccessibilityHighContrastAqua , NSAppearanceNameAccessibilityHighContrastDarkAqua]]; if ([appearanceName isEqualToString:NSAppearanceNameDarkAqua]) { - flag = 1; + flag = AppearanceDark; } else if ([appearanceName isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) { - flag = 2; + flag = AppearanceLightHighContrast; } else if ([appearanceName isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) { - flag = 3; + flag = AppearanceDarkHighContrast; } } #endif diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 3538acbbae..0d030f04ca 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -633,6 +633,17 @@ ASLogDebug(@"back=%ld norm=%ld", gui.def_back_pixel, gui.def_norm_pixel); + [[MMBackend sharedInstance] + setDefaultColorsBackground:gui.def_back_pixel + foreground:gui.def_norm_pixel]; +} + +/* + * Called when any highlight has been changed in general + */ + void +gui_mch_update_highlight(void) +{ // If using a highlight group for fullscreen background color we need to // update the app when a new color scheme has been picked. This function // technically wouldn't be called if a user manually set the relevant @@ -641,9 +652,34 @@ if (fuoptions_flags & FUOPT_BGCOLOR_HLGROUP) gui_mch_fuopt_update(); - [[MMBackend sharedInstance] - setDefaultColorsBackground:gui.def_back_pixel - foreground:gui.def_norm_pixel]; + // Update the GUI with tab colors + // We can cache the tabline syn IDs because they will never change. + static int tablineSynIds[3] = { 0 }; + char *tablineSynNames[3] = {"TabLine", "TabLineFill", "TabLineSel"}; + + BOOL hasTablineColors = YES; + int tablineColors[6] = { 0 }; + for (int i = 0; i < 3; i++) { + if (tablineSynIds[i] <= 0) + tablineSynIds[i] = syn_name2id((char_u *)tablineSynNames[i]); + if (tablineSynIds[i] > 0) { + guicolor_T bg, fg; + syn_id2colors(tablineSynIds[i], &fg, &bg); + tablineColors[i*2] = (int)bg; + tablineColors[i*2+1] = (int)fg; + } else { + hasTablineColors = NO; + } + } + if (hasTablineColors) { + // Cache the old colors just so we don't spam the IPC channel if the + // colors didn't actually change. + static int oldTablineColors[6] = { 0 }; + if (memcmp(oldTablineColors, tablineColors, sizeof(oldTablineColors)) != 0) { + memcpy(oldTablineColors, tablineColors, sizeof(oldTablineColors)); + [[MMBackend sharedInstance] setTablineColors:tablineColors]; + } + } } /* @@ -1995,6 +2031,8 @@ { if (!gui.in_use) return; + if (!p_fullscreen) + return; guicolor_T fg, bg; if (fuoptions_flags & FUOPT_BGCOLOR_HLGROUP) { diff --git a/src/highlight.c b/src/highlight.c index a04f158e09..0bf781ab9b 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -4069,6 +4069,12 @@ highlight_changed(void) #endif // USER_HIGHLIGHT +#if defined(FEAT_GUI) && defined(FEAT_GUI_MACVIM) + // MacVim needs to know about the other highlight colors other than just + // the default fg/bg colors. + if (gui.in_use) + gui_mch_update_highlight(); +#endif return OK; } diff --git a/src/proto/gui_macvim.pro b/src/proto/gui_macvim.pro index 869727d044..7d27521d5d 100644 --- a/src/proto/gui_macvim.pro +++ b/src/proto/gui_macvim.pro @@ -63,6 +63,7 @@ int gui_mch_haskey(char_u *name); void gui_mch_iconify(void); void gui_mch_invert_rectangle(int r, int c, int nr, int nc, int invert); void gui_mch_new_colors(void); +void gui_mch_update_highlight(void); void gui_mch_set_bg_color(guicolor_T color); int gui_mch_is_blinking(void); int gui_mch_is_blink_off(void);