fix: respect notch in MacOS non-native fullscreen mode

This patch adds top padding to respect macOS notch cutout in non-native
fullscreen mode.

I do not have any experience with swift & swiftui, so please let me know
if there's a better way to implement this!
This commit is contained in:
Andrey Lushnikov
2024-12-31 04:29:00 +03:00
parent 6cbd69da78
commit b99b2b7030
5 changed files with 42 additions and 10 deletions

View File

@ -333,7 +333,15 @@ class BaseTerminalController: NSWindowController,
}
}
func fullscreenDidChange() {
func fullscreenDidChange(mode: FullscreenMode, enabled: Bool) {
NotificationCenter.default.post(
name: Ghostty.Notification.ghosttyDidToggleFullscreen,
object: self.window,
userInfo: [
Ghostty.Notification.FullscreenEnabledKey: enabled,
Ghostty.Notification.FullscreenModeKey: mode,
]
)
// For some reason focus can get lost when we change fullscreen. Regardless of
// mode above we just move it back.
if let focusedSurface {

View File

@ -95,8 +95,8 @@ class TerminalController: BaseTerminalController {
}
override func fullscreenDidChange() {
super.fullscreenDidChange()
override func fullscreenDidChange(mode: FullscreenMode, enabled: Bool) {
super.fullscreenDidChange(mode: mode, enabled: enabled)
// When our fullscreen state changes, we resync our appearance because some
// properties change when fullscreen or not.

View File

@ -269,6 +269,10 @@ extension Ghostty.Notification {
static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen")
static let FullscreenModeKey = ghosttyToggleFullscreen.rawValue
/// Toggle fullscreen of current window
static let ghosttyDidToggleFullscreen = Notification.Name("com.mitchellh.ghostty.didToggleFullscreen")
static let FullscreenEnabledKey = ghosttyDidToggleFullscreen.rawValue + ".bool"
/// Notification sent to toggle split maximize/unmaximize.
static let didToggleSplitZoom = Notification.Name("com.mitchellh.ghostty.didToggleSplitZoom")

View File

@ -49,6 +49,9 @@ extension Ghostty {
// Maintain whether our window has focus (is key) or not
@State private var windowFocus: Bool = true
// Maintain whether our window is fullscreen or not
@State private var paddingTop: CGFloat = 0
// True if we're hovering over the left URL view, so we can show it on the right.
@State private var isHoveringURLLeft: Bool = false
@ -70,9 +73,11 @@ extension Ghostty {
#if canImport(AppKit)
let pubBecomeKey = center.publisher(for: NSWindow.didBecomeKeyNotification)
let pubResign = center.publisher(for: NSWindow.didResignKeyNotification)
let pubFullscreen = center.publisher(for: Ghostty.Notification.ghosttyDidToggleFullscreen)
#endif
Surface(view: surfaceView, size: geo.size)
let adjustedSize = CGSize(width: geo.size.width, height: geo.size.height - paddingTop)
Surface(view: surfaceView, size: adjustedSize)
.focused($surfaceFocus)
.focusedValue(\.ghosttySurfaceTitle, surfaceView.title)
.focusedValue(\.ghosttySurfacePwd, surfaceView.pwd)
@ -80,6 +85,19 @@ extension Ghostty {
.focusedValue(\.ghosttySurfaceCellSize, surfaceView.cellSize)
#if canImport(AppKit)
.backport.pointerStyle(surfaceView.pointerStyle)
.onReceive(pubFullscreen) { notification in
guard let enabled = notification.userInfo?[Ghostty.Notification.FullscreenEnabledKey] as? Bool else {
return
}
guard let mode = notification.userInfo?[Ghostty.Notification.FullscreenModeKey] as? FullscreenMode else {
return
}
guard let window = notification.object as? NSWindow else { return }
guard let screen = window.screen else {
return
}
paddingTop = enabled && mode == FullscreenMode.nonNative ? screen.safeAreaInsets.top : 0;
}
.onReceive(pubBecomeKey) { notification in
guard let window = notification.object as? NSWindow else { return }
guard let surfaceWindow = surfaceView.window else { return }
@ -92,6 +110,7 @@ extension Ghostty {
windowFocus = false
}
}
.padding(.top, paddingTop)
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
providers.forEach { provider in
_ = provider.loadObject(ofClass: URL.self) { url, _ in
@ -125,6 +144,7 @@ extension Ghostty {
.ghosttySurfaceView(surfaceView)
#if canImport(AppKit)
// If we are in the middle of a key sequence, then we show a visual element. We only
// support this on macOS currently although in theory we can support mobile with keyboards!
if !surfaceView.keySequence.isEmpty {

View File

@ -38,11 +38,11 @@ protocol FullscreenStyle {
protocol FullscreenDelegate: AnyObject {
/// Called whenever the fullscreen state changed. You can call isFullscreen to see
/// the current state.
func fullscreenDidChange()
func fullscreenDidChange(mode: FullscreenMode, enabled: Bool)
}
extension FullscreenDelegate {
func fullscreenDidChange() {}
func fullscreenDidChange(mode: FullscreenMode, enabled: Bool) {}
}
/// The base class for fullscreen implementations, cannot be used as a FullscreenStyle on its own.
@ -74,11 +74,11 @@ class FullscreenBase {
}
@objc private func didEnterFullScreenNotification(_ notification: Notification) {
delegate?.fullscreenDidChange()
delegate?.fullscreenDidChange(mode: FullscreenMode.native, enabled: true)
}
@objc private func didExitFullScreenNotification(_ notification: Notification) {
delegate?.fullscreenDidChange()
delegate?.fullscreenDidChange(mode: FullscreenMode.native, enabled: false)
}
}
@ -202,7 +202,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
// https://github.com/ghostty-org/ghostty/issues/1996
DispatchQueue.main.async {
self.window.setFrame(self.fullscreenFrame(screen), display: true)
self.delegate?.fullscreenDidChange()
self.delegate?.fullscreenDidChange(mode: FullscreenMode.nonNative, enabled: true)
}
}
@ -258,7 +258,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
window.makeKeyAndOrderFront(nil)
// Notify the delegate
self.delegate?.fullscreenDidChange()
self.delegate?.fullscreenDidChange(mode: FullscreenMode.nonNative, enabled: false)
}
private func fullscreenFrame(_ screen: NSScreen) -> NSRect {