diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d34f6574e9..6d3087026c 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2439,9 +2439,12 @@ v:operator The last operator given in Normal mode. This is a single Read-only. *v:os_appearance* *os-appearance-variable* -v:os_appearance The current OS appearance mode. Useful if you want to change +v:os_appearance The current OS appearance mode. Useful if you want to change options |background| or |colorscheme| according to the - appearance of the GUI frontend. See also |OSAppearanceChanged|. + appearance of the GUI frontend. See also + |OSAppearanceChanged|. If the "Dark mode selection" setting + is not set to "Automatic", then this value will reflect that + setting instead. value description ~ 0 Light Mode (always 0 on unsupported platforms) 1 Dark Mode diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m index 04a29f6cd8..cdfd33c639 100644 --- a/src/MacVim/MacVimTests/MacVimTests.m +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -458,6 +458,83 @@ - (void) testGuifontSystemMonospace { [self waitForVimClose]; } +/// Test that dark mode settings work and the corresponding Vim bindings are functional. +/// +/// Note that `v:os_appearance` and OSAppearanceChanged respond to the view's appearance +/// rather than the OS setting. When using manual light/dark or "use background" settings, +/// they do not reflect the current OS dark mode setting. +- (void) testDarkMode { + NSUserDefaults *ud = NSUserDefaults.standardUserDefaults; + + MMAppController *app = MMAppController.sharedInstance; + + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimOpenAndMessages]; + + MMVimView *vimView = [[[app keyVimController] windowController] vimView]; + + // We just use the system appearance to determine the initial state. Otherwise + // we have to change the system appearance to light mode first which we don't + // have permission to do. + const BOOL systemUsingDarkMode = [[ud stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"]; + const NSAppearance *systemAppearance = systemUsingDarkMode ? + [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua] : [NSAppearance appearanceNamed: NSAppearanceNameAqua]; + + // Default setting uses system appearance + XCTAssertEqualObjects(vimView.effectiveAppearance, systemAppearance); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"v:os_appearance"], systemUsingDarkMode ? @"1" : @"0"); + + // Cache original settings / set up setting overrides + NSDictionary *defaults = [ud volatileDomainForName:NSArgumentDomain]; + NSMutableDictionary *newDefaults = [defaults mutableCopy]; + + // Manual Light / Dark mode setting + newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt:MMAppearanceModeSelectionLight]; + [ud setVolatileDomain:newDefaults forName:NSArgumentDomain]; + [app refreshAllAppearances]; + XCTAssertEqualObjects(vimView.effectiveAppearance, [NSAppearance appearanceNamed: NSAppearanceNameAqua]); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"v:os_appearance"], @"0"); + + // Set up a listener for OSAppearanceChanged event to make sure it's called + // when the view appearance changes. + [self sendStringToVim:@":let g:os_appearance_changed_called=0\n" withMods:0]; + [self sendStringToVim:@":autocmd OSAppearanceChanged * let g:os_appearance_changed_called+=1\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + + newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt:MMAppearanceModeSelectionDark]; + [ud setVolatileDomain:newDefaults forName:NSArgumentDomain]; + [app refreshAllAppearances]; + XCTAssertEqualObjects(vimView.effectiveAppearance, [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"v:os_appearance"], @"1"); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"g:os_appearance_changed_called"], @"1"); + + // "Use background" setting + [self sendStringToVim:@":set background=dark\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + + newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt:MMAppearanceModeSelectionBackgroundOption]; + [NSUserDefaults.standardUserDefaults setVolatileDomain:newDefaults forName:NSArgumentDomain]; + [app refreshAllAppearances]; + XCTAssertEqualObjects(vimView.effectiveAppearance, [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"v:os_appearance"], @"1"); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"g:os_appearance_changed_called"], @"1"); // we stayed in dark mode, so OSAppearnceChanged didn't trigger + + [self sendStringToVim:@":set background=light\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(vimView.effectiveAppearance, [NSAppearance appearanceNamed: NSAppearanceNameAqua]); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"v:os_appearance"], @"0"); + XCTAssertEqualObjects([[app keyVimController] evaluateVimExpression:@"g:os_appearance_changed_called"], @"2"); + + // Restore original settings and make sure it's reset + [NSUserDefaults.standardUserDefaults setVolatileDomain:defaults forName:NSArgumentDomain]; + [app refreshAllAppearances]; + XCTAssertEqualObjects(vimView.effectiveAppearance, systemAppearance); + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimClose]; +} + /// Test that document icon is shown in title bar when enabled. - (void) testTitlebarDocumentIcon { MMAppController *app = MMAppController.sharedInstance;