mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
Merge pull request #98 from mitchellh/macos-cpu2
macos: free surface as soon as split is closed, properly track focus state
This commit is contained in:
@ -45,6 +45,23 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
/// Close the surface associated with this node. This will likely deinitialize the
|
||||
/// surface. At this point, the surface view in this node tree can never be used again.
|
||||
func close() {
|
||||
switch (self) {
|
||||
case .noSplit(let leaf):
|
||||
leaf.surface.close()
|
||||
|
||||
case .horizontal(let container):
|
||||
container.topLeft.close()
|
||||
container.bottomRight.close()
|
||||
|
||||
case .vertical(let container):
|
||||
container.topLeft.close()
|
||||
container.bottomRight.close()
|
||||
}
|
||||
}
|
||||
|
||||
class Leaf: ObservableObject {
|
||||
let app: ghostty_app_t
|
||||
@Published var surface: SurfaceView
|
||||
@ -144,6 +161,11 @@ extension Ghostty {
|
||||
)
|
||||
.onChange(of: requestClose) { value in
|
||||
guard value else { return }
|
||||
|
||||
// Free any resources associated with this root, we're closing.
|
||||
node.close()
|
||||
|
||||
// Call our callback
|
||||
guard let onClose = self.onClose else { return }
|
||||
onClose()
|
||||
}
|
||||
@ -289,6 +311,9 @@ extension Ghostty {
|
||||
.onChange(of: closeTopLeft) { value in
|
||||
guard value else { return }
|
||||
|
||||
// Close the top left and release all resources
|
||||
container.topLeft.close()
|
||||
|
||||
// When closing the topLeft, our parent becomes the bottomRight.
|
||||
node = container.bottomRight
|
||||
TerminalSplitLeaf.moveFocus(node, previous: container.topLeft)
|
||||
@ -307,6 +332,9 @@ extension Ghostty {
|
||||
.onChange(of: closeBottomRight) { value in
|
||||
guard value else { return }
|
||||
|
||||
// Close the node and release all resources
|
||||
container.bottomRight.close()
|
||||
|
||||
// When closing the bottomRight, our parent becomes the topLeft.
|
||||
node = container.topLeft
|
||||
TerminalSplitLeaf.moveFocus(node, previous: container.bottomRight)
|
||||
|
@ -38,24 +38,42 @@ extension Ghostty {
|
||||
// remains the same, the surface that is being rendered remains the same.
|
||||
@ObservedObject var surfaceView: SurfaceView
|
||||
|
||||
// Maintain whether our view has focus or not
|
||||
@FocusState private var surfaceFocus: Bool
|
||||
|
||||
// https://nilcoalescing.com/blog/DetectFocusedWindowOnMacOS/
|
||||
@Environment(\.controlActiveState) var controlActiveState
|
||||
// Maintain whether our window has focus (is key) or not
|
||||
@State private var windowFocus: Bool = false
|
||||
|
||||
// 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 && controlActiveState == .key }
|
||||
private var hasFocus: Bool { surfaceFocus && windowFocus }
|
||||
|
||||
var body: some View {
|
||||
// We use a GeometryReader to get the frame bounds so that our metal surface
|
||||
// is up to date. See TerminalSurfaceView for why we don't use the NSView
|
||||
// resize callback.
|
||||
GeometryReader { geo in
|
||||
// We use these notifications to determine when the window our surface is
|
||||
// attached to is or is not focused.
|
||||
let pubBecomeKey = NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification)
|
||||
let pubResign = NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification)
|
||||
|
||||
Surface(view: surfaceView, hasFocus: hasFocus, size: geo.size)
|
||||
.focused($surfaceFocus)
|
||||
.focusedValue(\.ghosttySurfaceTitle, surfaceView.title)
|
||||
.focusedValue(\.ghosttySurfaceView, surfaceView)
|
||||
.onReceive(pubBecomeKey) { notification in
|
||||
guard let window = notification.object as? NSWindow else { return }
|
||||
guard let surfaceWindow = surfaceView.window else { return }
|
||||
windowFocus = surfaceWindow == window
|
||||
}
|
||||
.onReceive(pubResign) { notification in
|
||||
guard let window = notification.object as? NSWindow else { return }
|
||||
guard let surfaceWindow = surfaceView.window else { return }
|
||||
if (surfaceWindow == window) {
|
||||
windowFocus = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.ghosttySurfaceView(surfaceView)
|
||||
}
|
||||
@ -153,6 +171,16 @@ extension Ghostty {
|
||||
ghostty_surface_free(surface)
|
||||
}
|
||||
|
||||
/// Close the surface early. This will free the associated Ghostty surface and the view will
|
||||
/// no longer render. The view can never be used again. This is a way for us to free the
|
||||
/// Ghostty resources while references may still be held to this view. I've found that SwiftUI
|
||||
/// tends to hold this view longer than it should so we free the expensive stuff explicitly.
|
||||
func close() {
|
||||
guard let surface = self.surface else { return }
|
||||
ghostty_surface_free(surface)
|
||||
self.surface = nil
|
||||
}
|
||||
|
||||
func focusDidChange(_ focused: Bool) {
|
||||
guard let surface = self.surface else { return }
|
||||
ghostty_surface_set_focus(surface, focused)
|
||||
|
Reference in New Issue
Block a user