Merge branch 'main' into ssh-integration

This commit is contained in:
Jason Rayne
2025-07-07 10:01:22 -07:00
9 changed files with 85 additions and 37 deletions

View File

@ -396,6 +396,18 @@ pub const compatibility = std.StaticStringMap(
/// Thickness in pixels or percentage adjustment of box drawing characters.
/// See the notes about adjustments in `adjust-cell-width`.
@"adjust-box-thickness": ?MetricModifier = null,
/// Height in pixels or percentage adjustment of maximum height for nerd font icons.
///
/// Increasing this value will allow nerd font icons to be larger, but won't
/// necessarily force them to be. Decreasing this value will make nerd font
/// icons smaller.
///
/// The default value for the icon height is 1.2 times the height of capital
/// letters in your primary font, so something like -16.6% would make icons
/// roughly the same height as capital letters.
///
/// See the notes about adjustments in `adjust-cell-width`.
@"adjust-icon-height": ?MetricModifier = null,
/// The method to use for calculating the cell width of a grapheme cluster.
/// The default value is `unicode` which uses the Unicode standard to determine

View File

@ -1072,6 +1072,7 @@ test "metrics" {
.overline_thickness = 1,
.box_thickness = 1,
.cursor_height = 17,
.icon_height = 11,
}, c.metrics);
// Resize should change metrics
@ -1088,6 +1089,7 @@ test "metrics" {
.overline_thickness = 2,
.box_thickness = 2,
.cursor_height = 34,
.icon_height = 23,
}, c.metrics);
}

View File

