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 };