mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: hacky API to get a CTFont for QuickLook
This commit is contained in:
@ -556,6 +556,7 @@ uintptr_t ghostty_surface_selection(ghostty_surface_t, char*, uintptr_t);
|
|||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
void ghostty_surface_set_display_id(ghostty_surface_t, uint32_t);
|
void ghostty_surface_set_display_id(ghostty_surface_t, uint32_t);
|
||||||
|
void* ghostty_surface_quicklook_font(ghostty_surface_t);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
|
ghostty_inspector_t ghostty_surface_inspector(ghostty_surface_t);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import CoreText
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
@ -929,7 +930,13 @@ extension Ghostty.SurfaceView: NSTextInputClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func selectedRange() -> NSRange {
|
func selectedRange() -> NSRange {
|
||||||
return NSRange()
|
guard let surface = self.surface else { return NSRange() }
|
||||||
|
guard ghostty_surface_has_selection(surface) else { return NSRange() }
|
||||||
|
|
||||||
|
// If we have a selection, we just return a non-empty range. The actual
|
||||||
|
// values are meaningless but the non-emptiness of it tells AppKit we
|
||||||
|
// have a selection.
|
||||||
|
return NSRange(location: 0, length: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMarkedText(_ string: Any, selectedRange: NSRange, replacementRange: NSRange) {
|
func setMarkedText(_ string: Any, selectedRange: NSRange, replacementRange: NSRange) {
|
||||||
@ -954,7 +961,33 @@ extension Ghostty.SurfaceView: NSTextInputClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func attributedSubstring(forProposedRange range: NSRange, actualRange: NSRangePointer?) -> NSAttributedString? {
|
func attributedSubstring(forProposedRange range: NSRange, actualRange: NSRangePointer?) -> NSAttributedString? {
|
||||||
return nil
|
// We ignore the proposed range and always return the selection from
|
||||||
|
// this (if we have one). This enables features like QuickLook. I don't
|
||||||
|
// know if this breaks anything else...
|
||||||
|
guard let surface = self.surface else { return nil }
|
||||||
|
guard ghostty_surface_has_selection(surface) else { return nil }
|
||||||
|
|
||||||
|
// Get our selection. We cap it at 1MB for the purpose of this. This is
|
||||||
|
// arbitrary. If this is a good reason to increase it I'm happy to.
|
||||||
|
let v = String(unsafeUninitializedCapacity: 1000000) {
|
||||||
|
Int(ghostty_surface_selection(surface, $0.baseAddress, UInt($0.count)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can get a font then we use the font. This should always work
|
||||||
|
// since we always have a primary font. The only scenario this doesn't
|
||||||
|
// work is if someone is using a non-CoreText build which would be
|
||||||
|
// unofficial.
|
||||||
|
var attributes: [ NSAttributedString.Key : Any ] = [:];
|
||||||
|
if let fontRaw = ghostty_surface_quicklook_font(surface) {
|
||||||
|
// Memory management here is wonky: ghostty_surface_quicklook_font
|
||||||
|
// will create a copy of a CTFont, Swift will auto-retain the
|
||||||
|
// unretained value passed into the dict, so we release the original.
|
||||||
|
let font = Unmanaged<CTFont>.fromOpaque(fontRaw)
|
||||||
|
attributes[.font] = font.takeUnretainedValue()
|
||||||
|
font.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
return .init(string: v, attributes: attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func characterIndex(for point: NSPoint) -> Int {
|
func characterIndex(for point: NSPoint) -> Int {
|
||||||
|
@ -10,6 +10,7 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
const apprt = @import("../apprt.zig");
|
const apprt = @import("../apprt.zig");
|
||||||
|
const font = @import("../font/main.zig");
|
||||||
const input = @import("../input.zig");
|
const input = @import("../input.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
@ -1771,6 +1772,37 @@ pub const CAPI = struct {
|
|||||||
surface.renderer_thread.wakeup.notify() catch {};
|
surface.renderer_thread.wakeup.notify() catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This returns a CTFontRef that should be used for quicklook
|
||||||
|
/// highlighted text. This is always the primary font in use
|
||||||
|
/// regardless of the selected text. If coretext is not in use
|
||||||
|
/// then this will return nothing.
|
||||||
|
export fn ghostty_surface_quicklook_font(ptr: *Surface) ?*anyopaque {
|
||||||
|
// For non-CoreText we just return null.
|
||||||
|
if (comptime font.options.backend != .coretext) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the shared font grid. We acquire a read lock to
|
||||||
|
// read the font face. It should not be deffered since
|
||||||
|
// we're loading the primary face.
|
||||||
|
const grid = ptr.core_surface.renderer.font_grid;
|
||||||
|
grid.lock.lockShared();
|
||||||
|
defer grid.lock.unlockShared();
|
||||||
|
|
||||||
|
const collection = &grid.resolver.collection;
|
||||||
|
const face = collection.getFace(.{}) catch return null;
|
||||||
|
|
||||||
|
// The font is not the right size by default so we need
|
||||||
|
// to set it to our configured window size.
|
||||||
|
const copy = face.font.copyWithAttributes(
|
||||||
|
ptr.app.config.@"font-size",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
) catch return null;
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool {
|
export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool {
|
||||||
return ptr.initMetal(objc.Object.fromId(device));
|
return ptr.initMetal(objc.Object.fromId(device));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user