mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
font/sprite: separate out cursor rendering from Box
(Fixes width handling when hovering wide chars)
This commit is contained in:
@ -214,26 +214,11 @@ pub fn renderGlyph(
|
|||||||
) !font.Glyph {
|
) !font.Glyph {
|
||||||
const metrics = self.metrics;
|
const metrics = self.metrics;
|
||||||
|
|
||||||
// Some codepoints (such as a few cursors) should not
|
|
||||||
// grow when the cell height is adjusted to be larger.
|
|
||||||
// And we also will need to adjust the vertical position.
|
|
||||||
const height, const dy = adjust: {
|
|
||||||
const h = metrics.cell_height;
|
|
||||||
if (unadjustedCodepoint(cp)) {
|
|
||||||
if (metrics.original_cell_height) |original| {
|
|
||||||
if (h > original) {
|
|
||||||
break :adjust .{ original, (h - original) / 2 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break :adjust .{ h, 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the canvas we'll use to draw
|
// Create the canvas we'll use to draw
|
||||||
var canvas = try font.sprite.Canvas.init(
|
var canvas = try font.sprite.Canvas.init(
|
||||||
alloc,
|
alloc,
|
||||||
metrics.cell_width,
|
metrics.cell_width,
|
||||||
height,
|
metrics.cell_height,
|
||||||
);
|
);
|
||||||
defer canvas.deinit(alloc);
|
defer canvas.deinit(alloc);
|
||||||
|
|
||||||
@ -246,15 +231,11 @@ pub fn renderGlyph(
|
|||||||
// Our coordinates start at the BOTTOM for our renderers so we have to
|
// Our coordinates start at the BOTTOM for our renderers so we have to
|
||||||
// specify an offset of the full height because we rendered a full size
|
// specify an offset of the full height because we rendered a full size
|
||||||
// cell.
|
// cell.
|
||||||
//
|
const offset_y = @as(i32, @intCast(metrics.cell_height));
|
||||||
// If we have an adjustment (see above) to the cell height that we need
|
|
||||||
// to account for, we subtract half the difference (dy) to keep the glyph
|
|
||||||
// centered.
|
|
||||||
const offset_y = @as(i32, @intCast(metrics.cell_height - dy));
|
|
||||||
|
|
||||||
return font.Glyph{
|
return font.Glyph{
|
||||||
.width = metrics.cell_width,
|
.width = metrics.cell_width,
|
||||||
.height = height,
|
.height = metrics.cell_height,
|
||||||
.offset_x = 0,
|
.offset_x = 0,
|
||||||
.offset_y = offset_y,
|
.offset_y = offset_y,
|
||||||
.atlas_x = region.x,
|
.atlas_x = region.x,
|
||||||
@ -263,19 +244,6 @@ pub fn renderGlyph(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this codepoint should be rendered with the
|
|
||||||
/// width/height set to unadjusted values.
|
|
||||||
pub fn unadjustedCodepoint(cp: u32) bool {
|
|
||||||
return switch (cp) {
|
|
||||||
@intFromEnum(Sprite.cursor_rect),
|
|
||||||
@intFromEnum(Sprite.cursor_hollow_rect),
|
|
||||||
@intFromEnum(Sprite.cursor_bar),
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
else => false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void {
|
fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void {
|
||||||
_ = alloc;
|
_ = alloc;
|
||||||
switch (cp) {
|
switch (cp) {
|
||||||
@ -1656,12 +1624,6 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void
|
|||||||
.right = true,
|
.right = true,
|
||||||
}, .light),
|
}, .light),
|
||||||
|
|
||||||
// Not official box characters but special characters we hide
|
|
||||||
// in the high bits of a unicode codepoint.
|
|
||||||
@intFromEnum(Sprite.cursor_rect) => self.draw_cursor_rect(canvas),
|
|
||||||
@intFromEnum(Sprite.cursor_hollow_rect) => self.draw_cursor_hollow_rect(canvas),
|
|
||||||
@intFromEnum(Sprite.cursor_bar) => self.draw_cursor_bar(canvas),
|
|
||||||
|
|
||||||
else => return error.InvalidCodepoint,
|
else => return error.InvalidCodepoint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2842,42 +2804,6 @@ fn draw_dash_vertical(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_cursor_rect(self: Box, canvas: *font.sprite.Canvas) void {
|
|
||||||
// The cursor should fit itself to the canvas it's given, since if
|
|
||||||
// the cell height is adjusted upwards it will be given a canvas
|
|
||||||
// with the original un-adjusted height, so we can't use the height
|
|
||||||
// from the metrics.
|
|
||||||
const height: u32 = @intCast(canvas.sfc.getHeight());
|
|
||||||
self.rect(canvas, 0, 0, self.metrics.cell_width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_cursor_hollow_rect(self: Box, canvas: *font.sprite.Canvas) void {
|
|
||||||
// The cursor should fit itself to the canvas it's given, since if
|
|
||||||
// the cell height is adjusted upwards it will be given a canvas
|
|
||||||
// with the original un-adjusted height, so we can't use the height
|
|
||||||
// from the metrics.
|
|
||||||
const height: u32 = @intCast(canvas.sfc.getHeight());
|
|
||||||
|
|
||||||
const thick_px = Thickness.super_light.height(self.metrics.cursor_thickness);
|
|
||||||
|
|
||||||
self.rect(canvas, 0, 0, self.metrics.cell_width, thick_px);
|
|
||||||
self.rect(canvas, 0, 0, thick_px, height);
|
|
||||||
self.rect(canvas, self.metrics.cell_width -| thick_px, 0, self.metrics.cell_width, height);
|
|
||||||
self.rect(canvas, 0, height -| thick_px, self.metrics.cell_width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_cursor_bar(self: Box, canvas: *font.sprite.Canvas) void {
|
|
||||||
// The cursor should fit itself to the canvas it's given, since if
|
|
||||||
// the cell height is adjusted upwards it will be given a canvas
|
|
||||||
// with the original un-adjusted height, so we can't use the height
|
|
||||||
// from the metrics.
|
|
||||||
const height: u32 = @intCast(canvas.sfc.getHeight());
|
|
||||||
|
|
||||||
const thick_px = Thickness.light.height(self.metrics.cursor_thickness);
|
|
||||||
|
|
||||||
self.rect(canvas, 0, 0, thick_px, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vline_middle(self: Box, canvas: *font.sprite.Canvas, thickness: Thickness) void {
|
fn vline_middle(self: Box, canvas: *font.sprite.Canvas, thickness: Thickness) void {
|
||||||
const thick_px = thickness.height(self.metrics.box_thickness);
|
const thick_px = thickness.height(self.metrics.box_thickness);
|
||||||
self.vline(canvas, 0, self.metrics.cell_height, (self.metrics.cell_width -| thick_px) / 2, thick_px);
|
self.vline(canvas, 0, self.metrics.cell_height, (self.metrics.cell_width -| thick_px) / 2, thick_px);
|
||||||
|
@ -21,6 +21,7 @@ const Sprite = font.sprite.Sprite;
|
|||||||
const Box = @import("Box.zig");
|
const Box = @import("Box.zig");
|
||||||
const Powerline = @import("Powerline.zig");
|
const Powerline = @import("Powerline.zig");
|
||||||
const underline = @import("underline.zig");
|
const underline = @import("underline.zig");
|
||||||
|
const cursor = @import("cursor.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.font_sprite);
|
const log = std.log.scoped(.font_sprite);
|
||||||
|
|
||||||
@ -123,6 +124,35 @@ pub fn renderGlyph(
|
|||||||
|
|
||||||
break :powerline try f.renderGlyph(alloc, atlas, cp);
|
break :powerline try f.renderGlyph(alloc, atlas, cp);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.cursor => cursor: {
|
||||||
|
// Cursors should be drawn with the original cell height if
|
||||||
|
// it has been adjusted larger, so they don't get stretched.
|
||||||
|
const height, const dy = adjust: {
|
||||||
|
const h = metrics.cell_height;
|
||||||
|
if (metrics.original_cell_height) |original| {
|
||||||
|
if (h > original) {
|
||||||
|
break :adjust .{ original, (h - original) / 2 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :adjust .{ h, 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
var g = try cursor.renderGlyph(
|
||||||
|
alloc,
|
||||||
|
atlas,
|
||||||
|
@enumFromInt(cp),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
metrics.cursor_thickness,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Keep the cursor centered in the cell if it's shorter.
|
||||||
|
g.offset_y += @intCast(dy);
|
||||||
|
|
||||||
|
break :cursor g;
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +163,7 @@ const Kind = enum {
|
|||||||
overline,
|
overline,
|
||||||
strikethrough,
|
strikethrough,
|
||||||
powerline,
|
powerline,
|
||||||
|
cursor,
|
||||||
|
|
||||||
pub fn init(cp: u32) ?Kind {
|
pub fn init(cp: u32) ?Kind {
|
||||||
return switch (cp) {
|
return switch (cp) {
|
||||||
@ -153,7 +184,7 @@ const Kind = enum {
|
|||||||
.cursor_rect,
|
.cursor_rect,
|
||||||
.cursor_hollow_rect,
|
.cursor_hollow_rect,
|
||||||
.cursor_bar,
|
.cursor_bar,
|
||||||
=> .box,
|
=> .cursor,
|
||||||
},
|
},
|
||||||
|
|
||||||
// == Box fonts ==
|
// == Box fonts ==
|
||||||
|
61
src/font/sprite/cursor.zig
Normal file
61
src/font/sprite/cursor.zig
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//! This file renders cursor sprites.
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const font = @import("../main.zig");
|
||||||
|
const Sprite = font.sprite.Sprite;
|
||||||
|
|
||||||
|
/// Draw a cursor.
|
||||||
|
pub fn renderGlyph(
|
||||||
|
alloc: Allocator,
|
||||||
|
atlas: *font.Atlas,
|
||||||
|
sprite: Sprite,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
thickness: u32,
|
||||||
|
) !font.Glyph {
|
||||||
|
// Make a canvas of the desired size
|
||||||
|
var canvas = try font.sprite.Canvas.init(alloc, width, height);
|
||||||
|
defer canvas.deinit(alloc);
|
||||||
|
|
||||||
|
// Draw the appropriate sprite
|
||||||
|
switch (sprite) {
|
||||||
|
Sprite.cursor_rect => canvas.rect(.{
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
}, .on),
|
||||||
|
Sprite.cursor_hollow_rect => {
|
||||||
|
// left
|
||||||
|
canvas.rect(.{ .x = 0, .y = 0, .width = thickness, .height = height }, .on);
|
||||||
|
// right
|
||||||
|
canvas.rect(.{ .x = width -| thickness, .y = 0, .width = thickness, .height = height }, .on);
|
||||||
|
// top
|
||||||
|
canvas.rect(.{ .x = 0, .y = 0, .width = width, .height = thickness }, .on);
|
||||||
|
// bottom
|
||||||
|
canvas.rect(.{ .x = 0, .y = height -| thickness, .width = width, .height = thickness }, .on);
|
||||||
|
},
|
||||||
|
Sprite.cursor_bar => canvas.rect(.{
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = thickness,
|
||||||
|
.height = height,
|
||||||
|
}, .on),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the drawing to the atlas
|
||||||
|
const region = try canvas.writeAtlas(alloc, atlas);
|
||||||
|
|
||||||
|
return font.Glyph{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.offset_x = 0,
|
||||||
|
.offset_y = @intCast(height),
|
||||||
|
.atlas_x = region.x,
|
||||||
|
.atlas_y = region.y,
|
||||||
|
.advance_x = @floatFromInt(width),
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user