mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge pull request #504 from mitchellh/macos-appearance
macos: window-theme setting to force light or dark theme
This commit is contained in:
@ -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() {
|
||||||
|
@ -15,6 +15,14 @@ class FocusedSurfaceWrapper {
|
|||||||
// such as non-native fullscreen.
|
// such as non-native fullscreen.
|
||||||
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(
|
||||||
@ -53,12 +61,4 @@ class PrimaryWindow: NSWindow {
|
|||||||
|
|
||||||
return mask
|
return mask
|
||||||
}
|
}
|
||||||
|
|
||||||
override var canBecomeKey: Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override var canBecomeMain: Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,26 @@ extension Ghostty {
|
|||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
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 {
|
||||||
|
@ -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,
|
||||||
|
};
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user