this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 162 lines 5.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 FlexColumn = @This(); 9 10children: []const vxfw.FlexItem, 11 12pub fn widget(self: *const FlexColumn) vxfw.Widget { 13 return .{ 14 .userdata = @constCast(self), 15 .drawFn = typeErasedDrawFn, 16 }; 17} 18 19fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { 20 const self: *const FlexColumn = @ptrCast(@alignCast(ptr)); 21 return self.draw(ctx); 22} 23 24pub fn draw(self: *const FlexColumn, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { 25 std.debug.assert(ctx.max.height != null); 26 std.debug.assert(ctx.max.width != null); 27 if (self.children.len == 0) return vxfw.Surface.init(ctx.arena, self.widget(), ctx.min); 28 29 // Store the inherent size of each widget 30 const size_list = try ctx.arena.alloc(u16, self.children.len); 31 32 var layout_arena = std.heap.ArenaAllocator.init(ctx.arena); 33 34 const layout_ctx: vxfw.DrawContext = .{ 35 .min = .{ .width = 0, .height = 0 }, 36 .max = .{ .width = ctx.max.width, .height = null }, 37 .arena = layout_arena.allocator(), 38 .cell_size = ctx.cell_size, 39 }; 40 41 // Store the inherent size of each widget 42 var first_pass_height: u16 = 0; 43 var total_flex: u16 = 0; 44 for (self.children, 0..) |child, i| { 45 const surf = try child.widget.draw(layout_ctx); 46 first_pass_height += surf.size.height; 47 total_flex += child.flex; 48 size_list[i] = surf.size.height; 49 } 50 51 // We are done with the layout arena 52 layout_arena.deinit(); 53 54 // make our children list 55 var children: std.ArrayList(vxfw.SubSurface) = .empty; 56 57 // Draw again, but with distributed heights 58 var second_pass_height: u16 = 0; 59 var max_width: u16 = 0; 60 const remaining_space = ctx.max.height.? - first_pass_height; 61 for (self.children, 1..) |child, i| { 62 const inherent_height = size_list[i - 1]; 63 const child_height = if (child.flex == 0) 64 inherent_height 65 else if (i == self.children.len) 66 // If we are the last one, we just get the remainder 67 ctx.max.height.? - second_pass_height 68 else 69 inherent_height + (remaining_space * child.flex) / total_flex; 70 71 // Create a context for the child 72 const child_ctx = ctx.withConstraints( 73 .{ .width = 0, .height = child_height }, 74 .{ .width = ctx.max.width.?, .height = child_height }, 75 ); 76 const surf = try child.widget.draw(child_ctx); 77 78 try children.append(ctx.arena, .{ 79 .origin = .{ .col = 0, .row = second_pass_height }, 80 .surface = surf, 81 .z_index = 0, 82 }); 83 max_width = @max(max_width, surf.size.width); 84 second_pass_height += surf.size.height; 85 } 86 87 const size: vxfw.Size = .{ .width = max_width, .height = second_pass_height }; 88 return .{ 89 .size = size, 90 .widget = self.widget(), 91 .buffer = &.{}, 92 .children = children.items, 93 }; 94} 95 96test FlexColumn { 97 // Create child widgets 98 const Text = @import("Text.zig"); 99 // Will be height=1, width=3 100 const abc: Text = .{ .text = "abc" }; 101 const def: Text = .{ .text = "def" }; 102 const ghi: Text = .{ .text = "ghi" }; 103 const jklmno: Text = .{ .text = "jkl\nmno" }; 104 105 // Create the flex column 106 const flex_column: FlexColumn = .{ 107 .children = &.{ 108 .{ .widget = abc.widget(), .flex = 0 }, // flex=0 means we are our inherent size 109 .{ .widget = def.widget(), .flex = 1 }, 110 .{ .widget = ghi.widget(), .flex = 1 }, 111 .{ .widget = jklmno.widget(), .flex = 1 }, 112 }, 113 }; 114 115 // Boiler plate draw context 116 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 117 defer arena.deinit(); 118 vxfw.DrawContext.init(.unicode); 119 120 const flex_widget = flex_column.widget(); 121 const ctx: vxfw.DrawContext = .{ 122 .arena = arena.allocator(), 123 .min = .{}, 124 .max = .{ .width = 16, .height = 16 }, 125 .cell_size = .{ .width = 10, .height = 20 }, 126 }; 127 128 const surface = try flex_widget.draw(ctx); 129 // FlexColumn expands to max height and widest child 130 try std.testing.expectEqual(16, surface.size.height); 131 try std.testing.expectEqual(3, surface.size.width); 132 // We have four children 133 try std.testing.expectEqual(4, surface.children.len); 134 135 // We will track the row we are on to confirm the origins 136 var row: u16 = 0; 137 // First child has flex=0, it should be it's inherent height 138 try std.testing.expectEqual(1, surface.children[0].surface.size.height); 139 try std.testing.expectEqual(row, surface.children[0].origin.row); 140 // Add the child height each time 141 row += surface.children[0].surface.size.height; 142 // Let's do some math 143 // - We have 4 children to fit into 16 rows. 3 children will be 1 row tall, one will be 2 rows 144 // tall for a total height of 5 rows. 145 // - The first child is 1 row and no flex. The rest of the height gets distributed evenly among 146 // the remaining 3 children. The remainder height is 16 - 5 = 11, so each child should get 11 / 147 // 3 = 3 extra rows, and the last will receive the remainder 148 try std.testing.expectEqual(1 + 3, surface.children[1].surface.size.height); 149 try std.testing.expectEqual(row, surface.children[1].origin.row); 150 row += surface.children[1].surface.size.height; 151 152 try std.testing.expectEqual(1 + 3, surface.children[2].surface.size.height); 153 try std.testing.expectEqual(row, surface.children[2].origin.row); 154 row += surface.children[2].surface.size.height; 155 156 try std.testing.expectEqual(2 + 3 + 2, surface.children[3].surface.size.height); 157 try std.testing.expectEqual(row, surface.children[3].origin.row); 158} 159 160test "refAllDecls" { 161 std.testing.refAllDecls(@This()); 162}