diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index ac3dc56ee7..f9b1d27797 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -1129,11 +1129,22 @@ That's all. XLFDs are not used. For Chinese this is reported to work well: > < (Replace gui_gtk2 with gui_gtk3 for the GTK+ 3 GUI) -For Mac OSX you can use something like this: > - :set guifont=Monaco:h10 -Also see 'macatsui', it can help fix display problems {not in MacVim}. -In MacVim, fonts with spaces are set like this: > +MacVim *macvim-guifont* + +For MacVim you can use something like this: > + :set guifont=Menlo:h10 +Fonts with spaces are set like this: > :set guifont=DejaVu\ Sans\ Mono:h13 +To use bold/italic fonts, use the fully specified PostScript name of the +font, like so: > + :set guifont=Menlo-Bold:h13 +To use the system native monospace font (which is SF Mono in new macOS +versions), use the `-monospace-` keyword: > + :set guifont=-monospace-:h12 +You can also specify the font weight of the native monospace font (refer to +Apple documentation for `NSFontWeight` for possible values): > + :set guifont=-monospace-Light:h11 + :set guifont=-monospace-Medium:h11 < Mono-spaced fonts *E236* diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index 869cec0f26..926da6e14a 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -118,6 +118,9 @@ These are the non-standard options that MacVim supports: 'fuoptions' 'macligatures' 'macmeta' 'macthinstrokes' 'toolbariconsize' 'transparency' +These are GUI-related Vim options that have MacVim-specific behaviors: + 'guifont' + *macvim-commands* These are the non-standard commands that MacVim supports: |:macaction| |:macmenu| diff --git a/runtime/doc/tags b/runtime/doc/tags index d1ce8e9840..c8541b5767 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -8622,6 +8622,7 @@ macvim-encoding gui_mac.txt /*macvim-encoding* macvim-find gui_mac.txt /*macvim-find* macvim-full-screen gui_mac.txt /*macvim-full-screen* macvim-gestures gui_mac.txt /*macvim-gestures* +macvim-guifont gui.txt /*macvim-guifont* macvim-help-menu gui_mac.txt /*macvim-help-menu* macvim-hints gui_mac.txt /*macvim-hints* macvim-internal-variables gui_mac.txt /*macvim-internal-variables* diff --git a/src/MacVim/MMCoreTextView.h b/src/MacVim/MMCoreTextView.h index f698d55bbd..9bdf59f7e4 100644 --- a/src/MacVim/MMCoreTextView.h +++ b/src/MacVim/MMCoreTextView.h @@ -87,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN // NSFontChanging methods // - (void)changeFont:(nullable id)sender; +- (NSFontPanelModeMask)validModesForFontPanel:(NSFontPanel *)fontPanel; // // NSMenuItemValidation diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index 38b508ee04..6c146e7e3f 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -1299,9 +1299,10 @@ - (NSSize)minSize MMMinRows * cellSize.height + insetSize.height + bot); } -// Called when font panel selection has been made. Send the selected font to -// MMBackend so it would set guifont which will send a message back to MacVim to -// call MMWindowController::setFont. +// Called when font panel selection has been made or when adjusting font size +// using modifyFont/NSSizeUpFontAction. Send the selected font to MMBackend so +// it would set guifont which will send a message back to MacVim to call +// MMWindowController::setFont. - (void)changeFont:(id)sender { NSFont *newFont = [sender convertFont:font]; @@ -1319,11 +1320,25 @@ - (void)changeFont:(id)sender [data appendBytes:&len length:sizeof(unsigned)]; [data appendBytes:[name UTF8String] length:len]; + // We don't update guifontwide for now, as panel font selection + // shouldn't affect them. This does mean Cmd +/- does not work for + // them for now. + const unsigned wideLen = 0; + [data appendBytes:&wideLen length:sizeof(unsigned)]; + [[self vimController] sendMessage:SetFontMsgID data:data]; } } } +- (NSFontPanelModeMask)validModesForFontPanel:(NSFontPanel *)fontPanel +{ + // Lets the user pick only the font face / size, as other properties as not + // useful. Still enable text/document colors as these affect the preview. + // Otherwise it could just be white text on white background in the preview. + return NSFontPanelModesMaskStandardModes & (~NSFontPanelModeMaskAllEffects | NSFontPanelModeMaskTextColorEffect | NSFontPanelModeMaskDocumentColorEffect); +} + /// Specifies whether the menu item should be enabled/disabled. - (BOOL)validateMenuItem:(NSMenuItem *)item { diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index cfb236376a..ef23eec78b 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -900,7 +900,44 @@ - (void)handleMessage:(int)msgid data:(NSData *)data NSString *name = [[NSString alloc] initWithBytes:(void*)bytes length:len encoding:NSUTF8StringEncoding]; - NSFont *font = [NSFont fontWithName:name size:size]; + NSFont *font = nil; + if ([name hasPrefix:MMSystemFontAlias]) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 + if (@available(macos 10.15, *)) { + NSFontWeight fontWeight = NSFontWeightRegular; + if (name.length > MMSystemFontAlias.length) { + const NSRange cmpRange = NSMakeRange(MMSystemFontAlias.length, name.length - MMSystemFontAlias.length); + if ([name compare:@"UltraLight" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightUltraLight; + else if ([name compare:@"Thin" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightThin; + else if ([name compare:@"Light" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightLight; + else if ([name compare:@"Regular" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightRegular; + else if ([name compare:@"Medium" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightMedium; + else if ([name compare:@"Semibold" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightSemibold; + else if ([name compare:@"Bold" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightBold; + else if ([name compare:@"Heavy" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightHeavy; + else if ([name compare:@"Black" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) + fontWeight = NSFontWeightBlack; + } + font = [NSFont monospacedSystemFontOfSize:size weight:fontWeight]; + } + else +#endif + { + // Fallback to Menlo on older macOS versions that don't support the system monospace font API + font = [NSFont fontWithName:@"Menlo-Regular" size:size]; + } + } + else { + font = [NSFont fontWithName:name size:size]; + } if (!font) { // This should only happen if the system default font has changed // name since MacVim was compiled in which case we fall back on diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index 0a9bd33378..bad6f8e09a 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -433,7 +433,8 @@ enum { extern NSString *VimFindPboardType; - +// Alias for system monospace font name +extern NSString *MMSystemFontAlias; @interface NSString (MMExtras) diff --git a/src/MacVim/MacVim.m b/src/MacVim/MacVim.m index 7cffc0b89f..127f18454a 100644 --- a/src/MacVim/MacVim.m +++ b/src/MacVim/MacVim.m @@ -40,6 +40,8 @@ // Vim find pasteboard type (string contains Vim regex patterns) NSString *VimFindPboardType = @"VimFindPboardType"; +NSString *MMSystemFontAlias = @"-monospace-"; + int ASLogLevel = MM_ASL_LEVEL_DEFAULT; diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m index 1ba1999851..f74f8a7356 100644 --- a/src/MacVim/MacVimTests/MacVimTests.m +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -10,6 +10,8 @@ #import +#import + #import "Miscellaneous.h" #import "MMAppController.h" #import "MMApplication.h" @@ -425,4 +427,35 @@ - (void) testCmdlineRowCalculation { [self waitForVimClose]; } +/// Test that using "-monospace-" for system default monospace font works. +- (void) testGuifontSystemMonospace { + MMAppController *app = MMAppController.sharedInstance; + + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimOpenAndMessages]; + + MMTextView *textView = [[[[app keyVimController] windowController] vimView] textView]; + XCTAssertEqualObjects(@"Menlo-Regular", [[textView font] fontName]); + + [self sendStringToVim:@":set guifont=-monospace-\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:11 weight:NSFontWeightRegular]); + + [self sendStringToVim:@":set guifont=-monospace-Heavy:h12\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightHeavy]); + + [[[app keyVimController] windowController] fontSizeUp:nil]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:13 weight:NSFontWeightHeavy]); + + [[[app keyVimController] windowController] fontSizeDown:nil]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightHeavy]); + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimClose]; +} + @end diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index d245fbfa32..c3a9367caf 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -153,7 +153,8 @@ enum { @interface NSNumber (MMExtras) -// HACK to allow font size to be changed via menu (bound to Cmd+/Cmd-) +// Used by modifyFont:/convertFont: to allow font size to be changed via menu +// (bound to Cmd+/Cmd-) or using macaction fontSizeUp:/fontSizeDown:. - (NSInteger)tag; @end diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index ea30b75d9f..7704ccb978 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -32,11 +32,13 @@ static NSString *MMDefaultFontName = @"Menlo-Regular"; static int MMDefaultFontSize = 11; -static char *MMDefaultFontStr = "Menlo-Regular:h11"; static char *MMDefaultFontSizeStr = "h11"; static int MMMinFontSize = 6; static int MMMaxFontSize = 100; +// This is duplicated in MMVimController. Could consolidate in the future. +static NSString *(system_font_weights[]) = { @"UltraLight", @"Thin", @"Light", @"Regular", @"Medium", @"Semibold", @"Bold", @"Heavy", @"Black" }; + static BOOL MMShareFindPboard = YES; static GuiFont gui_macvim_font_with_name(char_u *name); @@ -1141,6 +1143,22 @@ componentsJoinedByString:@" "]; } + const BOOL isSystemFont = [fontName hasPrefix:MMSystemFontAlias]; + if (isSystemFont) { + if (fontName.length > MMSystemFontAlias.length) { + BOOL invalidWeight = YES; + const NSRange cmpRange = NSMakeRange(MMSystemFontAlias.length, fontName.length - MMSystemFontAlias.length); + for (size_t i = 0; i < ARRAY_LENGTH(system_font_weights); i++) { + if ([fontName compare:system_font_weights[i] options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) { + invalidWeight = NO; + break; + } + } + if (invalidWeight) + return NOFONT; + } + } + if (!parseFailed && [fontName length] > 0) { if (size < MMMinFontSize) size = MMMinFontSize; if (size > MMMaxFontSize) size = MMMaxFontSize; @@ -1148,6 +1166,7 @@ // If the default font is requested we don't need to check if NSFont // can load it. Otherwise we ask NSFont if it can load it. if ([fontName isEqualToString:MMDefaultFontName] + || isSystemFont || [NSFont fontWithName:fontName size:size]) return [[NSString alloc] initWithFormat:@"%@:h%d", fontName, size]; } @@ -1170,7 +1189,9 @@ { // If guifont is empty, and we want to fill in the orig value, suggest // the default so the user can modify it. - if (add_match((char_u *)MMDefaultFontStr) != OK) + NSString *defaultFontStr = [NSString stringWithFormat:@"%@:h%d", + MMDefaultFontName, MMDefaultFontSize]; + if (add_match((char_u *)[defaultFontStr UTF8String]) != OK) return; } @@ -1185,6 +1206,27 @@ return; } + if (!wide) { + // Add system-native monospace font alias to completion. + char buf[40]; + [MMSystemFontAlias getCString:buf maxLength:ARRAY_LENGTH(buf) encoding:NSASCIIStringEncoding]; + if (add_match((char_u*)buf) != OK) + return; + const size_t fontAliasLen = STRLEN(buf); + if (STRNCMP(xp->xp_pattern, buf, fontAliasLen) == 0) { + // We additionally complete with font weights like "bold". We only + // do so if starting with "-monospace-" already to avoid spamming + // the user with too many variations on this. + for (size_t i = 0; i < ARRAY_LENGTH(system_font_weights); i++) { + [system_font_weights[i] getCString:buf+fontAliasLen + maxLength:ARRAY_LENGTH(buf)-fontAliasLen + encoding:NSASCIIStringEncoding]; + if (add_match((char_u*)buf) != OK) + return; + } + } + } + NSFontManager *fontManager = [NSFontManager sharedFontManager]; NSArray *availableFonts; if (wide) diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index a8f44f75ac..a150146a2d 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -433,6 +433,28 @@ func Test_set_guifont() let &guifont = guifont_saved endfunc +func Test_set_guifont_macvim() + CheckFeature gui_macvim + let guifont_saved = &guifont + let guifontwide_saved = &guifontwide + + set guifont=-monospace- + call assert_equal('-monospace-:h11', getfontname()) + set guifont=-monospace-Semibold + call assert_equal('-monospace-Semibold:h11', getfontname()) + + call assert_fails('set guifont=-monospace-SemiboldInvalidWeight', 'E596') + + set guifont=Menlo\ Regular + call assert_equal('Menlo Regular:h11', getfontname()) + + set guifont= + call assert_equal('Menlo-Regular:h11', getfontname()) + + let &guifontwide = guifontwide_saved + let &guifont = guifont_saved +endfunc + func Test_set_guifontset() CheckFeature xfontset let skipped = '' @@ -641,6 +663,13 @@ func Test_expand_guifont() call assert_equal(['Menlo-Regular'], getcompletion('set guifont=Menl*lar$', 'cmdline')) call assert_equal(['Menlo-Regular'], getcompletion('set guifontwide=Menl*lar$', 'cmdline')) + " Test system monospace font option. It's always the first option after + " the existing font. + call assert_equal('-monospace-', getcompletion('set guifont=', 'cmdline')[1]) + call assert_equal('-monospace-', getcompletion('set guifont=-monospace-', 'cmdline')[0]) + call assert_equal('-monospace-UltraLight', getcompletion('set guifont=-monospace-', 'cmdline')[1]) + call assert_equal(['-monospace-Medium'], getcompletion('set guifont=-monospace-Med', 'cmdline')) + " Make sure non-monospace fonts are filtered out only in 'guifont' call assert_equal([], getcompletion('set guifont=Hel*tica$', 'cmdline')) call assert_equal(['Helvetica'], getcompletion('set guifontwide=Hel*tica$', 'cmdline'))