mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: change key window detection
This commit is contained in:
@ -7,7 +7,6 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
||||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB529B6F47F0055DE60 /* AppState.swift */; };
|
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB529B6F47F0055DE60 /* AppState.swift */; };
|
||||||
@ -21,7 +20,6 @@
|
|||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
A518502529A1A45100E4CC4F /* WindowTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTracker.swift; sourceTree = "<group>"; };
|
|
||||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
||||||
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
A55B7BB529B6F47F0055DE60 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||||
@ -57,7 +55,6 @@
|
|||||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
|
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
|
||||||
A55685DF29A03A9F004303CE /* AppError.swift */,
|
A55685DF29A03A9F004303CE /* AppError.swift */,
|
||||||
A59444F629A2ED5200725BBA /* SettingsView.swift */,
|
A59444F629A2ED5200725BBA /* SettingsView.swift */,
|
||||||
A518502529A1A45100E4CC4F /* WindowTracker.swift */,
|
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -178,7 +175,6 @@
|
|||||||
files = (
|
files = (
|
||||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||||
A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */,
|
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||||
A55B7BBE29B701360055DE60 /* SplitView.swift in Sources */,
|
A55B7BBE29B701360055DE60 /* SplitView.swift in Sources */,
|
||||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
||||||
|
@ -31,11 +31,13 @@ extension Ghostty {
|
|||||||
@ObservedObject var surfaceView: SurfaceView
|
@ObservedObject var surfaceView: SurfaceView
|
||||||
|
|
||||||
@FocusState private var surfaceFocus: Bool
|
@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
|
// 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.
|
// 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 {
|
var body: some View {
|
||||||
// We use a GeometryReader to get the frame bounds so that our metal surface
|
// We use a GeometryReader to get the frame bounds so that our metal surface
|
||||||
|
@ -23,7 +23,6 @@ struct GhosttyApp: App {
|
|||||||
case .ready:
|
case .ready:
|
||||||
Ghostty.TerminalSplitView()
|
Ghostty.TerminalSplitView()
|
||||||
.ghosttyApp(ghostty.app!)
|
.ghosttyApp(ghostty.app!)
|
||||||
.modifier(WindowObservationModifier())
|
|
||||||
}
|
}
|
||||||
}.commands {
|
}.commands {
|
||||||
CommandGroup(after: .newItem) {
|
CommandGroup(after: .newItem) {
|
||||||
|
@ -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) {}
|
|
||||||
}
|
|
Reference in New Issue
Block a user