From c515cb9b5f440db47527cedb0b2e92a3b1da8552 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Nov 2022 16:19:20 -0800 Subject: [PATCH 1/5] initial padding options --- src/Window.zig | 7 ++++++- src/config.zig | 21 +++++++++++++++++++++ src/renderer.zig | 1 + src/renderer/OpenGL.zig | 28 +++++++++++++++++----------- src/renderer/Options.zig | 21 +++++++++++++++++++++ src/renderer/size.zig | 8 ++++---- 6 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 src/renderer/Options.zig diff --git a/src/Window.zig b/src/Window.zig index 6d8b87f68..d03137016 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -260,7 +260,12 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { errdefer font_group.deinit(alloc); // Create our terminal grid with the initial window size - var renderer_impl = try Renderer.init(alloc, font_group); + var renderer_impl = try Renderer.init(alloc, .{ + .font_group = font_group, + .padding = .{ + .balance = config.@"window-padding-balance", + }, + }); errdefer renderer_impl.deinit(); renderer_impl.background = .{ .r = config.background.r, diff --git a/src/config.zig b/src/config.zig index 341b41bb8..796894d37 100644 --- a/src/config.zig +++ b/src/config.zig @@ -89,6 +89,27 @@ pub const Config = struct { /// keybind: Keybinds = .{}, + /// Window padding. This applies padding between the terminal cells and + /// the window border. The "x" option applies to the left and right + /// padding and the "y" option is top and bottom. The value is in points, + /// meaning that it will be scaled appropriately for screen DPI. + @"window-padding-x": u32 = 0, + @"window-padding-y": u32 = 0, + + /// The viewport dimensions are usually not perfectly divisible by + /// the cell size. In this case, some extra padding on the end of a + /// column and the bottom of the final row may exist. If this is true, + /// then this extra padding is automatically balanced between all four + /// edges to minimize imbalance on one side. If this is false, the top + /// left grid cell will always hug the edge with zero padding other than + /// what may be specified with the other "window-padding" options. + /// + /// If other "window-padding" fields are set and this is true, this will + /// still 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 = true, + /// Additional configuration files to read. @"config-file": RepeatableString = .{}, diff --git a/src/renderer.zig b/src/renderer.zig index 8d2a2fe40..f52cff302 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -14,6 +14,7 @@ pub usingnamespace @import("renderer/message.zig"); pub usingnamespace @import("renderer/size.zig"); pub const Metal = @import("renderer/Metal.zig"); pub const OpenGL = @import("renderer/OpenGL.zig"); +pub const Options = @import("renderer/Options.zig"); pub const Thread = @import("renderer/Thread.zig"); pub const State = @import("renderer/State.zig"); diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 076bcd2bb..fea033d9b 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -77,6 +77,9 @@ background: terminal.color.RGB, /// True if the window is focused focused: bool, +/// Padding options +padding: renderer.Options.Padding, + /// The raw structure that maps directly to the buffer sent to the vertex shader. /// This must be "extern" so that the field order is not reordered by the /// Zig compiler. @@ -146,7 +149,7 @@ const GPUCellMode = enum(u8) { } }; -pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL { +pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL { // Create the initial font shaper var shape_buf = try alloc.alloc(font.Shaper.Cell, 1); errdefer alloc.free(shape_buf); @@ -157,8 +160,8 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL { // Doesn't matter, any normal ASCII will do we're just trying to make // sure we use the regular font. const metrics = metrics: { - const index = (try font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; - const face = try font_group.group.faceFromIndex(index); + const index = (try options.font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; + const face = try options.font_group.group.faceFromIndex(index); break :metrics face.metrics; }; log.debug("cell dimensions={}", .{metrics}); @@ -248,12 +251,12 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL { try texbind.image2D( 0, .Red, - @intCast(c_int, font_group.atlas_greyscale.size), - @intCast(c_int, font_group.atlas_greyscale.size), + @intCast(c_int, options.font_group.atlas_greyscale.size), + @intCast(c_int, options.font_group.atlas_greyscale.size), 0, .Red, .UnsignedByte, - font_group.atlas_greyscale.data.ptr, + options.font_group.atlas_greyscale.data.ptr, ); } @@ -269,12 +272,12 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL { try texbind.image2D( 0, .RGBA, - @intCast(c_int, font_group.atlas_color.size), - @intCast(c_int, font_group.atlas_color.size), + @intCast(c_int, options.font_group.atlas_color.size), + @intCast(c_int, options.font_group.atlas_color.size), 0, .BGRA, .UnsignedByte, - font_group.atlas_color.data.ptr, + options.font_group.atlas_color.data.ptr, ); } @@ -289,13 +292,14 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !OpenGL { .vbo = vbo, .texture = tex, .texture_color = tex_color, - .font_group = font_group, + .font_group = options.font_group, .font_shaper = shaper, .cursor_visible = true, .cursor_style = .box, .background = .{ .r = 0, .g = 0, .b = 0 }, .foreground = .{ .r = 255, .g = 255, .b = 255 }, .focused = true, + .padding = options.padding, }; } @@ -952,7 +956,9 @@ fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void { // Determine if we need to pad the window. For "auto" padding, we take // the leftover amounts on the right/bottom that don't fit a full grid cell // and we split them equal across all boundaries. - const padding = renderer.Padding.balanced(dim, grid_size, self.cell_size); + const padding: renderer.Padding = if (self.padding.balance) + renderer.Padding.balanced(dim, grid_size, self.cell_size) + else .{}; const padded_dim = dim.subPadding(padding); log.debug("screen size padded={} screen={} grid={} cell={}", .{ padded_dim, dim, grid_size, self.cell_size }); diff --git a/src/renderer/Options.zig b/src/renderer/Options.zig new file mode 100644 index 000000000..f4e083537 --- /dev/null +++ b/src/renderer/Options.zig @@ -0,0 +1,21 @@ +//! The options that are used to configure a renderer. + +const font = @import("../font/main.zig"); + +/// The font group that should be used. +font_group: *font.GroupCache, + +/// Padding options for the viewport. +padding: Padding, + +pub const Padding = struct { + // Explicit padding options, in pixels. The windowing thread is + // expected to convert points to pixels for a given DPI. + top: u32 = 0, + bottom: u32 = 0, + right: u32 = 0, + left: u32 = 0, + + // Balance options + balance: bool = false, +}; diff --git a/src/renderer/size.zig b/src/renderer/size.zig index 849603941..c59ffb944 100644 --- a/src/renderer/size.zig +++ b/src/renderer/size.zig @@ -78,10 +78,10 @@ pub const GridSize = struct { /// The padding to add to a screen. pub const Padding = struct { - top: f32, - bottom: f32, - right: f32, - left: f32, + top: f32 = 0, + bottom: f32 = 0, + right: f32 = 0, + left: f32 = 0, /// Returns padding that balances the whitespace around the screen /// for the given grid and cell sizes. From d7d12d9469e400db3a29b0e6dd1d64265e784293 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Nov 2022 17:13:56 -0800 Subject: [PATCH 2/5] explicit padding works --- src/Window.zig | 21 ++++++++++++++++++--- src/cli_args.zig | 6 ++++++ src/renderer/OpenGL.zig | 31 ++++++++++++++++++++++--------- src/renderer/Options.zig | 6 ++---- src/renderer/size.zig | 10 ++++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index d03137016..1ea177465 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -82,6 +82,9 @@ io_thr: std.Thread, /// The dimensions of the grid in rows and columns. grid_size: renderer.GridSize, +/// Explicit padding due to configuration +padding: renderer.Padding, + /// The app configuration config: *const Config, @@ -259,10 +262,21 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { }); errdefer font_group.deinit(alloc); + // Convert our padding from points to pixels + const padding_x = (@intToFloat(f32, config.@"window-padding-x") * x_dpi) / 72; + const padding_y = (@intToFloat(f32, config.@"window-padding-y") * y_dpi) / 72; + const padding: renderer.Padding = .{ + .top = padding_y, + .bottom = padding_y, + .right = padding_x, + .left = padding_x, + }; + // Create our terminal grid with the initial window size var renderer_impl = try Renderer.init(alloc, .{ .font_group = font_group, .padding = .{ + .explicit = padding, .balance = config.@"window-padding-balance", }, }); @@ -284,7 +298,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { .width = window_size.width, .height = window_size.height, }; - const grid_size = renderer.GridSize.init(screen_size, renderer_impl.cell_size); + const grid_size = renderer_impl.gridSize(screen_size); // Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app // but is otherwise somewhat arbitrary. @@ -358,6 +372,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { .io_thread = io_thread, .io_thr = undefined, .grid_size = grid_size, + .padding = padding, .config = config, .imgui_ctx = if (!DevMode.enabled) {} else try imgui.Context.create(), @@ -565,13 +580,13 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { // overhead of inter-thread communication // Recalculate our grid size - win.grid_size.update(screen_size, win.renderer.cell_size); + win.grid_size = win.renderer.gridSize(screen_size); // Mail the IO thread _ = win.io_thread.mailbox.push(.{ .resize = .{ .grid_size = win.grid_size, - .screen_size = screen_size, + .screen_size = screen_size.subPadding(win.padding), }, }, .{ .forever = {} }); win.io_thread.wakeup.send() catch {}; diff --git a/src/cli_args.zig b/src/cli_args.zig index 97bae03c3..4167b6085 100644 --- a/src/cli_args.zig +++ b/src/cli_args.zig @@ -138,6 +138,12 @@ fn parseIntoField( 0, ), + u32 => try std.fmt.parseInt( + u32, + value orelse return error.ValueRequired, + 0, + ), + else => unreachable, }; diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index fea033d9b..41f025206 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -947,21 +947,34 @@ pub fn updateCell( return true; } +/// Returns the grid size for a given screen size. This is safe to call +/// on any thread. +pub fn gridSize(self: *OpenGL, screen_size: renderer.ScreenSize) renderer.GridSize { + return renderer.GridSize.init( + screen_size.subPadding(self.padding.explicit), + self.cell_size, + ); +} + /// Set the screen size for rendering. This will update the projection /// used for the shader so that the scaling of the grid is correct. fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void { // Recalculate the rows/columns. - const grid_size = renderer.GridSize.init(dim, self.cell_size); + const grid_size = self.gridSize(dim); - // Determine if we need to pad the window. For "auto" padding, we take - // the leftover amounts on the right/bottom that don't fit a full grid cell - // and we split them equal across all boundaries. - const padding: renderer.Padding = if (self.padding.balance) + // Apply our padding + const padding = self.padding.explicit.add(if (self.padding.balance) renderer.Padding.balanced(dim, grid_size, self.cell_size) - else .{}; + else .{}); const padded_dim = dim.subPadding(padding); - log.debug("screen size padded={} screen={} grid={} cell={}", .{ padded_dim, dim, grid_size, self.cell_size }); + log.debug("screen size padded={} screen={} grid={} cell={} padding={}", .{ + padded_dim, + dim, + grid_size, + self.cell_size, + self.padding.explicit, + }); // Update our LRU. We arbitrarily support a certain number of pages here. // We also always support a minimum number of caching in case a user @@ -997,8 +1010,8 @@ fn setScreenSize(self: *OpenGL, dim: renderer.ScreenSize) !void { // 2D orthographic projection with the full w/h math.ortho2d( -1 * padding.left, - @intToFloat(f32, padded_dim.width), - @intToFloat(f32, padded_dim.height), + @intToFloat(f32, padded_dim.width) + padding.right, + @intToFloat(f32, padded_dim.height) + padding.bottom, -1 * padding.top, ), ); diff --git a/src/renderer/Options.zig b/src/renderer/Options.zig index f4e083537..c313d91c6 100644 --- a/src/renderer/Options.zig +++ b/src/renderer/Options.zig @@ -1,6 +1,7 @@ //! The options that are used to configure a renderer. const font = @import("../font/main.zig"); +const renderer = @import("../renderer.zig"); /// The font group that should be used. font_group: *font.GroupCache, @@ -11,10 +12,7 @@ padding: Padding, pub const Padding = struct { // Explicit padding options, in pixels. The windowing thread is // expected to convert points to pixels for a given DPI. - top: u32 = 0, - bottom: u32 = 0, - right: u32 = 0, - left: u32 = 0, + explicit: renderer.Padding, // Balance options balance: bool = false, diff --git a/src/renderer/size.zig b/src/renderer/size.zig index c59ffb944..51a3b135c 100644 --- a/src/renderer/size.zig +++ b/src/renderer/size.zig @@ -112,6 +112,16 @@ pub const Padding = struct { .left = padding_left, }; } + + /// Add another padding to ths one + pub fn add(self: Padding, other: Padding) Padding { + return .{ + .top = self.top + other.top, + .bottom = self.bottom + other.bottom, + .right = self.right + other.right, + .left = self.left + other.left, + }; + } }; test "GridSize update exact" { From 860fbc3aee45e6e49fd9156e3913e9dd09f61288 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Nov 2022 17:25:35 -0800 Subject: [PATCH 3/5] padding needs to be sent to termio --- src/Window.zig | 3 ++- src/termio/Exec.zig | 7 +++++-- src/termio/Thread.zig | 2 +- src/termio/message.zig | 8 ++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index 1ea177465..a96cd73fb 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -586,7 +586,8 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { _ = win.io_thread.mailbox.push(.{ .resize = .{ .grid_size = win.grid_size, - .screen_size = screen_size.subPadding(win.padding), + .screen_size = screen_size, + .padding = win.padding, }, }, .{ .forever = {} }); win.io_thread.wakeup.send() catch {}; diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 8926ea671..cb7b8f6e6 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -231,13 +231,16 @@ pub fn resize( self: *Exec, grid_size: renderer.GridSize, screen_size: renderer.ScreenSize, + padding: renderer.Padding, ) !void { + const padded_size = screen_size.subPadding(padding); + // Update the size of our pty try self.pty.setSize(.{ .ws_row = @intCast(u16, grid_size.rows), .ws_col = @intCast(u16, grid_size.columns), - .ws_xpixel = @intCast(u16, screen_size.width), - .ws_ypixel = @intCast(u16, screen_size.height), + .ws_xpixel = @intCast(u16, padded_size.width), + .ws_ypixel = @intCast(u16, padded_size.height), }); // Update our cached grid size diff --git a/src/termio/Thread.zig b/src/termio/Thread.zig index e8db4d3ff..7c75d9603 100644 --- a/src/termio/Thread.zig +++ b/src/termio/Thread.zig @@ -172,7 +172,7 @@ fn drainMailbox(self: *Thread) !void { log.debug("mailbox message={}", .{message}); switch (message) { - .resize => |v| try self.impl.resize(v.grid_size, v.screen_size), + .resize => |v| try self.impl.resize(v.grid_size, v.screen_size, v.padding), .write_small => |v| try self.impl.queueWrite(v.data[0..v.len]), .write_stable => |v| try self.impl.queueWrite(v), .write_alloc => |v| { diff --git a/src/termio/message.zig b/src/termio/message.zig index 623c41b40..9c03cf1e6 100644 --- a/src/termio/message.zig +++ b/src/termio/message.zig @@ -12,8 +12,16 @@ const terminal = @import("../terminal/main.zig"); pub const Message = union(enum) { /// Resize the window. resize: struct { + /// The grid size for the given screen size with padding applied. grid_size: renderer.GridSize, + + /// The full screen (drawable) size. This does NOT include padding. + /// This should be sent on to the renderer. screen_size: renderer.ScreenSize, + + /// The padding, so that the terminal implementation can subtract + /// this to send to the pty. + padding: renderer.Padding, }, /// Write where the data fits in the union. From e6c7fd02147165f63a725b413ea811c8cd702162 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Nov 2022 17:35:10 -0800 Subject: [PATCH 4/5] metal: understand explicit padding --- src/renderer/Metal.zig | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 3985d7c6c..a565df99f 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -33,6 +33,9 @@ alloc: std.mem.Allocator, /// Current cell dimensions for this grid. cell_size: renderer.CellSize, +/// Explicit padding. +padding: renderer.Options.Padding, + /// True if the window is focused focused: bool, @@ -130,7 +133,7 @@ pub fn windowInit(window: glfw.Window) !void { // else up during actual initialization. } -pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal { +pub fn init(alloc: Allocator, options: renderer.Options) !Metal { // Initialize our metal stuff const device = objc.Object.fromId(MTLCreateSystemDefaultDevice()); const queue = device.msgSend(objc.Object, objc.sel("newCommandQueue"), .{}); @@ -150,8 +153,8 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal { // Doesn't matter, any normal ASCII will do we're just trying to make // sure we use the regular font. const metrics = metrics: { - const index = (try font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; - const face = try font_group.group.faceFromIndex(index); + const index = (try options.font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?; + const face = try options.font_group.group.faceFromIndex(index); break :metrics face.metrics; }; log.debug("cell dimensions={}", .{metrics}); @@ -200,12 +203,13 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal { // Initialize our shader (MTLLibrary) const library = try initLibrary(device, @embedFile("../shaders/cell.metal")); const pipeline_state = try initPipelineState(device, library); - const texture_greyscale = try initAtlasTexture(device, &font_group.atlas_greyscale); - const texture_color = try initAtlasTexture(device, &font_group.atlas_color); + const texture_greyscale = try initAtlasTexture(device, &options.font_group.atlas_greyscale); + const texture_color = try initAtlasTexture(device, &options.font_group.atlas_color); return Metal{ .alloc = alloc, .cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height }, + .padding = options.padding, .background = .{ .r = 0, .g = 0, .b = 0 }, .foreground = .{ .r = 255, .g = 255, .b = 255 }, .focused = true, @@ -224,7 +228,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal { }, // Fonts - .font_group = font_group, + .font_group = options.font_group, .font_shaper = font_shaper, // Metal stuff @@ -302,6 +306,15 @@ pub fn threadExit(self: *const Metal) void { // Metal requires no per-thread state. } +/// Returns the grid size for a given screen size. This is safe to call +/// on any thread. +pub fn gridSize(self: *Metal, screen_size: renderer.ScreenSize) renderer.GridSize { + return renderer.GridSize.init( + screen_size.subPadding(self.padding.explicit), + self.cell_size, + ); +} + /// Callback when the focus changes for the terminal this is rendering. pub fn setFocus(self: *Metal, focus: bool) !void { self.focused = focus; @@ -562,12 +575,14 @@ fn setScreenSize(self: *Metal, bounds: macos.graphics.Size) !void { }; // Recalculate the rows/columns. - const grid_size = renderer.GridSize.init(dim, self.cell_size); + const grid_size = self.gridSize(dim); // Determine if we need to pad the window. For "auto" padding, we take // the leftover amounts on the right/bottom that don't fit a full grid cell // and we split them equal across all boundaries. - const padding = renderer.Padding.balanced(dim, grid_size, self.cell_size); + const padding = self.padding.explicit.add(if (self.padding.balance) + renderer.Padding.balanced(dim, grid_size, self.cell_size) + else .{}); const padded_dim = dim.subPadding(padding); // Update our shaper @@ -585,8 +600,8 @@ fn setScreenSize(self: *Metal, bounds: macos.graphics.Size) !void { self.uniforms = .{ .projection_matrix = math.ortho2d( -1 * padding.left, - @intToFloat(f32, padded_dim.width), - @intToFloat(f32, padded_dim.height), + @intToFloat(f32, padded_dim.width) + padding.right, + @intToFloat(f32, padded_dim.height) + padding.bottom, -1 * padding.top, ), .cell_size = .{ self.cell_size.width, self.cell_size.height }, From 334743e8a7182fd7f233cbcf7c4a057700d7c7fe Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Nov 2022 17:41:15 -0800 Subject: [PATCH 5/5] Don't crash on huge padding, warn users if padding is absurd --- src/Window.zig | 8 ++++++++ src/config.zig | 5 +++++ src/renderer/size.zig | 8 ++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index a96cd73fb..ad1b9b836 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -581,6 +581,14 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void { // Recalculate our grid size win.grid_size = win.renderer.gridSize(screen_size); + if (win.grid_size.columns < 5 and (win.padding.left > 0 or win.padding.right > 0)) { + log.warn("WARNING: very small terminal grid detected with padding " ++ + "set. Is your padding reasonable?", .{}); + } + if (win.grid_size.rows < 2 and (win.padding.top > 0 or win.padding.bottom > 0)) { + log.warn("WARNING: very small terminal grid detected with padding " ++ + "set. Is your padding reasonable?", .{}); + } // Mail the IO thread _ = win.io_thread.mailbox.push(.{ diff --git a/src/config.zig b/src/config.zig index 796894d37..d7a60753e 100644 --- a/src/config.zig +++ b/src/config.zig @@ -93,6 +93,11 @@ pub const Config = struct { /// the window border. The "x" option applies to the left and right /// padding and the "y" option is top and bottom. The value is in points, /// meaning that it will be scaled appropriately for screen DPI. + /// + /// If this value is set too large, the screen will render nothing, because + /// the grid will be completely squished by the padding. It is up to you + /// as the user to pick a reasonable value. If you pick an unreasonable + /// value, a warning will appear in the logs. @"window-padding-x": u32 = 0, @"window-padding-y": u32 = 0, diff --git a/src/renderer/size.zig b/src/renderer/size.zig index 51a3b135c..a96ae160e 100644 --- a/src/renderer/size.zig +++ b/src/renderer/size.zig @@ -48,8 +48,8 @@ pub const ScreenSize = struct { /// Subtract padding from the screen size. pub fn subPadding(self: ScreenSize, padding: Padding) ScreenSize { return .{ - .width = self.width - @floatToInt(u32, padding.left + padding.right), - .height = self.height - @floatToInt(u32, padding.top + padding.bottom), + .width = self.width -| @floatToInt(u32, padding.left + padding.right), + .height = self.height -| @floatToInt(u32, padding.top + padding.bottom), }; } }; @@ -71,8 +71,8 @@ pub const GridSize = struct { /// Update the columns/rows for the grid based on the given screen and /// cell size. pub fn update(self: *GridSize, screen: ScreenSize, cell: CellSize) void { - self.columns = @floatToInt(Unit, @intToFloat(f32, screen.width) / cell.width); - self.rows = @floatToInt(Unit, @intToFloat(f32, screen.height) / cell.height); + self.columns = @max(1, @floatToInt(Unit, @intToFloat(f32, screen.width) / cell.width)); + self.rows = @max(1, @floatToInt(Unit, @intToFloat(f32, screen.height) / cell.height)); } };