font: Apply `adjust-icon-height` to both large and small icons (#9160)

As pointed out in #9156, an unintended consequence of all the work to
get icon sizing right is that `adjust-icon-height` now only applies to
the small icons you get when the next cell is not whitespace. Large
icons are unaffected.

With this PR, `adjust-icon-height` affects the maximum height of every
symbol specifying the `.icon` constraint height, regardless of
constraint width. This includes most Nerd Font icons, but excludes emoji
and other unicode symbols, and also excludes terminal graphics-oriented
Nerd Font symbols such as Powerline symbols.

In the following screenshots, **Baseline** is without
`adjust-icon-height`, while **Before** and **After** are with
`adjust-icon-height = -25%`.

**Baseline**
<img width="711" height="95" alt="Screenshot 2025-10-11 at 23 28 20"
src="https://github.com/user-attachments/assets/7499db4d-75a4-4dbd-b107-8cb5849e31a3"
/>

**Before** (only small icons affected)
<img width="711" height="95" alt="Screenshot 2025-10-11 at 23 20 12"
src="https://github.com/user-attachments/assets/9afd9fbf-ef25-44cc-9d8e-c39a69875163"
/>


**After** (both small and large icons affected, but not emoji)
<img width="711" height="95" alt="Screenshot 2025-10-11 at 23 21 05"
src="https://github.com/user-attachments/assets/90999f59-3b43-4684-9c8e-2c3c1edd6d18"
/>
pull/9168/head
Daniel Wennberg 2025-10-12 07:31:54 -07:00 committed by GitHub
parent 47a8f8083d
commit 65f73f5d20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 22 deletions

View File

@ -412,16 +412,13 @@ pub const compatibility = std.StaticStringMap(
@"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.
/// A positive (negative) value will increase (decrease) the maximum icon
/// height. This may not affect all icons equally: the effect depends on whether
/// the default size of the icon is height-constrained, which in turn depends on
/// the aspect ratio of both the icon and your primary font.
///
/// This value only applies to icons that are constrained to a single cell by
/// neighboring characters. An icon that is free to spread across two cells
/// can always use up to the full line height of the primary font.
///
/// The default value is 2/3 times the height of capital letters in your primary
/// font plus 1/3 times the font's line height.
/// Certain icons designed for box drawing and terminal graphics, such as
/// Powerline symbols, are not affected by this option.
///
/// See the notes about adjustments in `adjust-cell-width`.
///

View File

@ -1228,7 +1228,8 @@ test "metrics" {
.overline_thickness = 1,
.box_thickness = 1,
.cursor_height = 17,
.icon_height = 12.24,
.icon_height = 16.784,
.icon_height_single = 12.24,
.face_width = 8.0,
.face_height = 16.784,
.face_y = -0.04,
@ -1248,7 +1249,8 @@ test "metrics" {
.overline_thickness = 2,
.box_thickness = 2,
.cursor_height = 34,
.icon_height = 24.48,
.icon_height = 33.568,
.icon_height_single = 24.48,
.face_width = 16.0,
.face_height = 33.568,
.face_y = -0.08,

View File

@ -38,6 +38,9 @@ cursor_height: u32,
/// The constraint height for nerd fonts icons.
icon_height: f64,
/// The constraint height for nerd fonts icons limited to a single cell width.
icon_height_single: f64,
/// The unrounded face width, used in scaling calculations.
face_width: f64,
@ -60,6 +63,7 @@ const Minimums = struct {
const cursor_thickness = 1;
const cursor_height = 1;
const icon_height = 1.0;
const icon_height_single = 1.0;
const face_height = 1.0;
const face_width = 1.0;
};
@ -251,8 +255,11 @@ pub fn calc(face: FaceMetrics) Metrics {
const underline_position = @round(top_to_baseline - face.underlinePosition());
const strikethrough_position = @round(top_to_baseline - face.strikethroughPosition());
// Same heuristic as the font_patcher script
const icon_height = (2 * cap_height + face_height) / 3;
// Same heuristic as the font_patcher script. We store icon_height
// separately from face_height such that modifiers can apply to the former
// without affecting the latter.
const icon_height = face_height;
const icon_height_single = (2 * cap_height + face_height) / 3;
var result: Metrics = .{
.cell_width = @intFromFloat(cell_width),
@ -267,6 +274,7 @@ pub fn calc(face: FaceMetrics) Metrics {
.box_thickness = @intFromFloat(underline_thickness),
.cursor_height = @intFromFloat(cell_height),
.icon_height = icon_height,
.icon_height_single = icon_height_single,
.face_width = face_width,
.face_height = face_height,
.face_y = face_y,
@ -328,6 +336,10 @@ pub fn apply(self: *Metrics, mods: ModifierSet) void {
}
}
},
inline .icon_height => {
self.icon_height = entry.value_ptr.apply(self.icon_height);
self.icon_height_single = entry.value_ptr.apply(self.icon_height_single);
},
inline else => |tag| {
@field(self, @tagName(tag)) = entry.value_ptr.apply(@field(self, @tagName(tag)));
@ -529,6 +541,7 @@ fn init() Metrics {
.box_thickness = 0,
.cursor_height = 0,
.icon_height = 0.0,
.icon_height_single = 0.0,
.face_width = 0.0,
.face_height = 0.0,
.face_y = 0.0,
@ -609,6 +622,48 @@ test "Metrics: adjust cell height larger" {
try testing.expectEqual(@as(u32, 100), m.cursor_height);
}
test "Metrics: adjust icon height by percentage" {
const testing = std.testing;
const alloc = testing.allocator;
var set: ModifierSet = .{};
defer set.deinit(alloc);
try set.put(alloc, .icon_height, .{ .percent = 0.75 });
var m: Metrics = init();
m.icon_height = 100.0;
m.icon_height_single = 80.0;
m.face_height = 100.0;
m.face_y = 1.0;
m.apply(set);
try testing.expectEqual(75.0, m.icon_height);
try testing.expectEqual(60.0, m.icon_height_single);
// Face metrics not affected
try testing.expectEqual(100.0, m.face_height);
try testing.expectEqual(1.0, m.face_y);
}
test "Metrics: adjust icon height by absolute pixels" {
const testing = std.testing;
const alloc = testing.allocator;
var set: ModifierSet = .{};
defer set.deinit(alloc);
try set.put(alloc, .icon_height, .{ .absolute = -5 });
var m: Metrics = init();
m.icon_height = 100.0;
m.icon_height_single = 80.0;
m.face_height = 100.0;
m.face_y = 1.0;
m.apply(set);
try testing.expectEqual(95.0, m.icon_height);
try testing.expectEqual(75.0, m.icon_height_single);
// Face metrics not affected
try testing.expectEqual(100.0, m.face_height);
try testing.expectEqual(1.0, m.face_y);
}
test "Modifier: parse absolute" {
const testing = std.testing;

View File

@ -216,11 +216,13 @@ pub const RenderOptions = struct {
};
pub const Height = enum {
/// Always use the full height of the cell for constraining this glyph.
/// Use the full line height of the primary face for
/// constraining this glyph.
cell,
/// When the constraint width is 1, use the "icon height" from the grid
/// metrics as the height. (When the constraint width is >1, the
/// constraint height is always the full cell height.)
/// Use the icon height from the grid metrics for
/// constraining this glyph. Unlike `cell`, the value of
/// this height depends on both the constraint width and the
/// affected by the `adjust-icon-height` config option.
icon,
};
@ -346,12 +348,14 @@ pub const RenderOptions = struct {
const target_width = pad_width_factor * metrics.face_width;
const target_height = pad_height_factor * switch (self.height) {
.cell => metrics.face_height,
// icon_height only applies with single-cell constraints.
// This mirrors font_patcher.
// Like font-patcher, the icon constraint height depends on the
// constraint width. Unlike font-patcher, the multi-cell
// icon_height may be different from face_height due to the
// `adjust-icon-height` config option.
.icon => if (multi_cell)
metrics.face_height
metrics.icon_height
else
metrics.icon_height,
metrics.icon_height_single,
};
var width_factor = target_width / group.width;
@ -528,7 +532,8 @@ test "Constraints" {
.box_thickness = 1,
.cursor_thickness = 1,
.cursor_height = 22,
.icon_height = 44.48 / 3.0,
.icon_height = 21.12,
.icon_height_single = 44.48 / 3.0,
.face_width = 9.6,
.face_height = 21.12,
.face_y = 0.2,