diff --git a/src/config/Config.zig b/src/config/Config.zig index 1816499e21..563c8a706c 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -241,16 +241,15 @@ const c = @cImport({ /// `-100%`) can cause the terminal to be unusable. Use with caution and reason. /// /// Some values are clamped to minimum or maximum values. This can make it -/// appear that certain values are ignored. For example, the underline position -/// is clamped to the height of a cell. If you set the underline position so -/// high that it extends beyond the bottom of the cell size, it will be clamped -/// to the bottom of the cell. +/// appear that certain values are ignored. For example, many `*-thickness` +/// adjustments cannot go below 1px. /// /// `adjust-cell-height` has some additional behaviors to describe: /// /// * The font will be centered vertically in the cell. /// -/// * The cursor will remain the same size as the font. +/// * The cursor will remain the same size as the font, but may be +/// adjusted separately with `adjust-cursor-height`. /// /// * Powerline glyphs will be adjusted along with the cell height so /// that things like status lines continue to look aligned. @@ -276,6 +275,9 @@ const c = @cImport({ @"adjust-overline-thickness": ?MetricModifier = null, /// Thickness in pixels of the bar cursor and outlined rect cursor. @"adjust-cursor-thickness": ?MetricModifier = null, +/// Height in pixels of the cursor. Currently applies to all cursor types: +/// bar, rect, and outlined rect. +@"adjust-cursor-height": ?MetricModifier = null, /// Thickness in pixels of box drawing characters. @"adjust-box-thickness": ?MetricModifier = null, diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index 2f25ec5214..95ef02495e 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -430,6 +430,7 @@ pub const DerivedConfig = struct { @"adjust-overline-position": ?Metrics.Modifier, @"adjust-overline-thickness": ?Metrics.Modifier, @"adjust-cursor-thickness": ?Metrics.Modifier, + @"adjust-cursor-height": ?Metrics.Modifier, @"adjust-box-thickness": ?Metrics.Modifier, @"freetype-load-flags": font.face.FreetypeLoadFlags, @@ -468,6 +469,7 @@ pub const DerivedConfig = struct { .@"adjust-overline-position" = config.@"adjust-overline-position", .@"adjust-overline-thickness" = config.@"adjust-overline-thickness", .@"adjust-cursor-thickness" = config.@"adjust-cursor-thickness", + .@"adjust-cursor-height" = config.@"adjust-cursor-height", .@"adjust-box-thickness" = config.@"adjust-box-thickness", .@"freetype-load-flags" = if (font.face.FreetypeLoadFlags != void) config.@"freetype-load-flags" else {}, @@ -613,6 +615,7 @@ pub const Key = struct { if (config.@"adjust-overline-position") |m| try set.put(alloc, .overline_position, m); if (config.@"adjust-overline-thickness") |m| try set.put(alloc, .overline_thickness, m); if (config.@"adjust-cursor-thickness") |m| try set.put(alloc, .cursor_thickness, m); + if (config.@"adjust-cursor-height") |m| try set.put(alloc, .cursor_height, m); if (config.@"adjust-box-thickness") |m| try set.put(alloc, .box_thickness, m); break :set set; }; diff --git a/src/font/face/Metrics.zig b/src/font/face/Metrics.zig index d6b1bdd0c7..7bc4566294 100644 --- a/src/font/face/Metrics.zig +++ b/src/font/face/Metrics.zig @@ -32,10 +32,12 @@ box_thickness: u32, /// because it is not determined by fonts but rather by user configuration. cursor_thickness: u32 = 1, -/// Original cell width and height. These are used to render the cursor -/// in the original cell size after modification. +/// The height in pixels of the cursor sprite. +cursor_height: u32, + +/// Original cell width in pixels. This is used to keep +/// glyphs centered if the cell width is adjusted wider. original_cell_width: ?u32 = null, -original_cell_height: ?u32 = null, /// Minimum acceptable values for some fields to prevent modifiers /// from being able to, for example, cause 0-thickness underlines. @@ -47,6 +49,7 @@ const Minimums = struct { const overline_thickness = 1; const box_thickness = 1; const cursor_thickness = 1; + const cursor_height = 1; }; const CalcOpts = struct { @@ -167,6 +170,7 @@ pub fn calc(opts: CalcOpts) Metrics { .overline_position = 0, .overline_thickness = @intFromFloat(underline_thickness), .box_thickness = @intFromFloat(underline_thickness), + .cursor_height = @intFromFloat(cell_height), }; // Ensure all metrics are within their allowable range. @@ -192,10 +196,9 @@ pub fn apply(self: *Metrics, mods: ModifierSet) void { const new = @max(entry.value_ptr.apply(original), 1); if (new == original) continue; - // Preserve the original cell width and height if not set. + // Preserve the original cell width if not set. if (self.original_cell_width == null) { self.original_cell_width = self.cell_width; - self.original_cell_height = self.cell_height; } // Set the new value @@ -410,6 +413,7 @@ fn init() Metrics { .overline_position = 0, .overline_thickness = 0, .box_thickness = 0, + .cursor_height = 0, }; } @@ -440,12 +444,14 @@ test "Metrics: adjust cell height smaller" { m.underline_position = 55; m.strikethrough_position = 30; m.cell_height = 100; + m.cursor_height = 100; m.apply(set); try testing.expectEqual(@as(u32, 50), m.cell_height); try testing.expectEqual(@as(u32, 25), m.cell_baseline); try testing.expectEqual(@as(u32, 30), m.underline_position); try testing.expectEqual(@as(u32, 5), m.strikethrough_position); - try testing.expectEqual(@as(u32, 100), m.original_cell_height.?); + // Cursor height is separate from cell height and does not follow it. + try testing.expectEqual(@as(u32, 100), m.cursor_height); } test "Metrics: adjust cell height larger" { @@ -461,12 +467,14 @@ test "Metrics: adjust cell height larger" { m.underline_position = 55; m.strikethrough_position = 30; m.cell_height = 100; + m.cursor_height = 100; m.apply(set); try testing.expectEqual(@as(u32, 200), m.cell_height); try testing.expectEqual(@as(u32, 100), m.cell_baseline); try testing.expectEqual(@as(u32, 105), m.underline_position); try testing.expectEqual(@as(u32, 80), m.strikethrough_position); - try testing.expectEqual(@as(u32, 100), m.original_cell_height.?); + // Cursor height is separate from cell height and does not follow it. + try testing.expectEqual(@as(u32, 100), m.cursor_height); } test "Modifier: parse absolute" { diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 885ea277e9..92ab4d396a 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -1045,6 +1045,7 @@ test "coretext: metrics" { .overline_position = 0, .overline_thickness = 1, .box_thickness = 1, + .cursor_height = 17, }, ct_font.metrics); // Resize should change metrics @@ -1060,5 +1061,6 @@ test "coretext: metrics" { .overline_position = 0, .overline_thickness = 2, .box_thickness = 2, + .cursor_height = 34, }, ct_font.metrics); } diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index f5ec9e7ec1..bc503a3afb 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -906,6 +906,7 @@ test "color emoji" { .overline_position = 0, .overline_thickness = 0, .box_thickness = 0, + .cursor_height = 0, }, }); try testing.expectEqual(@as(u32, 24), glyph.height); @@ -952,6 +953,7 @@ test "metrics" { .overline_position = 0, .overline_thickness = 1, .box_thickness = 1, + .cursor_height = 17, }, ft_font.metrics); // Resize should change metrics @@ -967,6 +969,7 @@ test "metrics" { .overline_position = 0, .overline_thickness = 2, .box_thickness = 2, + .cursor_height = 34, }, ft_font.metrics); } diff --git a/src/font/sprite/Face.zig b/src/font/sprite/Face.zig index 83dfffefab..7c42fb3942 100644 --- a/src/font/sprite/Face.zig +++ b/src/font/sprite/Face.zig @@ -126,29 +126,20 @@ pub fn renderGlyph( }, .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_height, metrics.cursor_thickness, ); - // Keep the cursor centered in the cell if it's shorter. - g.offset_y += @intCast(dy); + // Cursors are drawn at their specified height + // and are centered vertically within the cell. + const cursor_height: i32 = @intCast(metrics.cursor_height); + const cell_height: i32 = @intCast(metrics.cell_height); + g.offset_y += @divTrunc(cell_height - cursor_height, 2); break :cursor g; },