a modern tui library written in zig
at main 3.9 kB view raw
1const std = @import("std"); 2const vaxis = @import("../main.zig"); 3 4const Allocator = std.mem.Allocator; 5 6const vxfw = @import("vxfw.zig"); 7 8const Padding = @This(); 9const PadValues = struct { 10 left: u16 = 0, 11 right: u16 = 0, 12 top: u16 = 0, 13 bottom: u16 = 0, 14}; 15 16child: vxfw.Widget, 17padding: PadValues = .{}, 18 19/// Vertical padding will be divided by 2 to approximate equal padding 20pub fn all(padding: u16) PadValues { 21 return .{ 22 .left = padding, 23 .right = padding, 24 .top = padding / 2, 25 .bottom = padding / 2, 26 }; 27} 28 29pub fn horizontal(padding: u16) PadValues { 30 return .{ 31 .left = padding, 32 .right = padding, 33 }; 34} 35 36pub fn vertical(padding: u16) PadValues { 37 return .{ 38 .top = padding, 39 .bottom = padding, 40 }; 41} 42 43pub fn widget(self: *const Padding) vxfw.Widget { 44 return .{ 45 .userdata = @constCast(self), 46 .drawFn = typeErasedDrawFn, 47 }; 48} 49 50fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { 51 const self: *const Padding = @ptrCast(@alignCast(ptr)); 52 return self.draw(ctx); 53} 54 55pub fn draw(self: *const Padding, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { 56 const pad = self.padding; 57 if (pad.left > 0 or pad.right > 0) 58 std.debug.assert(ctx.max.width != null); 59 if (pad.top > 0 or pad.bottom > 0) 60 std.debug.assert(ctx.max.height != null); 61 const inner_min: vxfw.Size = .{ 62 .width = ctx.min.width -| (pad.right + pad.left), 63 .height = ctx.min.height -| (pad.top + pad.bottom), 64 }; 65 66 const max_width: ?u16 = if (ctx.max.width) |max| 67 max -| (pad.right + pad.left) 68 else 69 null; 70 const max_height: ?u16 = if (ctx.max.height) |max| 71 max -| (pad.top + pad.bottom) 72 else 73 null; 74 75 const inner_max: vxfw.MaxSize = .{ 76 .width = max_width, 77 .height = max_height, 78 }; 79 80 const child_surface = try self.child.draw(ctx.withConstraints(inner_min, inner_max)); 81 82 const children = try ctx.arena.alloc(vxfw.SubSurface, 1); 83 children[0] = .{ 84 .surface = child_surface, 85 .z_index = 0, 86 .origin = .{ .row = pad.top, .col = pad.left }, 87 }; 88 89 const size: vxfw.Size = .{ 90 .width = child_surface.size.width + (pad.right + pad.left), 91 .height = child_surface.size.height + (pad.top + pad.bottom), 92 }; 93 94 // Create the padding surface 95 return .{ 96 .size = size, 97 .widget = self.widget(), 98 .buffer = &.{}, 99 .children = children, 100 }; 101} 102 103test Padding { 104 const Text = @import("Text.zig"); 105 // Will be height=1, width=3 106 const text: Text = .{ .text = "abc" }; 107 108 const padding: Padding = .{ 109 .child = text.widget(), 110 .padding = horizontal(1), 111 }; 112 113 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 114 defer arena.deinit(); 115 vxfw.DrawContext.init(.unicode); 116 117 // Center expands to the max size. It must therefore have non-null max width and max height. 118 // These values are asserted in draw 119 const ctx: vxfw.DrawContext = .{ 120 .arena = arena.allocator(), 121 .min = .{}, 122 .max = .{ .width = 10, .height = 10 }, 123 .cell_size = .{ .width = 10, .height = 20 }, 124 }; 125 126 const pad_widget = padding.widget(); 127 128 const surface = try pad_widget.draw(ctx); 129 // Padding does not produce any drawable cells 130 try std.testing.expectEqual(0, surface.buffer.len); 131 // Padding has 1 child 132 try std.testing.expectEqual(1, surface.children.len); 133 const child = surface.children[0]; 134 // Padding is the child size + padding 135 try std.testing.expectEqual(child.surface.size.width + 2, surface.size.width); 136 try std.testing.expectEqual(0, child.origin.row); 137 try std.testing.expectEqual(1, child.origin.col); 138} 139 140test "refAllDecls" { 141 std.testing.refAllDecls(@This()); 142}