lib/ui/src/layout/input.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const arrange = @import("arrange");
 3 const abi = @import("../abi/root.zig");
 4 const style = @import("../style/root.zig");
 5 const tree = @import("../tree/root.zig");
 6 
 7 pub const Styles = union(enum) {
 8     records: []const style.Record,
 9     engine: *const style.Engine,
10 
11     pub fn count(self: Styles) usize {
12         return switch (self) {
13             .records => |records| records.len,
14             .engine => |engine| engine.count,
15         };
16     }
17 
18     pub fn record(self: Styles, index: u32) *const style.Record {
19         return switch (self) {
20             .records => |records| &records[index],
21             .engine => |engine| engine.record(index),
22         };
23     }
24 };
25 
26 pub const Input = struct {
27     view: tree.View,
28     styles: Styles,
29     rects: []arrange.Rect,
30 
31     pub fn indexed(self: *Input) arrange.IndexedInput {
32         std.debug.assert(self.view.nodes.len <= std.math.maxInt(u32));
33         std.debug.assert(self.styles.count() >= self.view.nodes.len);
34         std.debug.assert(self.rects.len >= self.view.nodes.len);
35         return .{
36             .context = self,
37             .count = @intCast(self.view.nodes.len),
38             .get = get,
39             .descendants = descendants,
40             .is_leaf = isLeaf,
41             .rects = self.rects,
42         };
43     }
44 
45     fn get(context: ?*anyopaque, index: u32) arrange.Node {
46         const self: *const Input = @ptrCast(@alignCast(context.?));
47         const node = self.view.nodes[index];
48         const record = self.styles.record(index);
49         var result = arrange.Node{
50             .id = index,
51             .style = record.layout,
52             .size = record.dimensions,
53             .measure_token = if (node.text != abi.text_absent) index else null,
54             .clip_x = abi.holds(node.flags, .clip) or abi.holds(node.flags, .scroll_x),
55             .clip_y = abi.holds(node.flags, .clip) or abi.holds(node.flags, .scroll_y),
56         };
57         if (abi.holds(node.flags, .solved)) {
58             const solved = self.solvedRoot(index);
59             const rect = self.view.solvedRectsOf(solved)[0];
60             result.size = .{ .width = rect.width, .height = rect.height };
61             result.style.constraints = .{};
62             result.style.flex_basis = null;
63             result.style.flex_grow = 0;
64             result.style.flex_shrink = 0;
65             result.measure_token = null;
66         }
67         return result;
68     }
69 
70     fn descendants(context: ?*anyopaque, index: u32) u32 {
71         const self: *const Input = @ptrCast(@alignCast(context.?));
72         return self.view.nodes[index].subtree_count;
73     }
74 
75     fn isLeaf(context: ?*anyopaque, index: u32) bool {
76         const self: *const Input = @ptrCast(@alignCast(context.?));
77         return abi.holds(self.view.nodes[index].flags, .solved);
78     }
79 
80     fn solvedRoot(self: *const Input, index: u32) abi.SolvedRoot {
81         var first: usize = 0;
82         var last: usize = self.view.solved_roots.len;
83         while (first < last) {
84             const middle = first + (last - first) / 2;
85             const root = self.view.solved_roots[middle];
86             if (root.node < index) {
87                 first = middle + 1;
88             } else {
89                 last = middle;
90             }
91         }
92         std.debug.assert(first < self.view.solved_roots.len);
93         std.debug.assert(self.view.solved_roots[first].node == index);
94         return self.view.solved_roots[first];
95     }
96 };