From 13dd4bd8971d882ee7ba5b155b06808802217956 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Fri, 13 Dec 2024 12:16:15 -0500 Subject: [PATCH] font/sprite: separate out cursor rendering from Box (Fixes width handling when hovering wide chars) --- src/font/sprite/Box.zig | 80 ++------------------------------------ src/font/sprite/Face.zig | 33 +++++++++++++++- src/font/sprite/cursor.zig | 61 +++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 78 deletions(-) create mode 100644 src/font/sprite/cursor.zig diff --git a/src/font/sprite/Box.zig b/src/font/sprite/Box.zig index a6317196f..cf929eb67 100644 --- a/src/font/sprite/Box.zig +++ b/src/font/sprite/Box.zig @@ -214,26 +214,11 @@ pub fn renderGlyph( ) !font.Glyph { 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 var canvas = try font.sprite.Canvas.init( alloc, metrics.cell_width, - height, + metrics.cell_height, ); defer canvas.deinit(alloc); @@ -246,15 +231,11 @@ pub fn renderGlyph( // 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 // cell. - // - // 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)); + const offset_y = @as(i32, @intCast(metrics.cell_height)); return font.Glyph{ .width = metrics.cell_width, - .height = height, + .height = metrics.cell_height, .offset_x = 0, .offset_y = offset_y, .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 { _ = alloc; switch (cp) { @@ -1656,12 +1624,6 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void .right = true, }, .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, } } @@ -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 { 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); diff --git a/src/font/sprite/Face.zig b/src/font/sprite/Face.zig index b8c89c74e..ede67d00d 100644 --- a/src/font/sprite/Face.zig +++ b/src/font/sprite/Face.zig @@ -21,6 +21,7 @@ const Sprite = font.sprite.Sprite; const Box = @import("Box.zig"); const Powerline = @import("Powerline.zig"); const underline = @import("underline.zig"); +const cursor = @import("cursor.zig"); const log = std.log.scoped(.font_sprite); @@ -123,6 +124,35 @@ pub fn renderGlyph( 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, strikethrough, powerline, + cursor, pub fn init(cp: u32) ?Kind { return switch (cp) { @@ -153,7 +184,7 @@ const Kind = enum { .cursor_rect, .cursor_hollow_rect, .cursor_bar, - => .box, + => .cursor, }, // == Box fonts == diff --git a/src/font/sprite/cursor.zig b/src/font/sprite/cursor.zig new file mode 100644 index 000000000..b20b6c531 --- /dev/null +++ b/src/font/sprite/cursor.zig @@ -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), + }; +}