Merge pull request #1437 from mitchellh/theme-auto

config: add window-theme = auto for automatic choosing based on bg color
This commit is contained in:
Mitchell Hashimoto
2024-02-01 21:11:07 -08:00
committed by GitHub
6 changed files with 57 additions and 14 deletions

View File

@ -377,6 +377,11 @@ class AppDelegate: NSObject,
// Config could change window appearance // Config could change window appearance
syncAppearance() syncAppearance()
// Update all of our windows
terminalManager.windows.forEach { window in
window.controller.configDidReload()
}
// If we have configuration errors, we need to show them. // If we have configuration errors, we need to show them.
let c = ConfigurationErrorsController.sharedInstance let c = ConfigurationErrorsController.sharedInstance
c.errors = state.config.errors c.errors = state.config.errors
@ -399,6 +404,11 @@ class AppDelegate: NSObject,
let appearance = NSAppearance(named: .aqua) let appearance = NSAppearance(named: .aqua)
NSApplication.shared.appearance = appearance NSApplication.shared.appearance = appearance
case "auto":
let color = OSColor(ghostty.config.backgroundColor)
let appearance = NSAppearance(named: color.isLightColor ? .aqua : .darkAqua)
NSApplication.shared.appearance = appearance
default: default:
NSApplication.shared.appearance = nil NSApplication.shared.appearance = nil
} }

View File

