From 80700d524db0d7e1c622379abae2519668c48e06 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 20 Jun 2024 14:05:47 -0700 Subject: [PATCH] macos: add API to get selection top-left for quicklook --- include/ghostty.h | 1 + .../Sources/Ghostty/SurfaceView_AppKit.swift | 14 +++++- src/Surface.zig | 47 +++++++++++++++++++ src/apprt/embedded.zig | 10 ++++ 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index a0160af43..1deea1864 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -558,6 +558,7 @@ uintptr_t ghostty_surface_selection(ghostty_surface_t, char*, uintptr_t); void ghostty_surface_set_display_id(ghostty_surface_t, uint32_t); void* ghostty_surface_quicklook_font(ghostty_surface_t); void ghostty_surface_selection_range(ghostty_surface_t, uint32_t*, uint32_t*); +void ghostty_surface_selection_point(ghostty_surface_t, double*, double*); #endif ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t); diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 91bc37f35..a95538729 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -1006,11 +1006,21 @@ extension Ghostty.SurfaceView: NSTextInputClient { guard let surface = self.surface else { return NSMakeRect(frame.origin.x, frame.origin.y, 0, 0) } - + // Ghostty will tell us where it thinks an IME keyboard should render. var x: Double = 0; var y: Double = 0; - ghostty_surface_ime_point(surface, &x, &y) + + // QuickLook never gives us a matching range to our selection so if we detect + // this then we return the top-left selection point rather than the cursor point. + // This is hacky but I can't think of a better way to get the right IME vs. QuickLook + // point right now. I'm sure I'm missing something fundamental... + if range.length > 0 && range != self.selectedRange() { + // QuickLook + ghostty_surface_selection_point(surface, &x, &y) + } else { + ghostty_surface_ime_point(surface, &x, &y) + } // Ghostty coordinates are in top-left (0, 0) so we have to convert to // bottom-left since that is what UIKit expects diff --git a/src/Surface.zig b/src/Surface.zig index e7ae87fe4..c0c011936 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -933,6 +933,53 @@ pub fn imePoint(self: *const Surface) apprt.IMEPos { return .{ .x = x, .y = y }; } +/// Returns the x/y coordinate of where the selection top-left is. This is +/// used currently only by macOS to render the QuickLook highlight in the +/// proper location. +pub fn selectionPoint(self: *const Surface) ?apprt.IMEPos { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // Get the top-left coordinate of the selection in the viewport. + const sel = self.io.terminal.screen.selection orelse return null; + const tl_pt = self.io.terminal.screen.pages.pointFromPin( + .viewport, + sel.topLeft(&self.io.terminal.screen), + ) orelse return null; + const tl_coord = tl_pt.coord(); + + // Our sizes are all scaled so we need to send the unscaled values back. + const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 }; + + const x: f64 = x: { + // Simple x * cell width gives the top-left corner + var x: f64 = @floatFromInt(tl_coord.x * self.cell_size.width); + + // We want the midpoint + x += @as(f64, @floatFromInt(self.cell_size.width)) / 2; + + // And scale it + x /= content_scale.x; + + break :x x; + }; + + const y: f64 = y: { + // Simple x * cell width gives the top-left corner + var y: f64 = @floatFromInt(tl_coord.y * self.cell_size.height); + + // We want the bottom + y += @floatFromInt(self.cell_size.height); + + // And scale it + y /= content_scale.y; + + break :y y; + }; + + return .{ .x = x, .y = y }; +} + fn clipboardWrite(self: *const Surface, data: []const u8, loc: apprt.Clipboard) !void { if (self.config.clipboard_write == .deny) { log.info("application attempted to write clipboard, but 'clipboard-write' is set to deny", .{}); diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 5726bc1f9..98a96345b 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -1823,6 +1823,16 @@ pub const CAPI = struct { len.* = range.len; } + export fn ghostty_surface_selection_point( + ptr: *Surface, + x: *f64, + y: *f64, + ) void { + const point = ptr.core_surface.selectionPoint() orelse return; + x.* = point.x; + y.* = point.y; + } + export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool { return ptr.initMetal(objc.Object.fromId(device)); }