diff --git a/src/vxfw/Padding.zig b/src/vxfw/Padding.zig new file mode 100644 index 0000000..070a1b4 --- /dev/null +++ b/src/vxfw/Padding.zig @@ -0,0 +1,148 @@ +const std = @import("std"); +const vaxis = @import("../main.zig"); + +const Allocator = std.mem.Allocator; + +const vxfw = @import("vxfw.zig"); + +const Padding = @This(); +const PadValues = struct { + left: u16 = 0, + right: u16 = 0, + top: u16 = 0, + bottom: u16 = 0, +}; + +child: vxfw.Widget, +padding: PadValues = .{}, + +/// Vertical padding will be divided by 2 to approximate equal padding +pub fn all(padding: u16) PadValues { + return .{ + .left = padding, + .right = padding, + .top = padding / 2, + .bottom = padding / 2, + }; +} + +pub fn horizontal(padding: u16) PadValues { + return .{ + .left = padding, + .right = padding, + }; +} + +pub fn vertical(padding: u16) PadValues { + return .{ + .top = padding, + .bottom = padding, + }; +} + +pub fn widget(self: *const Padding) vxfw.Widget { + return .{ + .userdata = @constCast(self), + .eventHandler = typeErasedEventHandler, + .drawFn = typeErasedDrawFn, + }; +} + +fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { + const self: *const Padding = @ptrCast(@alignCast(ptr)); + return self.child.handleEvent(ctx, event); +} + +fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { + const self: *const Padding = @ptrCast(@alignCast(ptr)); + return self.draw(ctx); +} + +pub fn draw(self: *const Padding, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { + const pad = self.padding; + if (pad.left > 0 or pad.right > 0) + std.debug.assert(ctx.max.width != null); + if (pad.top > 0 or pad.bottom > 0) + std.debug.assert(ctx.max.height != null); + const inner_min: vxfw.Size = .{ + .width = ctx.min.width -| (pad.right + pad.left), + .height = ctx.min.height -| (pad.top + pad.bottom), + }; + + const max_width: ?u16 = if (ctx.max.width) |max| + max -| (pad.right + pad.left) + else + null; + const max_height: ?u16 = if (ctx.max.height) |max| + max -| (pad.top + pad.bottom) + else + null; + + const inner_max: vxfw.MaxSize = .{ + .width = max_width, + .height = max_height, + }; + + const child_surface = try self.child.draw(ctx.withConstraints(inner_min, inner_max)); + + const children = try ctx.arena.alloc(vxfw.SubSurface, 1); + children[0] = .{ + .surface = child_surface, + .z_index = 0, + .origin = .{ .row = pad.top, .col = pad.left }, + }; + + const size = .{ + .width = child_surface.size.width + (pad.right + pad.left), + .height = child_surface.size.height + (pad.top + pad.bottom), + }; + + // Create the padding surface + return .{ + .size = size, + .widget = self.widget(), + .buffer = &.{}, + .children = children, + }; +} + +test Padding { + const Text = @import("Text.zig"); + // Will be height=1, width=3 + const text: Text = .{ .text = "abc" }; + + const padding: Padding = .{ + .child = text.widget(), + .padding = horizontal(1), + }; + + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const ucd = try vaxis.Unicode.init(arena.allocator()); + vxfw.DrawContext.init(&ucd, .unicode); + + // Center expands to the max size. It must therefore have non-null max width and max height. + // These values are asserted in draw + const ctx: vxfw.DrawContext = .{ + .arena = arena.allocator(), + .min = .{}, + .max = .{ .width = 10, .height = 10 }, + }; + + const pad_widget = padding.widget(); + + const surface = try pad_widget.draw(ctx); + // Padding does not produce any drawable cells + try std.testing.expectEqual(0, surface.buffer.len); + // Padding has 1 child + try std.testing.expectEqual(1, surface.children.len); + const child = surface.children[0]; + // Padding is the child size + padding + try std.testing.expectEqual(child.surface.size.width + 2, surface.size.width); + try std.testing.expectEqual(0, child.origin.row); + try std.testing.expectEqual(1, child.origin.col); +} + +test "refAllDecls" { + std.testing.refAllDecls(@This()); +} diff --git a/src/vxfw/vxfw.zig b/src/vxfw/vxfw.zig index 43a60d4..201da17 100644 --- a/src/vxfw/vxfw.zig +++ b/src/vxfw/vxfw.zig @@ -16,6 +16,7 @@ pub const Center = @import("Center.zig"); pub const FlexColumn = @import("FlexColumn.zig"); pub const FlexRow = @import("FlexRow.zig"); pub const ListView = @import("ListView.zig"); +pub const Padding = @import("Padding.zig"); pub const RichText = @import("RichText.zig"); pub const Text = @import("Text.zig"); pub const TextField = @import("TextField.zig");