lib/ui/src/abi/vocabulary.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const css = @import("css");
3
4 pub const Kind = enum(u16) {
5 box,
6 text,
7 image,
8 field,
9 canvas,
10 scroll,
11 layer,
12 };
13
14 /// `Role` has 46 values. `generic` is 0. The names follow WAI-ARIA 1.2 roles.
15 pub const Role = enum(u16) {
16 generic = 0,
17 article,
18 cell,
19 code,
20 columnheader,
21 document,
22 group,
23 heading,
24 img,
25 list,
26 listitem,
27 paragraph,
28 row,
29 rowheader,
30 separator,
31 table,
32 toolbar,
33 banner,
34 complementary,
35 contentinfo,
36 form,
37 main,
38 navigation,
39 region,
40 search,
41 button,
42 checkbox,
43 link,
44 menuitem,
45 option,
46 progressbar,
47 radio,
48 searchbox,
49 tab,
50 tabpanel,
51 textbox,
52 treeitem,
53 combobox,
54 listbox,
55 menu,
56 radiogroup,
57 tablist,
58 tree,
59 alert,
60 status,
61 dialog,
62 };
63
64 /// `Flag` names bit positions in `Node.flags`.
65 pub const Flag = enum(u4) {
66 focusable,
67 scroll_x,
68 scroll_y,
69 clip,
70 hidden,
71 presentational,
72 retained,
73 modal,
74 live,
75 selectable_text,
76 solved,
77 };
78
79 /// `State` names bit positions in `Node.state`, in the same order as `css.selector.states`.
80 /// A test holds that order.
81 pub const State = enum(u5) {
82 selected,
83 disabled,
84 expanded,
85 checked,
86 mixed,
87 pressed,
88 busy,
89 invalid,
90 required,
91 current,
92 };
93
94 pub const kind_count: usize = 7;
95 pub const role_count: usize = 46;
96 pub const flag_count: usize = 11;
97 pub const state_count: usize = 10;
98
99 /// `flag` returns the single bit for a `Flag`.
100 pub fn flag(value: Flag) u16 {
101 return @as(u16, 1) << @backingInt(value);
102 }
103
104 /// `state` returns the single bit for a `State`.
105 pub fn state(value: State) u32 {
106 return @as(u32, 1) << @backingInt(value);
107 }
108
109 /// `holds` reports whether a flags word has that flag's bit set.
110 pub fn holds(flags: u16, value: Flag) bool {
111 return flags & flag(value) != 0;
112 }
113
114 /// `active` reports whether a state word has that state's bit set.
115 pub fn active(states: u32, value: State) bool {
116 return states & state(value) != 0;
117 }
118
119 /// `flag_mask` has every defined flag bit set, 0x07FF.
120 pub const flag_mask: u16 = (@as(u16, 1) << flag_count) - 1;
121 /// `state_mask` has every defined state bit set, 0x03FF.
122 pub const state_mask: u32 = (@as(u32, 1) << state_count) - 1;
123
124 comptime {
125 std.debug.assert(@typeInfo(Kind).@"enum".field_names.len == kind_count);
126 std.debug.assert(@typeInfo(Role).@"enum".field_names.len == role_count);
127 std.debug.assert(@typeInfo(Flag).@"enum".field_names.len == flag_count);
128 std.debug.assert(@typeInfo(State).@"enum".field_names.len == state_count);
129 std.debug.assert(@backingInt(Role.generic) == 0);
130 std.debug.assert(state_count == css.selector.states.len);
131 }
132
133 test "the closed vocabularies hold the counts the envelope fixes" {
134 try std.testing.expectEqual(@as(usize, 7), @typeInfo(Kind).@"enum".field_names.len);
135 try std.testing.expectEqual(@as(usize, 46), @typeInfo(Role).@"enum".field_names.len);
136 try std.testing.expectEqual(@as(usize, 11), @typeInfo(Flag).@"enum".field_names.len);
137 try std.testing.expectEqual(@as(usize, 10), @typeInfo(State).@"enum".field_names.len);
138 try std.testing.expectEqual(@as(u16, 0x07FF), flag_mask);
139 try std.testing.expectEqual(@as(u32, 0x03FF), state_mask);
140 }
141
142 test "every state bit names the css published state at the same position" {
143 const names = @typeInfo(State).@"enum".field_names;
144 try std.testing.expectEqual(css.selector.states.len, names.len);
145 inline for (names, 0..) |name, index| {
146 const published = css.selector.states[index];
147 try std.testing.expectEqualStrings(@tagName(published), name);
148 const value: State = @fromBackingInt(@intCast(index));
149 try std.testing.expectEqual(@as(u32, 1) << @intCast(index), state(value));
150 try std.testing.expect(active(state(value), value));
151 }
152 }
153
154 test "a role resolves the name a css role selector interns" {
155 try std.testing.expectEqualStrings("img", @tagName(Role.img));
156 try std.testing.expectEqualStrings("region", @tagName(Role.region));
157 try std.testing.expectEqual(@as(u16, 0), @backingInt(Role.generic));
158 try std.testing.expect(!std.mem.eql(u8, @tagName(Role.dialog), "modal"));
159 }
160
161 test "a flag bit is one shift and never collides with another" {
162 var seen: u16 = 0;
163 inline for (@typeInfo(Flag).@"enum".field_names, 0..) |_, index| {
164 const value: Flag = @fromBackingInt(@intCast(index));
165 const mask = flag(value);
166 try std.testing.expectEqual(@as(u16, 0), seen & mask);
167 try std.testing.expect(holds(mask, value));
168 seen |= mask;
169 }
170 try std.testing.expectEqual(flag_mask, seen);
171 }