2025-06-11 15:18:02 -07:00

76 lines
2.8 KiB
Swift

import AppKit
/// The base class for all standalone, "normal" terminal windows. This sets the basic
/// style and configuration of the window based on the app configuration.
class TerminalWindow: NSWindow {
/// This is the key in UserDefaults to use for the default `level` value. This is
/// used by the manual float on top menu item feature.
static let defaultLevelKey: String = "TerminalDefaultLevel"
// MARK: NSWindow Overrides
override func awakeFromNib() {
guard let appDelegate = NSApp.delegate as? AppDelegate else { return }
// All new windows are based on the app config at the time of creation.
let config = appDelegate.ghostty.config
// If window decorations are disabled, remove our title
if (!config.windowDecorations) { styleMask.remove(.titled) }
// Set our window positioning to coordinates if config value exists, otherwise
// fallback to original centering behavior
setInitialWindowPosition(
x: config.windowPositionX,
y: config.windowPositionY,
windowDecorations: config.windowDecorations)
// If our traffic buttons should be hidden, then hide them
if config.macosWindowButtons == .hidden {
hideWindowButtons()
}
// Get our saved level
level = UserDefaults.standard.value(forKey: Self.defaultLevelKey) as? NSWindow.Level ?? .normal
}
// Both of these must be true for windows without decorations to be able to
// still become key/main and receive events.
override var canBecomeKey: Bool { return true }
override var canBecomeMain: Bool { return true }
// MARK: Positioning And Styling
/// This is called by the controller when there is a need to reset the window apperance.
func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {}
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
// If we don't have an X/Y then we try to use the previously saved window pos.
guard let x, let y else {
if (!LastWindowPosition.shared.restore(self)) {
center()
}
return
}
// Prefer the screen our window is being placed on otherwise our primary screen.
guard let screen = screen ?? NSScreen.screens.first else {
center()
return
}
// Orient based on the top left of the primary monitor
let frame = screen.visibleFrame
setFrameOrigin(.init(
x: frame.minX + CGFloat(x),
y: frame.maxY - (CGFloat(y) + frame.height)))
}
private func hideWindowButtons() {
standardWindowButton(.closeButton)?.isHidden = true
standardWindowButton(.miniaturizeButton)?.isHidden = true
standardWindowButton(.zoomButton)?.isHidden = true
}
}