macOS: Add option to hide window buttons (#7504)
Conversion of #7497 to a PR. This implements a feature requested in #7331: an option to hide the default window buttons on macOS for a cleaner aesthetic. ~~Builds on #7502 as it requires the same change to avoid the main toolbar title showing on top of the tab bar.~~ EDIT: rebased on main now that #7502 was merged. I aligned the scope of the new option with `macos-titlebar-style`, since they both customize titlebar elements. This means it has the same edge case quirks: For example, if you change the setting, reload the config, and then open a new tab, the appearance of the current window will depend on which tab is in the foreground. I did it this way because `macos-titlebar-style` provided an easy template for which derived configs and functions to modify. Let me know if you want me to try adjusting this so that a change in the setting also takes effect for current windows/tabs, which I _think_ should be possible. Screenshots: * `macos-titlebar-style = transparent` (default)   * `macos-titlebar-style = tabs`  pull/7527/head
commit
a2a3863ad2
|
|
@ -377,6 +377,14 @@ class TerminalController: BaseTerminalController {
|
||||||
shouldCascadeWindows = false
|
shouldCascadeWindows = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func hideWindowButtons() {
|
||||||
|
guard let window else { return }
|
||||||
|
|
||||||
|
window.standardWindowButton(.closeButton)?.isHidden = true
|
||||||
|
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||||
|
window.standardWindowButton(.zoomButton)?.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func applyHiddenTitlebarStyle() {
|
fileprivate func applyHiddenTitlebarStyle() {
|
||||||
guard let window else { return }
|
guard let window else { return }
|
||||||
|
|
||||||
|
|
@ -398,9 +406,7 @@ class TerminalController: BaseTerminalController {
|
||||||
window.titlebarAppearsTransparent = true
|
window.titlebarAppearsTransparent = true
|
||||||
|
|
||||||
// Hide the traffic lights (window control buttons)
|
// Hide the traffic lights (window control buttons)
|
||||||
window.standardWindowButton(.closeButton)?.isHidden = true
|
hideWindowButtons()
|
||||||
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
|
||||||
window.standardWindowButton(.zoomButton)?.isHidden = true
|
|
||||||
|
|
||||||
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
||||||
window.tabbingMode = .disallowed
|
window.tabbingMode = .disallowed
|
||||||
|
|
@ -456,6 +462,10 @@ class TerminalController: BaseTerminalController {
|
||||||
y: config.windowPositionY,
|
y: config.windowPositionY,
|
||||||
windowDecorations: config.windowDecorations)
|
windowDecorations: config.windowDecorations)
|
||||||
|
|
||||||
|
if config.macosWindowButtons == .hidden {
|
||||||
|
hideWindowButtons()
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure our theme is set on the window so styling is correct.
|
// Make sure our theme is set on the window so styling is correct.
|
||||||
if let windowTheme = config.windowTheme {
|
if let windowTheme = config.windowTheme {
|
||||||
window.windowTheme = .init(rawValue: windowTheme)
|
window.windowTheme = .init(rawValue: windowTheme)
|
||||||
|
|
@ -872,17 +882,20 @@ class TerminalController: BaseTerminalController {
|
||||||
|
|
||||||
struct DerivedConfig {
|
struct DerivedConfig {
|
||||||
let backgroundColor: Color
|
let backgroundColor: Color
|
||||||
|
let macosWindowButtons: Ghostty.MacOSWindowButtons
|
||||||
let macosTitlebarStyle: String
|
let macosTitlebarStyle: String
|
||||||
let maximize: Bool
|
let maximize: Bool
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
||||||
|
self.macosWindowButtons = .visible
|
||||||
self.macosTitlebarStyle = "system"
|
self.macosTitlebarStyle = "system"
|
||||||
self.maximize = false
|
self.maximize = false
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ config: Ghostty.Config) {
|
init(_ config: Ghostty.Config) {
|
||||||
self.backgroundColor = config.backgroundColor
|
self.backgroundColor = config.backgroundColor
|
||||||
|
self.macosWindowButtons = config.macosWindowButtons
|
||||||
self.macosTitlebarStyle = config.macosTitlebarStyle
|
self.macosTitlebarStyle = config.macosTitlebarStyle
|
||||||
self.maximize = config.maximize
|
self.maximize = config.maximize
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,18 @@ class TerminalWindow: NSWindow {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private var hasWindowButtons: Bool {
|
||||||
|
get {
|
||||||
|
if let close = standardWindowButton(.closeButton),
|
||||||
|
let miniaturize = standardWindowButton(.miniaturizeButton),
|
||||||
|
let zoom = standardWindowButton(.zoomButton) {
|
||||||
|
return !(close.isHidden && miniaturize.isHidden && zoom.isHidden)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Both of these must be true for windows without decorations to be able to
|
// Both of these must be true for windows without decorations to be able to
|
||||||
// still become key/main and receive events.
|
// still become key/main and receive events.
|
||||||
override var canBecomeKey: Bool { return true }
|
override var canBecomeKey: Bool { return true }
|
||||||
|
|
@ -613,7 +625,7 @@ class TerminalWindow: NSWindow {
|
||||||
|
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
view.leftAnchor.constraint(equalTo: toolbarView.leftAnchor).isActive = true
|
view.leftAnchor.constraint(equalTo: toolbarView.leftAnchor).isActive = true
|
||||||
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: 78).isActive = true
|
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: hasWindowButtons ? 78 : 0).isActive = true
|
||||||
view.topAnchor.constraint(equalTo: toolbarView.topAnchor).isActive = true
|
view.topAnchor.constraint(equalTo: toolbarView.topAnchor).isActive = true
|
||||||
view.heightAnchor.constraint(equalTo: toolbarView.heightAnchor).isActive = true
|
view.heightAnchor.constraint(equalTo: toolbarView.heightAnchor).isActive = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,17 @@ extension Ghostty {
|
||||||
return String(cString: ptr)
|
return String(cString: ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var macosWindowButtons: MacOSWindowButtons {
|
||||||
|
let defaultValue = MacOSWindowButtons.visible
|
||||||
|
guard let config = self.config else { return defaultValue }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "macos-window-buttons"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
||||||
|
guard let ptr = v else { return defaultValue }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return MacOSWindowButtons(rawValue: str) ?? defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
var macosTitlebarStyle: String {
|
var macosTitlebarStyle: String {
|
||||||
let defaultValue = "transparent"
|
let defaultValue = "transparent"
|
||||||
guard let config = self.config else { return defaultValue }
|
guard let config = self.config else { return defaultValue }
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,12 @@ extension Ghostty {
|
||||||
case chrome
|
case chrome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum for the macos-window-buttons config option
|
||||||
|
enum MacOSWindowButtons: String {
|
||||||
|
case visible
|
||||||
|
case hidden
|
||||||
|
}
|
||||||
|
|
||||||
/// Enum for the macos-titlebar-proxy-icon config option
|
/// Enum for the macos-titlebar-proxy-icon config option
|
||||||
enum MacOSTitlebarProxyIcon: String {
|
enum MacOSTitlebarProxyIcon: String {
|
||||||
case visible
|
case visible
|
||||||
|
|
|
||||||
|
|
@ -2069,6 +2069,25 @@ keybind: Keybinds = .{},
|
||||||
/// it will retain the previous setting until fullscreen is exited.
|
/// it will retain the previous setting until fullscreen is exited.
|
||||||
@"macos-non-native-fullscreen": NonNativeFullscreen = .false,
|
@"macos-non-native-fullscreen": NonNativeFullscreen = .false,
|
||||||
|
|
||||||
|
/// Whether the window buttons in the macOS titlebar are visible. The window
|
||||||
|
/// buttons are the colored buttons in the upper left corner of most macOS apps,
|
||||||
|
/// also known as the traffic lights, that allow you to close, miniaturize, and
|
||||||
|
/// zoom the window.
|
||||||
|
///
|
||||||
|
/// This setting has no effect when `window-decoration = false` or
|
||||||
|
/// `macos-titlebar-style = hidden`, as the window buttons are always hidden in
|
||||||
|
/// these modes.
|
||||||
|
///
|
||||||
|
/// Valid values are:
|
||||||
|
///
|
||||||
|
/// * `visible` - Show the window buttons.
|
||||||
|
/// * `hidden` - Hide the window buttons.
|
||||||
|
///
|
||||||
|
/// The default value is `visible`.
|
||||||
|
///
|
||||||
|
/// Changing this option at runtime only applies to new windows.
|
||||||
|
@"macos-window-buttons": MacWindowButtons = .visible,
|
||||||
|
|
||||||
/// The style of the macOS titlebar. Available values are: "native",
|
/// The style of the macOS titlebar. Available values are: "native",
|
||||||
/// "transparent", "tabs", and "hidden".
|
/// "transparent", "tabs", and "hidden".
|
||||||
///
|
///
|
||||||
|
|
@ -5819,6 +5838,12 @@ pub const WindowColorspace = enum {
|
||||||
@"display-p3",
|
@"display-p3",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// See macos-window-buttons
|
||||||
|
pub const MacWindowButtons = enum {
|
||||||
|
visible,
|
||||||
|
hidden,
|
||||||
|
};
|
||||||
|
|
||||||
/// See macos-titlebar-style
|
/// See macos-titlebar-style
|
||||||
pub const MacTitlebarStyle = enum {
|
pub const MacTitlebarStyle = enum {
|
||||||
native,
|
native,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue