Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macos: hide dock globally if the dock conflicts #5363

Merged
merged 1 commit into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 72 additions & 18 deletions macos/Sources/Features/QuickTerminal/QuickTerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ class QuickTerminalController: BaseTerminalController {
// The active space when the quick terminal was last shown.
private var previousActiveSpace: size_t = 0

/// This is set to true of the dock was autohid when the terminal animated in. This lets us
/// know if we have to unhide when the terminal is animated out.
private var hidDock: Bool = false
/// Non-nil if we have hidden dock state.
private var hiddenDock: HiddenDock? = nil

/// The configuration derived from the Ghostty config so we don't need to rely on references.
private var derivedConfig: DerivedConfig
Expand All @@ -45,6 +44,11 @@ class QuickTerminalController: BaseTerminalController {

// Setup our notifications for behaviors
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(applicationWillTerminate(_:)),
name: NSApplication.willTerminateNotification,
object: nil)
center.addObserver(
self,
selector: #selector(onToggleFullscreen),
Expand All @@ -65,6 +69,9 @@ class QuickTerminalController: BaseTerminalController {
// Remove all of our notificationcenter subscriptions
let center = NotificationCenter.default
center.removeObserver(self)

// Make sure we restore our hidden dock
hiddenDock = nil
}

// MARK: NSWindowController
Expand Down Expand Up @@ -100,6 +107,17 @@ class QuickTerminalController: BaseTerminalController {

// MARK: NSWindowDelegate

override func windowDidBecomeKey(_ notification: Notification) {
super.windowDidBecomeKey(notification)

// If we're not visible we don't care to run the logic below. It only
// applies if we can be seen.
guard visible else { return }

// Re-hide the dock if we were hiding it before.
hiddenDock?.hide()
}

override func windowDidResignKey(_ notification: Notification) {
super.windowDidResignKey(notification)

Expand All @@ -120,6 +138,10 @@ class QuickTerminalController: BaseTerminalController {
self.previousApp = nil
}

// Regardless of autohide, we always want to bring the dock back
// when we lose focus.
hiddenDock?.restore()

if derivedConfig.quickTerminalAutoHide {
switch derivedConfig.quickTerminalSpaceBehavior {
case .remain:
Expand Down Expand Up @@ -228,18 +250,6 @@ class QuickTerminalController: BaseTerminalController {
animateWindowOut(window: window, to: position)
}

private func hideDock() {
guard !hidDock else { return }
NSApp.acquirePresentationOption(.autoHideDock)
hidDock = true
}

private func unhideDock() {
guard hidDock else { return }
NSApp.releasePresentationOption(.autoHideDock)
hidDock = false
}

private func animateWindowIn(window: NSWindow, from position: QuickTerminalPosition) {
guard let screen = derivedConfig.quickTerminalScreen.screen else { return }

Expand All @@ -259,7 +269,15 @@ class QuickTerminalController: BaseTerminalController {
// If our dock position would conflict with our target location then
// we autohide the dock.
if position.conflictsWithDock(on: screen) {
hideDock()
if (hiddenDock == nil) {
hiddenDock = .init()
}

hiddenDock?.hide()
} else {
// Ensure we don't have any hidden dock if we don't conflict.
// The deinit will restore.
hiddenDock = nil
}

// Run the animation that moves our window into the proper place and makes
Expand All @@ -274,7 +292,7 @@ class QuickTerminalController: BaseTerminalController {
DispatchQueue.main.async {
// If we canceled our animation clean up some state.
guard self.visible else {
self.unhideDock()
self.hiddenDock = nil
return
}

Expand Down Expand Up @@ -346,7 +364,7 @@ class QuickTerminalController: BaseTerminalController {

private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
// If we hid the dock then we unhide it.
unhideDock()
hiddenDock = nil

// If the window isn't on our active space then we don't animate, we just
// hide it.
Expand Down Expand Up @@ -443,6 +461,13 @@ class QuickTerminalController: BaseTerminalController {

// MARK: Notifications

@objc private func applicationWillTerminate(_ notification: Notification) {
// If the application is going to terminate we want to make sure we
// restore any global dock state. I think deinit should be called which
// would call this anyways but I can't be sure so I will do this too.
hiddenDock = nil
}

@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
guard let target = notification.object as? Ghostty.SurfaceView else { return }
guard target == self.focusedSurface else { return }
Expand Down Expand Up @@ -490,6 +515,35 @@ class QuickTerminalController: BaseTerminalController {
self.backgroundOpacity = config.backgroundOpacity
}
}

/// Hides the dock globally (not just NSApp). This is only used if the quick terminal is
/// in a conflicting position with the dock.
private class HiddenDock {
let previousAutoHide: Bool
private var hidden: Bool = false

init() {
previousAutoHide = Dock.autoHideEnabled
}

deinit {
restore()
}

func hide() {
guard !hidden else { return }
NSApp.acquirePresentationOption(.autoHideDock)
Dock.autoHideEnabled = true
hidden = true
}

func restore() {
guard hidden else { return }
NSApp.releasePresentationOption(.autoHideDock)
Dock.autoHideEnabled = previousAutoHide
hidden = false
}
}
}

extension Notification.Name {
Expand Down
9 changes: 7 additions & 2 deletions macos/Sources/Helpers/Dock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ func CoreDockGetOrientationAndPinning(
@_silgen_name("CoreDockGetAutoHideEnabled")
func CoreDockGetAutoHideEnabled() -> Bool

// Toggles the Dock's auto-hide state
@_silgen_name("CoreDockSetAutoHideEnabled")
func CoreDockSetAutoHideEnabled(_ flag: Bool)

enum DockOrientation: Int {
case top = 1
case bottom = 2
Expand All @@ -26,8 +30,9 @@ class Dock {
return .init(rawValue: Int(orientation)) ?? nil
}

/// Returns true if the dock has auto-hide enabled.
/// Set the dock autohide.
static var autoHideEnabled: Bool {
return CoreDockGetAutoHideEnabled()
get { return CoreDockGetAutoHideEnabled() }
set { CoreDockSetAutoHideEnabled(newValue) }
}
}