this repo has no description
13
fork

Configure Feed

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

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