@ -35,9 +35,8 @@ cursor_thickness: u32 = 1,
/// 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,
/// The constraint height for nerd fonts icons.
icon_height: u32,
/// Minimum acceptable values for some fields to prevent modifiers
/// from being able to, for example, cause 0-thickness underlines.
@ -50,6 +49,7 @@ const Minimums = struct {
const box_thickness = 1;
const cursor_thickness = 1;
const cursor_height = 1;
const icon_height = 1;
};
/// Metrics extracted from a font face, based on
@ -133,7 +133,7 @@ pub fn calc(face: FaceMetrics) Metrics {
// that the cell is large enough for the provided size, since we cast
// it to an integer later.
const cell_width = @ceil(face.cell_width);
const cell_height = @ceil(face.ascent - face.descent + face.line_gap);
const cell_height = @ceil(face.lineHeight());
// We split our line gap in two parts, and put half of it on the top
// of the cell and the other half on the bottom, so that our text never
@ -177,6 +177,17 @@ pub fn calc(face: FaceMetrics) Metrics {
(face.strikethrough_position orelse
ex_height * 0.5 + strikethrough_thickness * 0.5));
// The calculation for icon height in the nerd fonts patcher
// is two thirds cap height to one third line height, but we
// use an opinionated default of 1.2 * cap height instead.
//
// Doing this prevents fonts with very large line heights
// from having excessively oversized icons, and allows fonts
// with very small line heights to still have roomy icons.
//
// We do cap it at `cell_height` though for obvious reasons.
const icon_height = @min(cell_height, cap_height * 1.2);
var result: Metrics = .{
.cell_width = @intFromFloat(cell_width),
.cell_height = @intFromFloat(cell_height),
@ -189,6 +200,7 @@ pub fn calc(face: FaceMetrics) Metrics {
.overline_thickness = @intFromFloat(underline_thickness),
.box_thickness = @intFromFloat(underline_thickness),
.cursor_height = @intFromFloat(cell_height),
.icon_height = @intFromFloat(icon_height),
};
// Ensure all metrics are within their allowable range.
@ -214,11 +226,6 @@ 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 if not set.
if (self.original_cell_width == null) {
self.original_cell_width = self.cell_width;
}
// Set the new value
@field(self, @tagName(tag)) = new;
@ -432,6 +439,7 @@ fn init() Metrics {
.overline_thickness = 0,
.box_thickness = 0,
.cursor_height = 0,
.icon_height = 0,
};
}

View File

@ -449,6 +449,7 @@ pub const DerivedConfig = struct {
@"adjust-cursor-thickness": ?Metrics.Modifier,
@"adjust-cursor-height": ?Metrics.Modifier,
@"adjust-box-thickness": ?Metrics.Modifier,
@"adjust-icon-height": ?Metrics.Modifier,
@"freetype-load-flags": font.face.FreetypeLoadFlags,
/// Initialize a DerivedConfig. The config should be either a
@ -488,6 +489,7 @@ pub const DerivedConfig = struct {
.@"adjust-cursor-thickness" = config.@"adjust-cursor-thickness",
.@"adjust-cursor-height" = config.@"adjust-cursor-height",
.@"adjust-box-thickness" = config.@"adjust-box-thickness",
.@"adjust-icon-height" = config.@"adjust-icon-height",
.@"freetype-load-flags" = if (font.face.FreetypeLoadFlags != void) config.@"freetype-load-flags" else {},
// This must be last so the arena contains all our allocations
@ -634,6 +636,7 @@ pub const Key = struct {
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);
if (config.@"adjust-icon-height") |m| try set.put(alloc, .icon_height, m);
break :set set;
};

View File

@ -150,6 +150,9 @@ pub const RenderOptions = struct {
/// Maximum number of cells horizontally to use.
max_constraint_width: u2 = 2,
/// What to use as the height metric when constraining the glyph.
height: Height = .cell,
pub const Size = enum {
/// Don't change the size of this glyph.
none,
@ -176,6 +179,13 @@ pub const RenderOptions = struct {
center,
};
pub const Height = enum {
/// Use the full height of the cell for constraining this glyph.
cell,
/// Use the "icon height" from the grid metrics as the height.
icon,
};
/// The size and position of a glyph.
pub const GlyphSize = struct {
width: f64,
@ -189,35 +199,35 @@ pub const RenderOptions = struct {
pub fn constrain(
self: Constraint,
glyph: GlyphSize,
/// Width of one cell.
cell_width: f64,
/// Height of one cell.
cell_height: f64,
metrics: Metrics,
/// Number of cells horizontally available for this glyph.
constraint_width: u2,
) GlyphSize {
var g = glyph;
const available_width =
cell_width * @as(f64, @floatFromInt(
@min(
const available_width: f64 = @floatFromInt(
metrics.cell_width * @min(
self.max_constraint_width,
constraint_width,
),
));
);
const available_height: f64 = @floatFromInt(switch (self.height) {
.cell => metrics.cell_height,
.icon => metrics.icon_height,
});
const w = available_width -
self.pad_left * available_width -
self.pad_right * available_width;
const h = cell_height -
self.pad_top * cell_height -
self.pad_bottom * cell_height;
const h = available_height -
self.pad_top * available_height -
self.pad_bottom * available_height;
// Subtract padding from the bearings so that our
// alignment and sizing code works correctly. We
// re-add before returning.
g.x -= self.pad_left * available_width;
g.y -= self.pad_bottom * cell_height;
g.y -= self.pad_bottom * available_height;
switch (self.size_horizontal) {
.none => {},
@ -319,7 +329,16 @@ pub const RenderOptions = struct {
// Re-add our padding before returning.
g.x += self.pad_left * available_width;
g.y += self.pad_bottom * cell_height;
g.y += self.pad_bottom * available_height;
// If the available height is less than the cell height, we
// add half of the difference to center it in the full height.
//
// If necessary, in the future, we can adjust this to account
// for alignment, but that isn't necessary with any of the nf
// icons afaict.
const cell_height: f64 = @floatFromInt(metrics.cell_height);
g.y += (cell_height - available_height) / 2;
return g;
}

View File

@ -341,7 +341,7 @@ pub const Face = struct {
const metrics = opts.grid_metrics;
const cell_width: f64 = @floatFromInt(metrics.cell_width);
const cell_height: f64 = @floatFromInt(metrics.cell_height);
// const cell_height: f64 = @floatFromInt(metrics.cell_height);
const glyph_size = opts.constraint.constrain(
.{
@ -350,8 +350,7 @@ pub const Face = struct {
.x = rect.origin.x,
.y = rect.origin.y + @as(f64, @floatFromInt(metrics.cell_baseline)),
},
cell_width,
cell_height,
metrics,
opts.constraint_width,
);

View File

@ -395,7 +395,7 @@ pub const Face = struct {
const metrics = opts.grid_metrics;
const cell_width: f64 = @floatFromInt(metrics.cell_width);
const cell_height: f64 = @floatFromInt(metrics.cell_height);
// const cell_height: f64 = @floatFromInt(metrics.cell_height);
const glyph_x: f64 = f26dot6ToF64(glyph.*.metrics.horiBearingX);
const glyph_y: f64 = f26dot6ToF64(glyph.*.metrics.horiBearingY) - glyph_height;
@ -407,8 +407,7 @@ pub const Face = struct {
.x = glyph_x,
.y = glyph_y + @as(f64, @floatFromInt(metrics.cell_baseline)),
},
cell_width,
cell_height,
metrics,
opts.constraint_width,
);
@ -1058,6 +1057,7 @@ test "color emoji" {
.overline_thickness = 0,
.box_thickness = 0,
.cursor_height = 0,
.icon_height = 0,
}, .constraint_width = 2, .constraint = .{
.size_horizontal = .cover,
.size_vertical = .cover,

View File

@ -25,6 +25,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
.height = .icon,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
@ -285,7 +286,7 @@ pub fn getConstraint(cp: u21) Constraint {
0xe0d0...0xe0d1,
=> .{
.size_horizontal = .cover,
.size_vertical = .cover,
.size_vertical = .fit,
.align_horizontal = .start,
.align_vertical = .center,
},
@ -294,7 +295,7 @@ pub fn getConstraint(cp: u21) Constraint {
0xe0d5,
=> .{
.size_horizontal = .cover,
.size_vertical = .cover,
.size_vertical = .fit,
.align_horizontal = .center,
.align_vertical = .center,
},
@ -362,6 +363,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
},

View File

@ -180,16 +180,19 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
if "xy" in stretch:
s += " .size_horizontal = .stretch,\n"
s += " .size_vertical = .stretch,\n"
elif "!" in stretch:
elif "!" in stretch or "^" in stretch:
s += " .size_horizontal = .cover,\n"
s += " .size_vertical = .fit,\n"
elif "^" in stretch:
s += " .size_horizontal = .cover,\n"
s += " .size_vertical = .cover,\n"
else:
s += " .size_horizontal = .fit,\n"
s += " .size_vertical = .fit,\n"
# `^` indicates that scaling should fill
# the whole cell, not just the icon height.
if "^" not in stretch:
s += " .height = .icon,\n"
# There are two cases where we want to limit the constraint width to 1:
# - If there's a `1` in the stretch mode string.
# - If the stretch mode is `xy` and there's not an explicit `2`.