From 86d9a04ece47f2309e4882c1ca3334fbba23ba3c Mon Sep 17 00:00:00 2001 From: Jaeseok Lee Date: Sat, 14 Mar 2026 23:37:14 +0900 Subject: [PATCH] config: add `equal` option to `window-padding-balance` 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. --- src/Surface.zig | 12 ++++---- src/config/Config.zig | 17 ++++++++++- src/renderer/size.zig | 68 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 4d66622e3..c948d4408 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -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); } diff --git a/src/config/Config.zig b/src/config/Config.zig index 278ffd7e2..856aa1df5 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -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, diff --git a/src/renderer/size.zig b/src/renderer/size.zig index d8b529c26..e9754d1f3 100644 --- a/src/renderer/size.zig +++ b/src/renderer/size.zig @@ -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.