@@ -458,6 +458,83 @@ - (void) testGuifontSystemMonospace {
458
458
[self waitForVimClose ];
459
459
}
460
460
461
+ // / Test that dark mode settings work and the corresponding Vim bindings are functional.
462
+ // /
463
+ // / Note that `v:os_appearance` and OSAppearanceChanged respond to the view's appearance
464
+ // / rather than the OS setting. When using manual light/dark or "use background" settings,
465
+ // / they do not reflect the current OS dark mode setting.
466
+ - (void ) testDarkMode {
467
+ NSUserDefaults *ud = NSUserDefaults .standardUserDefaults ;
468
+
469
+ MMAppController *app = MMAppController.sharedInstance ;
470
+
471
+ [app openNewWindow: NewWindowClean activate: YES ];
472
+ [self waitForVimOpenAndMessages ];
473
+
474
+ MMVimView *vimView = [[[app keyVimController ] windowController ] vimView ];
475
+
476
+ // We just use the system appearance to determine the initial state. Otherwise
477
+ // we have to change the system appearance to light mode first which we don't
478
+ // have permission to do.
479
+ const BOOL systemUsingDarkMode = [[ud stringForKey: @" AppleInterfaceStyle" ] isEqualToString: @" Dark" ];
480
+ const NSAppearance *systemAppearance = systemUsingDarkMode ?
481
+ [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua ] : [NSAppearance appearanceNamed: NSAppearanceNameAqua ];
482
+
483
+ // Default setting uses system appearance
484
+ XCTAssertEqualObjects (vimView.effectiveAppearance , systemAppearance);
485
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" v:os_appearance" ], systemUsingDarkMode ? @" 1" : @" 0" );
486
+
487
+ // Cache original settings / set up setting overrides
488
+ NSDictionary <NSString *, id > *defaults = [ud volatileDomainForName: NSArgumentDomain ];
489
+ NSMutableDictionary <NSString *, id > *newDefaults = [defaults mutableCopy ];
490
+
491
+ // Manual Light / Dark mode setting
492
+ newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt: MMAppearanceModeSelectionLight];
493
+ [ud setVolatileDomain: newDefaults forName: NSArgumentDomain ];
494
+ [app refreshAllAppearances ];
495
+ XCTAssertEqualObjects (vimView.effectiveAppearance , [NSAppearance appearanceNamed: NSAppearanceNameAqua ]);
496
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" v:os_appearance" ], @" 0" );
497
+
498
+ // Set up a listener for OSAppearanceChanged event to make sure it's called
499
+ // when the view appearance changes.
500
+ [self sendStringToVim: @" :let g:os_appearance_changed_called=0\n " withMods: 0 ];
501
+ [self sendStringToVim: @" :autocmd OSAppearanceChanged * let g:os_appearance_changed_called+=1\n " withMods: 0 ];
502
+ [self waitForEventHandlingAndVimProcess ];
503
+
504
+ newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt: MMAppearanceModeSelectionDark];
505
+ [ud setVolatileDomain: newDefaults forName: NSArgumentDomain ];
506
+ [app refreshAllAppearances ];
507
+ XCTAssertEqualObjects (vimView.effectiveAppearance , [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua ]);
508
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" v:os_appearance" ], @" 1" );
509
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" g:os_appearance_changed_called" ], @" 1" );
510
+
511
+ // "Use background" setting
512
+ [self sendStringToVim: @" :set background=dark\n " withMods: 0 ];
513
+ [self waitForEventHandlingAndVimProcess ];
514
+
515
+ newDefaults[MMAppearanceModeSelectionKey] = [NSNumber numberWithInt: MMAppearanceModeSelectionBackgroundOption];
516
+ [NSUserDefaults .standardUserDefaults setVolatileDomain: newDefaults forName: NSArgumentDomain ];
517
+ [app refreshAllAppearances ];
518
+ XCTAssertEqualObjects (vimView.effectiveAppearance , [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua ]);
519
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" v:os_appearance" ], @" 1" );
520
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" g:os_appearance_changed_called" ], @" 1" ); // we stayed in dark mode, so OSAppearnceChanged didn't trigger
521
+
522
+ [self sendStringToVim: @" :set background=light\n " withMods: 0 ];
523
+ [self waitForEventHandlingAndVimProcess ];
524
+ XCTAssertEqualObjects (vimView.effectiveAppearance , [NSAppearance appearanceNamed: NSAppearanceNameAqua ]);
525
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" v:os_appearance" ], @" 0" );
526
+ XCTAssertEqualObjects ([[app keyVimController ] evaluateVimExpression: @" g:os_appearance_changed_called" ], @" 2" );
527
+
528
+ // Restore original settings and make sure it's reset
529
+ [NSUserDefaults .standardUserDefaults setVolatileDomain: defaults forName: NSArgumentDomain ];
530
+ [app refreshAllAppearances ];
531
+ XCTAssertEqualObjects (vimView.effectiveAppearance , systemAppearance);
532
+
533
+ // Clean up
534
+ [[app keyVimController ] sendMessage: VimShouldCloseMsgID data: nil ];
535
+ [self waitForVimClose ];
536
+ }
537
+
461
538
// / Test that document icon is shown in title bar when enabled.
462
539
- (void ) testTitlebarDocumentIcon {
463
540
MMAppController *app = MMAppController.sharedInstance ;
0 commit comments