macos: drag preview is an image of the surface

This commit is contained in:
Mitchell Hashimoto
2025-12-27 14:37:25 -08:00
parent 5d8c9357c0
commit 485b861342
3 changed files with 59 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ extension Ghostty {
/// Only appears when hovering in the top region of the surface.
struct SurfaceGrabHandle: View {
private let handleHeight: CGFloat = 10
private let previewScale: CGFloat = 0.2
let surfaceView: SurfaceView
@@ -34,7 +35,35 @@ extension Ghostty {
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.draggable(surfaceView)
.draggable(surfaceView) {
SurfaceDragPreview(surfaceView: surfaceView, scale: previewScale)
}
}
}
/// A miniature preview of the surface view for drag operations that updates periodically.
private struct SurfaceDragPreview: View {
let surfaceView: SurfaceView
let scale: CGFloat
var body: some View {
// We need to use a TimelineView to ensure that this doesn't
// cache forever. This will NOT let the view live update while
// being dragged; macOS doesn't seem to allow that. But it will
// make sure on new drags the screenshot is updated.
TimelineView(.periodic(from: .now, by: 1.0 / 30.0)) { _ in
if let snapshot = surfaceView.asImage {
Image(nsImage: snapshot)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(
width: snapshot.size.width * scale,
height: snapshot.size.height * scale
)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 10)
}
}
}
}
}

View File

@@ -0,0 +1,28 @@
#if canImport(AppKit)
import AppKit
#elseif canImport(UIKit)
import UIKit
#endif
extension Ghostty.SurfaceView {
#if canImport(AppKit)
/// A snapshot image of the current surface view.
var asImage: NSImage? {
guard let bitmapRep = bitmapImageRepForCachingDisplay(in: bounds) else {
return nil
}
cacheDisplay(in: bounds, to: bitmapRep)
let image = NSImage(size: bounds.size)
image.addRepresentation(bitmapRep)
return image
}
#elseif canImport(UIKit)
/// A snapshot image of the current surface view.
var asImage: UIImage? {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
#endif
}

View File

@@ -2213,6 +2213,7 @@ extension Ghostty.SurfaceView {
return NSAttributedString(string: plainString, attributes: attributes)
}
}
/// Caches a value for some period of time, evicting it automatically when that time expires.