From e5400bad0661a52833bf72476dac0a28f9ef56df Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 1 Feb 2024 20:43:42 -0800 Subject: [PATCH] config: add window-theme = auto for automatic choosing based on bg color --- macos/Sources/App/macOS/AppDelegate.swift | 10 ++++++++++ .../Terminal/TerminalController.swift | 19 +++++++++++++++++++ .../Features/Terminal/TerminalWindow.swift | 10 ---------- src/apprt/gtk/App.zig | 8 ++++++++ src/config/Config.zig | 13 +++++++++---- src/terminal/color.zig | 11 +++++++++++ 6 files changed, 57 insertions(+), 14 deletions(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 22ab223dc..5f70c6ce6 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -377,6 +377,11 @@ class AppDelegate: NSObject, // Config could change window appearance syncAppearance() + // Update all of our windows + terminalManager.windows.forEach { window in + window.controller.configDidReload() + } + // If we have configuration errors, we need to show them. let c = ConfigurationErrorsController.sharedInstance c.errors = state.config.errors @@ -399,6 +404,11 @@ class AppDelegate: NSObject, let appearance = NSAppearance(named: .aqua) 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: NSApplication.shared.appearance = nil } diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 3d23a22bf..080f2d871 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -105,6 +105,10 @@ class TerminalController: NSWindowController, NSWindowDelegate, //MARK: - Methods + func configDidReload() { + syncAppearance() + } + /// Update the accessory view of each tab according to the keyboard /// shortcut that activates it (if any). This is called when the key window /// changes and when a window is closed. @@ -151,6 +155,20 @@ class TerminalController: NSWindowController, NSWindowDelegate, 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 override func windowWillLoad() { @@ -214,6 +232,7 @@ class TerminalController: NSWindowController, NSWindowDelegate, if (ghostty.config.macosTitlebarTabs) { window.tabbingMode = .preferred window.titlebarTabs = true + syncAppearance() DispatchQueue.main.async { window.tabbingMode = .automatic } diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index 7555dad0e..b01d14ebe 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -59,16 +59,6 @@ class TerminalWindow: NSWindow { }) { 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 { // "expanded" places the toolbar below the titlebar, so setting this style and // removing the toolbar ensures that the titlebar will be the default height. diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 9b1deea9e..0a33f0cc0 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -133,6 +133,14 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { c.adw_style_manager_set_color_scheme( style_manager, 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, .dark => c.ADW_COLOR_SCHEME_FORCE_DARK, .light => c.ADW_COLOR_SCHEME_FORCE_LIGHT, diff --git a/src/config/Config.zig b/src/config/Config.zig index 59b04606e..4012c5cf8 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -600,9 +600,13 @@ keybind: Keybinds = .{}, /// borders. @"window-decoration": bool = true, -/// The theme to use for the windows. The default is `system` which means that -/// 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. +/// The theme to use for the windows. Valid values: +/// +/// * `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 /// automatically set based on the luminosity of the terminal background color. @@ -610,7 +614,7 @@ keybind: Keybinds = .{}, /// non-terminal windows within Ghostty. /// /// 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 /// 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. pub const WindowTheme = enum { + auto, system, light, dark, diff --git a/src/terminal/color.zig b/src/terminal/color.zig index de5773aac..194cee8b1 100644 --- a/src/terminal/color.zig +++ b/src/terminal/color.zig @@ -144,6 +144,17 @@ pub const RGB = struct { 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" { try std.testing.expectEqual(@as(usize, 24), @bitSizeOf(RGB)); try std.testing.expectEqual(@as(usize, 3), @sizeOf(RGB));