From 27e42855cad9e886aa88b0c384c60c2b0b87d889 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 22 Feb 2023 20:49:33 -0800 Subject: [PATCH] macos: use GeometryReader for terminal size for macOS 12 We were using the NSView resize func before but this isn't called by SwiftUI on macOS 12. Instead we wrap it in a GeometryReader and detect the size change in updateNSView so we can call the proper ghostty callback. --- macos/Sources/TerminalSurfaceView.swift | 33 ++++++++++++++++++------- macos/Sources/TerminalView.swift | 11 ++++++--- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/macos/Sources/TerminalSurfaceView.swift b/macos/Sources/TerminalSurfaceView.swift index 9a8e0bbbc..d4f3c4813 100644 --- a/macos/Sources/TerminalSurfaceView.swift +++ b/macos/Sources/TerminalSurfaceView.swift @@ -10,10 +10,25 @@ import GhosttyKit /// since that is what the Metal renderer in Ghostty expects. In the future, it may make more sense to /// wrap an MTKView and use that, but for legacy reasons we didn't do that to begin with. struct TerminalSurfaceView: NSViewRepresentable { + static let logger = Logger( + subsystem: Bundle.main.bundleIdentifier!, + category: String(describing: TerminalSurfaceView.self) + ) + /// This should be set to true wen the surface has focus. This is up to the parent because /// focus is also defined by window focus. It is important this is set correctly since if it is /// false then the surface will idle at almost 0% CPU. - var hasFocus: Bool + let hasFocus: Bool + + /// The size of the frame containing this view. We use this to update the the underlying + /// surface. This does not actually SET the size of our frame, this only sets the size + /// of our Metal surface for drawing. + /// + /// Note: we do NOT use the NSView.resize function because SwiftUI on macOS 12 + /// does not call this callback (macOS 13+ does). + /// + /// The best approach is to wrap this view in a GeometryReader and pass in the geo.size. + let size: CGSize /// This is set to the title of the surface as defined by the pty. Callers should use this to /// set the appropriate title of the window/tab/split/etc. if they care. @@ -21,10 +36,11 @@ struct TerminalSurfaceView: NSViewRepresentable { @StateObject private var state: TerminalSurfaceView_Real - init(_ app: ghostty_app_t, hasFocus: Bool, title: Binding) { + init(_ app: ghostty_app_t, hasFocus: Bool, size: CGSize, title: Binding) { self._state = StateObject(wrappedValue: TerminalSurfaceView_Real(app)) self._title = title self.hasFocus = hasFocus + self.size = size } func makeNSView(context: Context) -> TerminalSurfaceView_Real { @@ -38,6 +54,7 @@ struct TerminalSurfaceView: NSViewRepresentable { func updateNSView(_ view: TerminalSurfaceView_Real, context: Context) { state.delegate = context.coordinator state.focusDidChange(hasFocus) + state.sizeDidChange(size) } func makeCoordinator() -> Coordinator { @@ -243,14 +260,12 @@ class TerminalSurfaceView_Real: NSView, NSTextInputClient, ObservableObject { ghostty_surface_set_focus(surface, focused) } - override func resize(withOldSuperviewSize oldSize: NSSize) { - super.resize(withOldSuperviewSize: oldSize) + func sizeDidChange(_ size: CGSize) { + guard let surface = self.surface else { return } - if let surface = self.surface { - // Ghostty wants to know the actual framebuffer size... - let fbFrame = self.convertToBacking(self.frame); - ghostty_surface_set_size(surface, UInt32(fbFrame.size.width), UInt32(fbFrame.size.height)) - } + // Ghostty wants to know the actual framebuffer size... + let fbFrame = self.convertToBacking(self.frame); + ghostty_surface_set_size(surface, UInt32(fbFrame.size.width), UInt32(fbFrame.size.height)) } override func updateTrackingAreas() { diff --git a/macos/Sources/TerminalView.swift b/macos/Sources/TerminalView.swift index fee8920b0..b03abbcb9 100644 --- a/macos/Sources/TerminalView.swift +++ b/macos/Sources/TerminalView.swift @@ -12,8 +12,13 @@ struct TerminalView: View { private var hasFocus: Bool { surfaceFocus && isKeyWindow } var body: some View { - TerminalSurfaceView(app, hasFocus: hasFocus, title: $title) - .focused($surfaceFocus) - .navigationTitle(title) + // 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 + TerminalSurfaceView(app, hasFocus: hasFocus, size: geo.size, title: $title) + .focused($surfaceFocus) + .navigationTitle(title) + } } }