this repo has no description
13
fork

Configure Feed

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

vxfw: add FlexRow widget

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