Merge pull request #504 from mitchellh/macos-appearance

macos: window-theme setting to force light or dark theme
This commit is contained in:
Mitchell Hashimoto
2023-09-20 22:14:26 -07:00
committed by GitHub
5 changed files with 87 additions and 11 deletions

View File

@ -187,6 +187,9 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
// Config could change keybindings, so update our menu // Config could change keybindings, so update our menu
syncMenuShortcuts() syncMenuShortcuts()
// Config could change window appearance
syncAppearance()
// 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.model.errors = state.configErrors() c.model.errors = state.configErrors()
@ -197,6 +200,23 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
} }
} }
/// Sync the appearance of our app with the theme specified in the config.
private func syncAppearance() {
guard let theme = ghostty.windowTheme else { return }
switch (theme) {
case "dark":
let appearance = NSAppearance(named: .darkAqua)
NSApplication.shared.appearance = appearance
case "light":
let appearance = NSAppearance(named: .aqua)
NSApplication.shared.appearance = appearance
default:
NSApplication.shared.appearance = nil
}
}
//MARK: - Dock Menu //MARK: - Dock Menu
private func reloadDockMenu() { private func reloadDockMenu() {

View File

@ -16,6 +16,14 @@ class FocusedSurfaceWrapper {
class PrimaryWindow: NSWindow { class PrimaryWindow: NSWindow {
var focusedSurfaceWrapper: FocusedSurfaceWrapper = FocusedSurfaceWrapper() var focusedSurfaceWrapper: FocusedSurfaceWrapper = FocusedSurfaceWrapper()
override var canBecomeKey: Bool {
return true
}
override var canBecomeMain: Bool {
return true
}
static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate, baseConfig: ghostty_surface_config_s? = nil) -> PrimaryWindow { static func create(ghostty: Ghostty.AppState, appDelegate: AppDelegate, baseConfig: ghostty_surface_config_s? = nil) -> PrimaryWindow {
let window = PrimaryWindow( let window = PrimaryWindow(
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600), contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
@ -53,12 +61,4 @@ class PrimaryWindow: NSWindow {
return mask return mask
} }
override var canBecomeKey: Bool {
return true
}
override var canBecomeMain: Bool {
return true
}
} }

View File

@ -72,6 +72,25 @@ extension Ghostty {
return v; return v;
} }
/// The window theme as a string.
var windowTheme: String? {
guard let config = self.config else { return nil }
var v: UnsafePointer<Int8>? = nil
let key = "window-theme"
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
guard let ptr = v else { return nil }
return String(cString: ptr)
}
/// The background opacity.
var backgroundOpacity: Double {
guard let config = self.config else { return 1 }
var v: Double = 1
let key = "background-opacity"
_ = ghostty_config_get(config, &v, key, UInt(key.count))
return v;
}
init() { init() {
// Initialize ghostty global state. This happens once per process. // Initialize ghostty global state. This happens once per process.
guard ghostty_init() == GHOSTTY_SUCCESS else { guard ghostty_init() == GHOSTTY_SUCCESS else {

View File

@ -244,6 +244,14 @@ 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 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.
///
/// This is currently only supported on macOS.
@"window-theme": WindowTheme = .system,
/// Whether to allow programs running in the terminal to read/write to /// Whether to allow programs running in the terminal to read/write to
/// the system clipboard (OSC 52, for googling). The default is to /// the system clipboard (OSC 52, for googling). The default is to
/// disallow clipboard reading but allow writing. /// disallow clipboard reading but allow writing.
@ -1507,3 +1515,10 @@ pub const OSCColorReportFormat = enum {
@"8-bit", @"8-bit",
@"16-bit", @"16-bit",
}; };
/// The default window theme.
pub const WindowTheme = enum {
system,
light,
dark,
};

View File

@ -19,7 +19,7 @@ pub fn get(config: *const Config, k: Key, ptr_raw: *anyopaque) bool {
const value = fieldByKey(config, tag); const value = fieldByKey(config, tag);
switch (@TypeOf(value)) { switch (@TypeOf(value)) {
?[:0]const u8 => { ?[:0]const u8 => {
const ptr: *[*c]const u8 = @ptrCast(@alignCast(ptr_raw)); const ptr: *?[*:0]const u8 = @ptrCast(@alignCast(ptr_raw));
ptr.* = if (value) |slice| @ptrCast(slice.ptr) else null; ptr.* = if (value) |slice| @ptrCast(slice.ptr) else null;
}, },
@ -38,7 +38,14 @@ pub fn get(config: *const Config, k: Key, ptr_raw: *anyopaque) bool {
ptr.* = @floatCast(value); ptr.* = @floatCast(value);
}, },
else => return false, else => |T| switch (@typeInfo(T)) {
.Enum => {
const ptr: *[*:0]const u8 = @ptrCast(@alignCast(ptr_raw));
ptr.* = @tagName(value);
},
else => return false,
},
} }
return true; return true;
@ -74,3 +81,18 @@ test "u8" {
try testing.expect(get(&c, .@"font-size", &cval)); try testing.expect(get(&c, .@"font-size", &cval));
try testing.expectEqual(@as(c_uint, 24), cval); try testing.expectEqual(@as(c_uint, 24), cval);
} }
test "enum" {
const testing = std.testing;
const alloc = testing.allocator;
var c = try Config.default(alloc);
defer c.deinit();
c.@"window-theme" = .dark;
var cval: [*:0]u8 = undefined;
try testing.expect(get(&c, .@"window-theme", @ptrCast(&cval)));
const str = std.mem.sliceTo(cval, 0);
try testing.expectEqualStrings("dark", str);
}