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 }