diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 20df07e2c..8e0846e29 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518502529A1A45100E4CC4F /* WindowTracker.swift */; }; A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; }; A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; }; A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB529B6F47F0055DE60 /* AppState.swift */; }; @@ -21,7 +20,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - A518502529A1A45100E4CC4F /* WindowTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTracker.swift; sourceTree = ""; }; A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = ""; }; A55B7BB529B6F47F0055DE60 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; @@ -57,7 +55,6 @@ A535B9D9299C569B0017E2E4 /* ErrorView.swift */, A55685DF29A03A9F004303CE /* AppError.swift */, A59444F629A2ED5200725BBA /* SettingsView.swift */, - A518502529A1A45100E4CC4F /* WindowTracker.swift */, ); path = Sources; sourceTree = ""; @@ -178,7 +175,6 @@ files = ( A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */, A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */, - A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */, A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */, A55B7BBE29B701360055DE60 /* SplitView.swift in Sources */, A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */, diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index 0e0f18b76..aad9f2a75 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -31,11 +31,13 @@ extension Ghostty { @ObservedObject var surfaceView: SurfaceView @FocusState private var surfaceFocus: Bool - @Environment(\.isKeyWindow) private var isKeyWindow: Bool + + // https://nilcoalescing.com/blog/DetectFocusedWindowOnMacOS/ + @Environment(\.controlActiveState) var controlActiveState // This is true if the terminal is considered "focused". The terminal is focused if // it is both individually focused and the containing window is key. - private var hasFocus: Bool { surfaceFocus && isKeyWindow } + private var hasFocus: Bool { surfaceFocus && controlActiveState == .key } var body: some View { // We use a GeometryReader to get the frame bounds so that our metal surface diff --git a/macos/Sources/GhosttyApp.swift b/macos/Sources/GhosttyApp.swift index dff4624fd..be2dff50b 100644 --- a/macos/Sources/GhosttyApp.swift +++ b/macos/Sources/GhosttyApp.swift @@ -23,7 +23,6 @@ struct GhosttyApp: App { case .ready: Ghostty.TerminalSplitView() .ghosttyApp(ghostty.app!) - .modifier(WindowObservationModifier()) } }.commands { CommandGroup(after: .newItem) { diff --git a/macos/Sources/WindowTracker.swift b/macos/Sources/WindowTracker.swift deleted file mode 100644 index 54b53fc2e..000000000 --- a/macos/Sources/WindowTracker.swift +++ /dev/null @@ -1,90 +0,0 @@ -import SwiftUI - -/// This modifier tracks whether the window is the key window in the isKeyWindow environment value. -struct WindowObservationModifier: ViewModifier { - @StateObject var windowObserver: WindowObserver = WindowObserver() - - func body(content: Content) -> some View { - content.background( - HostingWindowFinder { [weak windowObserver] window in - windowObserver?.window = window - } - ).environment(\.isKeyWindow, windowObserver.isKeyWindow) - } -} - -extension EnvironmentValues { - struct IsKeyWindowKey: EnvironmentKey { - static var defaultValue: Bool = false - typealias Value = Bool - } - - fileprivate(set) var isKeyWindow: Bool { - get { - self[IsKeyWindowKey.self] - } - set { - self[IsKeyWindowKey.self] = newValue - } - } -} - -class WindowObserver: ObservableObject { - @Published public private(set) var isKeyWindow: Bool = false - - private var becomeKeyobserver: NSObjectProtocol? - private var resignKeyobserver: NSObjectProtocol? - - weak var window: NSWindow? { - didSet { - // Always remove our previous observers if we have any - if let previous = self.becomeKeyobserver { - NotificationCenter.default.removeObserver(previous) - self.becomeKeyobserver = nil - } - if let previous = self.resignKeyobserver { - NotificationCenter.default.removeObserver(previous) - self.resignKeyobserver = nil - } - - // If our window is becoming nil then we clear everything - guard let window = window else { - self.isKeyWindow = false - return - } - - self.isKeyWindow = window.isKeyWindow - self.becomeKeyobserver = NotificationCenter.default.addObserver( - forName: NSWindow.didBecomeKeyNotification, - object: window, - queue: .main - ) { (n) in - self.isKeyWindow = true - } - - self.resignKeyobserver = NotificationCenter.default.addObserver( - forName: NSWindow.didResignKeyNotification, - object: window, - queue: .main - ) { (n) in - self.isKeyWindow = false - } - } - } -} - -/// This view calls the callback with the window value that hosts the view. -struct HostingWindowFinder: NSViewRepresentable { - var callback: (NSWindow?) -> () - - func makeNSView(context: Self.Context) -> NSView { - let view = NSView() - view.translatesAutoresizingMaskIntoConstraints = false - DispatchQueue.main.async { [weak view] in - self.callback(view?.window) - } - return view - } - - func updateNSView(_ nsView: NSView, context: Context) {} -}