From 4680c87c8b923446456231677661bd092634eba3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 11 Aug 2024 10:34:53 -0700 Subject: [PATCH 1/3] macos: pause resize overlay until 500ms passes --- macos/Sources/Ghostty/SurfaceView.swift | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index dba3ca632..c6556e838 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -279,11 +279,18 @@ extension Ghostty { // timer state. @State var lastSize: CGSize? = nil + // Ready is set to true after a short delay. This avoids some of the + // challenges of initial view sizing from SwiftUI. + @State var ready: Bool = false + // Fixed value set based on personal taste. private let padding: CGFloat = 5 // This computed boolean is set to true when the overlay should be hidden. private var hidden: Bool { + // If we aren't ready yet then we wait... + if (!ready) { return true; } + // Hidden if we already processed this size. if (lastSize == geoSize) { return true; } @@ -327,11 +334,24 @@ extension Ghostty { } .allowsHitTesting(false) .opacity(hidden ? 0 : 1) + .task { + // Sleep chosen arbitrarily... a better long term solution would be to detect + // when the size stabilizes (coalesce a value) for the first time and then after + // that show the resize overlay consistently. + try? await Task.sleep(nanoseconds: 500 * 1_000_000) + ready = true + } .task(id: geoSize) { // By ID-ing the task on the geoSize, we get the task to restart if our // geoSize changes. This also ensures that future resize overlays are shown // properly. - try? await Task.sleep(nanoseconds: UInt64(duration) * 1_000_000) + + // We only sleep if we're ready. If we're not ready then we want to set + // our last size right away to avoid a flash. + if (ready) { + try? await Task.sleep(nanoseconds: UInt64(duration) * 1_000_000) + } + lastSize = geoSize } } From 2e0e8897e7c29bbaaec8d30187292e11c7409dd8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 11 Aug 2024 11:07:07 -0700 Subject: [PATCH 2/3] macos: avoid showing overlay if gained focus recently --- macos/Sources/Ghostty/SurfaceView.swift | 19 +++++++++++++++++-- .../Sources/Ghostty/SurfaceView_AppKit.swift | 13 ++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index c6556e838..9ec41f95d 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -153,7 +153,8 @@ extension Ghostty { size: surfaceSize, overlay: ghostty.config.resizeOverlay, position: ghostty.config.resizeOverlayPosition, - duration: ghostty.config.resizeOverlayDuration) + duration: ghostty.config.resizeOverlayDuration, + focusInstant: surfaceView.focusInstant) } } @@ -274,6 +275,7 @@ extension Ghostty { let overlay: Ghostty.Config.ResizeOverlay let position: Ghostty.Config.ResizeOverlayPosition let duration: UInt + let focusInstant: Any? // This is the last size that we processed. This is how we handle our // timer state. @@ -293,7 +295,20 @@ extension Ghostty { // Hidden if we already processed this size. if (lastSize == geoSize) { return true; } - + + // If we were focused recently we hide it as well. This avoids showing + // the resize overlay when SwiftUI is lazily resizing. + if #available(macOS 13, iOS 16, *) { + if let instant = focusInstant as? ContinuousClock.Instant { + let d = instant.duration(to: ContinuousClock.now) + if (d < .milliseconds(500)) { + // Avoid this size completely. + lastSize = geoSize + return true; + } + } + } + // Hidden depending on overlay config switch (overlay) { case .never: return true; diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 3d6c17750..1a68a2ea0 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -29,7 +29,11 @@ extension Ghostty { // The hovered URL string @Published var hoverUrl: String? = nil - + + // The time this surface last became focused. This is a ContinuousClock.Instant + // on supported platforms. + @Published var focusInstant: Any? = nil + // An initial size to request for a window. This will only affect // then the view is moved to a new window. var initialSize: NSSize? = nil @@ -208,6 +212,13 @@ extension Ghostty { guard self.focused != focused else { return } self.focused = focused ghostty_surface_set_focus(surface, focused) + + // On macOS 13+ we can store our continuous clock... + if #available(macOS 13, iOS 16, *) { + if (focused) { + focusInstant = ContinuousClock.now + } + } } func sizeDidChange(_ size: CGSize) { From affe12d68c08a9100506176392c2c5ebe98f8ac2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 11 Aug 2024 11:10:35 -0700 Subject: [PATCH 3/3] ios: add focusInstant field to surface --- macos/Sources/Ghostty/SurfaceView_UIKit.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/macos/Sources/Ghostty/SurfaceView_UIKit.swift b/macos/Sources/Ghostty/SurfaceView_UIKit.swift index a9739d7d4..ac5270c6a 100644 --- a/macos/Sources/Ghostty/SurfaceView_UIKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_UIKit.swift @@ -28,6 +28,10 @@ extension Ghostty { // The hovered URL @Published var hoverUrl: String? = nil + // The time this surface last became focused. This is a ContinuousClock.Instant + // on supported platforms. + @Published var focusInstant: Any? = nil + // Returns sizing information for the surface. This is the raw C // structure because I'm lazy. var surfaceSize: ghostty_surface_size_s? { @@ -67,6 +71,13 @@ extension Ghostty { func focusDidChange(_ focused: Bool) { guard let surface = self.surface else { return } ghostty_surface_set_focus(surface, focused) + + // On macOS 13+ we can store our continuous clock... + if #available(macOS 13, iOS 16, *) { + if (focused) { + focusInstant = ContinuousClock.now + } + } } func sizeDidChange(_ size: CGSize) {