@ -105,6 +105,10 @@ class TerminalController: NSWindowController, NSWindowDelegate,
//MARK: - Methods //MARK: - Methods
func configDidReload() {
syncAppearance()
}
/// Update the accessory view of each tab according to the keyboard /// Update the accessory view of each tab according to the keyboard
/// shortcut that activates it (if any). This is called when the key window /// shortcut that activates it (if any). This is called when the key window
/// changes and when a window is closed. /// changes and when a window is closed.
@ -151,6 +155,20 @@ class TerminalController: NSWindowController, NSWindowDelegate,
self.relabelTabs() self.relabelTabs()
} }
private func syncAppearance() {
guard let window = self.window as? TerminalWindow else { return }
// We match the appearance depending on the lightness/darkness of the
// background color. We have to do this because our titlebars in tabs inherit
// our background color for the focused tab but use the macOS theme for the
// rest of the titlebar.
if (window.titlebarTabs) {
let color = OSColor(ghostty.config.backgroundColor)
let appearance = NSAppearance(named: color.isLightColor ? .aqua : .darkAqua)
window.appearance = appearance
}
}
//MARK: - NSWindowController //MARK: - NSWindowController
override func windowWillLoad() { override func windowWillLoad() {
@ -214,6 +232,7 @@ class TerminalController: NSWindowController, NSWindowDelegate,
if (ghostty.config.macosTitlebarTabs) { if (ghostty.config.macosTitlebarTabs) {
window.tabbingMode = .preferred window.tabbingMode = .preferred
window.titlebarTabs = true window.titlebarTabs = true
syncAppearance()
DispatchQueue.main.async { DispatchQueue.main.async {
window.tabbingMode = .automatic window.tabbingMode = .automatic
} }

View File

@ -59,16 +59,6 @@ class TerminalWindow: NSWindow {
}) { }) {
toolbarTitleView.isHidden = true toolbarTitleView.isHidden = true
} }
// We match the appearance depending on the lightness/darkness of the
// background color. We have to do this because our titlebars in tabs inherit
// our background color for the focused tab but use the macOS theme for the
// rest of the titlebar.
if let appDelegate = NSApp.delegate as? AppDelegate {
let color = OSColor(appDelegate.ghostty.config.backgroundColor)
let appearance = NSAppearance(named: color.isLightColor ? .aqua : .darkAqua)
self.appearance = appearance
}
} else { } else {
// "expanded" places the toolbar below the titlebar, so setting this style and // "expanded" places the toolbar below the titlebar, so setting this style and
// removing the toolbar ensures that the titlebar will be the default height. // removing the toolbar ensures that the titlebar will be the default height.

View File

@ -133,6 +133,14 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
c.adw_style_manager_set_color_scheme( c.adw_style_manager_set_color_scheme(
style_manager, style_manager,
switch (config.@"window-theme") { switch (config.@"window-theme") {
.auto => auto: {
const lum = config.background.toTerminalRGB().perceivedLuminance();
break :auto if (lum > 0.5)
c.ADW_COLOR_SCHEME_PREFER_LIGHT
else
c.ADW_COLOR_SCHEME_PREFER_DARK;
},
.system => c.ADW_COLOR_SCHEME_PREFER_LIGHT, .system => c.ADW_COLOR_SCHEME_PREFER_LIGHT,
.dark => c.ADW_COLOR_SCHEME_FORCE_DARK, .dark => c.ADW_COLOR_SCHEME_FORCE_DARK,
.light => c.ADW_COLOR_SCHEME_FORCE_LIGHT, .light => c.ADW_COLOR_SCHEME_FORCE_LIGHT,

View File

@ -600,9 +600,13 @@ keybind: Keybinds = .{},
/// borders. /// borders.
@"window-decoration": bool = true, @"window-decoration": bool = true,
/// The theme to use for the windows. The default is `system` which means that /// The theme to use for the windows. Valid values:
/// whatever the system theme is will be used. This can also be set to `light` ///
/// or `dark` to force a specific theme regardless of the system settings. /// * `auto` - Determine the theme based on the configured terminal
/// background color.
/// * `system` - Use the system theme.
/// * `light` - Use the light theme regardless of system theme.
/// * `dark` - Use the dark theme regardless of system theme.
/// ///
/// On macOS, if `macos-titlebar-tabs` is set, the window theme will be /// On macOS, if `macos-titlebar-tabs` is set, the window theme will be
/// automatically set based on the luminosity of the terminal background color. /// automatically set based on the luminosity of the terminal background color.
@ -610,7 +614,7 @@ keybind: Keybinds = .{},
/// non-terminal windows within Ghostty. /// non-terminal windows within Ghostty.
/// ///
/// This is currently only supported on macOS and Linux. /// This is currently only supported on macOS and Linux.
@"window-theme": WindowTheme = .system, @"window-theme": WindowTheme = .auto,
/// The colorspace to use for the terminal window. The default is `srgb` but /// The colorspace to use for the terminal window. The default is `srgb` but
/// this can also be set to `display-p3` to use the Display P3 colorspace. /// this can also be set to `display-p3` to use the Display P3 colorspace.
@ -3342,6 +3346,7 @@ pub const OSCColorReportFormat = enum {
/// The default window theme. /// The default window theme.
pub const WindowTheme = enum { pub const WindowTheme = enum {
auto,
system, system,
light, light,
dark, dark,

View File

@ -144,6 +144,17 @@ pub const RGB = struct {
return std.math.pow(f64, (normalized + 0.055) / 1.055, 2.4); return std.math.pow(f64, (normalized + 0.055) / 1.055, 2.4);
} }
/// Calculates "perceived luminance" which is better for determining
/// light vs dark.
///
/// Source: https://www.w3.org/TR/AERT/#color-contrast
pub fn perceivedLuminance(self: RGB) f64 {
const r_f64: f64 = @floatFromInt(self.r);
const g_f64: f64 = @floatFromInt(self.g);
const b_f64: f64 = @floatFromInt(self.b);
return 0.299 * (r_f64 / 255) + 0.587 * (g_f64 / 255) + 0.114 * (b_f64 / 255);
}
test "size" { test "size" {
try std.testing.expectEqual(@as(usize, 24), @bitSizeOf(RGB)); try std.testing.expectEqual(@as(usize, 24), @bitSizeOf(RGB));
try std.testing.expectEqual(@as(usize, 3), @sizeOf(RGB)); try std.testing.expectEqual(@as(usize, 3), @sizeOf(RGB));