config: add equal option to window-padding-balance (#11491)

Change `window-padding-balance` from `bool` to an enum with three
values:

- `false` - no balancing (default, unchanged)
- `true` - balance with vshift that caps top padding and shifts excess
to bottom (existing behavior, unchanged)
- `equal` - balance whitespace equally on all four sides

This gives users who prefer truly equal padding a way to opt in without
changing the default behavior.
This commit is contained in:
Mitchell Hashimoto
2026-03-14 22:04:23 -07:00
committed by GitHub
3 changed files with 81 additions and 16 deletions

View File

@@ -324,7 +324,7 @@ const DerivedConfig = struct {
window_padding_bottom: u32,
window_padding_left: u32,
window_padding_right: u32,
window_padding_balance: bool,
window_padding_balance: configpkg.Config.WindowPaddingBalance,
window_height: u32,
window_width: u32,
title: ?[:0]const u8,
@@ -536,8 +536,8 @@ pub fn init(
x_dpi,
y_dpi,
);
if (derived_config.window_padding_balance) {
size.balancePadding(explicit);
if (derived_config.window_padding_balance != .false) {
size.balancePadding(explicit, derived_config.window_padding_balance);
} else {
size.padding = explicit;
}
@@ -2462,11 +2462,11 @@ fn resize(self: *Surface, size: rendererpkg.ScreenSize) !void {
/// Recalculate the balanced padding if needed.
fn balancePaddingIfNeeded(self: *Surface) void {
if (!self.config.window_padding_balance) return;
if (self.config.window_padding_balance == .false) return;
const content_scale = try self.rt_surface.getContentScale();
const x_dpi = content_scale.x * font.face.default_dpi;
const y_dpi = content_scale.y * font.face.default_dpi;
self.size.balancePadding(self.config.scaledPadding(x_dpi, y_dpi));
self.size.balancePadding(self.config.scaledPadding(x_dpi, y_dpi), self.config.window_padding_balance);
}
/// Called to set the preedit state for character input. Preedit is used
@@ -3576,7 +3576,7 @@ pub fn contentScaleCallback(self: *Surface, content_scale: apprt.ContentScale) !
// Update our padding which is dependent on DPI. We only do this for
// unbalanced padding since balanced padding is not dependent on DPI.
if (!self.config.window_padding_balance) {
if (self.config.window_padding_balance == .false) {
self.size.padding = self.config.scaledPadding(x_dpi, y_dpi);
}

View File

@@ -1963,7 +1963,16 @@ keybind: Keybinds = .{},
/// apply. The other padding is applied first and may affect how many grid cells
/// actually exist, and this is applied last in order to balance the padding
/// given a certain viewport size and grid cell size.
@"window-padding-balance": bool = false,
///
/// Valid values are:
///
/// * `false` - No balancing is applied.
/// * `true` - Balance the padding, but cap the top padding to avoid
/// excessive space above the first row. Any excess is shifted to the
/// bottom.
/// * `equal` - Balance the padding equally on all sides without any
/// top-padding cap. (Available since: 1.4.0)
@"window-padding-balance": WindowPaddingBalance = .false,
/// The color of the padding area of the window. Valid values are:
///
@@ -5229,6 +5238,12 @@ pub const Fullscreen = enum(c_int) {
@"non-native-padded-notch",
};
pub const WindowPaddingBalance = enum {
false,
true,
equal,
};
pub const WindowPaddingColor = enum {
background,
extend,

View File

@@ -1,5 +1,6 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const configpkg = @import("../config.zig");
const font = @import("../font/main.zig");
const terminal = @import("../terminal/main.zig");
@@ -34,7 +35,11 @@ pub const Size = struct {
/// Set the padding to be balanced around the grid. The balanced
/// padding is calculated AFTER the explicit padding is taken
/// into account.
pub fn balancePadding(self: *Size, explicit: Padding) void {
pub fn balancePadding(
self: *Size,
explicit: Padding,
mode: configpkg.Config.WindowPaddingBalance,
) void {
// This ensure grid() does the right thing
self.padding = explicit;
@@ -45,14 +50,20 @@ pub const Size = struct {
self.cell,
);
// The top/bottom padding is interesting. Subjectively, lots of padding
// at the top looks bad. So instead of always being equal (like left/right),
// we force the top padding to be at most equal to the maximum left padding,
// which is the balanced explicit horizontal padding plus half a cell width.
const max_padding_left = (explicit.left + explicit.right + self.cell.width) / 2;
const vshift = self.padding.top -| max_padding_left;
self.padding.top -= vshift;
self.padding.bottom += vshift;
switch (mode) {
.false => unreachable,
.equal => {},
.true => {
// Cap the top padding to avoid excessive space above the
// first row. The maximum is the balanced explicit horizontal
// padding plus half a cell width. Any excess is shifted to
// the bottom.
const max_top = (explicit.left + explicit.right + self.cell.width) / 2;
const vshift = self.padding.top -| max_top;
self.padding.top -= vshift;
self.padding.bottom += vshift;
},
}
}
};
@@ -302,6 +313,45 @@ pub const Padding = struct {
}
};
test "Size.balancePadding equal distributes whitespace equally" {
const testing = std.testing;
// screen=1050x850, cell=10x20, explicit=4 each side
// grid: (1050-8)/10=104 cols, (850-8)/20=42 rows
// leftover: 1050-1040=10 horizontal, 850-840=10 vertical
// balanced: left=right=5, top=bottom=5
var size: Size = .{
.screen = .{ .width = 1050, .height = 850 },
.cell = .{ .width = 10, .height = 20 },
.padding = .{},
};
size.balancePadding(.{ .top = 4, .bottom = 4, .left = 4, .right = 4 }, .equal);
try testing.expectEqual(size.padding.left, size.padding.right);
try testing.expectEqual(size.padding.top, size.padding.bottom);
try testing.expect(size.padding.top > 0);
}
test "Size.balancePadding true shifts excess top to bottom" {
const testing = std.testing;
// screen=1090x1070, cell=20x40, explicit=0
// grid: 1090/20=54 cols, 1070/40=26 rows
// leftover: 1090-1080=10, 1070-1040=30
// balanced: left=right=5, top=bottom=15
// vshift cap: (0+0+20)/2=10, vshift=15-10=5
// result: top=10, bottom=20
var size: Size = .{
.screen = .{ .width = 1090, .height = 1070 },
.cell = .{ .width = 20, .height = 40 },
.padding = .{},
};
size.balancePadding(.{}, .true);
try testing.expectEqual(size.padding.left, size.padding.right);
try testing.expect(size.padding.top < size.padding.bottom);
try testing.expectEqual(@as(u32, 10), size.padding.top);
try testing.expectEqual(@as(u32, 20), size.padding.bottom);
}
test "Padding balanced on zero" {
// On some systems, our screen can be zero-sized for a bit, and we
// don't want to end up with negative padding.