Skip to main content

compose_rust/
schema.rs

1//! Closed Rust-side schema and metadata used for Kotlin code generation.
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4pub struct EnumVariantSchema {
5    pub name: &'static str,
6    pub tag: u16,
7}
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum FieldType {
11    Float,
12    U32,
13    U64,
14    /// A `Paint`, either a `ColorRole` or a literal ARGB `Color`.
15    Paint,
16    /// A role enum, named so codegen can emit the matching Kotlin type.
17    Role(&'static str),
18    /// A word that is 0 or 1. Any other value is a protocol error, not a third state.
19    Bool,
20    /// An `f32` that has to be a number. NaN or an infinity is a protocol error, for a
21    /// value that would otherwise turn a whole subtree into nothing on one side only.
22    FiniteFloat,
23}
24
25/// Which half of which `u64` word of a modifier's value a field occupies.
26///
27/// Two `f32` share one `u64`: the low 32 bits hold the first value. Every modifier has the
28/// `first` and `second` words; the words after them exist only for a modifier whose
29/// schema entry gives it `extra_words`.
30#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31pub enum FieldSlot {
32    FirstLow,
33    FirstHigh,
34    First,
35    SecondLow,
36    SecondHigh,
37    Second,
38    ThirdLow,
39    ThirdHigh,
40    Third,
41    FourthLow,
42    FourthHigh,
43    Fourth,
44    FifthLow,
45    FifthHigh,
46    Fifth,
47    SixthLow,
48    SixthHigh,
49    Sixth,
50    SeventhLow,
51    SeventhHigh,
52    Seventh,
53    EighthLow,
54    EighthHigh,
55    Eighth,
56    NinthLow,
57    NinthHigh,
58    Ninth,
59    TenthLow,
60    TenthHigh,
61    Tenth,
62}
63
64impl FieldSlot {
65    /// Which word, counting from zero, and whether the field is that word's high half.
66    pub const fn word(self) -> (usize, bool) {
67        match self {
68            Self::FirstLow | Self::First => (0, false),
69            Self::FirstHigh => (0, true),
70            Self::SecondLow | Self::Second => (1, false),
71            Self::SecondHigh => (1, true),
72            Self::ThirdLow | Self::Third => (2, false),
73            Self::ThirdHigh => (2, true),
74            Self::FourthLow | Self::Fourth => (3, false),
75            Self::FourthHigh => (3, true),
76            Self::FifthLow | Self::Fifth => (4, false),
77            Self::FifthHigh => (4, true),
78            Self::SixthLow | Self::Sixth => (5, false),
79            Self::SixthHigh => (5, true),
80            Self::SeventhLow | Self::Seventh => (6, false),
81            Self::SeventhHigh => (6, true),
82            Self::EighthLow | Self::Eighth => (7, false),
83            Self::EighthHigh => (7, true),
84            Self::NinthLow | Self::Ninth => (8, false),
85            Self::NinthHigh => (8, true),
86            Self::TenthLow | Self::Tenth => (9, false),
87            Self::TenthHigh => (9, true),
88        }
89    }
90}
91
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub struct FieldSchema {
94    pub name: &'static str,
95    pub ty: FieldType,
96    pub slot: FieldSlot,
97}
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub struct VariantSchema {
101    pub name: &'static str,
102    pub tag: u16,
103    /// How many eight byte words this modifier's value takes beyond the two every
104    /// `SetModifier` record carries. The record is `28 + 8 * extra_words` bytes long, fixed
105    /// per tag, so a decoder knows the length from the tag before it reads the value.
106    pub extra_words: u8,
107    pub fields: &'static [FieldSchema],
108}
109
110/// The most words any modifier's value takes, the two every record has included.
111pub const MAX_MODIFIER_WORDS: usize = 10;
112
113/// The `extra_words` the schema gives a modifier tag, or `None` for a tag it does not list.
114pub const fn modifier_extra_words(tag: u16) -> Option<u8> {
115    let mut index = 0;
116    while index < MODIFIER_SCHEMA.len() {
117        if MODIFIER_SCHEMA[index].tag == tag {
118            return Some(MODIFIER_SCHEMA[index].extra_words);
119        }
120        index += 1;
121    }
122    None
123}
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub enum EventPayloadType {
127    None,
128    Text,
129    ProtocolError,
130    KeyDown,
131    Range,
132    /// One `f64`. Every value-carrying widget shares it, so the same concept has one
133    /// name on the wire whether the value counts days or slides between two ends.
134    Double,
135    /// Width, height, classes, and three safe-area insets.
136    WindowSize,
137    /// One `u16`: the tag of the design system the Renderer resolved the theme to.
138    DesignSystem,
139    /// A `u32` saying what was pressed (0 for the body, 1 or 2 for an action button), then
140    /// the notification's key as a string reference.
141    NotificationActivation,
142    /// One `u32`: the notification permission the Renderer last saw.
143    NotificationPermission,
144    /// One moment in a renderer-played animation: its id, what happened, the property and
145    /// slot it plays on, the iteration, the elapsed time and the frame time.
146    Animation,
147    /// One `u32`: whether the platform asks for reduced motion.
148    ReducedMotion,
149    /// One committed edit to a code editor's document: the version it produced, the range
150    /// it replaced in the document as it stood before, and the text that replaced it.
151    CodeChange,
152    /// A Host edit the Renderer would not apply: the request it answers, the version it
153    /// was written against, the version the document had reached, and the range as sent.
154    CodeEditRejected,
155    /// A pointer resting on, or leaving, a place in a code editor: the decoration under it,
156    /// where it is, and which of the two it was.
157    CodeHover,
158    /// The save shortcut, with the version of the document it was pressed on.
159    CodeSave,
160    /// The id the application gave one decoration, which was pressed or accepted.
161    Decoration,
162    /// One pointer happening inside a subscribed node: its kind, which pointer, where,
163    /// which buttons and modifiers, the wheel deltas and when. A fixed 72-byte record.
164    Pointer,
165    /// Two `f32` and an `i32`: the whole zoom factor, the system's text size, and the
166    /// application's zoom level.
167    Zoom,
168    /// Where a code editor's selection is: the document version and the two ends.
169    CodeSelection,
170    /// Which lines of a code editor are on screen, folded lines left out.
171    CodeVisibleRange,
172    /// One word, 0 or 1: whether a code editor holds the keyboard focus.
173    CodeFocus,
174    /// The line a fold starts on and whether it is now collapsed.
175    CodeFold,
176    /// One word, 0 or 1: whether an input method is composing in a code editor. The
177    /// composed text is never part of it.
178    CodeComposition,
179    /// One word, 0 or 1: whether a window is the key window.
180    WindowFocus,
181    /// Where a window's outer frame is and how large it is: `x`, `y`, `width`, `height`,
182    /// all `f32` in screen dp.
183    WindowMove,
184    /// A torn-off window being dragged: the phase, the pointer's screen position, and the
185    /// window under the pointer.
186    WindowDrag,
187    /// A dragged window passing over a subscribed node: the kind, the dragged window, and
188    /// the position inside the node.
189    WindowDragOver,
190    /// A window the platform or the user asked the application for: a request number, then
191    /// the `RestoreKey` as a string reference. The one window event that carries a string.
192    WindowRequest,
193    /// What became of one file dialog: the request number, the status, the filter in use
194    /// and the paths as one string reference.
195    FileDialogResult,
196    /// What one clipboard read found: the request number, the status word, the text's length
197    /// in bytes, and the text as one string reference.
198    ClipboardText,
199    /// The one answer to an input or output request the Host made of the Renderer: the
200    /// request number, an `IoStatus`, and the bytes it carries as one reference.
201    IoAnswer,
202    /// Files the Renderer holds handles for: the request number, the status, the filter in
203    /// use, how they were delivered, the first handle number, then the names (NUL
204    /// separated) and one kind letter per name, as two references.
205    FileHandles,
206    /// Where a scroll container is: the first visible item, the flags, then six `f64` in
207    /// physical pixels (offset, furthest offset and viewport, x before y). A fixed 72-byte
208    /// record.
209    ScrollChanged,
210    /// The size a node's size motion settled at: `width_dp` and `height_dp`, two `f32` in
211    /// dp. A fixed 24-byte record.
212    MotionFinished,
213    /// Where a code editor's caret is drawn: the line and the column it stands at, then
214    /// `x`, `y`, `width` and `height` as `f32` in the window's dp. A fixed 40-byte record.
215    CodeCaretRect,
216    /// The answer to one position query: the request number, a word that is 1 when the
217    /// position is on screen and 0 when it is not, then the rectangle as in
218    /// `CodeCaretRect`, all zero when there is none. A fixed 40-byte record.
219    CodePositionRect,
220    /// A pointer event over a code editor: the 72-byte `Pointer` record, then the line and
221    /// the column of the text under the pointer, both `u32::MAX` where it is over none. A
222    /// fixed 80-byte record.
223    CodePointer,
224    /// Every selection of a code editor, the primary first: the document version, then a
225    /// reference to 16-byte records behind the record (anchor line, anchor column, active
226    /// line, active column). A fixed 28-byte record and its selections.
227    CodeSelections,
228    /// What became of one grouped edit of a code editor: the request id, an
229    /// `EditGroupStatus` word, the version the group was written against, the version the
230    /// document is at now and the index of the edit that decided it (or, applied, how many
231    /// edits went in). A fixed 36-byte record.
232    CodeEditGroupResult,
233    /// One `u32`: whether the system appearance is light (0) or dark (1).
234    ColorScheme,
235}
236
237#[derive(Clone, Copy, Debug, Eq, PartialEq)]
238pub struct EventSchema {
239    pub name: &'static str,
240    pub tag: u16,
241    pub payload: EventPayloadType,
242}
243
244/// Canonical schema text. Variant order is wire-significant and must only be appended to.
245pub const SCHEMA_DESCRIPTOR: &str = concat!(
246    "compose-rust/v1;",
247    "widgets=Column,Row,Box,Text,TextField,Button,Spacer,LazyColumn,ScrollColumn,Image,Icon,Checkbox,RadioButton,Switch,Slider,ProgressIndicator,Divider,Card,Surface,Dialog,Menu,Tabs,TopAppBar,LazyRow,Tooltip,Canvas,DatePicker,TimePicker,Dropdown,Navigation,NavigationItem,Sheet,Scaffold,ScaffoldSlot,LazyGrid,FileDropTarget,ScrollRow,Chip,FloatingAction,Badge,SelectionContainer,SplitPane,CodeEditor,AbsoluteBox,Window,FlowRow,Popover;",
248    "properties=text,placeholder,enabled,multiline,on_click,on_value_change,on_submit,on_focus_lost,on_key_down,item_count,item_key,on_range_requested,type_role,font_size,font_weight,line_height,letter_spacing,color,text_align,max_lines,overflow,arrangement,spacing,space_role,alignment,variant,asset,checked,steps,determinate,circular,vertical,open,on_dismiss,selected_index,commands,value,min,max,icon,slot,columns,min_column_width,spans,on_files_entered,on_files_dropped,section,count,collapsible,button_kind,decorations,syntax_spans,tab_width,on_edit_rejected,on_hover,on_save,on_decoration_click,folds,gutter_decorations,on_selection_change,font,word_break,overflow_wrap,tab_size,absolute_size,soft_wrap,span_fonts,on_visible_range_change,on_editor_focus_change,on_fold_change,on_composition_change,on_pointer,pointer_kinds,cursor,user_select,island_zoom,window_title,window_size,window_min_size,window_resizable,window_chrome,window_title_bar,window_icon,window_position,window_restore_key,window_zoom_level,drag_from_pointer,on_window_close_requested,on_window_focus_changed,on_window_moved,on_window_drag,on_window_unavailable,on_window_closed,on_window_drag_over,tear_off_key,on_focus_gained,on_key_up,accessible_name,track_color,thumb_color,active_track_color,container_color,check_color,control_size,report_size,scrim,bare,horizontal_spacing,vertical_spacing,horizontal_alignment,vertical_alignment,max_items_in_each_row,placement,align,offset_dp,line_numbers,gutter_min_width_dp,gutter_padding_dp,text_inset_dp,code_editor_colors,on_code_metrics,on_code_scrolled,compact_navigation,on_scroll_changed,collapse_below,snap,max_height_fraction,font_feature_settings,on_motion_finished,on_edit_group_result,on_caret_rect,on_key_preview,on_code_pointer,radio_group,minimap,view_zones,word_wrap,content_scale,thumb_size;",
249    "modifiers=Empty,Padding,FillMaxWidth,FillMaxHeight,Width,Height,Size,Background,Clickable,PaddingRole,PaddingEach,Weight,Shape,ShapeRole,Border,Elevation,ObserveSize,Motion,Material,Offset,RequiredSize,BorderEach,CornerEach,Shadow,Clip,Alpha,Transform,Transform3D,MatchParentSize,WindowDrag,PressFeedback,Semantics,CornerCurve,BackgroundBrush;",
250    "keys=Enter,Other;",
251    "events=Clicked,TextChanged,TextSubmitted,FocusLost,ProtocolError,KeyDown,RangeRequested,ValueChanged,WindowSizeChanged,DesignSystemResolved,FilesEntered,FilesDropped,NotificationActivated,NotificationPermissionChanged,CodeChanged,CodeEditRejected,CodeHovered,CodeSaveRequested,DecorationActivated,AnimationEvent,ReducedMotionChanged,Pointer,ZoomChanged,SelectionChanged,VisibleRangeChanged,EditorFocusChanged,FoldChanged,CompositionChanged,WindowCloseRequested,WindowFocusChanged,WindowMoved,WindowDrag,WindowDragOver,WindowUnavailable,WindowClosed,WindowRequested,ApplicationReopen,FocusGained,KeyUp,FileDialogResult,CodeEditGroupResult,ClipboardText,CodeMetrics,CodeScrolled,IoAnswer,FileHandles,StorageLocation,ScrollChanged,MotionFinished,CaretRect,PositionRect,CodePointer,ColorSchemeChanged,SelectionsChanged;",
252    "windowsizeclasses=Compact,Medium,Expanded;",
253    "windowheightclasses=Compact,Medium,Expanded;",
254    "commands=Create,SetProp,SetModifier,Insert,Move,Remove,SetText,AppendText,SetTheme,SetWindow,RegisterAsset,ReleaseAsset,ShowMessage,PostNotification,WithdrawNotification,RequestNotificationPermission,EditCode,StartAnimation,ControlAnimation,SetSelection,RevealRange,SetFolded,SetWindowTheme,FocusWindow,RequestFocus,ShowFileDialog,SetOverlayTokens,EditCodeGroup,SetClipboardText,ReadClipboard,SetScrollOffset,StorageCall,FileCall,ReleaseFileHandle,ScrollTo,ScrollIntoView,QueryPositionRect,InsertSnippet,SetSelections;",
255    // The fields of the overlay tokens record in wire order. A field is only appended, and
256    // appending one changes the record's length, so it is part of what the hash covers.
257    "overlaytokens=sheet_corner_radius,dialog_corner_radius,popup_corner_radius,sheet_elevation,popup_elevation,dialog_elevation,sheet_padding,dialog_padding,popup_padding,scrim,toast_elevation,toast_corner_radius;",
258    // The fields of the message record in wire order, appended to in the same way.
259    "showmessage=handler_id,text,action,duration,vertical,horizontal,offset_dp"
260);
261
262const fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
263    let mut index = 0;
264    while index < bytes.len() {
265        hash ^= bytes[index] as u64;
266        hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
267        index += 1;
268    }
269    hash
270}
271
272const fn hash_enum_schema(mut hash: u64, schema: &[EnumVariantSchema]) -> u64 {
273    let mut index = 0;
274    while index < schema.len() {
275        hash = hash_bytes(hash, schema[index].name.as_bytes());
276        hash = hash_bytes(hash, &schema[index].tag.to_le_bytes());
277        index += 1;
278    }
279    hash
280}
281
282const fn schema_hash() -> u64 {
283    let mut hash = hash_bytes(0xcbf2_9ce4_8422_2325_u64, SCHEMA_DESCRIPTOR.as_bytes());
284    hash = hash_bytes(hash, crate::extensions::SCHEMA_DESCRIPTOR.as_bytes());
285    hash = hash_bytes(hash, crate::measure::SCHEMA_DESCRIPTOR.as_bytes());
286    hash = hash_enum_schema(hash, WIDGET_SCHEMA);
287    hash = hash_enum_schema(hash, PROPERTY_SCHEMA);
288    hash = hash_enum_schema(hash, KEY_SCHEMA);
289    hash = hash_enum_schema(hash, WINDOW_SIZE_CLASS_SCHEMA);
290    hash = hash_enum_schema(hash, WINDOW_HEIGHT_CLASS_SCHEMA);
291    let mut role_index = 0;
292    while role_index < ROLE_ENUM_SCHEMA.len() {
293        hash = hash_bytes(hash, ROLE_ENUM_SCHEMA[role_index].name.as_bytes());
294        hash = hash_enum_schema(hash, ROLE_ENUM_SCHEMA[role_index].variants);
295        role_index += 1;
296    }
297    let mut index = 0;
298    while index < MODIFIER_SCHEMA.len() {
299        let modifier = MODIFIER_SCHEMA[index];
300        hash = hash_bytes(hash, modifier.name.as_bytes());
301        hash = hash_bytes(hash, &modifier.tag.to_le_bytes());
302        hash = hash_bytes(hash, &[modifier.extra_words]);
303        let mut field_index = 0;
304        while field_index < modifier.fields.len() {
305            let field = modifier.fields[field_index];
306            hash = hash_bytes(hash, field.name.as_bytes());
307            hash = hash_bytes(
308                hash,
309                &[match field.ty {
310                    FieldType::Float => 1,
311                    FieldType::U32 => 2,
312                    FieldType::U64 => 3,
313                    FieldType::Paint => 4,
314                    FieldType::Role(_) => 5,
315                    FieldType::Bool => 6,
316                    FieldType::FiniteFloat => 7,
317                }],
318            );
319            if let FieldType::Role(role) = field.ty {
320                hash = hash_bytes(hash, role.as_bytes());
321            }
322            hash = hash_bytes(
323                hash,
324                &[match field.slot {
325                    FieldSlot::FirstLow => 0,
326                    FieldSlot::FirstHigh => 1,
327                    FieldSlot::First => 2,
328                    FieldSlot::SecondLow => 3,
329                    FieldSlot::SecondHigh => 4,
330                    FieldSlot::Second => 5,
331                    FieldSlot::ThirdLow => 6,
332                    FieldSlot::ThirdHigh => 7,
333                    FieldSlot::Third => 8,
334                    FieldSlot::FourthLow => 9,
335                    FieldSlot::FourthHigh => 10,
336                    FieldSlot::Fourth => 11,
337                    FieldSlot::FifthLow => 12,
338                    FieldSlot::FifthHigh => 13,
339                    FieldSlot::Fifth => 14,
340                    FieldSlot::SixthLow => 15,
341                    FieldSlot::SixthHigh => 16,
342                    FieldSlot::Sixth => 17,
343                    FieldSlot::SeventhLow => 18,
344                    FieldSlot::SeventhHigh => 19,
345                    FieldSlot::Seventh => 20,
346                    FieldSlot::EighthLow => 21,
347                    FieldSlot::EighthHigh => 22,
348                    FieldSlot::Eighth => 23,
349                    FieldSlot::NinthLow => 24,
350                    FieldSlot::NinthHigh => 25,
351                    FieldSlot::Ninth => 26,
352                    FieldSlot::TenthLow => 27,
353                    FieldSlot::TenthHigh => 28,
354                    FieldSlot::Tenth => 29,
355                }],
356            );
357            field_index += 1;
358        }
359        index += 1;
360    }
361    index = 0;
362    while index < crate::drawing::DRAW_COMMAND_SCHEMA.len() {
363        let command = crate::drawing::DRAW_COMMAND_SCHEMA[index];
364        hash = hash_bytes(hash, command.name.as_bytes());
365        hash = hash_bytes(hash, &command.tag.to_le_bytes());
366        let mut field_index = 0;
367        while field_index < command.fields.len() {
368            let field = command.fields[field_index];
369            hash = hash_bytes(hash, field.name.as_bytes());
370            hash = hash_bytes(hash, &[field.word]);
371            hash = hash_bytes(
372                hash,
373                &[match field.ty {
374                    crate::drawing::DrawFieldType::Float => 1,
375                    crate::drawing::DrawFieldType::U32 => 2,
376                    crate::drawing::DrawFieldType::Role(_) => 3,
377                }],
378            );
379            if let crate::drawing::DrawFieldType::Role(role) = field.ty {
380                hash = hash_bytes(hash, role.as_bytes());
381            }
382            field_index += 1;
383        }
384        index += 1;
385    }
386    index = 0;
387    while index < EVENT_SCHEMA.len() {
388        let event = EVENT_SCHEMA[index];
389        hash = hash_bytes(hash, event.name.as_bytes());
390        hash = hash_bytes(hash, &event.tag.to_le_bytes());
391        hash = hash_bytes(
392            hash,
393            &[match event.payload {
394                EventPayloadType::None => 0,
395                EventPayloadType::Text => 1,
396                EventPayloadType::ProtocolError => 2,
397                EventPayloadType::KeyDown => 3,
398                EventPayloadType::Range => 4,
399                EventPayloadType::Double => 5,
400                EventPayloadType::WindowSize => 6,
401                EventPayloadType::DesignSystem => 7,
402                EventPayloadType::NotificationActivation => 8,
403                EventPayloadType::NotificationPermission => 9,
404                EventPayloadType::Animation => 10,
405                EventPayloadType::ReducedMotion => 11,
406                EventPayloadType::CodeChange => 12,
407                EventPayloadType::CodeEditRejected => 13,
408                EventPayloadType::CodeHover => 14,
409                EventPayloadType::CodeSave => 15,
410                EventPayloadType::Decoration => 16,
411                EventPayloadType::Pointer => 17,
412                EventPayloadType::Zoom => 18,
413                EventPayloadType::CodeSelection => 19,
414                EventPayloadType::CodeVisibleRange => 20,
415                EventPayloadType::CodeFocus => 21,
416                EventPayloadType::CodeFold => 22,
417                EventPayloadType::CodeComposition => 23,
418                EventPayloadType::WindowFocus => 24,
419                EventPayloadType::WindowMove => 25,
420                EventPayloadType::WindowDrag => 26,
421                EventPayloadType::WindowDragOver => 27,
422                EventPayloadType::WindowRequest => 28,
423                EventPayloadType::FileDialogResult => 29,
424                EventPayloadType::ClipboardText => 30,
425                EventPayloadType::IoAnswer => 31,
426                EventPayloadType::FileHandles => 32,
427                EventPayloadType::ScrollChanged => 33,
428                EventPayloadType::MotionFinished => 34,
429                EventPayloadType::CodeCaretRect => 35,
430                EventPayloadType::CodePositionRect => 36,
431                EventPayloadType::CodePointer => 37,
432                EventPayloadType::CodeEditGroupResult => 38,
433                EventPayloadType::ColorScheme => 39,
434                EventPayloadType::CodeSelections => 40,
435            }],
436        );
437        index += 1;
438    }
439    hash = crate::code::hash_record_schema(hash, crate::code::DECORATION_RECORD);
440    hash = crate::code::hash_record_schema(hash, crate::code::SYNTAX_SPAN_RECORD);
441    hash = crate::code::hash_record_schema(hash, crate::code::FOLD_RECORD);
442    hash = crate::code::hash_record_schema(hash, crate::code::GUTTER_DECORATION_RECORD);
443    hash = crate::code::hash_record_schema(hash, crate::code::CODE_EDITOR_COLORS_RECORD);
444    hash = crate::code::hash_record_schema(hash, crate::code::VIEW_ZONE_RECORD);
445    hash
446}
447
448/// Stable handshake hash of the complete ordered schema metadata.
449pub const SCHEMA_HASH: u64 = schema_hash();
450pub const PROTOCOL_VERSION: u16 = 1;
451
452fn wire_name_eq(input: &str, schema_name: &str) -> bool {
453    input
454        .bytes()
455        .filter(|byte| *byte != b'_')
456        .map(|byte| byte.to_ascii_lowercase())
457        .eq(schema_name.bytes().map(|byte| byte.to_ascii_lowercase()))
458}
459
460macro_rules! define_wire_enum {
461    ($schema:ident, $name:ident { $($variant:ident = $tag:literal),+ $(,)? }) => {
462        define_wire_enum!($schema, $name { $($variant = $tag),+ } unlisted {});
463    };
464    // `unlisted` variants exist in Rust and travel as their tag, but are not in the schema
465    // table, so codegen mirrors no Kotlin enum entry for them and `TryFrom` does not
466    // produce them. The one there is, `TypeRole::None`, is tag 0, which every role
467    // decoder already reads as "no role", and an entry for it in the Kotlin enum would
468    // shift every ordinal the Renderer indexes its token tables by.
469    (
470        $schema:ident,
471        $name:ident { $($variant:ident = $tag:literal),+ $(,)? }
472        unlisted { $($extra:ident = $extra_tag:literal),* $(,)? }
473    ) => {
474        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
475        #[repr(u16)]
476        pub enum $name {
477            $($extra = $extra_tag,)*
478            $($variant = $tag),+
479        }
480
481        pub const $schema: &[EnumVariantSchema] = &[
482            $(EnumVariantSchema { name: stringify!($variant), tag: $tag }),+
483        ];
484
485        impl From<$name> for u16 {
486            fn from(value: $name) -> Self {
487                value as Self
488            }
489        }
490
491        impl TryFrom<u16> for $name {
492            type Error = ();
493
494            // Spelled `()` rather than `Self::Error`: a `ColorRole::Error` variant would
495            // otherwise make the associated item ambiguous.
496            fn try_from(value: u16) -> Result<Self, ()> {
497                match value {
498                    $($tag => Ok(Self::$variant),)+
499                    _ => Err(()),
500                }
501            }
502        }
503
504        impl $name {
505            // The unit error is the wire's own convention: a name either is a variant or is
506            // not, and the caller reports which name it was.
507            #[allow(dead_code, clippy::result_unit_err)]
508            #[doc(hidden)]
509            #[allow(clippy::result_unit_err)]
510            pub fn from_name(value: &str) -> Result<Self, ()> {
511                $(if wire_name_eq(value, stringify!($variant)) {
512                    return Ok(Self::$variant);
513                })+
514                Err(())
515            }
516        }
517    };
518}
519
520crate::extensions::define_widget_schema_with_extensions!(define_wire_enum; WIDGET_SCHEMA, WidgetKind {
521    Column = 1,
522    Row = 2,
523    Box = 3,
524    Text = 4,
525    TextField = 5,
526    Button = 6,
527    Spacer = 7,
528    LazyColumn = 8,
529    ScrollColumn = 9,
530    Image = 10,
531    Icon = 11,
532    // The selection controls and the indicators. Each one emits a state and a role and
533    // nothing about how it is drawn: the tick, the track, the thumb and the sweep of an
534    // indeterminate bar are the design system's.
535    Checkbox = 12,
536    RadioButton = 13,
537    Switch = 14,
538    Slider = 15,
539    ProgressIndicator = 16,
540    Divider = 17,
541    Card = 18,
542    Surface = 19,
543    Dialog = 20,
544    Menu = 21,
545    Tabs = 22,
546    TopAppBar = 23,
547    LazyRow = 24,
548    Tooltip = 25,
549    // Drawing commands instead of child nodes. The command list is its only property.
550    Canvas = 26,
551    DatePicker = 27,
552    TimePicker = 28,
553    Dropdown = 29,
554    // One declaration, three presentations. The Renderer picks a bottom bar, a rail or a
555    // permanent drawer from the width it has already measured, so the same tree looks
556    // native on a phone and on a desktop without the Host branching on the size class.
557    Navigation = 30,
558    // One destination. Its label and its icon are properties rather than children,
559    // because a child tree would fix the arrangement the presentations need to differ in.
560    NavigationItem = 31,
561    // A temporary surface that slides in from an edge of the screen. Which edge is the
562    // Renderer's decision, for the same reason the navigation presentation is.
563    Sheet = 32,
564    // The screen's frame. The application fills slots and the Renderer decides what each
565    // one becomes on this platform and at this width, which is the same decision
566    // `Navigation` already makes for itself, widened to the whole frame.
567    Scaffold = 33,
568    // One filled slot. A wrapper rather than a property on the slot's own root, because
569    // an application may put any tree in a slot and the Renderer has to know which slot
570    // it is looking at without reading into it.
571    ScaffoldSlot = 34,
572    // A lazy grid. The windowing protocol is the list's, unchanged: the Renderer asks for a
573    // range of items and the Host materialises exactly that range. What the grid adds is
574    // that the Renderer rounds the range it asks for to whole rows, because a row is what
575    // it lays out, and how many items a row holds is its own decision when the columns
576    // were given as a minimum width rather than a count.
577    LazyGrid = 35,
578    // A place files may be dropped. Being this widget is the willingness: a node that is
579    // not one is never offered as a target, so the platform shows no drop cursor over it
580    // and nothing is reported.
581    FileDropTarget = 36,
582    // The whole content with a horizontal scroll, which is a ScrollColumn turned on its
583    // side. Every child is materialised and the scroll position is the Renderer's, so
584    // nothing about scrolling crosses the boundary; a long horizontal list is a LazyRow.
585    ScrollRow = 37,
586    // A small token that is chosen or filters. It carries its label and whether it is
587    // chosen, and the chosen state is the Host's: it changes only because the Host's own
588    // click handler changed it. The shape is the design system's.
589    Chip = 38,
590    // The one action a screen is about. It carries an icon, a label and a click. Where it
591    // goes and what it is drawn as is the design system's: a disc floating over the page
592    // is one system's answer, a plus at the trailing end of the bar is another's, and an
593    // accent button at the head of the command bar is a third.
594    FloatingAction = 39,
595    // A small mark on something else that says how many, that there is something new, or
596    // one short word. With one child it is attached to that child; with none it stands on
597    // its own, at the end of a row. Where it sits on the child, its shape, and how a large
598    // count is shortened are the design system's: nothing here can ask for a corner or a
599    // ceiling.
600    Badge = 40,
601    // A region whose text can be dragged over and copied, as one selection across every
602    // Text inside it. Text outside one cannot be selected. The selection and the copy are
603    // the Renderer's entirely, so nothing about either crosses the boundary.
604    SelectionContainer = 41,
605    // Two panes side by side, a side pane and a body, with a divider the user drags to
606    // change the side pane's width. Exactly two children, the side pane first.
607    //
608    // The drag is the Renderer's, like a scroll position: nothing crosses the boundary
609    // while it moves, and the width it ends on is reported once through the value change
610    // event. How the divider looks and how far its grip reaches, the side pane's default
611    // width, the body's minimum, and whether a narrow place shows the two panes side by
612    // side, with the side pane laid over the body, or one at a time, are the design
613    // system's answers.
614    SplitPane = 42,
615    // Several lines of code that the reader edits. The text, the caret, the selection,
616    // the scroll position and the undo history are the Renderer's, the way a text field's
617    // are, and the Host is told about each committed edit with the version it produced.
618    // What the Host adds on top is said as ranges: colour runs for the syntax, and
619    // decorations for diagnostics, lenses, hover anchors and inline suggestions. How any
620    // of it is drawn, from the gutter to the weight of an underline, is the design
621    // system's.
622    CodeEditor = 43,
623    // Children laid where the Host already put them. Each child is placed at its own
624    // `Offset`, relative to this box, and no child moves or sizes another: the layout was
625    // computed on the Host (an HTML engine, for instance) and laying it out a second time
626    // here would only be a second answer that can disagree with the first. Later children
627    // draw over earlier ones, so a stacking order is the child order.
628    AbsoluteBox = 44,
629    // A platform window, a child of the root and only of the root. The children of a
630    // `Window` are that window's content. Putting one anywhere else, or moving a node from
631    // one window into another, is a protocol error: Compose keeps one composition per
632    // window and has no way to hand a node from one to the next.
633    Window = 45,
634    // A row that wraps. Children are measured at their own width and placed one after
635    // another on a line; a child that does not fit in what is left of the line starts the
636    // next one. A child wider than the whole line has the line to itself and is measured
637    // at the line's width, so nothing runs out of the row.
638    FlowRow = 46,
639    // A floating box of any content attached to an anchor. The first child is the anchor
640    // and is always drawn; the rest is the content, drawn only while the Host says it is
641    // open. A `Menu` is the same shape with entries in it and a place the design system
642    // fixes; here the Host names the side and the edge, and the Renderer keeps the box
643    // inside the window by flipping it to the other side or sliding it along the edge.
644    // 46 is held for a widget on another branch.
645    Popover = 47,
646});
647
648#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
649#[repr(u8)]
650pub enum LoopMode {
651    #[default]
652    Renderer = 0,
653    Platform = 1,
654}
655
656#[derive(Clone, Copy, Debug, Eq, PartialEq)]
657pub struct Selection {
658    pub start: u32,
659    pub end: u32,
660}
661
662// Compose key identities supported by the M0 schema.
663define_wire_enum!(KEY_SCHEMA, Key {
664    Enter = 1,
665    // Any key the enum does not name. The event's `code` and `text` say which one.
666    Other = 2,
667    // Keys a menu, a list or an overlay acts on. Appended only: a tag never moves, so an
668    // older Renderer that sends `Other` for these still decodes.
669    Escape = 3,
670    Tab = 4,
671    Backspace = 5,
672    Delete = 6,
673    ArrowUp = 7,
674    ArrowDown = 8,
675    ArrowLeft = 9,
676    ArrowRight = 10,
677    Home = 11,
678    End = 12,
679    PageUp = 13,
680    PageDown = 14,
681    // The function keys and the key that opens a context menu, appended after the rest.
682    F1 = 15,
683    F2 = 16,
684    F3 = 17,
685    F4 = 18,
686    F5 = 19,
687    F6 = 20,
688    F7 = 21,
689    F8 = 22,
690    F9 = 23,
691    F10 = 24,
692    F11 = 25,
693    F12 = 26,
694    ContextMenu = 27,
695});
696
697define_wire_enum!(WINDOW_SIZE_CLASS_SCHEMA, WindowSizeClass {
698    Compact = 0,
699    Medium = 1,
700    Expanded = 2,
701});
702
703impl WindowSizeClass {
704    /// The width in dp at which `Medium` begins, and the width at which `Expanded` does.
705    ///
706    /// These are the Material 3 window size class boundaries. Using different numbers
707    /// would make the Compose components adapt at one width and the Rust layout branch at
708    /// another, and that mismatch is visible on screen.
709    pub const MEDIUM_MIN_WIDTH_DP: f32 = 600.0;
710    pub const EXPANDED_MIN_WIDTH_DP: f32 = 840.0;
711
712    /// The class a window of this width belongs to. Height does not take part.
713    pub fn from_width_dp(width_dp: f32) -> Self {
714        if width_dp >= Self::EXPANDED_MIN_WIDTH_DP {
715            Self::Expanded
716        } else if width_dp >= Self::MEDIUM_MIN_WIDTH_DP {
717            Self::Medium
718        } else {
719            Self::Compact
720        }
721    }
722}
723
724define_wire_enum!(WINDOW_HEIGHT_CLASS_SCHEMA, WindowHeightClass {
725    Compact = 0,
726    Medium = 1,
727    Expanded = 2,
728});
729
730impl WindowHeightClass {
731    /// The height in dp at which `Medium` begins, and the height at which `Expanded` does.
732    ///
733    /// The same published table the width boundaries came from, so a layout that folds
734    /// when it runs out of vertical room folds at the height Compose's own components
735    /// treat as short.
736    pub const MEDIUM_MIN_HEIGHT_DP: f32 = 480.0;
737    pub const EXPANDED_MIN_HEIGHT_DP: f32 = 900.0;
738
739    /// The class a window of this height belongs to. Width does not take part.
740    pub fn from_height_dp(height_dp: f32) -> Self {
741        if height_dp >= Self::EXPANDED_MIN_HEIGHT_DP {
742            Self::Expanded
743        } else if height_dp >= Self::MEDIUM_MIN_HEIGHT_DP {
744            Self::Medium
745        } else {
746            Self::Compact
747        }
748    }
749}
750
751/// A role enum that codegen mirrors into Kotlin.
752#[derive(Clone, Copy, Debug, Eq, PartialEq)]
753pub struct RoleEnumSchema {
754    pub name: &'static str,
755    pub variants: &'static [EnumVariantSchema],
756}
757
758// An opaque ARGB colour. Gradients and image brushes are deliberately absent: they do not
759// fit the two words a modifier variant has, and they would need resources to cross the
760// boundary, which the fixed-layout records cannot carry.
761#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
762#[repr(transparent)]
763pub struct Color(pub u32);
764
765impl Color {
766    /// An opaque colour from a 24-bit `0xRRGGBB` value.
767    pub const fn rgb(value: u32) -> Self {
768        Self(0xff00_0000 | (value & 0x00ff_ffff))
769    }
770
771    /// A colour from a full 32-bit `0xAARRGGBB` value.
772    pub const fn argb(value: u32) -> Self {
773        Self(value)
774    }
775
776    pub const fn to_argb(self) -> u32 {
777        self.0
778    }
779}
780
781// Semantic colour slots. The design system resolves them, inside the Renderer.
782define_wire_enum!(COLOR_ROLE_SCHEMA, ColorRole {
783    Primary = 1,
784    OnPrimary = 2,
785    Secondary = 3,
786    OnSecondary = 4,
787    Surface = 5,
788    OnSurface = 6,
789    SurfaceVariant = 7,
790    OnSurfaceVariant = 8,
791    Background = 9,
792    OnBackground = 10,
793    Outline = 11,
794    OutlineVariant = 12,
795    Error = 13,
796    OnError = 14,
797    // The layer a panel is made of: a thing raised off the page, holding the page's own
798    // reading ink. It exists because `Surface` cannot do that job everywhere. Material 3
799    // gives `Surface` and `Background` one value on purpose and expresses depth through
800    // tonal containers instead, so a panel filled with `Surface` on a page of `Background`
801    // is drawn, in the right colour, and is invisible. `SurfaceVariant` is not the answer
802    // either: its ink pair is the muted secondary ink, so a panel holding primary content
803    // would have to set that content half faded. This is the one role every design system
804    // promises to make visible against the page, and `OnSurface` is what reads on it.
805    SurfaceContainer = 15,
806    // The third accent, and a quiet fill for each of the three.
807    //
808    // Two accents and a page say a button, a bar and a heading. They cannot say a grid of
809    // subject tiles where the subject is the colour, a mood picker, or a panel of costs
810    // beside a panel of totals. Those screens need fills that read as relatives of one
811    // another, are not reading surfaces, and are quiet enough that body text sits on them.
812    //
813    // A container is a colour in the table rather than its accent at a lower opacity.
814    // Opacity only means something once you know what is behind it, and a role has to
815    // answer before anyone knows that. So each container is its own value and carries its
816    // own ink, held to the body-text bound rather than the label bound, because the reason
817    // it exists is that paragraphs land on it.
818    //
819    // The three containers are not required to be told apart from each other. Material 3's
820    // baseline primary and secondary containers are neighbouring tones of one palette, and
821    // that is its published scheme, not a mistake. A caller who needs three fills that
822    // separate at a glance reaches for the tertiary pair.
823    Tertiary = 16,
824    OnTertiary = 17,
825    PrimaryContainer = 18,
826    OnPrimaryContainer = 19,
827    SecondaryContainer = 20,
828    OnSecondaryContainer = 21,
829    TertiaryContainer = 22,
830    OnTertiaryContainer = 23,
831    // What a highlighter paints code with. A conversation shows code blocks and diffs, and
832    // with only literals to paint them the same grey comment would be stamped into seven
833    // systems and two schemes, which is how a light comment grey disappears on a dark page.
834    // So a token kind is a role like any other, and each system answers it from the code
835    // editor of its own platform.
836    //
837    // The split follows what tree-sitter's highlight names, VS Code's semantic token kinds
838    // and TextMate scopes all agree on. Each of those divides more finely, and the finer
839    // names fold into one of these (`keyword.return` is a keyword). A token that fits none
840    // is left in the body ink rather than given a colour of its own.
841    //
842    // The fifteen syntax inks hold the body-text bound on the code block's panel, comments
843    // included: a comment can be quieter in hue and lightness, but one nobody can read is
844    // a comment that is not there.
845    SyntaxKeyword = 24,
846    SyntaxString = 25,
847    SyntaxComment = 26,
848    SyntaxNumber = 27,
849    SyntaxConstant = 28,
850    SyntaxType = 29,
851    SyntaxFunction = 30,
852    SyntaxVariable = 31,
853    SyntaxProperty = 32,
854    SyntaxOperator = 33,
855    SyntaxPunctuation = 34,
856    SyntaxTag = 35,
857    SyntaxAttribute = 36,
858    SyntaxEscape = 37,
859    SyntaxMacro = 38,
860    // A diff. The ink marks an added or removed line and its text; the container is the
861    // whole line's background, drawn as the line node's own background; the emphasis is
862    // the background behind the words that actually changed, drawn by the span. Added and
863    // removed are never told apart by colour alone: the `+` and `-` at the head of a line
864    // stay, because the two backgrounds can look the same to a red-green colour blind
865    // reader.
866    DiffAdded = 39,
867    DiffRemoved = 40,
868    DiffModified = 41,
869    DiffAddedContainer = 42,
870    DiffRemovedContainer = 43,
871    DiffAddedEmphasis = 44,
872    DiffRemovedEmphasis = 45,
873});
874
875/// How many colour roles there are, which is how many rows every token table has and how
876/// many slots a palette carries per scheme.
877pub const COLOR_ROLE_COUNT: usize = 45;
878
879// The nine-rung type ladder every supported design system maps onto.
880//
881// `None` is text with no rung at all, which is what a page laid out by CSS draws: its size,
882// weight, line height, letter spacing and font come from the text's own values and nothing
883// from the design system. It is the only text that may name a font of its own. It is not
884// in the schema table, because it is not a rung: see `define_wire_enum!`.
885define_wire_enum!(TYPE_ROLE_SCHEMA, TypeRole {
886    Display = 1,
887    Headline = 2,
888    Title = 3,
889    Subtitle = 4,
890    Body = 5,
891    BodyStrong = 6,
892    Label = 7,
893    Caption = 8,
894    Mono = 9,
895} unlisted {
896    None = 0,
897});
898
899// Where a line of text may break, as CSS `word-break` says it. `Normal` breaks where the
900// language does: between words, and between syllables in Korean. `KeepAll` breaks only at
901// spaces, which is how Korean prose is usually set. `BreakAll` breaks between any two
902// characters.
903define_wire_enum!(WORD_BREAK_SCHEMA, WordBreak {
904    Normal = 1,
905    KeepAll = 2,
906    BreakAll = 3,
907});
908
909// Whether a word too long for its line may be broken inside, as CSS `overflow-wrap` says
910// it. `Anywhere` also lets that break count when the narrowest width the text can take is
911// worked out; `BreakWord` does not.
912define_wire_enum!(OVERFLOW_WRAP_SCHEMA, OverflowWrap {
913    Normal = 1,
914    Anywhere = 2,
915    BreakWord = 3,
916});
917
918// The families CSS names without naming a font. Which installed font each one is, is the
919// platform's answer.
920define_wire_enum!(GENERIC_FAMILY_SCHEMA, GenericFamily {
921    SystemUi = 1,
922    SansSerif = 2,
923    Serif = 3,
924    Monospace = 4,
925});
926
927// Corner roles. The radius is the design system's decision.
928define_wire_enum!(SHAPE_ROLE_SCHEMA, ShapeRole {
929    None = 1,
930    ExtraSmall = 2,
931    Small = 3,
932    Medium = 4,
933    Large = 5,
934    Full = 6,
935});
936
937// How long a change takes and along which curve. A role rather than a duration, because
938// Material's emphasized curve and Cupertino's spring are different answers to the same
939// question, and a milliseconds figure from the application would settle it in the wrong
940// place. A system asked to reduce motion answers every one of these instantly.
941define_wire_enum!(MOTION_ROLE_SCHEMA, MotionRole {
942    Instant = 1,
943    Quick = 2,
944    Standard = 3,
945    Slow = 4,
946    Emphasized = 5,
947});
948
949// What a surface is made of. Four roles rather than a blur radius, because Cupertino and
950// Liquid Glass answer with blur, Material 3 with elevation and a tone laid over the
951// surface, and the GNOME and KDE systems with an opaque fill. A radius from the
952// application would be a blur instruction to systems that do not blur.
953define_wire_enum!(MATERIAL_ROLE_SCHEMA, MaterialRole {
954    Thin = 1,
955    Regular = 2,
956    Thick = 3,
957    Chrome = 4,
958});
959
960// What a node is to assistive technology. Compose's own `Role` names, so the Renderer maps
961// each one to the platform's. `None` leaves the role the widget already has.
962define_wire_enum!(SEMANTICS_ROLE_SCHEMA, SemanticsRole {
963    None = 1,
964    Button = 2,
965    Checkbox = 3,
966    Switch = 4,
967    RadioButton = 5,
968    Tab = 6,
969    Image = 7,
970    DropdownList = 8,
971    TabList = 9,
972    Tree = 10,
973    TreeItem = 11,
974    Menu = 12,
975    MenuItem = 13,
976    TextBox = 14,
977    Heading = 15,
978    Navigation = 16,
979    List = 17,
980    ListItem = 18,
981    Group = 19,
982});
983
984// Density roles, because dp density differs per design system.
985define_wire_enum!(SPACE_ROLE_SCHEMA, SpaceRole {
986    None = 1,
987    Xs = 2,
988    Sm = 3,
989    Md = 4,
990    Lg = 5,
991    Xl = 6,
992    Xxl = 7,
993});
994
995// `Start` and `End` follow the writing direction and swap in right-to-left text. `Left`
996// and `Right` are physical, as CSS `text-align: left|right` is, and never swap.
997define_wire_enum!(TEXT_ALIGN_SCHEMA, TextAlign {
998    Start = 1,
999    Center = 2,
1000    End = 3,
1001    Justify = 4,
1002    Left = 5,
1003    Right = 6,
1004});
1005
1006define_wire_enum!(TEXT_OVERFLOW_SCHEMA, TextOverflow {
1007    Clip = 1,
1008    Ellipsis = 2,
1009    Visible = 3,
1010});
1011
1012define_wire_enum!(ARRANGEMENT_SCHEMA, Arrangement {
1013    Start = 1,
1014    Center = 2,
1015    End = 3,
1016    SpaceBetween = 4,
1017    SpaceAround = 5,
1018    SpaceEvenly = 6,
1019});
1020
1021define_wire_enum!(ALIGNMENT_SCHEMA, Alignment {
1022    TopStart = 1,
1023    TopCenter = 2,
1024    TopEnd = 3,
1025    CenterStart = 4,
1026    Center = 5,
1027    CenterEnd = 6,
1028    BottomStart = 7,
1029    BottomCenter = 8,
1030    BottomEnd = 9,
1031});
1032
1033// The neutral component variant names the design system styles.
1034define_wire_enum!(BUTTON_VARIANT_SCHEMA, ButtonVariant {
1035    Filled = 1,
1036    Tonal = 2,
1037    Outlined = 3,
1038    Text = 4,
1039    Operator = 5,
1040});
1041
1042// What a button is, independently from the visual emphasis its variant carries.
1043define_wire_enum!(BUTTON_KIND_SCHEMA, ButtonKind {
1044    Standard = 1,
1045    ActionKey = 2,
1046});
1047
1048// What the bytes behind an asset id are. A kind the Renderer cannot read is a reported
1049// protocol error, not a guess.
1050define_wire_enum!(ASSET_KIND_SCHEMA, AssetKind {
1051    Png = 1,
1052    Jpeg = 2,
1053    Svg = 3,
1054    VectorIcon = 4,
1055    // A font file, registered so that a type role can resolve to it. The bytes are the
1056    // font, which is why this is an asset and not a name: a name would put the check that
1057    // the font exists at run time on the reader's machine, and a missing one there is a
1058    // screen in the wrong typeface with nobody to tell.
1059    Font = 5,
1060    // A gradient or an image fill, registered so that any Paint may name it. The bytes
1061    // are the brush's own fixed layout rather than a file: nothing on any platform stores
1062    // a list of stops as a document, and a format would be a parser to write and keep.
1063    Brush = 6,
1064});
1065
1066// How a brush carries on past the area it was given.
1067define_wire_enum!(TILE_MODE_SCHEMA, TileMode {
1068    // Stops at the edge and drags the last colour outwards, which is what a gradient
1069    // filling a surface wants.
1070    Clamp = 1,
1071    Repeat = 2,
1072    // Repeats, flipping every other copy, so the seams do not show.
1073    Mirror = 3,
1074});
1075
1076// The closed set of icon meanings. An icon is addressed by what it is for, never by a
1077// system icon name: a name would defer the check that the icon exists to run time, and it
1078// would pin one platform's artwork into the protocol. The role is what lets the same
1079// declaration come out as SF Symbols under Cupertino and Material Symbols under Material 3.
1080define_wire_enum!(ICON_ROLE_SCHEMA, IconRole {
1081    Back = 1,
1082    Forward = 2,
1083    Close = 3,
1084    Search = 4,
1085    Add = 5,
1086    Check = 6,
1087    Settings = 7,
1088    More = 8,
1089    // The meanings a set of destinations needs before any other: where the application
1090    // starts, the list it is mostly about, and what has arrived.
1091    Home = 9,
1092    List = 10,
1093    Inbox = 11,
1094    // What a window's own bar needs: the button that opens everything this screen does
1095    // not have room for, and the one that goes back over what has already happened. Both
1096    // are in the reference bar of more than one sample, and neither is a destination, so
1097    // neither could be said with the three above.
1098    Menu = 12,
1099    History = 13,
1100    // What putting the samples beside the applications they are drawn from turned up: eight
1101    // meanings the set could not say. Each is a meaning rather than a picture, so a system
1102    // that draws sending as a paper aeroplane is still answering `Send`.
1103    //
1104    // None of them is an existing role used loosely. A composer's send was drawn with
1105    // `Forward` for a while, and an arrow pointing sideways says "on to the next one"
1106    // rather than "send this".
1107    Send = 14,
1108    Mic = 15,
1109    Image = 16,
1110    Video = 17,
1111    Library = 18,
1112    Sidebar = 19,
1113    Compose = 20,
1114    Collapse = 21,
1115});
1116
1117// How long a transient message stays on screen. Closed, and deliberately short: a message
1118// that has to be acknowledged is a `Dialog`, so there is no indefinite duration. What the
1119// two names mean in milliseconds is the design system's decision.
1120define_wire_enum!(MESSAGE_DURATION_SCHEMA, MessageDuration {
1121    Short = 1,
1122    Long = 2,
1123});
1124
1125// The edge a transient message is drawn along, where the application chose one. Tag 0 on the
1126// wire is "the design system's place", which is what a message that names no placement
1127// gets, so neither enum lists it.
1128define_wire_enum!(MESSAGE_VERTICAL_SCHEMA, MessageVertical {
1129    Top = 1,
1130    Bottom = 2,
1131});
1132
1133// Where along that edge the message sits. Start and End follow the layout direction, the
1134// same way a `Row`'s do.
1135define_wire_enum!(MESSAGE_HORIZONTAL_SCHEMA, MessageHorizontal {
1136    Start = 1,
1137    Center = 2,
1138    End = 3,
1139});
1140
1141/// Where a transient message is drawn, for an application that wants a place other than
1142/// the design system's.
1143///
1144/// Wherever it is, the message stays clear of the safe area and of the application's own
1145/// bars: a message at the bottom sits above a bottom tab bar, and one at the top below the
1146/// top bar.
1147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1148pub struct MessagePlacement {
1149    pub vertical: MessageVertical,
1150    pub horizontal: MessageHorizontal,
1151}
1152
1153// How much a notification matters. Closed for the same reason a message's duration is: what
1154// it is worth in sound, in an Android channel's importance or in getting through Do Not
1155// Disturb is the platform's answer, so the Host has no way to send a sound file or a
1156// priority number.
1157define_wire_enum!(NOTIFICATION_IMPORTANCE_SCHEMA, NotificationImportance {
1158    Normal = 1,
1159    Urgent = 2,
1160});
1161
1162// Whether a notification is shown while the application's window is the active one.
1163// `WhenInactive` is dropped by the Renderer when the window has focus: whether it has is
1164// the Renderer's state, and a Host that wanted to judge it would need every focus change
1165// sent across.
1166define_wire_enum!(NOTIFICATION_PRESENTATION_SCHEMA, NotificationPresentation {
1167    Always = 1,
1168    WhenInactive = 2,
1169});
1170
1171// Whether this run may show notifications. `Unsupported` is a platform or a run that has
1172// no way to show one (no bundle identifier, no AppUserModelID, no notification daemon),
1173// which is an answer and not an error.
1174define_wire_enum!(NOTIFICATION_PERMISSION_SCHEMA, NotificationPermission {
1175    NotDetermined = 1,
1176    Granted = 2,
1177    Denied = 3,
1178    Unsupported = 4,
1179});
1180
1181// The design systems of phase one. Later ones append variants here and nowhere else: a new
1182// design system is one variant plus one token table and rule implementation in the Renderer.
1183//
1184// `LiquidGlass` is the language macOS 26 and iOS 26 draw, and it sits beside `Cupertino`
1185// rather than replacing it: one draws grouped inset lists on a grey page, the other draws
1186// translucent floating capsules, and an application that wants one does not want the
1187// other. Tags are append only, so it lands after the Linux three even though it belongs
1188// next to `Cupertino` in any list a reader would write.
1189define_wire_enum!(DESIGN_SYSTEM_SCHEMA, DesignSystem {
1190    Material3 = 1,
1191    Cupertino = 2,
1192    Fluent = 3,
1193    Gnome = 4,
1194    Breeze = 5,
1195    Deepin = 6,
1196    LiquidGlass = 7,
1197});
1198
1199// Light and dark selection. `FollowSystem` leaves the choice to the Renderer, which learns
1200// of a system change first and applies it without involving the Host.
1201define_wire_enum!(COLOR_SCHEME_SCHEMA, ColorScheme {
1202    Light = 1,
1203    Dark = 2,
1204    FollowSystem = 3,
1205});
1206
1207// Which part of the screen's frame a `Scaffold` slot fills.
1208//
1209// What each one becomes is the Renderer's: a top bar may be the window's caption, an
1210// ordinary bar under the system title bar, or a large title that shrinks as the page
1211// scrolls; a bottom bar may be a bar, a rail or a permanent drawer; a floating action
1212// may float, sit in the toolbar, or fold into a menu. The application says which slot
1213// it filled and nothing about the answer.
1214define_wire_enum!(SLOT_ROLE_SCHEMA, SlotRole {
1215    TopBar = 1,
1216    BottomBar = 2,
1217    FloatingAction = 3,
1218    Content = 4,
1219});
1220
1221// What a code editor decoration is: a mark under a range, a line of text above a line, a
1222// range that answers a resting pointer, and a suggestion shown in place. Each is what a
1223// language server or an extension host already speaks in, so an application can pass its
1224// answers through without inventing its own. After them, the two backgrounds a diff view
1225// is drawn with: a tint across whole lines, the width of the editor, and a stronger one
1226// over the characters that changed. Values are only appended.
1227define_wire_enum!(DECORATION_KIND_SCHEMA, DecorationKind {
1228    Underline = 1,
1229    CodeLens = 2,
1230    HoverAnchor = 3,
1231    GhostText = 4,
1232    LineBackground = 5,
1233    RangeBackground = 6,
1234});
1235
1236// How serious an underlined diagnostic is. The four a language server reports, in its
1237// order. The colour each one gets is the design system's unless the application names a
1238// role, and how the mark is drawn, a squiggle or a straight line, is always the system's.
1239define_wire_enum!(SEVERITY_SCHEMA, Severity {
1240    Error = 1,
1241    Warning = 2,
1242    Information = 3,
1243    Hint = 4,
1244});
1245
1246// Whether a pointer has come to rest over a place in a code editor or has left it. Two
1247// values, because the Host is told once on arriving and once on leaving and never while
1248// the pointer stays.
1249define_wire_enum!(HOVER_PHASE_SCHEMA, HoverPhase {
1250    Rest = 1,
1251    Leave = 2,
1252});
1253
1254// What a mark beside the line numbers of a code editor looks like: a dot, a bar the height
1255// of the line, or one character the application names. The colour is a role or the design
1256// system's; the size and the placement of all three are always the system's.
1257define_wire_enum!(GUTTER_DECORATION_KIND_SCHEMA, GutterDecorationKind {
1258    Dot = 1,
1259    Bar = 2,
1260    Glyph = 3,
1261});
1262
1263// The side of its anchor a `Popover` opens on. `Start` and `End` follow the layout
1264// direction. It is the side asked for: the Renderer opens on the opposite one when the
1265// content does not fit between the anchor and the window's edge.
1266define_wire_enum!(POPOVER_PLACEMENT_SCHEMA, PopoverPlacement {
1267    Top = 1,
1268    Bottom = 2,
1269    Start = 3,
1270    End = 4,
1271});
1272
1273// Which edge of its anchor a `Popover` lines up with, along the side it opened on.
1274define_wire_enum!(POPOVER_ALIGN_SCHEMA, PopoverAlign {
1275    Start = 1,
1276    Center = 2,
1277    End = 3,
1278});
1279
1280// Where `RevealRange` puts the range it brings into view: the least scroll that shows it,
1281// the middle of the view, or the top.
1282define_wire_enum!(REVEAL_ALIGN_SCHEMA, RevealAlign {
1283    Nearest = 1,
1284    Center = 2,
1285    Top = 3,
1286});
1287
1288// The vocabulary of animations the Renderer plays on its own frame clock. Every one is a
1289// byte on the wire and every one starts at 1, so a zero is always a record the two sides
1290// disagree about.
1291
1292// What an animation changes. Each has a place it is drawn from: the node's first `Alpha`,
1293// its `color` property, its first `Background`, its first `Transform`, and the colour of
1294// every side of its first `Border` or `BorderEach`. A container's `color` is the colour
1295// the text inside it inherits, so one animation on it recolours all of that text.
1296define_wire_enum!(ANIMATED_PROPERTY_SCHEMA, AnimatedProperty {
1297    Alpha = 1,
1298    Color = 2,
1299    Background = 3,
1300    Transform = 4,
1301    BorderColor = 5,
1302});
1303
1304define_wire_enum!(PLAYBACK_DIRECTION_SCHEMA, PlaybackDirection {
1305    Normal = 1,
1306    Reverse = 2,
1307    Alternate = 3,
1308    AlternateReverse = 4,
1309});
1310
1311define_wire_enum!(FILL_MODE_SCHEMA, FillMode {
1312    None = 1,
1313    Forwards = 2,
1314    Backwards = 3,
1315    Both = 4,
1316});
1317
1318define_wire_enum!(PLAY_STATE_SCHEMA, PlayState {
1319    Running = 1,
1320    Paused = 2,
1321});
1322
1323// How two colours are mixed: the way CSS's older colour syntax does, or in Oklab, the
1324// default of CSS Color 4's newer syntax.
1325define_wire_enum!(COLOR_INTERPOLATION_SCHEMA, ColorInterpolation {
1326    SrgbPremultiplied = 1,
1327    Oklab = 2,
1328});
1329
1330define_wire_enum!(TIMING_KIND_SCHEMA, TimingKind {
1331    Linear = 1,
1332    CubicBezier = 2,
1333    Steps = 3,
1334});
1335
1336define_wire_enum!(STEP_POSITION_SCHEMA, StepPosition {
1337    JumpStart = 1,
1338    JumpEnd = 2,
1339    JumpNone = 3,
1340    JumpBoth = 4,
1341});
1342
1343define_wire_enum!(TRANSFORM_FUNCTION_KIND_SCHEMA, TransformFunctionKind {
1344    Translate = 1,
1345    Rotate = 2,
1346    Scale = 3,
1347    Skew = 4,
1348    Matrix = 5,
1349});
1350
1351define_wire_enum!(ANIMATION_CONTROL_SCHEMA, AnimationControl {
1352    Pause = 1,
1353    Resume = 2,
1354    Cancel = 3,
1355});
1356
1357define_wire_enum!(ANIMATION_EVENT_KIND_SCHEMA, AnimationEventKind {
1358    Ready = 1,
1359    Active = 2,
1360    Iteration = 3,
1361    End = 4,
1362});
1363
1364define_wire_enum!(REDUCED_MOTION_SCHEMA, ReducedMotion {
1365    Off = 1,
1366    On = 2,
1367    Unknown = 3,
1368});
1369
1370// Whether the system appearance is light or dark, as the Renderer reads it. Unlike
1371// `ColorScheme`, it has no `FollowSystem`: it is the answer that choice follows.
1372define_wire_enum!(SYSTEM_COLOR_SCHEME_SCHEMA, SystemColorScheme {
1373    Light = 0,
1374    Dark = 1,
1375});
1376
1377define_wire_enum!(POINTER_KIND_SCHEMA, PointerKind {
1378    Move = 1,
1379    Enter = 2,
1380    Leave = 3,
1381    Down = 4,
1382    Up = 5,
1383    Cancel = 6,
1384    Wheel = 7,
1385    ContextMenu = 8,
1386});
1387
1388define_wire_enum!(POINTER_TYPE_SCHEMA, PointerType {
1389    Mouse = 1,
1390    Touch = 2,
1391    Pen = 3,
1392});
1393
1394define_wire_enum!(WHEEL_DELTA_MODE_SCHEMA, WheelDeltaMode {
1395    Pixel = 0,
1396    Line = 1,
1397    Page = 2,
1398});
1399
1400// CSS `cursor` keywords, by name and in the order CSS lists them. `Auto` is "let the widget
1401// under the pointer decide".
1402define_wire_enum!(CURSOR_SCHEMA, Cursor {
1403    Auto = 0,
1404    Default = 1,
1405    Pointer = 2,
1406    Text = 3,
1407    VerticalText = 4,
1408    Crosshair = 5,
1409    Move = 6,
1410    Grab = 7,
1411    Grabbing = 8,
1412    NotAllowed = 9,
1413    NoDrop = 10,
1414    Wait = 11,
1415    Progress = 12,
1416    Help = 13,
1417    ContextMenu = 14,
1418    Cell = 15,
1419    Copy = 16,
1420    Alias = 17,
1421    AllScroll = 18,
1422    ZoomIn = 19,
1423    ZoomOut = 20,
1424    ColResize = 21,
1425    RowResize = 22,
1426    EResize = 23,
1427    NResize = 24,
1428    NeResize = 25,
1429    NwResize = 26,
1430    SResize = 27,
1431    SeResize = 28,
1432    SwResize = 29,
1433    WResize = 30,
1434    EwResize = 31,
1435    NsResize = 32,
1436    NeswResize = 33,
1437    NwseResize = 34,
1438    None = 35,
1439});
1440
1441// CSS `user-select`, resolved by the Host. `Auto` follows the parent.
1442define_wire_enum!(USER_SELECT_SCHEMA, UserSelect {
1443    Auto = 0,
1444    None = 1,
1445    Text = 2,
1446    All = 3,
1447});
1448
1449/// Every role enum codegen mirrors, in wire order. Appending is the only allowed edit.
1450pub const ROLE_ENUM_SCHEMA: &[RoleEnumSchema] = &[
1451    RoleEnumSchema {
1452        name: "ColorRole",
1453        variants: COLOR_ROLE_SCHEMA,
1454    },
1455    RoleEnumSchema {
1456        name: "TypeRole",
1457        variants: TYPE_ROLE_SCHEMA,
1458    },
1459    RoleEnumSchema {
1460        name: "ShapeRole",
1461        variants: SHAPE_ROLE_SCHEMA,
1462    },
1463    RoleEnumSchema {
1464        name: "MotionRole",
1465        variants: MOTION_ROLE_SCHEMA,
1466    },
1467    RoleEnumSchema {
1468        name: "TileMode",
1469        variants: TILE_MODE_SCHEMA,
1470    },
1471    RoleEnumSchema {
1472        name: "MaterialRole",
1473        variants: MATERIAL_ROLE_SCHEMA,
1474    },
1475    RoleEnumSchema {
1476        name: "SemanticsRole",
1477        variants: SEMANTICS_ROLE_SCHEMA,
1478    },
1479    RoleEnumSchema {
1480        name: "SpaceRole",
1481        variants: SPACE_ROLE_SCHEMA,
1482    },
1483    RoleEnumSchema {
1484        name: "TextAlign",
1485        variants: TEXT_ALIGN_SCHEMA,
1486    },
1487    RoleEnumSchema {
1488        name: "TextOverflow",
1489        variants: TEXT_OVERFLOW_SCHEMA,
1490    },
1491    RoleEnumSchema {
1492        name: "Arrangement",
1493        variants: ARRANGEMENT_SCHEMA,
1494    },
1495    RoleEnumSchema {
1496        name: "Alignment",
1497        variants: ALIGNMENT_SCHEMA,
1498    },
1499    RoleEnumSchema {
1500        name: "ButtonVariant",
1501        variants: BUTTON_VARIANT_SCHEMA,
1502    },
1503    RoleEnumSchema {
1504        name: "ButtonKind",
1505        variants: BUTTON_KIND_SCHEMA,
1506    },
1507    RoleEnumSchema {
1508        name: "DesignSystem",
1509        variants: DESIGN_SYSTEM_SCHEMA,
1510    },
1511    RoleEnumSchema {
1512        name: "ColorScheme",
1513        variants: COLOR_SCHEME_SCHEMA,
1514    },
1515    RoleEnumSchema {
1516        name: "AssetKind",
1517        variants: ASSET_KIND_SCHEMA,
1518    },
1519    RoleEnumSchema {
1520        name: "IconRole",
1521        variants: ICON_ROLE_SCHEMA,
1522    },
1523    RoleEnumSchema {
1524        name: "MessageDuration",
1525        variants: MESSAGE_DURATION_SCHEMA,
1526    },
1527    RoleEnumSchema {
1528        name: "MessageVertical",
1529        variants: MESSAGE_VERTICAL_SCHEMA,
1530    },
1531    RoleEnumSchema {
1532        name: "MessageHorizontal",
1533        variants: MESSAGE_HORIZONTAL_SCHEMA,
1534    },
1535    RoleEnumSchema {
1536        name: "Chrome",
1537        variants: CHROME_SCHEMA,
1538    },
1539    RoleEnumSchema {
1540        name: "TitleBar",
1541        variants: TITLE_BAR_SCHEMA,
1542    },
1543    RoleEnumSchema {
1544        name: "SlotRole",
1545        variants: SLOT_ROLE_SCHEMA,
1546    },
1547    RoleEnumSchema {
1548        name: "NotificationImportance",
1549        variants: NOTIFICATION_IMPORTANCE_SCHEMA,
1550    },
1551    RoleEnumSchema {
1552        name: "NotificationPresentation",
1553        variants: NOTIFICATION_PRESENTATION_SCHEMA,
1554    },
1555    RoleEnumSchema {
1556        name: "NotificationPermission",
1557        variants: NOTIFICATION_PERMISSION_SCHEMA,
1558    },
1559    RoleEnumSchema {
1560        name: "WordBreak",
1561        variants: WORD_BREAK_SCHEMA,
1562    },
1563    RoleEnumSchema {
1564        name: "OverflowWrap",
1565        variants: OVERFLOW_WRAP_SCHEMA,
1566    },
1567    RoleEnumSchema {
1568        name: "GenericFamily",
1569        variants: GENERIC_FAMILY_SCHEMA,
1570    },
1571    RoleEnumSchema {
1572        name: "AnimatedProperty",
1573        variants: ANIMATED_PROPERTY_SCHEMA,
1574    },
1575    RoleEnumSchema {
1576        name: "PlaybackDirection",
1577        variants: PLAYBACK_DIRECTION_SCHEMA,
1578    },
1579    RoleEnumSchema {
1580        name: "FillMode",
1581        variants: FILL_MODE_SCHEMA,
1582    },
1583    RoleEnumSchema {
1584        name: "PlayState",
1585        variants: PLAY_STATE_SCHEMA,
1586    },
1587    RoleEnumSchema {
1588        name: "ColorInterpolation",
1589        variants: COLOR_INTERPOLATION_SCHEMA,
1590    },
1591    RoleEnumSchema {
1592        name: "TimingKind",
1593        variants: TIMING_KIND_SCHEMA,
1594    },
1595    RoleEnumSchema {
1596        name: "StepPosition",
1597        variants: STEP_POSITION_SCHEMA,
1598    },
1599    RoleEnumSchema {
1600        name: "TransformFunctionKind",
1601        variants: TRANSFORM_FUNCTION_KIND_SCHEMA,
1602    },
1603    RoleEnumSchema {
1604        name: "AnimationControl",
1605        variants: ANIMATION_CONTROL_SCHEMA,
1606    },
1607    RoleEnumSchema {
1608        name: "AnimationEventKind",
1609        variants: ANIMATION_EVENT_KIND_SCHEMA,
1610    },
1611    RoleEnumSchema {
1612        name: "ReducedMotion",
1613        variants: REDUCED_MOTION_SCHEMA,
1614    },
1615    RoleEnumSchema {
1616        name: "SystemColorScheme",
1617        variants: SYSTEM_COLOR_SCHEME_SCHEMA,
1618    },
1619    RoleEnumSchema {
1620        name: "DecorationKind",
1621        variants: DECORATION_KIND_SCHEMA,
1622    },
1623    RoleEnumSchema {
1624        name: "Severity",
1625        variants: SEVERITY_SCHEMA,
1626    },
1627    RoleEnumSchema {
1628        name: "HoverPhase",
1629        variants: HOVER_PHASE_SCHEMA,
1630    },
1631    RoleEnumSchema {
1632        name: "GutterDecorationKind",
1633        variants: GUTTER_DECORATION_KIND_SCHEMA,
1634    },
1635    RoleEnumSchema {
1636        name: "RevealAlign",
1637        variants: REVEAL_ALIGN_SCHEMA,
1638    },
1639    RoleEnumSchema {
1640        name: "PointerKind",
1641        variants: POINTER_KIND_SCHEMA,
1642    },
1643    RoleEnumSchema {
1644        name: "PointerType",
1645        variants: POINTER_TYPE_SCHEMA,
1646    },
1647    RoleEnumSchema {
1648        name: "WheelDeltaMode",
1649        variants: WHEEL_DELTA_MODE_SCHEMA,
1650    },
1651    RoleEnumSchema {
1652        name: "Cursor",
1653        variants: CURSOR_SCHEMA,
1654    },
1655    RoleEnumSchema {
1656        name: "UserSelect",
1657        variants: USER_SELECT_SCHEMA,
1658    },
1659    RoleEnumSchema {
1660        name: "WindowMode",
1661        variants: WINDOW_MODE_SCHEMA,
1662    },
1663    RoleEnumSchema {
1664        name: "LastWindowClosed",
1665        variants: LAST_WINDOW_CLOSED_SCHEMA,
1666    },
1667    RoleEnumSchema {
1668        name: "FileDialogKind",
1669        variants: FILE_DIALOG_KIND_SCHEMA,
1670    },
1671    RoleEnumSchema {
1672        name: "FileDialogStatus",
1673        variants: FILE_DIALOG_STATUS_SCHEMA,
1674    },
1675    RoleEnumSchema {
1676        name: "EditGroupStatus",
1677        variants: EDIT_GROUP_STATUS_SCHEMA,
1678    },
1679    RoleEnumSchema {
1680        name: "CompactNavigation",
1681        variants: COMPACT_NAVIGATION_SCHEMA,
1682    },
1683    RoleEnumSchema {
1684        name: "Snap",
1685        variants: SNAP_SCHEMA,
1686    },
1687    RoleEnumSchema {
1688        name: "MinimapSide",
1689        variants: MINIMAP_SIDE_SCHEMA,
1690    },
1691    RoleEnumSchema {
1692        name: "WordWrapMode",
1693        variants: WORD_WRAP_MODE_SCHEMA,
1694    },
1695    RoleEnumSchema {
1696        name: "StorageOp",
1697        variants: STORAGE_OP_SCHEMA,
1698    },
1699    RoleEnumSchema {
1700        name: "IoStatus",
1701        variants: IO_STATUS_SCHEMA,
1702    },
1703    RoleEnumSchema {
1704        name: "FileDelivery",
1705        variants: FILE_DELIVERY_SCHEMA,
1706    },
1707    RoleEnumSchema {
1708        name: "FileOp",
1709        variants: FILE_OP_SCHEMA,
1710    },
1711    RoleEnumSchema {
1712        name: "HorizontalAlignment",
1713        variants: HORIZONTAL_ALIGNMENT_SCHEMA,
1714    },
1715    RoleEnumSchema {
1716        name: "VerticalAlignment",
1717        variants: VERTICAL_ALIGNMENT_SCHEMA,
1718    },
1719    RoleEnumSchema {
1720        name: "PopoverPlacement",
1721        variants: POPOVER_PLACEMENT_SCHEMA,
1722    },
1723    RoleEnumSchema {
1724        name: "PopoverAlign",
1725        variants: POPOVER_ALIGN_SCHEMA,
1726    },
1727    RoleEnumSchema {
1728        name: "ContentScale",
1729        variants: CONTENT_SCALE_SCHEMA,
1730    },
1731    RoleEnumSchema {
1732        name: "BackgroundRepeat",
1733        variants: BACKGROUND_REPEAT_SCHEMA,
1734    },
1735];
1736
1737/// Every place that takes a colour takes a `Paint`, so colour is expressed once.
1738///
1739/// Encoded as one `u64`: the high 32 bits are the kind, the low 32 bits are the value.
1740#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1741pub enum Paint {
1742    Role(ColorRole),
1743    Literal(Color),
1744    /// A registered brush: a gradient, or a picture laid out as a fill.
1745    ///
1746    /// An id rather than the thing itself, because a list of stops does not fit the two
1747    /// words a modifier has and because a brush outlives the frame that draws it, which
1748    /// is what the asset path already exists for.
1749    Asset(u32),
1750}
1751
1752const PAINT_KIND_ROLE: u64 = 1;
1753const PAINT_KIND_LITERAL: u64 = 2;
1754const PAINT_KIND_ASSET: u64 = 3;
1755
1756impl Paint {
1757    pub const fn to_bits(self) -> u64 {
1758        match self {
1759            Self::Role(role) => (PAINT_KIND_ROLE << 32) | role as u64,
1760            Self::Literal(color) => (PAINT_KIND_LITERAL << 32) | color.0 as u64,
1761            Self::Asset(id) => (PAINT_KIND_ASSET << 32) | id as u64,
1762        }
1763    }
1764
1765    pub fn from_bits(bits: u64) -> Option<Self> {
1766        let value = bits as u32;
1767        match bits >> 32 {
1768            PAINT_KIND_ROLE => Some(Self::Role(
1769                ColorRole::try_from(u16::try_from(value).ok()?).ok()?,
1770            )),
1771            PAINT_KIND_LITERAL => Some(Self::Literal(Color(value))),
1772            PAINT_KIND_ASSET => Some(Self::Asset(value)),
1773            _ => None,
1774        }
1775    }
1776}
1777
1778// How the window wears its title bar.
1779//
1780// `Modern` runs content into the title bar area and is the default, because every desktop
1781// platform now expects it and a window with a separate system bar looks a decade old.
1782//
1783// `System` is the platform's ordinary bar, and it is kept deliberately. Some tool-shaped
1784// applications want one, and more importantly it is where an application goes when
1785// `Modern` turns out to be wrong on some machine: a default with no way out of it is a
1786// default that strands people.
1787define_wire_enum!(CHROME_SCHEMA, Chrome {
1788    Modern = 1,
1789    System = 2,
1790    FullSize = 3,
1791});
1792
1793// Which of two title bars this window has. `Chrome` chooses who draws the caption; this
1794// chooses, inside that, what kind of window the caption belongs to.
1795//
1796// What each one means is not the same on every platform, and the numbers are never the
1797// Host's. On macOS the buttons sit further in and the window is rounded more; on Windows
1798// and Linux the caption is a different height and the buttons stay where that platform
1799// puts them.
1800define_wire_enum!(TITLE_BAR_SCHEMA, TitleBar {
1801    // What an application on that platform actually looks like now, which is why it is the
1802    // default.
1803    Normal = 1,
1804    // An ordinary window. For tool applications, for the places the other reads oddly, and
1805    // as the way back.
1806    Simple = 2,
1807});
1808
1809// What a `Navigation` becomes in a compact window, where the application chose. Medium and
1810// Expanded stay the design system's answer whatever is chosen here.
1811//
1812// `Default` is no choice at all: the design system answers, which is a bottom bar on most
1813// of them and the put-away sidebar on Apple's desktop systems. It travels as tag 0, which
1814// is the same as not sending the property, so it is not in the schema table.
1815//
1816// `Sidebar` is the sidebar put away behind a toggle that brings it over the page, on every
1817// design system. `None` leaves the destinations off a compact window altogether, with
1818// nothing to call them: the application draws its own way to them. The name is Compose's
1819// `NavigationSuiteType.None`.
1820define_wire_enum!(COMPACT_NAVIGATION_SCHEMA, CompactNavigation {
1821    Sidebar = 1,
1822    None = 2,
1823} unlisted {
1824    Default = 0,
1825});
1826
1827// Where a scroll container comes to rest once a drag or a fling ends.
1828//
1829// `None` is free scrolling: the container stops wherever the drag or the fling left it. It
1830// travels as tag 0, which is the same as not sending the property, so it is not in the
1831// schema table.
1832//
1833// `ItemStart` stops with the start edge of the nearest direct child on the start of the
1834// visible area, the way CSS `scroll-snap-type: mandatory` with `scroll-snap-align: start`
1835// does.
1836// Where a code editor breaks a long line, the mode part of the `WordWrap` property: at the
1837// edge of the view, at a number of columns, or at whichever of the two comes first. `Off`
1838// breaks no line, which is what an editor that never sets it does.
1839define_wire_enum!(WORD_WRAP_MODE_SCHEMA, WordWrapMode {
1840    Viewport = 1,
1841    Column = 2,
1842    Bounded = 3,
1843} unlisted {
1844    Off = 0,
1845});
1846
1847// Which side of a code editor its minimap is drawn on: the whole document shrunk, with the
1848// part on screen marked, which a press or a drag scrolls to. `Off` draws none, which is what
1849// an editor that never sets it does.
1850define_wire_enum!(MINIMAP_SIDE_SCHEMA, MinimapSide {
1851    Right = 1,
1852    Left = 2,
1853} unlisted {
1854    Off = 0,
1855});
1856
1857define_wire_enum!(SNAP_SCHEMA, Snap {
1858    ItemStart = 1,
1859} unlisted {
1860    None = 0,
1861});
1862
1863// How an `Image` lays its picture into the box it was given, as Compose's `ContentScale`.
1864//
1865// `Fit` keeps the picture's proportions and shows all of it, centred, leaving the rest of
1866// the box empty. `Crop` keeps the proportions and covers the whole box, centred, cutting
1867// off what falls outside. `FillBounds` stretches the picture to the box in each direction
1868// on its own, which is CSS `object-fit: fill`. Not sending the property draws `Fit`.
1869define_wire_enum!(CONTENT_SCALE_SCHEMA, ContentScale {
1870    Fit = 1,
1871    Crop = 2,
1872    FillBounds = 3,
1873});
1874
1875// How a `BackgroundBrush` repeats its rectangle across the node, as CSS
1876// `background-repeat` does: not at all, in both directions, along the width only, or
1877// along the height only. A repeat runs both ways from the rectangle, so a node is covered
1878// to its edges whichever corner the rectangle sits in.
1879define_wire_enum!(BACKGROUND_REPEAT_SCHEMA, BackgroundRepeat {
1880    NoRepeat = 1,
1881    Repeat = 2,
1882    RepeatX = 3,
1883    RepeatY = 4,
1884});
1885
1886// How the application uses windows. `Single` is every application until now: the
1887// root's content fills the one window `SetWindow` describes. `Windows` is the multi-window
1888// mode, in which the root's children are `Window` nodes and `SetWindow` describes nothing
1889// but the mode.
1890define_wire_enum!(WINDOW_MODE_SCHEMA, WindowMode {
1891    Single = 0,
1892    Windows = 1,
1893});
1894
1895// What happens when the last `Window` node is removed. `Exit` ends the renderer
1896// loop, `KeepRunning` leaves the process up with no window. Ignored by `Single`.
1897define_wire_enum!(LAST_WINDOW_CLOSED_SCHEMA, LastWindowClosed {
1898    Exit = 0,
1899    KeepRunning = 1,
1900});
1901
1902// What a file dialog asks the user to pick. Closed: each kind is a different platform panel
1903// with different rules about how many paths come back, so a free-form set of flags would
1904// allow combinations no panel can honour.
1905define_wire_enum!(FILE_DIALOG_KIND_SCHEMA, FileDialogKind {
1906    OpenFile = 1,
1907    OpenFiles = 2,
1908    OpenFolder = 3,
1909    OpenFolders = 4,
1910    Save = 5,
1911});
1912
1913// How a file dialog ended. `Unavailable` means this run has no way to show one, which is an
1914// answer and not an error, and is never used for a user's cancel.
1915define_wire_enum!(FILE_DIALOG_STATUS_SCHEMA, FileDialogStatus {
1916    Chosen = 1,
1917    Cancelled = 2,
1918    Unavailable = 3,
1919});
1920
1921// What became of one grouped edit of a code editor. The group goes in whole or not at all,
1922// and each way it can stay out asks the application for something different: `Rejected`
1923// means the reader typed where it would have written, so read the document again and send
1924// it again; `TooOld` means the version it was written against is older than the editor
1925// remembers, so the application's copy has to be brought back in step; `Invalid` means the
1926// group itself makes no sense (edits that overlap, a range outside the document, a version
1927// from the future).
1928define_wire_enum!(EDIT_GROUP_STATUS_SCHEMA, EditGroupStatus {
1929    Applied = 1,
1930    Rejected = 2,
1931    Invalid = 3,
1932    TooOld = 4,
1933});
1934
1935// How the files a Renderer holds handles for reached it. Closed, because what an
1936// application may do with a file depends on it: only a `Handle` can be written again.
1937define_wire_enum!(FILE_DELIVERY_SCHEMA, FileDelivery {
1938    // The file itself, in the file system. Writing it again changes the same file.
1939    Handle = 1,
1940    // A copy the browser handed over from a file input. It can only be read.
1941    Upload = 2,
1942    // A save that became a download. Each write is a new download.
1943    Download = 3,
1944});
1945
1946// What one `FileCall` asks of a file the Renderer holds a handle for.
1947define_wire_enum!(FILE_OP_SCHEMA, FileOp {
1948    Read = 1,
1949    Write = 2,
1950    Entries = 3,
1951    Child = 4,
1952});
1953
1954// What a `StorageCall` asks the Renderer to do with the application's storage. Closed: a
1955// Renderer that meets an operation it does not know cannot answer it truthfully.
1956define_wire_enum!(STORAGE_OP_SCHEMA, StorageOp {
1957    Read = 1,
1958    Write = 2,
1959    Remove = 3,
1960    Keys = 4,
1961});
1962
1963// How one input or output request ended, as an `IoAnswer` says it. Shared by storage and by
1964// files picked on the web, so one answer reads the same whichever asked. Closed, so a word
1965// this side does not know is a record the two sides disagree on.
1966define_wire_enum!(IO_STATUS_SCHEMA, IoStatus {
1967    Ok = 1,
1968    NotFound = 2,
1969    QuotaExceeded = 3,
1970    Unavailable = 4,
1971    PermissionDenied = 5,
1972    Failed = 6,
1973    ReadOnly = 7,
1974    TooLarge = 8,
1975});
1976
1977// Where the children of one line of a `FlowRow` sit along it, when they do not fill it.
1978define_wire_enum!(HORIZONTAL_ALIGNMENT_SCHEMA, HorizontalAlignment {
1979    Start = 1,
1980    Center = 2,
1981    End = 3,
1982});
1983
1984// Where a child of a `FlowRow` sits inside the height of its line, when it is shorter
1985// than the tallest child on that line.
1986define_wire_enum!(VERTICAL_ALIGNMENT_SCHEMA, VerticalAlignment {
1987    Top = 1,
1988    Center = 2,
1989    Bottom = 3,
1990});
1991
1992/// What an application may decide about its own window.
1993///
1994/// The window belongs to the Renderer, so this is short and stays short. What is here is
1995/// what an application knows and the Renderer cannot guess: what the window is called and
1996/// how large it should open. Nothing here names a colour or a corner, because those are
1997/// the design system's, and nothing here is read more than once.
1998#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1999pub struct Window {
2000    pub chrome: Chrome,
2001    /// Which of the two title bars this window has.
2002    pub title_bar: TitleBar,
2003    /// What the window calls itself.
2004    ///
2005    /// Empty means the application said nothing, and the renderer uses its own name for
2006    /// it. It is not decoration: a desktop lists windows by this, and a window with no
2007    /// title of its own is listed under whatever the renderer happened to be called.
2008    pub title: &'static str,
2009    /// The picture the window wears, as an asset id, or zero for none.
2010    ///
2011    /// An id rather than a path or a name, because a path is a fact about the machine the
2012    /// application was built on and a name asks the toolkit to find something it may not
2013    /// have. Registering the bytes and referring to them is what every other picture in
2014    /// this protocol does.
2015    pub icon: u32,
2016    /// Zero means the Renderer chooses, which is what an application that said nothing
2017    /// gets. A size is in the same density independent pixels gestures are measured in.
2018    pub width: u16,
2019    pub height: u16,
2020    pub min_width: u16,
2021    pub min_height: u16,
2022    pub resizable: bool,
2023    /// The application's zoom level, where it asked for one.
2024    ///
2025    /// `None` leaves the level to the Renderer, which starts from what the reader chose
2026    /// last time. A level set here is applied, remembered and reported back the way one
2027    /// chosen with the keys is. Kept inside `-8..=8`.
2028    pub zoom_level: Option<i8>,
2029    /// Whether the implicit single window reports every whole-dp change of its size,
2030    /// not only a change of size class. Off by default. Windows made with `Window`
2031    /// nodes opt in through the node's `report_size` property instead.
2032    pub report_size: bool,
2033    /// One window (the default) or several. In the second the root's children are
2034    /// `Window` nodes and the rest of this record describes no window.
2035    pub mode: WindowMode,
2036    /// What to do when the last window goes. Only read in `WindowMode::Windows`.
2037    pub last_window_closed: LastWindowClosed,
2038}
2039
2040impl Window {
2041    pub const fn new() -> Self {
2042        Self {
2043            chrome: Chrome::Modern,
2044            title_bar: TitleBar::Normal,
2045            title: "",
2046            icon: 0,
2047            width: 0,
2048            height: 0,
2049            min_width: 0,
2050            min_height: 0,
2051            resizable: true,
2052            zoom_level: None,
2053            report_size: false,
2054            mode: WindowMode::Single,
2055            last_window_closed: LastWindowClosed::Exit,
2056        }
2057    }
2058
2059    /// Several windows, one per `Window` node under the root. With the default
2060    /// policy of the platform: a macOS application stays up with no window, the others
2061    /// exit.
2062    pub const fn with_windows(mut self) -> Self {
2063        self.mode = WindowMode::Windows;
2064        self.last_window_closed = if cfg!(target_os = "macos") {
2065            LastWindowClosed::KeepRunning
2066        } else {
2067            LastWindowClosed::Exit
2068        };
2069        self
2070    }
2071
2072    /// What happens when the last window is removed. Only meaningful with
2073    /// [`Window::with_windows`].
2074    pub const fn with_last_window_closed(mut self, policy: LastWindowClosed) -> Self {
2075        self.last_window_closed = policy;
2076        self
2077    }
2078
2079    /// Which of the two title bars this window has.
2080    ///
2081    /// `Normal` unless it is said otherwise, because that is the window an application on
2082    /// this platform has now. What the mode is worth in pixels is the design system's, so
2083    /// nothing here is a measurement.
2084    pub const fn with_title_bar(mut self, title_bar: TitleBar) -> Self {
2085        self.title_bar = title_bar;
2086        self
2087    }
2088
2089    /// Names the window. A literal, because this is read once before the window is stood
2090    /// up and never again, so there is nothing for a computed title to change.
2091    pub const fn with_title(mut self, title: &'static str) -> Self {
2092        self.title = title;
2093        self
2094    }
2095
2096    /// Dresses the window in an asset that was registered with [`AssetKind::Png`].
2097    pub const fn with_icon(mut self, asset_id: u32) -> Self {
2098        self.icon = asset_id;
2099        self
2100    }
2101
2102    pub const fn chrome(self, chrome: Chrome) -> Self {
2103        self.with_chrome(chrome)
2104    }
2105
2106    pub const fn with_chrome(mut self, chrome: Chrome) -> Self {
2107        self.chrome = chrome;
2108        self
2109    }
2110
2111    pub const fn with_size(mut self, width: u16, height: u16) -> Self {
2112        self.width = width;
2113        self.height = height;
2114        self
2115    }
2116
2117    pub const fn with_min_size(mut self, width: u16, height: u16) -> Self {
2118        self.min_width = width;
2119        self.min_height = height;
2120        self
2121    }
2122
2123    pub const fn resizable(mut self, resizable: bool) -> Self {
2124        self.resizable = resizable;
2125        self
2126    }
2127
2128    /// Asks the implicit single window to report every whole-dp change of its size.
2129    pub const fn with_report_size(mut self, report: bool) -> Self {
2130        self.report_size = report;
2131        self
2132    }
2133
2134    /// Starts the window at an application zoom level, kept inside `-8..=8`.
2135    pub const fn with_zoom_level(mut self, level: i8) -> Self {
2136        self.zoom_level = Some(if level < -8 {
2137            -8
2138        } else if level > 8 {
2139            8
2140        } else {
2141            level
2142        });
2143        self
2144    }
2145}
2146
2147impl Default for Window {
2148    fn default() -> Self {
2149        Self::new()
2150    }
2151}
2152
2153/// A design system choice plus how it reacts to the host platform.
2154#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2155pub struct Theme {
2156    pub design_system: DesignSystem,
2157    pub fallback: DesignSystem,
2158    pub color_scheme: ColorScheme,
2159    pub adaptive: bool,
2160    /// The font asset each type role resolves to, or zero for the system font.
2161    ///
2162    /// Per theme and not per node. A node that could name its own font would be a node
2163    /// deciding typography, and the whole point of roles is that it does not: an
2164    /// application changes what `Display` is made of, and every title changes with it.
2165    ///
2166    /// Indexed by the role's wire tag minus one, so the array and the enum cannot drift
2167    /// apart without the compiler saying so.
2168    pub fonts: [u32; TYPE_ROLE_COUNT],
2169    /// The application's own colours, laid over whichever design system is chosen.
2170    ///
2171    /// A reference to something that lives for the whole program, so that a theme stays
2172    /// `Copy` and can still be built in a `const`. The same palette goes onto every system
2173    /// an adaptive theme may pick, because a brand colour does not change with the
2174    /// platform. A palette built while the application runs is handed to `use_theme`,
2175    /// which owns it, so application code never has to give it that lifetime itself.
2176    pub palette: Option<&'static crate::palette::Palette>,
2177    /// Application-level overlay theme tokens for Dialog, Menu, Dropdown, and Sheet.
2178    pub overlay: Option<&'static OverlayTokens>,
2179}
2180
2181/// How many type roles there are, which is how many font slots a theme carries.
2182pub const TYPE_ROLE_COUNT: usize = 9;
2183
2184/// Application-level overlay theme tokens for Dialog, Menu, Dropdown, Sheet and the
2185/// transient message. Every measure is in dp, and 0 keeps the design system's own.
2186#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2187pub struct OverlayTokens {
2188    pub sheet_corner_radius: u16,
2189    pub dialog_corner_radius: u16,
2190    pub popup_corner_radius: u16,
2191    pub sheet_elevation: u16,
2192    pub popup_elevation: u16,
2193    pub dialog_elevation: u16,
2194    pub sheet_padding: u16,
2195    pub dialog_padding: u16,
2196    pub popup_padding: u16,
2197    pub scrim: u32,
2198    /// How high the transient message floats, which is how much shadow it casts.
2199    pub toast_elevation: u16,
2200    /// The corner radius of the transient message.
2201    pub toast_corner_radius: u16,
2202}
2203
2204impl OverlayTokens {
2205    pub const fn new() -> Self {
2206        Self {
2207            sheet_corner_radius: 0,
2208            dialog_corner_radius: 0,
2209            popup_corner_radius: 0,
2210            sheet_elevation: 0,
2211            popup_elevation: 0,
2212            dialog_elevation: 0,
2213            sheet_padding: 0,
2214            dialog_padding: 0,
2215            popup_padding: 0,
2216            scrim: 0,
2217            toast_elevation: 0,
2218            toast_corner_radius: 0,
2219        }
2220    }
2221
2222    pub const fn sheet_corner_radius(mut self, radius: u16) -> Self {
2223        self.sheet_corner_radius = radius;
2224        self
2225    }
2226
2227    pub const fn dialog_corner_radius(mut self, radius: u16) -> Self {
2228        self.dialog_corner_radius = radius;
2229        self
2230    }
2231
2232    pub const fn popup_corner_radius(mut self, radius: u16) -> Self {
2233        self.popup_corner_radius = radius;
2234        self
2235    }
2236
2237    pub const fn sheet_elevation(mut self, elevation: u16) -> Self {
2238        self.sheet_elevation = elevation;
2239        self
2240    }
2241
2242    pub const fn popup_elevation(mut self, elevation: u16) -> Self {
2243        self.popup_elevation = elevation;
2244        self
2245    }
2246
2247    pub const fn dialog_elevation(mut self, elevation: u16) -> Self {
2248        self.dialog_elevation = elevation;
2249        self
2250    }
2251
2252    pub const fn sheet_padding(mut self, padding: u16) -> Self {
2253        self.sheet_padding = padding;
2254        self
2255    }
2256
2257    pub const fn dialog_padding(mut self, padding: u16) -> Self {
2258        self.dialog_padding = padding;
2259        self
2260    }
2261
2262    pub const fn popup_padding(mut self, padding: u16) -> Self {
2263        self.popup_padding = padding;
2264        self
2265    }
2266
2267    pub const fn scrim(mut self, color: Color) -> Self {
2268        self.scrim = color.to_argb();
2269        self
2270    }
2271
2272    pub const fn scrim_argb(mut self, argb: u32) -> Self {
2273        self.scrim = argb;
2274        self
2275    }
2276
2277    /// The transient message's elevation in dp: a shadow under it the height of a surface
2278    /// that far up. A message is not a node and takes no Modifier, so this is where its
2279    /// shadow is set, once for the application.
2280    pub const fn toast_elevation(mut self, elevation: u16) -> Self {
2281        self.toast_elevation = elevation;
2282        self
2283    }
2284
2285    /// The transient message's corner radius in dp.
2286    pub const fn toast_corner_radius(mut self, radius: u16) -> Self {
2287        self.toast_corner_radius = radius;
2288        self
2289    }
2290}
2291
2292impl Theme {
2293    /// The same design system on every platform.
2294    pub const fn unified(design_system: DesignSystem) -> Self {
2295        Self {
2296            design_system,
2297            fallback: design_system,
2298            color_scheme: ColorScheme::FollowSystem,
2299            adaptive: false,
2300            fonts: [0; TYPE_ROLE_COUNT],
2301            palette: None,
2302            overlay: None,
2303        }
2304    }
2305
2306    /// Follows the host platform. The fallback is required, so no platform is left undecided.
2307    pub const fn adaptive(fallback: DesignSystem) -> Self {
2308        Self {
2309            design_system: fallback,
2310            fallback,
2311            color_scheme: ColorScheme::FollowSystem,
2312            adaptive: true,
2313            fonts: [0; TYPE_ROLE_COUNT],
2314            palette: None,
2315            overlay: None,
2316        }
2317    }
2318
2319    pub const fn with_color_scheme(mut self, color_scheme: ColorScheme) -> Self {
2320        self.color_scheme = color_scheme;
2321        self
2322    }
2323
2324    /// Resolves one type role to a registered font, leaving every other role alone.
2325    ///
2326    /// The asset has to be registered before the theme that names it reaches the
2327    /// Renderer, the same way a picture does. An id that names nothing is reported and
2328    /// the role falls back to the system font, because a screen in the wrong typeface is
2329    /// better than no screen.
2330    ///
2331    /// `TypeRole::None` has no font slot: text with no role names its own font, so asking
2332    /// the theme to give it one changes nothing.
2333    pub const fn with_font(mut self, role: TypeRole, asset: u32) -> Self {
2334        if let TypeRole::None = role {
2335            return self;
2336        }
2337        self.fonts[role as usize - 1] = asset;
2338        self
2339    }
2340
2341    /// Resolves every type role but `Mono` to one registered font: the application's own
2342    /// typeface, set once for the whole screen.
2343    ///
2344    /// The font is usually a [`crate::FontStack`], so a role is set in the first family and
2345    /// each letter it lacks comes from the next, with the machine's fonts after the last.
2346    /// `Mono` is left alone because code set in a proportional face is columns that no
2347    /// longer line up; name a font for it with [`Theme::with_font`]. A role named before or
2348    /// after this with `with_font` keeps whichever was said last.
2349    pub const fn with_default_font(mut self, asset: u32) -> Self {
2350        let mut index = 0;
2351        while index < TYPE_ROLE_COUNT {
2352            if index != TypeRole::Mono as usize - 1 {
2353                self.fonts[index] = asset;
2354            }
2355            index += 1;
2356        }
2357        self
2358    }
2359
2360    /// Lays the application's own colours over the design system.
2361    ///
2362    /// Only colour roles change. Shape, type, spacing, elevation, motion and how each
2363    /// component is drawn stay the design system's, so the same brand colour reads as
2364    /// that system's button rather than as a copy of another's. Both schemes go to the
2365    /// Renderer at once, so a switch between light and dark never asks the Host.
2366    pub const fn with_palette(mut self, palette: &'static crate::palette::Palette) -> Self {
2367        self.palette = Some(palette);
2368        self
2369    }
2370
2371    /// Overrides overlay geometry, spacing, elevation and scrim for Dialog, Menu, Dropdown and Sheet.
2372    pub const fn with_overlay_tokens(mut self, overlay: &'static OverlayTokens) -> Self {
2373        self.overlay = Some(overlay);
2374        self
2375    }
2376
2377    /// The font this role resolves to, or `None` for the system font.
2378    pub const fn font(&self, role: TypeRole) -> Option<u32> {
2379        if let TypeRole::None = role {
2380            return None;
2381        }
2382        match self.fonts[role as usize - 1] {
2383            0 => None,
2384            asset => Some(asset),
2385        }
2386    }
2387}
2388
2389/// Saying nothing follows the host platform, with Material 3 where the platform has no look
2390/// of its own. The default used to be `unified(Material3)`, on the theory that a default
2391/// looking the same everywhere is more predictable. In practice that meant macOS showed a
2392/// Material screen with no configuration, which is the wrong first impression for a toolkit
2393/// that claims native desktop UI, and it meant nothing on the default path ever exercised
2394/// platform adaptation. A default that can be broken without anyone noticing is not
2395/// predictability. An application that wants one design system everywhere says
2396/// `Theme::unified(..)`, which is a clearer statement of that intent than silence was.
2397impl Default for Theme {
2398    fn default() -> Self {
2399        Self::adaptive(DesignSystem::Material3)
2400    }
2401}
2402
2403#[derive(Clone, Debug, PartialEq)]
2404pub enum Modifier {
2405    Empty,
2406    Padding(f32),
2407    FillMaxWidth,
2408    FillMaxHeight,
2409    Width(f32),
2410    Height(f32),
2411    Size {
2412        width: f32,
2413        height: f32,
2414    },
2415    /// A `Paint`, not a raw ARGB. Colour has exactly one wire representation.
2416    Background(Paint),
2417    Clickable {
2418        handler_id: u64,
2419    },
2420    PaddingRole(SpaceRole),
2421    PaddingEach {
2422        start: f32,
2423        top: f32,
2424        end: f32,
2425        bottom: f32,
2426    },
2427    Weight(f32),
2428    Shape {
2429        top_start: f32,
2430        top_end: f32,
2431        bottom_end: f32,
2432        bottom_start: f32,
2433    },
2434    ShapeRole(ShapeRole),
2435    Border {
2436        width: f32,
2437        paint: Paint,
2438    },
2439    /// One dp value. How the shadow is drawn is the design system's rule: tonal lift plus a
2440    /// shadow, a wide soft shadow, or layered shadow plus a hairline stroke.
2441    Elevation(f32),
2442    /// Asks the Renderer to report this node's measured size, the way it reports the
2443    /// window's.
2444    ///
2445    /// The token is the application's own name for the node. A node id belongs to the
2446    /// Renderer and is never something the Host chose, so a screen that wants to know how
2447    /// wide one of its own containers ended up needs a name it gave itself. The Renderer
2448    /// does not read it.
2449    ///
2450    /// With `every_dp` off, a report goes out on the first measurement and then only when
2451    /// the node's width or height class changes. With it on, a report also goes out when
2452    /// the width or height, rounded to the nearest whole dp, changes inside one class, at
2453    /// most once a frame and carrying the last size. It rides in the second word, which
2454    /// was always zero before, so a record with it off is the same bytes it always was.
2455    ObserveSize {
2456        token: u32,
2457        every_dp: bool,
2458    },
2459    /// Which curve and length this node's changes run along.
2460    ///
2461    /// The node says how important the change is, not how long it takes. Appearing,
2462    /// disappearing, a selection moving and a size changing all read this.
2463    Motion(MotionRole),
2464    /// What this node's surface is made of.
2465    ///
2466    /// A role, so that a system which blurs blurs and a system which does not lifts its
2467    /// surface instead. Nothing here claims a particular effect was achieved.
2468    Material(MaterialRole),
2469    /// Where this node sits inside its parent, in dp from the parent's top left corner.
2470    ///
2471    /// The position a layout engine on the Host already computed. Left and right are the
2472    /// screen's, not the reading direction's: the Host's coordinates are physical, so a
2473    /// right-to-left page is not mirrored a second time here.
2474    Offset {
2475        x: f32,
2476        y: f32,
2477    },
2478    /// This node's size in dp, whatever its parent's constraints say. The size a layout
2479    /// engine on the Host already computed, which is not open to negotiation.
2480    RequiredSize {
2481        width: f32,
2482        height: f32,
2483    },
2484    /// A border whose four sides each have their own width and paint. A node whose four
2485    /// sides are the same uses `Border`.
2486    BorderEach {
2487        top: f32,
2488        right: f32,
2489        bottom: f32,
2490        left: f32,
2491        top_paint: Paint,
2492        right_paint: Paint,
2493        bottom_paint: Paint,
2494        left_paint: Paint,
2495    },
2496    /// A radius in dp for each corner, cut as plain circular arcs. The background, the
2497    /// border, the shadow and a `Clip` all follow it. A node with one radius for every
2498    /// corner uses `Shape`.
2499    CornerEach {
2500        top_left: f32,
2501        top_right: f32,
2502        bottom_right: f32,
2503        bottom_left: f32,
2504    },
2505    /// One shadow cast by this node's outline: an offset, a blur radius and a spread in dp,
2506    /// and its paint, drawn only outside the node as CSS draws `box-shadow`. Several
2507    /// shadows are several of these. A modifier list draws in its order, so a later shadow
2508    /// lies over an earlier one; CSS puts the first of its list on top, so a Host sends
2509    /// that list last to first.
2510    Shadow {
2511        x: f32,
2512        y: f32,
2513        blur: f32,
2514        spread: f32,
2515        paint: Paint,
2516    },
2517    /// Whether this node's content is cut to its outline: the `CornerEach` shape where it
2518    /// has one, its rectangle otherwise.
2519    Clip(bool),
2520    /// Marks this node as a window drag region or excludes it from one.
2521    WindowDrag(bool),
2522    /// The opacity of this node and everything in it, from 0 to 1, applied once to the
2523    /// whole group rather than to each child.
2524    Alpha(f32),
2525    /// Draws this node and everything in it through a 2D affine matrix, the way CSS
2526    /// `transform` does, without changing its layout. Pointer input follows the drawn
2527    /// shape.
2528    ///
2529    /// The matrix means what CSS `matrix(a, b, c, d, e, f)` means: a point `(x, y)` goes to
2530    /// `(a x + c y + e, b x + d y + f)`, with `e` and `f` in dp. The origin is a fraction of
2531    /// the node's measured size, `(0.5, 0.5)` being its centre, so it stays right when the
2532    /// size is decided on the Renderer's side. An origin given as a length is folded into
2533    /// the matrix by the Host, which then sends `(0, 0)`. A matrix with no inverse draws
2534    /// nothing and takes no input. No value may be NaN or infinite.
2535    Transform {
2536        a: f32,
2537        b: f32,
2538        c: f32,
2539        d: f32,
2540        e: f32,
2541        f: f32,
2542        origin_x: f32,
2543        origin_y: f32,
2544    },
2545    /// Draws this node and everything in it through a 4x4 matrix, the way CSS 3D
2546    /// `transform` does, without changing its layout. Pointer input follows the drawn
2547    /// shape.
2548    ///
2549    /// `m` is column-major, in the order of CSS `matrix3d(...)`: element `m[4 * column +
2550    /// row]`. A point `(x, y, z, 1)` goes to `M * (x, y, z, 1)`, and the screen position is
2551    /// the first two results divided by the fourth. Translations are in dp. A perspective
2552    /// term is already inside the matrix, so the Renderer is never sent one separately. The
2553    /// origin means what it means for [`Modifier::Transform`] and its z is zero.
2554    ///
2555    /// `backface_hidden` is CSS `backface-visibility: hidden`: a node whose back is turned
2556    /// to the viewer is not drawn and takes no input. A matrix with no inverse draws
2557    /// nothing. No value may be NaN or infinite.
2558    Transform3D {
2559        m: [f32; 16],
2560        origin_x: f32,
2561        origin_y: f32,
2562        backface_hidden: bool,
2563    },
2564    /// Matches the bounds of a Box parent without contributing to its size.
2565    MatchParentSize,
2566    /// Controls the scale and tint of a pressed surface.
2567    PressFeedback {
2568        scale: f32,
2569        tint_alpha: f32,
2570    },
2571    /// What this node is to assistive technology: its role, and whether it is checked or
2572    /// selected. `state` holds two tri-state fields, `SEMANTICS_CHECKED_SHIFT` and
2573    /// `SEMANTICS_SELECTED_SHIFT` bits up, each 0 for unset, 1 for off and 2 for on. The
2574    /// accessible name is the `AccessibleName` property, because a string does not fit in
2575    /// a modifier's words. Above those, `expanded`, `disabled` and `focused` are tri-state
2576    /// fields in the same encoding, `SEMANTICS_HIDDEN_BIT` leaves the node out of the
2577    /// accessibility tree, and the two action bits expose a click and a focus action.
2578    Semantics {
2579        role: SemanticsRole,
2580        state: u32,
2581    },
2582    /// How this node's rounded corners curve, from 0 to 1. 0 is a plain circular arc; above
2583    /// it the curve starts further along each side and its curvature changes continuously,
2584    /// as Figma's corner smoothing does (0.6 is what iOS looks like). The radius still
2585    /// comes from `Shape`, `ShapeRole` or `CornerEach`; this changes only the curve, and
2586    /// a node without a radius is not changed. A value outside 0..=1 is drawn as the nearer
2587    /// end and NaN as 0, so one bad shape cannot stop the program; see
2588    /// [`corner_smoothing`].
2589    CornerCurve {
2590        smoothing: f32,
2591    },
2592    /// A brush laid into a rectangle of this node and repeated across it, the way CSS
2593    /// draws one `background-image` layer with its `background-position`,
2594    /// `background-size` and `background-repeat`.
2595    ///
2596    /// `x`, `y`, `width` and `height` are the rectangle in dp from the node's top left
2597    /// corner, which is one tile: a gradient is measured against it, not against the node.
2598    /// `repeat` says which ways the tile repeats and `tile_width` and `tile_height` how far
2599    /// apart the copies are, in dp; a plain CSS repeat sends the rectangle's own size, and
2600    /// `space` or `round` the step the Host worked out. The layer is cut to the node's
2601    /// outline, its corners included. Several layers are several of these, the first
2602    /// drawn at the bottom, so a Host sends a CSS list last to first. A tile width or
2603    /// height of zero or less, and any value that is not a finite number, is refused.
2604    BackgroundBrush {
2605        x: f32,
2606        y: f32,
2607        width: f32,
2608        height: f32,
2609        paint: Paint,
2610        repeat: BackgroundRepeat,
2611        tile_width: f32,
2612        tile_height: f32,
2613    },
2614}
2615
2616/// The smoothing a `CornerCurve` value draws with: clamped to 0..=1, NaN as 0.
2617pub fn corner_smoothing(smoothing: f32) -> f32 {
2618    if smoothing.is_nan() {
2619        0.0
2620    } else {
2621        smoothing.clamp(0.0, 1.0)
2622    }
2623}
2624
2625/// Where the checked field sits in `Modifier::Semantics::state`.
2626pub const SEMANTICS_CHECKED_SHIFT: u32 = 0;
2627/// Where the selected field sits in `Modifier::Semantics::state`.
2628pub const SEMANTICS_SELECTED_SHIFT: u32 = 2;
2629/// Where the expanded field sits in `Modifier::Semantics::state`: 0 unset, 1 collapsed,
2630/// 2 expanded.
2631pub const SEMANTICS_EXPANDED_SHIFT: u32 = 4;
2632/// Where the disabled field sits in `Modifier::Semantics::state`: 0 unset, 1 enabled,
2633/// 2 disabled.
2634pub const SEMANTICS_DISABLED_SHIFT: u32 = 6;
2635/// Where the focused field sits in `Modifier::Semantics::state`: 0 unset, 1 not focused,
2636/// 2 focused.
2637pub const SEMANTICS_FOCUSED_SHIFT: u32 = 8;
2638/// The bit that excludes the node from the accessibility tree.
2639pub const SEMANTICS_HIDDEN_BIT: u32 = 1 << 10;
2640/// The bit that exposes a click action on the node.
2641pub const SEMANTICS_CLICK_ACTION_BIT: u32 = 1 << 11;
2642/// The bit that exposes a focus action on the node.
2643pub const SEMANTICS_FOCUS_ACTION_BIT: u32 = 1 << 12;
2644/// Every bit `Modifier::Semantics::state` may set.
2645pub const SEMANTICS_STATE_MASK: u32 = 0b1_1111_1111_1111;
2646
2647impl Modifier {
2648    /// A border with a width and a paint for each side, in CSS order: top, right, bottom,
2649    /// left.
2650    ///
2651    /// Four equal sides are one `Border`, which is the record every node with an ordinary
2652    /// border already sends. `BorderEach` is written only where the sides differ, so a
2653    /// screen that never mixes them never sends the longer record.
2654    pub fn border_sides(widths: [f32; 4], paints: [Paint; 4]) -> Self {
2655        let [top, right, bottom, left] = widths;
2656        let [top_paint, right_paint, bottom_paint, left_paint] = paints;
2657        if widths.iter().all(|width| width.to_bits() == top.to_bits())
2658            && paints.iter().all(|paint| *paint == top_paint)
2659        {
2660            return Self::Border {
2661                width: top,
2662                paint: top_paint,
2663            };
2664        }
2665        Self::BorderEach {
2666            top,
2667            right,
2668            bottom,
2669            left,
2670            top_paint,
2671            right_paint,
2672            bottom_paint,
2673            left_paint,
2674        }
2675    }
2676
2677    /// A radius for each corner, in CSS order: top left, top right, bottom right, bottom
2678    /// left.
2679    ///
2680    /// One radius for every corner is one `Shape`, the record an ordinary rounded node
2681    /// already sends. `CornerEach` is written only where the corners differ.
2682    pub fn corner_radii(radii: [f32; 4]) -> Self {
2683        let [top_left, top_right, bottom_right, bottom_left] = radii;
2684        if radii
2685            .iter()
2686            .all(|radius| radius.to_bits() == top_left.to_bits())
2687        {
2688            return Self::Shape {
2689                top_start: top_left,
2690                top_end: top_left,
2691                bottom_end: top_left,
2692                bottom_start: top_left,
2693            };
2694        }
2695        Self::CornerEach {
2696            top_left,
2697            top_right,
2698            bottom_right,
2699            bottom_left,
2700        }
2701    }
2702}
2703
2704/// The timing function of one keyframe, which shapes the segment to the next one.
2705#[derive(Clone, Copy, Debug, PartialEq)]
2706pub enum Timing {
2707    Linear,
2708    /// CSS `cubic-bezier(x1, y1, x2, y2)`. Both `x` values are between 0 and 1.
2709    CubicBezier {
2710        x1: f32,
2711        y1: f32,
2712        x2: f32,
2713        y2: f32,
2714    },
2715    /// CSS `steps(count, position)`. `count` is at least 1, and at least 2 for `JumpNone`.
2716    Steps {
2717        count: u32,
2718        position: StepPosition,
2719    },
2720}
2721
2722/// One CSS transform function: its kind and up to six values, the unused ones zero.
2723///
2724/// `Translate` is x and y in dp, `Rotate` degrees (not folded into one turn, so 0 to 720
2725/// is two turns), `Scale` x and y, `Skew` the x and y angles in degrees, and `Matrix` the
2726/// six values of CSS `matrix()`.
2727#[derive(Clone, Copy, Debug, PartialEq)]
2728pub struct TransformFunction {
2729    pub kind: TransformFunctionKind,
2730    pub values: [f32; 6],
2731}
2732
2733impl TransformFunction {
2734    pub const fn translate(x: f32, y: f32) -> Self {
2735        Self {
2736            kind: TransformFunctionKind::Translate,
2737            values: [x, y, 0.0, 0.0, 0.0, 0.0],
2738        }
2739    }
2740
2741    pub const fn rotate(degrees: f32) -> Self {
2742        Self {
2743            kind: TransformFunctionKind::Rotate,
2744            values: [degrees, 0.0, 0.0, 0.0, 0.0, 0.0],
2745        }
2746    }
2747
2748    pub const fn scale(x: f32, y: f32) -> Self {
2749        Self {
2750            kind: TransformFunctionKind::Scale,
2751            values: [x, y, 0.0, 0.0, 0.0, 0.0],
2752        }
2753    }
2754
2755    pub const fn skew(x_degrees: f32, y_degrees: f32) -> Self {
2756        Self {
2757            kind: TransformFunctionKind::Skew,
2758            values: [x_degrees, y_degrees, 0.0, 0.0, 0.0, 0.0],
2759        }
2760    }
2761
2762    pub const fn matrix(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
2763        Self {
2764            kind: TransformFunctionKind::Matrix,
2765            values: [a, b, c, d, e, f],
2766        }
2767    }
2768}
2769
2770/// What one keyframe holds, which has to be what its animation's property takes.
2771#[derive(Clone, Debug, PartialEq)]
2772pub enum KeyframeValue {
2773    Alpha(f32),
2774    /// For `Color`, `Background` and `BorderColor`. A role is resolved against the theme every frame.
2775    Paint(Paint),
2776    /// A list of functions, the same kinds in the same order in every keyframe.
2777    Transform(std::borrow::Cow<'static, [TransformFunction]>),
2778}
2779
2780#[derive(Clone, Debug, PartialEq)]
2781pub struct Keyframe {
2782    /// Where in an iteration this keyframe stands, from 0 to 1.
2783    pub offset: f32,
2784    /// How the segment from this keyframe to the next is timed.
2785    pub timing: Timing,
2786    /// Starts from whatever the node shows the frame this animation begins, instead of
2787    /// `value`. Only the first keyframe may say so: it is how a CSS transition that
2788    /// changes course midway carries on from where it was without a jump.
2789    pub from_presented: bool,
2790    pub value: KeyframeValue,
2791}
2792
2793/// Which moments of an animation are reported back as `AnimationEvent`s. None, the
2794/// default, means the animation plays to its end without a single call to the Host.
2795#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2796pub struct AnimationEvents(pub u8);
2797
2798impl AnimationEvents {
2799    pub const NONE: Self = Self(0);
2800    pub const READY: Self = Self(1);
2801    pub const ACTIVE: Self = Self(2);
2802    pub const ITERATION: Self = Self(4);
2803    pub const END: Self = Self(8);
2804    /// Every kind there is. A bit outside these is a record the sides disagree about.
2805    pub const ALL: Self = Self(1 | 2 | 4 | 8);
2806
2807    pub const fn union(self, other: Self) -> Self {
2808        Self(self.0 | other.0)
2809    }
2810
2811    pub const fn contains(self, other: Self) -> bool {
2812        self.0 & other.0 == other.0
2813    }
2814}
2815
2816/// One animation the Renderer plays to its end on its own frame clock, described once.
2817///
2818/// The key is `(node_id, property, slot)`: a second animation with the same key replaces
2819/// the first, and of the ones in effect on one property the highest slot is what shows.
2820/// Timing follows Web Animations Level 1, which is what CSS transitions and animations
2821/// are defined by.
2822#[derive(Clone, Debug, PartialEq)]
2823pub struct Animation<'a> {
2824    pub node_id: u32,
2825    /// The Host's own name for the animation, returned in every event about it.
2826    pub animation_id: u32,
2827    pub property: AnimatedProperty,
2828    pub slot: u8,
2829    pub direction: PlaybackDirection,
2830    pub fill: FillMode,
2831    pub play_state: PlayState,
2832    /// For `Color`, `Background` and `BorderColor` only, and required there.
2833    pub interpolation: Option<ColorInterpolation>,
2834    /// On the Renderer's frame clock. Zero starts it on the first frame that draws it.
2835    pub start_time_nanos: u64,
2836    /// May be negative, which starts it partway through.
2837    pub delay_ms: f32,
2838    pub duration_ms: f32,
2839    /// Infinite for `infinite`.
2840    pub iterations: f32,
2841    /// For `Transform` only: the origin, as a fraction of the node's size.
2842    pub origin_x: f32,
2843    pub origin_y: f32,
2844    pub events: AnimationEvents,
2845    /// At least two, the first at offset 0 and the last at 1.
2846    pub keyframes: std::borrow::Cow<'a, [Keyframe]>,
2847}
2848
2849const NO_FIELDS: &[FieldSchema] = &[];
2850const OBSERVE_SIZE_FIELDS: &[FieldSchema] = &[
2851    FieldSchema {
2852        name: "token",
2853        ty: FieldType::U32,
2854        slot: FieldSlot::FirstLow,
2855    },
2856    FieldSchema {
2857        name: "everyDp",
2858        ty: FieldType::Bool,
2859        slot: FieldSlot::Second,
2860    },
2861];
2862const VALUE_FLOAT_FIELD: &[FieldSchema] = &[FieldSchema {
2863    name: "value",
2864    ty: FieldType::Float,
2865    slot: FieldSlot::FirstLow,
2866}];
2867const SIZE_FIELDS: &[FieldSchema] = &[
2868    FieldSchema {
2869        name: "width",
2870        ty: FieldType::Float,
2871        slot: FieldSlot::FirstLow,
2872    },
2873    FieldSchema {
2874        name: "height",
2875        ty: FieldType::Float,
2876        slot: FieldSlot::SecondLow,
2877    },
2878];
2879const PAINT_FIELD: &[FieldSchema] = &[FieldSchema {
2880    name: "paint",
2881    ty: FieldType::Paint,
2882    slot: FieldSlot::First,
2883}];
2884const HANDLER_ID_FIELD: &[FieldSchema] = &[FieldSchema {
2885    name: "handlerId",
2886    ty: FieldType::U64,
2887    slot: FieldSlot::First,
2888}];
2889const SPACE_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
2890    name: "role",
2891    ty: FieldType::Role("SpaceRole"),
2892    slot: FieldSlot::FirstLow,
2893}];
2894const SHAPE_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
2895    name: "role",
2896    ty: FieldType::Role("ShapeRole"),
2897    slot: FieldSlot::FirstLow,
2898}];
2899const MOTION_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
2900    name: "role",
2901    ty: FieldType::Role("MotionRole"),
2902    slot: FieldSlot::FirstLow,
2903}];
2904const MATERIAL_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
2905    name: "role",
2906    ty: FieldType::Role("MaterialRole"),
2907    slot: FieldSlot::FirstLow,
2908}];
2909const PADDING_EACH_FIELDS: &[FieldSchema] = &[
2910    FieldSchema {
2911        name: "start",
2912        ty: FieldType::Float,
2913        slot: FieldSlot::FirstLow,
2914    },
2915    FieldSchema {
2916        name: "top",
2917        ty: FieldType::Float,
2918        slot: FieldSlot::FirstHigh,
2919    },
2920    FieldSchema {
2921        name: "end",
2922        ty: FieldType::Float,
2923        slot: FieldSlot::SecondLow,
2924    },
2925    FieldSchema {
2926        name: "bottom",
2927        ty: FieldType::Float,
2928        slot: FieldSlot::SecondHigh,
2929    },
2930];
2931const SHAPE_FIELDS: &[FieldSchema] = &[
2932    FieldSchema {
2933        name: "topStart",
2934        ty: FieldType::Float,
2935        slot: FieldSlot::FirstLow,
2936    },
2937    FieldSchema {
2938        name: "topEnd",
2939        ty: FieldType::Float,
2940        slot: FieldSlot::FirstHigh,
2941    },
2942    FieldSchema {
2943        name: "bottomEnd",
2944        ty: FieldType::Float,
2945        slot: FieldSlot::SecondLow,
2946    },
2947    FieldSchema {
2948        name: "bottomStart",
2949        ty: FieldType::Float,
2950        slot: FieldSlot::SecondHigh,
2951    },
2952];
2953const BORDER_FIELDS: &[FieldSchema] = &[
2954    FieldSchema {
2955        name: "width",
2956        ty: FieldType::Float,
2957        slot: FieldSlot::FirstLow,
2958    },
2959    FieldSchema {
2960        name: "paint",
2961        ty: FieldType::Paint,
2962        slot: FieldSlot::Second,
2963    },
2964];
2965const OFFSET_FIELDS: &[FieldSchema] = &[
2966    FieldSchema {
2967        name: "x",
2968        ty: FieldType::Float,
2969        slot: FieldSlot::FirstLow,
2970    },
2971    FieldSchema {
2972        name: "y",
2973        ty: FieldType::Float,
2974        slot: FieldSlot::SecondLow,
2975    },
2976];
2977const BORDER_EACH_FIELDS: &[FieldSchema] = &[
2978    FieldSchema {
2979        name: "top",
2980        ty: FieldType::Float,
2981        slot: FieldSlot::FirstLow,
2982    },
2983    FieldSchema {
2984        name: "right",
2985        ty: FieldType::Float,
2986        slot: FieldSlot::FirstHigh,
2987    },
2988    FieldSchema {
2989        name: "bottom",
2990        ty: FieldType::Float,
2991        slot: FieldSlot::SecondLow,
2992    },
2993    FieldSchema {
2994        name: "left",
2995        ty: FieldType::Float,
2996        slot: FieldSlot::SecondHigh,
2997    },
2998    FieldSchema {
2999        name: "topPaint",
3000        ty: FieldType::Paint,
3001        slot: FieldSlot::Third,
3002    },
3003    FieldSchema {
3004        name: "rightPaint",
3005        ty: FieldType::Paint,
3006        slot: FieldSlot::Fourth,
3007    },
3008    FieldSchema {
3009        name: "bottomPaint",
3010        ty: FieldType::Paint,
3011        slot: FieldSlot::Fifth,
3012    },
3013    FieldSchema {
3014        name: "leftPaint",
3015        ty: FieldType::Paint,
3016        slot: FieldSlot::Sixth,
3017    },
3018];
3019const CORNER_EACH_FIELDS: &[FieldSchema] = &[
3020    FieldSchema {
3021        name: "topLeft",
3022        ty: FieldType::Float,
3023        slot: FieldSlot::FirstLow,
3024    },
3025    FieldSchema {
3026        name: "topRight",
3027        ty: FieldType::Float,
3028        slot: FieldSlot::FirstHigh,
3029    },
3030    FieldSchema {
3031        name: "bottomRight",
3032        ty: FieldType::Float,
3033        slot: FieldSlot::SecondLow,
3034    },
3035    FieldSchema {
3036        name: "bottomLeft",
3037        ty: FieldType::Float,
3038        slot: FieldSlot::SecondHigh,
3039    },
3040];
3041const SHADOW_FIELDS: &[FieldSchema] = &[
3042    FieldSchema {
3043        name: "x",
3044        ty: FieldType::Float,
3045        slot: FieldSlot::FirstLow,
3046    },
3047    FieldSchema {
3048        name: "y",
3049        ty: FieldType::Float,
3050        slot: FieldSlot::FirstHigh,
3051    },
3052    FieldSchema {
3053        name: "blur",
3054        ty: FieldType::Float,
3055        slot: FieldSlot::SecondLow,
3056    },
3057    FieldSchema {
3058        name: "spread",
3059        ty: FieldType::Float,
3060        slot: FieldSlot::SecondHigh,
3061    },
3062    FieldSchema {
3063        name: "paint",
3064        ty: FieldType::Paint,
3065        slot: FieldSlot::Third,
3066    },
3067];
3068const TRANSFORM_FIELDS: &[FieldSchema] = &[
3069    FieldSchema {
3070        name: "a",
3071        ty: FieldType::FiniteFloat,
3072        slot: FieldSlot::FirstLow,
3073    },
3074    FieldSchema {
3075        name: "b",
3076        ty: FieldType::FiniteFloat,
3077        slot: FieldSlot::FirstHigh,
3078    },
3079    FieldSchema {
3080        name: "c",
3081        ty: FieldType::FiniteFloat,
3082        slot: FieldSlot::SecondLow,
3083    },
3084    FieldSchema {
3085        name: "d",
3086        ty: FieldType::FiniteFloat,
3087        slot: FieldSlot::SecondHigh,
3088    },
3089    FieldSchema {
3090        name: "e",
3091        ty: FieldType::FiniteFloat,
3092        slot: FieldSlot::ThirdLow,
3093    },
3094    FieldSchema {
3095        name: "f",
3096        ty: FieldType::FiniteFloat,
3097        slot: FieldSlot::ThirdHigh,
3098    },
3099    FieldSchema {
3100        name: "originX",
3101        ty: FieldType::FiniteFloat,
3102        slot: FieldSlot::FourthLow,
3103    },
3104    FieldSchema {
3105        name: "originY",
3106        ty: FieldType::FiniteFloat,
3107        slot: FieldSlot::FourthHigh,
3108    },
3109];
3110const TRANSFORM_3D_FIELDS: &[FieldSchema] = &[
3111    FieldSchema {
3112        name: "m0",
3113        ty: FieldType::FiniteFloat,
3114        slot: FieldSlot::FirstLow,
3115    },
3116    FieldSchema {
3117        name: "m1",
3118        ty: FieldType::FiniteFloat,
3119        slot: FieldSlot::FirstHigh,
3120    },
3121    FieldSchema {
3122        name: "m2",
3123        ty: FieldType::FiniteFloat,
3124        slot: FieldSlot::SecondLow,
3125    },
3126    FieldSchema {
3127        name: "m3",
3128        ty: FieldType::FiniteFloat,
3129        slot: FieldSlot::SecondHigh,
3130    },
3131    FieldSchema {
3132        name: "m4",
3133        ty: FieldType::FiniteFloat,
3134        slot: FieldSlot::ThirdLow,
3135    },
3136    FieldSchema {
3137        name: "m5",
3138        ty: FieldType::FiniteFloat,
3139        slot: FieldSlot::ThirdHigh,
3140    },
3141    FieldSchema {
3142        name: "m6",
3143        ty: FieldType::FiniteFloat,
3144        slot: FieldSlot::FourthLow,
3145    },
3146    FieldSchema {
3147        name: "m7",
3148        ty: FieldType::FiniteFloat,
3149        slot: FieldSlot::FourthHigh,
3150    },
3151    FieldSchema {
3152        name: "m8",
3153        ty: FieldType::FiniteFloat,
3154        slot: FieldSlot::FifthLow,
3155    },
3156    FieldSchema {
3157        name: "m9",
3158        ty: FieldType::FiniteFloat,
3159        slot: FieldSlot::FifthHigh,
3160    },
3161    FieldSchema {
3162        name: "m10",
3163        ty: FieldType::FiniteFloat,
3164        slot: FieldSlot::SixthLow,
3165    },
3166    FieldSchema {
3167        name: "m11",
3168        ty: FieldType::FiniteFloat,
3169        slot: FieldSlot::SixthHigh,
3170    },
3171    FieldSchema {
3172        name: "m12",
3173        ty: FieldType::FiniteFloat,
3174        slot: FieldSlot::SeventhLow,
3175    },
3176    FieldSchema {
3177        name: "m13",
3178        ty: FieldType::FiniteFloat,
3179        slot: FieldSlot::SeventhHigh,
3180    },
3181    FieldSchema {
3182        name: "m14",
3183        ty: FieldType::FiniteFloat,
3184        slot: FieldSlot::EighthLow,
3185    },
3186    FieldSchema {
3187        name: "m15",
3188        ty: FieldType::FiniteFloat,
3189        slot: FieldSlot::EighthHigh,
3190    },
3191    FieldSchema {
3192        name: "originX",
3193        ty: FieldType::FiniteFloat,
3194        slot: FieldSlot::NinthLow,
3195    },
3196    FieldSchema {
3197        name: "originY",
3198        ty: FieldType::FiniteFloat,
3199        slot: FieldSlot::NinthHigh,
3200    },
3201    FieldSchema {
3202        name: "backfaceHidden",
3203        ty: FieldType::Bool,
3204        slot: FieldSlot::Tenth,
3205    },
3206];
3207const ENABLED_FIELD: &[FieldSchema] = &[FieldSchema {
3208    name: "enabled",
3209    ty: FieldType::Bool,
3210    slot: FieldSlot::First,
3211}];
3212
3213const PRESS_FEEDBACK_FIELDS: &[FieldSchema] = &[
3214    FieldSchema {
3215        name: "scale",
3216        ty: FieldType::Float,
3217        slot: FieldSlot::FirstLow,
3218    },
3219    FieldSchema {
3220        name: "tintAlpha",
3221        ty: FieldType::Float,
3222        slot: FieldSlot::FirstHigh,
3223    },
3224];
3225
3226const CORNER_CURVE_FIELDS: &[FieldSchema] = &[FieldSchema {
3227    name: "smoothing",
3228    ty: FieldType::Float,
3229    slot: FieldSlot::FirstLow,
3230}];
3231
3232const BACKGROUND_BRUSH_FIELDS: &[FieldSchema] = &[
3233    FieldSchema {
3234        name: "x",
3235        ty: FieldType::FiniteFloat,
3236        slot: FieldSlot::FirstLow,
3237    },
3238    FieldSchema {
3239        name: "y",
3240        ty: FieldType::FiniteFloat,
3241        slot: FieldSlot::FirstHigh,
3242    },
3243    FieldSchema {
3244        name: "width",
3245        ty: FieldType::FiniteFloat,
3246        slot: FieldSlot::SecondLow,
3247    },
3248    FieldSchema {
3249        name: "height",
3250        ty: FieldType::FiniteFloat,
3251        slot: FieldSlot::SecondHigh,
3252    },
3253    FieldSchema {
3254        name: "paint",
3255        ty: FieldType::Paint,
3256        slot: FieldSlot::Third,
3257    },
3258    FieldSchema {
3259        name: "repeat",
3260        ty: FieldType::Role("BackgroundRepeat"),
3261        slot: FieldSlot::FourthLow,
3262    },
3263    FieldSchema {
3264        name: "tile_width",
3265        ty: FieldType::FiniteFloat,
3266        slot: FieldSlot::FifthLow,
3267    },
3268    FieldSchema {
3269        name: "tile_height",
3270        ty: FieldType::FiniteFloat,
3271        slot: FieldSlot::FifthHigh,
3272    },
3273];
3274
3275const SEMANTICS_FIELDS: &[FieldSchema] = &[
3276    FieldSchema {
3277        name: "role",
3278        ty: FieldType::Role("SemanticsRole"),
3279        slot: FieldSlot::FirstLow,
3280    },
3281    FieldSchema {
3282        name: "state",
3283        ty: FieldType::U32,
3284        slot: FieldSlot::SecondLow,
3285    },
3286];
3287
3288pub const MODIFIER_SCHEMA: &[VariantSchema] = &[
3289    VariantSchema {
3290        name: "Empty",
3291        tag: 0,
3292        extra_words: 0,
3293        fields: NO_FIELDS,
3294    },
3295    VariantSchema {
3296        name: "Padding",
3297        tag: 1,
3298        extra_words: 0,
3299        fields: VALUE_FLOAT_FIELD,
3300    },
3301    VariantSchema {
3302        name: "FillMaxWidth",
3303        tag: 2,
3304        extra_words: 0,
3305        fields: NO_FIELDS,
3306    },
3307    VariantSchema {
3308        name: "FillMaxHeight",
3309        tag: 3,
3310        extra_words: 0,
3311        fields: NO_FIELDS,
3312    },
3313    VariantSchema {
3314        name: "Width",
3315        tag: 4,
3316        extra_words: 0,
3317        fields: VALUE_FLOAT_FIELD,
3318    },
3319    VariantSchema {
3320        name: "Height",
3321        tag: 5,
3322        extra_words: 0,
3323        fields: VALUE_FLOAT_FIELD,
3324    },
3325    VariantSchema {
3326        name: "Size",
3327        tag: 6,
3328        extra_words: 0,
3329        fields: SIZE_FIELDS,
3330    },
3331    VariantSchema {
3332        name: "Background",
3333        tag: 7,
3334        extra_words: 0,
3335        fields: PAINT_FIELD,
3336    },
3337    VariantSchema {
3338        name: "Clickable",
3339        tag: 8,
3340        extra_words: 0,
3341        fields: HANDLER_ID_FIELD,
3342    },
3343    VariantSchema {
3344        name: "PaddingRole",
3345        tag: 9,
3346        extra_words: 0,
3347        fields: SPACE_ROLE_FIELD,
3348    },
3349    VariantSchema {
3350        name: "PaddingEach",
3351        tag: 10,
3352        extra_words: 0,
3353        fields: PADDING_EACH_FIELDS,
3354    },
3355    VariantSchema {
3356        name: "Weight",
3357        tag: 11,
3358        extra_words: 0,
3359        fields: VALUE_FLOAT_FIELD,
3360    },
3361    VariantSchema {
3362        name: "Shape",
3363        tag: 12,
3364        extra_words: 0,
3365        fields: SHAPE_FIELDS,
3366    },
3367    VariantSchema {
3368        name: "ShapeRole",
3369        tag: 13,
3370        extra_words: 0,
3371        fields: SHAPE_ROLE_FIELD,
3372    },
3373    VariantSchema {
3374        name: "Border",
3375        tag: 14,
3376        extra_words: 0,
3377        fields: BORDER_FIELDS,
3378    },
3379    VariantSchema {
3380        name: "Elevation",
3381        tag: 15,
3382        extra_words: 0,
3383        fields: VALUE_FLOAT_FIELD,
3384    },
3385    VariantSchema {
3386        name: "ObserveSize",
3387        tag: 16,
3388        extra_words: 0,
3389        fields: OBSERVE_SIZE_FIELDS,
3390    },
3391    VariantSchema {
3392        name: "Motion",
3393        tag: 17,
3394        extra_words: 0,
3395        fields: MOTION_ROLE_FIELD,
3396    },
3397    VariantSchema {
3398        name: "Material",
3399        tag: 18,
3400        extra_words: 0,
3401        fields: MATERIAL_ROLE_FIELD,
3402    },
3403    // The elements an HTML and CSS screen is drawn with: boxes a layout engine on the Host
3404    // already placed and sized, and the decoration CSS gives them.
3405    VariantSchema {
3406        name: "Offset",
3407        tag: 19,
3408        extra_words: 0,
3409        fields: OFFSET_FIELDS,
3410    },
3411    VariantSchema {
3412        name: "RequiredSize",
3413        tag: 20,
3414        extra_words: 0,
3415        fields: SIZE_FIELDS,
3416    },
3417    // Four widths and four paints are six words, so the record is four words longer.
3418    VariantSchema {
3419        name: "BorderEach",
3420        tag: 21,
3421        extra_words: 4,
3422        fields: BORDER_EACH_FIELDS,
3423    },
3424    VariantSchema {
3425        name: "CornerEach",
3426        tag: 22,
3427        extra_words: 0,
3428        fields: CORNER_EACH_FIELDS,
3429    },
3430    // Four measurements and a paint are three words, one more than the record has.
3431    VariantSchema {
3432        name: "Shadow",
3433        tag: 23,
3434        extra_words: 1,
3435        fields: SHADOW_FIELDS,
3436    },
3437    VariantSchema {
3438        name: "Clip",
3439        tag: 24,
3440        extra_words: 0,
3441        fields: ENABLED_FIELD,
3442    },
3443    VariantSchema {
3444        name: "Alpha",
3445        tag: 25,
3446        extra_words: 0,
3447        fields: VALUE_FLOAT_FIELD,
3448    },
3449    // Eight `f32` are four words: the linear part, the translation, and the origin.
3450    VariantSchema {
3451        name: "Transform",
3452        tag: 26,
3453        extra_words: 2,
3454        fields: TRANSFORM_FIELDS,
3455    },
3456    // Sixteen matrix values, the origin and a flag are ten words, eight more than the two
3457    // every record has: 92 bytes.
3458    VariantSchema {
3459        name: "Transform3D",
3460        tag: 27,
3461        extra_words: 8,
3462        fields: TRANSFORM_3D_FIELDS,
3463    },
3464    VariantSchema {
3465        name: "MatchParentSize",
3466        tag: 31,
3467        extra_words: 0,
3468        fields: NO_FIELDS,
3469    },
3470    VariantSchema {
3471        name: "WindowDrag",
3472        tag: 32,
3473        extra_words: 0,
3474        fields: ENABLED_FIELD,
3475    },
3476    VariantSchema {
3477        name: "PressFeedback",
3478        tag: 33,
3479        extra_words: 0,
3480        fields: PRESS_FEEDBACK_FIELDS,
3481    },
3482    VariantSchema {
3483        name: "Semantics",
3484        tag: 34,
3485        extra_words: 0,
3486        fields: SEMANTICS_FIELDS,
3487    },
3488    // 28 to 30 were set aside for modifiers that have not landed, so the next free tag is
3489    // 35.
3490    VariantSchema {
3491        name: "CornerCurve",
3492        tag: 35,
3493        extra_words: 0,
3494        fields: CORNER_CURVE_FIELDS,
3495    },
3496    // 28, the first of the tags set aside above. The rectangle, the paint, the repeat and
3497    // the step take five words, three past the two every record has.
3498    VariantSchema {
3499        name: "BackgroundBrush",
3500        tag: 28,
3501        extra_words: 3,
3502        fields: BACKGROUND_BRUSH_FIELDS,
3503    },
3504];
3505
3506#[derive(Clone, Debug, PartialEq)]
3507pub struct ColumnProps {
3508    pub enabled: bool,
3509}
3510
3511#[derive(Clone, Debug, PartialEq)]
3512pub struct RowProps {
3513    pub enabled: bool,
3514}
3515
3516#[derive(Clone, Debug, PartialEq)]
3517pub struct BoxProps {
3518    pub enabled: bool,
3519}
3520
3521#[derive(Clone, Debug, PartialEq)]
3522pub struct TextProps<'a> {
3523    pub text: &'a str,
3524}
3525
3526#[derive(Clone, Debug, PartialEq)]
3527pub struct TextFieldProps<'a> {
3528    pub placeholder: &'a str,
3529    pub enabled: bool,
3530    pub multiline: bool,
3531}
3532
3533#[derive(Clone, Debug, PartialEq)]
3534pub struct ButtonProps<'a> {
3535    pub text: &'a str,
3536    pub enabled: bool,
3537}
3538
3539#[derive(Clone, Debug, PartialEq)]
3540pub struct SpacerProps {
3541    pub width: f32,
3542    pub height: f32,
3543}
3544
3545crate::extensions::define_property_schema_with_extensions!(define_wire_enum; PROPERTY_SCHEMA, PropertyKind {
3546    Text = 1,
3547    Placeholder = 2,
3548    Enabled = 3,
3549    Multiline = 4,
3550    OnClick = 5,
3551    OnValueChange = 6,
3552    OnSubmit = 7,
3553    OnFocusLost = 8,
3554    OnKeyDown = 9,
3555    ItemCount = 10,
3556    ItemKey = 11,
3557    OnRangeRequested = 12,
3558    TypeRole = 13,
3559    FontSize = 14,
3560    FontWeight = 15,
3561    LineHeight = 16,
3562    LetterSpacing = 17,
3563    Color = 18,
3564    TextAlign = 19,
3565    MaxLines = 20,
3566    Overflow = 21,
3567    Arrangement = 22,
3568    Spacing = 23,
3569    SpaceRole = 24,
3570    Alignment = 25,
3571    Variant = 26,
3572    // The id of an asset the Host registered. Image and Icon carry nothing else: the
3573    // bytes were copied into the Renderer's cache once, at registration.
3574    Asset = 28,
3575    // Whether a toggle is on. One boolean for all three of them: a selected radio button
3576    // and a switch that is on are the same fact, and the schema names a concept once.
3577    Checked = 32,
3578    // Discrete stops between a slider's two ends. Zero leaves it continuous.
3579    Steps = 33,
3580    // Whether a progress indicator knows how far along it is. When it does not, `Value`
3581    // is not read at all.
3582    Determinate = 34,
3583    // Which of the two forms a progress indicator takes. It picks a shape, not a size.
3584    Circular = 35,
3585    // A divider's axis, which is the only thing a divider carries.
3586    Vertical = 36,
3587    // Whether an overlay is showing. The Renderer owns the state; this seeds it and
3588    // carries changes that came from outside the Renderer.
3589    Open = 40,
3590    OnDismiss = 41,
3591    SelectedIndex = 42,
3592    // The Canvas drawing command list, a byte blob in the batch arena.
3593    Commands = 50,
3594    // The control's current value, in the widget's own unit: an epoch day count for a
3595    // date, minutes since midnight for a time, the chosen position for a Dropdown, a
3596    // position between the ends for a Slider, a fraction for a ProgressIndicator. One tag,
3597    // because "this control's value" is one concept whichever widget holds it.
3598    Value = 51,
3599    // The ends of the selectable range, in the same unit as `Value`.
3600    Min = 52,
3601    Max = 53,
3602    // The meaning of the icon a destination carries, as an `IconRole` tag. Tag 0 is "not
3603    // sent", so a destination without an icon is label only.
3604    Icon = 60,
3605    // Which slot of a Scaffold this subtree fills.
3606    Slot = 61,
3607    // How many columns a grid has, where the screen said a number.
3608    Columns = 62,
3609    // How narrow a column may get before the grid drops one, where the screen said a
3610    // width instead of a number. The Renderer divides its own width by this, which is the
3611    // same kind of judgement as a size class and for the same reason is not the Host's.
3612    MinColumnWidth = 63,
3613    // Runs of different treatment inside one string, as a blob of fixed-length records.
3614    // A Text that says nothing about runs carries none of this and travels as it always
3615    // did.
3616    Spans = 64,
3617    // A node that is willing to have files dropped on it. The handler is the willingness:
3618    // a node without one is never told that files are over it, which is what keeps a
3619    // screen from lighting up every container it has.
3620    OnFilesEntered = 65,
3621    OnFilesDropped = 66,
3622    // Which named group of a navigation's strip this destination belongs to. Neighbouring
3623    // destinations carrying the same string are one group and the string is its heading.
3624    //
3625    // 76 rather than 67, because 67 through 74 are reserved for the gestures and 75 for a
3626    // button's kind. A reservation is not a free tag.
3627    Section = 76,
3628    // How many a badge counts. Absent rather than zero when the badge is a dot or a word,
3629    // because "no count" and "a count of nothing" are different things to show.
3630    //
3631    // 80 opens a fresh block of ten for widget tags 40 onwards, after the last tag in use
3632    // anywhere, so two pieces of work landing in either order cannot collide.
3633    Count = 80,
3634    // Whether a split pane's side pane may be folded away by dragging past its minimum, or
3635    // by the keyboard, or by the platform's own show and hide sidebar command. Whether a
3636    // side pane can be hidden is a fact about the screen rather than about the design
3637    // system: a list of sessions can, a settings screen's list of sections cannot.
3638    //
3639    // 90 opens the split pane's block of ten, after the badge's.
3640    Collapsible = 90,
3641    // What a button is, as opposed to how loud it is: a plain button, or one key of a
3642    // dense grid of them. The variant still chooses the fill and the emphasis; this lets
3643    // the design system give the key its own calculator's silhouette, so the Host never
3644    // sends a radius or a shape role to get one.
3645    //
3646    // 75 was reserved for this when the gestures took 67 through 74, and stays here
3647    // rather than with its neighbours by number because this list is append only.
3648    ButtonKind = 75,
3649    // A code editor's own block, after the split pane's.
3650    //
3651    // The decorations over the document and the colour runs of its syntax, each a blob of
3652    // fixed-length records sent whole. Every record carries the document version its
3653    // range was written against, so a list computed while the reader kept typing still
3654    // lands where it was meant to.
3655    Decorations = 100,
3656    SyntaxSpans = 101,
3657    // How many columns a tab advances. Absent leaves it to the design system.
3658    TabWidth = 102,
3659    OnEditRejected = 103,
3660    OnHover = 104,
3661    OnSave = 105,
3662    // One listener for every decoration that can be pressed or accepted. The ids inside
3663    // the decoration blob are the application's own names for them, not handlers, so the
3664    // event carries the id and the application tells them apart.
3665    OnDecorationClick = 106,
3666    // The editor's additions: the stretches that can be folded and the marks beside the
3667    // line numbers, each a blob of fixed-length records sent whole, and the listener for
3668    // the selection moving. The other four listeners are in the block at 130.
3669    Folds = 107,
3670    GutterDecorations = 108,
3671    OnSelectionChange = 109,
3672    // The text a page laid out by CSS draws, which takes its letters from its own values
3673    // rather than from a rung of the type ladder. 110 opens the block for it.
3674    //
3675    // The fonts to try, in order, for text whose role is `TypeRole::None`: a list of up to
3676    // eight registered font assets, installed families by name and generic families, as a
3677    // blob. Text with a role ignores it, because its font is the theme's.
3678    Font = 110,
3679    // Where a line may break, and whether a word too long for its line may be broken
3680    // inside. The `WordBreak` and `OverflowWrap` roles.
3681    WordBreak = 111,
3682    OverflowWrap = 112,
3683    // How many spaces' width apart the tab stops are. Absent is eight. A tab advances to
3684    // the next stop, which only the side that measures the letters can work out.
3685    TabSize = 113,
3686    // Whether the sizes are CSS pixels, which the system's font scale does not enlarge.
3687    AbsoluteSize = 114,
3688    // Whether lines wrap at the width they are given. Absent is yes; `false` is CSS
3689    // `nowrap` and `pre`, whose lines run as long as their text.
3690    SoftWrap = 115,
3691    // The font each run of a `Text` with no role is set in, as a blob beside `Spans`
3692    // rather than inside its records: a run record keeps the 36 bytes it has always had,
3693    // and only text that names fonts carries this at all.
3694    SpanFonts = 116,
3695    // The rest of the code editor's listeners, in the first block free after the text and
3696    // pointer blocks. The visible lines changing, the editor gaining or losing focus, a
3697    // stretch folded or unfolded, and an input method starting or ending a composition.
3698    OnVisibleRangeChange = 130,
3699    OnEditorFocusChange = 131,
3700    OnFoldChange = 132,
3701    OnCompositionChange = 133,
3702    // The node's pointer subscription: the handler, and the kinds it wants as a bit mask
3703    // (`1 << (kind - 1)`). 67 was reserved for a gesture handler that was never sent, so
3704    // it is free to carry this one. It is listed here, after the newer blocks, because
3705    // this list is append only.
3706    OnPointer = 67,
3707    // 140 opens the block after the animation and HTML-text properties.
3708    PointerKinds = 140,
3709    // The cursor the platform shows over the node and whether its text can be selected.
3710    // The Host resolves CSS inheritance and sends the value for each node.
3711    Cursor = 141,
3712    UserSelect = 142,
3713    // How large an HTML island is drawn, as the f32 factor `k` (see `ZoomChanged`), on the
3714    // island's root `AbsoluteBox`. 120 opens the block for zoom. Absent is 1.
3715    IslandZoom = 120,
3716    // The window block, after the cursor block. Every one of them belongs to a
3717    // `Window` node except the last two, which can sit on any node.
3718    //
3719    // The title. A string.
3720    WindowTitle = 150,
3721    // The size when the window opens, width then height, two `f32` in dp as 8 bytes. A
3722    // later change resizes the window; a size the user chose is not given back.
3723    WindowSize = 151,
3724    // The smallest size, in the same 8 bytes.
3725    WindowMinSize = 152,
3726    WindowResizable = 153,
3727    // A `Chrome` tag and a `TitleBar` tag, as integers.
3728    WindowChrome = 154,
3729    WindowTitleBar = 155,
3730    // The asset id of the window's icon; zero is the application's own.
3731    WindowIcon = 156,
3732    // Where the window sits, x then y, two `f32` in screen dp as 8 bytes. Removing it
3733    // leaves the placement to the platform.
3734    WindowPosition = 157,
3735    // The application's stable name for the window, a string, which matches a window the
3736    // system brings back to the node that describes it.
3737    WindowRestoreKey = 158,
3738    // The window's own zoom level, an integer in -8..=8.
3739    WindowZoomLevel = 159,
3740    // Open the window under a pointer that is held down and follow it until it is let go:
3741    // `pointer_id` as a `u32`, then the anchor `x` and `y` as two `f32`, 12 bytes. Read
3742    // when the window opens and not again.
3743    DragFromPointer = 160,
3744    // The handlers of the window events, each the handler id as an integer.
3745    OnWindowCloseRequested = 161,
3746    OnWindowFocusChanged = 162,
3747    OnWindowMoved = 163,
3748    OnWindowDrag = 164,
3749    OnWindowUnavailable = 165,
3750    OnWindowClosed = 166,
3751    // Not window properties: any node may take them. A node that has the handler is told
3752    // when a dragged window passes over it, and a node that has the key can be pulled out
3753    // into a window of its own, named by it.
3754    OnWindowDragOver = 167,
3755    TearOffKey = 168,
3756    OnFocusGained = 117,
3757    OnKeyUp = 118,
3758    // The name assistive technology reads for a node, a string. On a widget it overrides
3759    // the visible label, and on an icon-only button it beats the name the icon's meaning
3760    // would give. 117 and 118 are reserved for another pending change, and 119 is the next
3761    // free number; 120 is `IslandZoom`.
3762    AccessibleName = 119,
3763    // Colours and size of the selection controls. A node that does not set one leaves
3764    // the design system's value, so a control that sends none draws as it always has.
3765    // The colours are `Paint` bits; the size is two f32 (width, height) in dp.
3766    // 120 is the island zoom, so these start at 121.
3767    TrackColor = 121,
3768    ThumbColor = 122,
3769    ActiveTrackColor = 123,
3770    ContainerColor = 124,
3771    CheckColor = 125,
3772    ControlSize = 126,
3773    // Opt in to a `WindowSizeChanged` whenever the window's width or height, rounded to
3774    // whole dp, changes, and not only when the size class does. A `Window` node's boolean;
3775    // absent is false.
3776    ReportSize = 127,
3777    // Scrim colour for modal overlays (Dialog, Sheet, Menu), as a `Paint` bit value.
3778    // A `Popover` draws no scrim unless it is asked to, so there it is also a boolean:
3779    // true draws the design system's scrim, and a `Paint` draws that colour.
3780    Scrim = 128,
3781    // A `TextField` or `Dropdown` drawn without the design system's frame (fill, line,
3782    // underline, corner clip, inner padding, minimum height, focus line). A boolean;
3783    // absent is false. A node `Background` or `Border` is still drawn.
3784    Bare = 169,
3785    // A `FlowRow`'s layout. The gap between children on one line and the gap between
3786    // lines, each a literal dp; where a line's children sit along it and where each child
3787    // sits inside its line's height, as `HorizontalAlignment` and `VerticalAlignment` tags;
3788    // and the most children one line may hold, an integer, absent for no limit. 169 is
3789    // taken by another pending property, so these start at 170.
3790    HorizontalSpacing = 170,
3791    VerticalSpacing = 171,
3792    HorizontalAlignment = 172,
3793    VerticalAlignment = 173,
3794    MaxItemsInEachRow = 174,
3795    // The popover block. All three belong to a `Popover`. 169 to 174 are held for
3796    // properties on other branches.
3797    //
3798    // The side of the anchor the content opens on, a `PopoverPlacement` tag. Absent is
3799    // `Bottom`.
3800    Placement = 175,
3801    // The edge of the anchor the content lines up with, a `PopoverAlign` tag. Absent is
3802    // `Start`.
3803    Align = 176,
3804    // The gap between the anchor and the content, an `f32` in dp. Absent leaves the gap
3805    // to the design system.
3806    OffsetDp = 177,
3807    // How a code editor is laid out and coloured where the application says so. A node
3808    // that sets none of them draws with the design system's values. 169 to 177 are
3809    // numbered for properties that are not here yet.
3810    //
3811    // Whether the line numbers are drawn, a boolean. Absent is yes.
3812    LineNumbers = 178,
3813    // The least width of the line number column with its padding, the padding on each side
3814    // of a line number, and the space between the gutter and the text. Each an f32 in dp.
3815    GutterMinWidthDp = 179,
3816    GutterPaddingDp = 180,
3817    TextInsetDp = 181,
3818    // One colour record as a blob: a `ColorRole` for each of the container, the gutter, a
3819    // line number, the current line's number and the current line. Zero leaves a slot to
3820    // the design system.
3821    CodeEditorColors = 182,
3822    // The handlers of the editor's row height and of its scroll offset.
3823    OnCodeMetrics = 183,
3824    OnCodeScrolled = 184,
3825    // What a `Navigation` becomes in a compact window, as a `CompactNavigation` tag. Absent
3826    // or 0 leaves it to the design system.
3827    CompactNavigation = 185,
3828    // The handler a scroll container reports its position to, the handler id as an
3829    // integer. One handler shared by every scroll container rather than one widget's
3830    // block, so it takes the number after the last one in use: 169 to 184 are held by
3831    // properties other requirements have numbered.
3832    OnScrollChanged = 186,
3833    // The width in dp, an f32, below which a split pane shows one pane at a time instead of
3834    // its size class deciding. At or above it the split pane never stacks, and the design
3835    // system still chooses between two columns and a side pane laid over the body. Absent
3836    // or 0 keeps the size class's own line. The next place in the split pane's block after
3837    // `Collapsible`.
3838    CollapseBelow = 91,
3839    // Where a `ScrollRow` or `ScrollColumn` comes to rest, as a `Snap` tag. Absent or 0 is
3840    // free scrolling. 187 is held by a property on another branch.
3841    Snap = 188,
3842    // The tallest a `Sheet` grows, as an `f32` fraction of the window's height above 0 and
3843    // at most 1. Below it the sheet is as tall as its content; at it the content scrolls.
3844    // Absent leaves the height to the design system. 187 is held by a property on another
3845    // branch.
3846    MaxHeightFraction = 189,
3847    // OpenType features for a `Text` or a `TextField`, such as tabular figures. A blob of
3848    // 8-byte records, at most 16: the four tag bytes, then the value as a little-endian
3849    // u32. A tag that comes twice takes the later value. Absent, or empty, is no features.
3850    // 187 to 191 are held by properties other requirements have numbered.
3851    FontFeatureSettings = 192,
3852    // The handler a node's size motion reports to once it settles, the handler id as an
3853    // integer. Any node may take it, so it is numbered after the last one in use rather
3854    // than inside a widget's block: 187 to 192 are held by properties other requirements
3855    // have numbered.
3856    OnMotionFinished = 193,
3857    // The handler a code editor reports what became of each grouped edit to, the handler id
3858    // as an integer: one `CodeEditGroupResult` for every `EditCodeGroup`.
3859    OnEditGroupResult = 194,
3860    // The handler a code editor tells where its caret is drawn. 190 and 191 are held by
3861    // properties on other branches.
3862    OnCaretRect = 195,
3863    // The handler a code editor offers each key press to before it handles the key itself.
3864    // It hears the `KeyDown` record a key handler does, and a press it consumes is not the
3865    // editor's. Never while an input method is composing.
3866    OnKeyPreview = 196,
3867    // The handler a code editor sends its pointer events to, each with the place in the
3868    // text under the pointer. The kinds it hears are the `PointerKinds` mask's.
3869    OnCodePointer = 197,
3870    // Whether a `Column` or a `Row` is a group of radio buttons, a boolean. Such a group is
3871    // one stop for Tab, its arrow keys move between the radio buttons inside it, and
3872    // assistive technology hears it as a radio group. Absent is no.
3873    RadioGroup = 198,
3874    // Whether a code editor draws a minimap beside its text, and on which side, as a
3875    // `MinimapSide` tag. Absent or 0 draws none. 199 and 200 are held by the editor's view
3876    // zones and its word wrap.
3877    Minimap = 201,
3878    // A code editor's view zones, rows after a line that are not text, as a blob of
3879    // fixed-length records sent whole, with the text they show behind them.
3880    ViewZones = 199,
3881    // Where a code editor breaks a line too long for it onto the rows below, as an integer:
3882    // the `WordWrapMode` tag in the low 16 bits and the column count above them. Absent or
3883    // 0 breaks no line.
3884    WordWrap = 200,
3885    // How an `Image` lays its picture into its box, as a `ContentScale` tag. Absent or 0
3886    // is `Fit`, which is how every picture was drawn before there was a choice. 37 is the
3887    // next place in the 28 to 39 block, after the divider's `Vertical`.
3888    ContentScale = 37,
3889    // The diameter in dp a Slider draws its thumb at, an f32. Absent is the design system's
3890    // size. The track and the area that takes a press stay as they are.
3891    ThumbSize = 202,
3892});
3893
3894#[derive(Clone, Debug, PartialEq)]
3895pub enum EventPayload<'a> {
3896    Clicked,
3897    TextChanged(&'a str),
3898    TextSubmitted(&'a str),
3899    FocusLost,
3900    ProtocolError {
3901        code: u32,
3902        message: &'a str,
3903    },
3904    KeyDown {
3905        key: Key,
3906        shift_key: bool,
3907        ctrl_key: bool,
3908        alt_key: bool,
3909        meta_key: bool,
3910        /// True when the event comes from a held key repeating.
3911        repeat: bool,
3912        /// The physical key as a USB HID usage value, the same on every layout.
3913        code: u32,
3914        /// The DOM `key` string, such as `"ArrowDown"` or `"a"`.
3915        text: &'a str,
3916    },
3917    /// The Renderer asks the Host to materialise the visible item range.
3918    RangeRequested {
3919        start: u32,
3920        count: u32,
3921    },
3922    /// A control's new value, in the widget's own unit: an epoch count for a picker, a
3923    /// position for a slider, 0.0 or 1.0 for a toggle. The Renderer decided how the user
3924    /// reached it, so nothing about calendars, wheels, tracks or thumbs crosses here.
3925    ///
3926    /// One `f64` holds all of them: integers up to 2^53 survive it exactly, so splitting
3927    /// the event in two would only give the same concept two names.
3928    ValueChanged(f64),
3929    /// The window, or one observed node, moved into a different size class. The Renderer
3930    /// measures it and sends this only when a class changes, never on every layout pass.
3931    /// A `node_id` of 0 is the window; anything else is the node that asked to be
3932    /// measured.
3933    WindowSizeChanged {
3934        width_dp: f32,
3935        height_dp: f32,
3936        class: WindowSizeClass,
3937        height_class: WindowHeightClass,
3938        safe_area_top: f32,
3939        safe_area_leading: f32,
3940        safe_area_trailing: f32,
3941    },
3942    /// Files are over a node that said it would take them. Nothing about what they are:
3943    /// the platforms disagree about what is knowable before a drop, and a screen that
3944    /// only needs to light up does not need to know.
3945    FilesEntered,
3946    /// Files were let go over a node. The paths arrive together, separated by a byte no
3947    /// path on any of the three desktops may contain.
3948    FilesDropped(&'a str),
3949    /// The Renderer resolved the theme and this is what it chose. Sent once and then
3950    /// only when the answer changes, the same way a size class is.
3951    DesignSystemResolved(DesignSystem),
3952    /// The Renderer lost its node table and asks for the whole tree again. A recreated
3953    /// Activity is the case that produces it.
3954    Resync,
3955    /// The platform brought the UI back. Timers and animations resume.
3956    LifecycleStart,
3957    /// The platform stopped the UI. Timers and animations are suppressed, so a process
3958    /// that is not on screen is not asked to draw.
3959    LifecycleStop,
3960    /// The user pressed a notification the application posted. `action` is 0 for the body
3961    /// and 1 or 2 for an action button; `key` is the name the application gave it.
3962    ///
3963    /// A name rather than a handler, because a notification outlives the component that
3964    /// posted it by hours and a callback dies with its scope.
3965    NotificationActivated {
3966        action: u32,
3967        key: &'a str,
3968    },
3969    /// Whether notifications may be shown, sent once after start and then only when the
3970    /// answer changes.
3971    NotificationPermissionChanged(NotificationPermission),
3972    /// One committed edit to a code editor's document.
3973    ///
3974    /// The range is in the document as it stood before the edit, and `version` is the
3975    /// version the edit produced. Lines and columns count from zero and columns are UTF-16
3976    /// code units, which is what a language server counts in, so the event can be handed
3977    /// to one unchanged. Text still being composed by an input method never arrives here:
3978    /// only what the reader committed does.
3979    CodeChanged {
3980        version: u32,
3981        start_line: u32,
3982        start_column: u32,
3983        end_line: u32,
3984        end_column: u32,
3985        text: &'a str,
3986    },
3987    /// An edit the Host asked for and the Renderer did not apply, because the reader had
3988    /// changed the same place since the version the edit was written against.
3989    CodeEditRejected {
3990        request_id: u32,
3991        base_version: u32,
3992        current_version: u32,
3993        start_line: u32,
3994        start_column: u32,
3995        end_line: u32,
3996        end_column: u32,
3997    },
3998    /// A pointer came to rest over a place in a code editor, or left it. `decoration` is
3999    /// the id of the hover anchor under it, or zero where there is none.
4000    CodeHovered {
4001        decoration: u64,
4002        line: u32,
4003        column: u32,
4004        phase: HoverPhase,
4005    },
4006    /// The reader pressed the platform's save shortcut on a code editor.
4007    CodeSaveRequested {
4008        version: u32,
4009    },
4010    /// A decoration was pressed or accepted, named by the id the application gave it.
4011    DecorationActivated {
4012        decoration: u64,
4013    },
4014    /// Something happened to an animation the Renderer is playing, sent only for the kinds
4015    /// the animation asked for. It names the node the animation plays on and no handler.
4016    AnimationEvent {
4017        animation_id: u32,
4018        kind: AnimationEventKind,
4019        property: AnimatedProperty,
4020        slot: u8,
4021        /// The iteration the animation was in at that moment.
4022        iteration: u32,
4023        /// What CSS reports as `elapsedTime`: active time, without the delay, in ms.
4024        elapsed_ms: f32,
4025        /// The frame the moment happened in; for `Ready`, the start time it was given.
4026        time_nanos: u64,
4027    },
4028    /// Whether the platform asks for reduced motion, sent once after start and then only
4029    /// when the answer changes.
4030    ReducedMotionChanged(ReducedMotion),
4031    /// Whether the system appearance is light or dark, sent once before the first frame and
4032    /// then only when it changes. It is sent whatever the theme's `color_scheme` is.
4033    ColorSchemeChanged(SystemColorScheme),
4034    /// A pointer did something inside a node that subscribed to it. Coordinates are in dp
4035    /// from the node's top left corner, `buttons` is what is held now and `button` is the
4036    /// one that caused a `Down` or an `Up`. `timestamp_nanos` is on the frame time's clock.
4037    Pointer {
4038        kind: PointerKind,
4039        pointer_id: u32,
4040        pointer_type: PointerType,
4041        x: f32,
4042        y: f32,
4043        buttons: u32,
4044        button: u32,
4045        modifiers: u32,
4046        click_count: u32,
4047        delta_x: f32,
4048        delta_y: f32,
4049        delta_mode: WheelDeltaMode,
4050        timestamp_nanos: u64,
4051    },
4052    /// How large the Renderer is drawing: `k = os × 1.2^level`. Sent once at start and
4053    /// again whenever any of the three changes. A move to a screen of another density
4054    /// changes none of them and is not reported.
4055    ZoomChanged {
4056        k: f32,
4057        os: f32,
4058        level: i32,
4059    },
4060    /// The selection of a code editor moved, or an edit moved it. `version` is the document
4061    /// version it is read against, the same series `CodeChanged` counts, and an edit's
4062    /// change always arrives first. Columns are UTF-16 units and never fall between the
4063    /// two halves of a pair.
4064    SelectionChanged {
4065        version: u32,
4066        anchor_line: u32,
4067        anchor_column: u32,
4068        active_line: u32,
4069        active_column: u32,
4070    },
4071    /// The lines of a code editor that are on screen changed. Folded lines are skipped, so
4072    /// `first_line` and `last_line` are the first and last lines the reader can see.
4073    VisibleRangeChanged {
4074        version: u32,
4075        first_line: u32,
4076        last_line: u32,
4077    },
4078    /// A code editor gained or lost the keyboard focus.
4079    EditorFocusChanged {
4080        focused: bool,
4081    },
4082    /// The reader folded or unfolded the stretch that starts on `start_line`.
4083    FoldChanged {
4084        start_line: u32,
4085        collapsed: bool,
4086    },
4087    /// An input method started or ended a composition in a code editor. What is being
4088    /// composed stays in the Renderer: only the fact crosses.
4089    CompositionChanged {
4090        composing: bool,
4091    },
4092    /// The user asked to close a window. The window stays until the application removes its
4093    /// `Window` node. Delivered to the node.
4094    WindowCloseRequested,
4095    /// A window became the key window or stopped being it.
4096    WindowFocusChanged {
4097        focused: bool,
4098    },
4099    /// A window's outer frame moved or changed size, in screen dp. At most once a frame.
4100    WindowMoved {
4101        x: f32,
4102        y: f32,
4103        width: f32,
4104        height: f32,
4105    },
4106    /// A torn-off window being dragged. `phase` is 1 for a move, 2 for the end and 3 for a
4107    /// cancel; `over_window` is the node id of the window under the pointer, or 0.
4108    WindowDrag {
4109        phase: u32,
4110        screen_x: f32,
4111        screen_y: f32,
4112        over_window: u32,
4113    },
4114    /// A dragged window passing over a node that has `OnWindowDragOver`. `kind` is 1 for
4115    /// enter, 2 for move, 3 for leave and 4 for drop, `window` is the dragged window's node
4116    /// id, and the position is inside the node, in dp.
4117    WindowDragOver {
4118        kind: u32,
4119        window: u32,
4120        x: f32,
4121        y: f32,
4122    },
4123    /// A window that could not be opened here: the platform has no second window, or it
4124    /// refused. Delivered once, to the node.
4125    WindowUnavailable,
4126    /// The platform closed a window without asking. The window is already gone.
4127    WindowClosed,
4128    /// A window the application has no node for yet, with the `RestoreKey` it was made
4129    /// under (empty for a new one) and a request number. Delivered to the root.
4130    WindowRequested {
4131        key: &'a str,
4132        request: u32,
4133    },
4134    /// The user opened the application again while it had no window. Delivered to the
4135    /// root.
4136    ApplicationReopen,
4137    /// The node or a descendant inside it gained keyboard focus.
4138    FocusGained,
4139    /// A key was released while the node held focus.
4140    KeyUp {
4141        key: Key,
4142        shift_key: bool,
4143        ctrl_key: bool,
4144        alt_key: bool,
4145        meta_key: bool,
4146        /// Always false: a release does not repeat. Present so a key-up carries the same
4147        /// fields as a key-down.
4148        repeat: bool,
4149        /// The physical key as a USB HID usage value, the same on every layout.
4150        code: u32,
4151        /// The DOM `key` string, such as `"ArrowDown"` or `"a"`.
4152        text: &'a str,
4153    },
4154    /// The height of one row of a code editor in physical pixels, as it is drawn. Sent when
4155    /// the editor is first drawn and whenever the height changes.
4156    CodeMetrics {
4157        line_height_px: f64,
4158    },
4159    /// How far a code editor is scrolled down, in physical pixels. At most once a frame.
4160    CodeScrolled {
4161        offset_px: f64,
4162    },
4163    /// A file dialog ended. `request` is the number the Host gave it, `filter` is the
4164    /// position of the filter in use (`u32::MAX` when there is none) and `paths` holds the
4165    /// chosen paths separated by one NUL byte, empty unless the status is `Chosen`.
4166    /// Delivered to the Host itself, with no node and no handler.
4167    FileDialogResult {
4168        request: u32,
4169        status: FileDialogStatus,
4170        filter: u32,
4171        paths: &'a str,
4172    },
4173    /// One clipboard read ended. `request` is the number the Host gave it, `status` is a
4174    /// word from `clipboard::read_status`, `len` is the text's length in UTF-8 bytes and
4175    /// `text` the text itself, empty unless the status is text. Delivered to the Host itself,
4176    /// with no node and no handler.
4177    ClipboardText {
4178        request: u32,
4179        status: u32,
4180        len: u32,
4181        text: &'a str,
4182    },
4183    /// The one answer to an input or output request: a storage operation the browser
4184    /// carried out, or an operation on a file picked on the web. `request` is the number
4185    /// the Host gave it, and `data` is what the operation hands back: a value or a file
4186    /// that was read, or the keys joined by NUL, and nothing for an operation that only
4187    /// writes. Delivered to the Host itself, with no node and no handler.
4188    IoAnswer {
4189        request: u32,
4190        status: IoStatus,
4191        data: &'a [u8],
4192    },
4193    /// Files the Renderer now holds handles for: the answer to a file dialog that was
4194    /// chosen, or to a folder's `Entries` or `Child`. The handles are `first_handle`,
4195    /// `first_handle + 1` and so on, one per name. `names` holds the names separated by one
4196    /// NUL byte and `kinds` one letter per name, `f` for a file and `d` for a folder.
4197    /// `filter` is the position of the filter in use, `u32::MAX` when there is none.
4198    /// Delivered to the Host itself, with no node and no handler.
4199    FileHandles {
4200        request: u32,
4201        status: FileDialogStatus,
4202        filter: u32,
4203        delivery: FileDelivery,
4204        first_handle: u32,
4205        names: &'a str,
4206        kinds: &'a str,
4207    },
4208    /// Where the application's storage lives on a platform whose Host cannot work it out
4209    /// from the environment: Android's application files directory, which only a
4210    /// `Context` knows. Sent once by that Renderer at start. Delivered to the Host itself.
4211    StorageLocation(&'a str),
4212    /// Where a scroll container is, reported by the Renderer to the node's
4213    /// `OnScrollChanged` handler: at most once a frame while it moves, and once more with
4214    /// `settled` when it stops. Every length is in physical pixels, the unit the Renderer
4215    /// scrolls in, so a value read here and sent back in a `ScrollTo` is the same place.
4216    ///
4217    /// In a lazy list `item` is the first visible item and the main-axis offset is the
4218    /// distance into that item; the list does not know its whole length, so its main-axis
4219    /// `max` is `-1.0`. Elsewhere `item` is 0.
4220    ScrollChanged {
4221        item: u32,
4222        /// The movement ended: a drag, a fling or an animation came to rest.
4223        settled: bool,
4224        /// The container can still scroll towards the start of its main axis.
4225        can_scroll_backward: bool,
4226        /// The container can still scroll towards the end of its main axis.
4227        can_scroll_forward: bool,
4228        x_px: f64,
4229        y_px: f64,
4230        max_x_px: f64,
4231        max_y_px: f64,
4232        viewport_width_px: f64,
4233        viewport_height_px: f64,
4234    },
4235    /// A node's size motion came to rest, reported to the node's `OnMotionFinished`
4236    /// handler with the size it settled at, in dp.
4237    ///
4238    /// Sent once for each run that reaches its target: a run that is interrupted by a new
4239    /// target sends nothing, and only the one that settles does. The first layout and a
4240    /// layout that leaves the size as it was send nothing, and neither does a node removed
4241    /// while its size is moving. Under reduced motion the size changes in one frame and
4242    /// this arrives in that frame.
4243    MotionFinished {
4244        width_dp: f32,
4245        height_dp: f32,
4246    },
4247    /// Where a code editor draws its caret, in the window's dp with the scroll and the zoom
4248    /// already in it. At most once a frame, when the caret or the view moves.
4249    CaretRect {
4250        line: u32,
4251        column: u32,
4252        rect: crate::code::CodeRect,
4253    },
4254    /// The answer to one `QueryPositionRect`, carrying the request number the Host gave
4255    /// it. `None` where the position is not on screen.
4256    PositionRect {
4257        request: u32,
4258        rect: Option<crate::code::CodeRect>,
4259    },
4260    /// A pointer event over a code editor: every field a `Pointer` event has, with the same
4261    /// meaning and the same rules, and the place in the text under the pointer. `position`
4262    /// is `None` where the pointer is not over a character: over the gutter, past the end
4263    /// of a line or below the last one.
4264    CodePointer {
4265        kind: PointerKind,
4266        pointer_id: u32,
4267        pointer_type: PointerType,
4268        x: f32,
4269        y: f32,
4270        buttons: u32,
4271        button: u32,
4272        modifiers: u32,
4273        click_count: u32,
4274        delta_x: f32,
4275        delta_y: f32,
4276        delta_mode: WheelDeltaMode,
4277        timestamp_nanos: u64,
4278        position: Option<crate::code::Position>,
4279    },
4280    /// Every selection of a code editor, the primary first, read against `version`: sent
4281    /// while there is more than one, and once more when it comes back to one, to the same
4282    /// listener as `SelectionChanged`, which goes on reporting the primary alone.
4283    /// `selections` is 16-byte records, which [`crate::code::SelectionsChange`] reads.
4284    SelectionsChanged {
4285        version: u32,
4286        selections: &'a [u8],
4287    },
4288    /// What became of one `EditCodeGroup`, sent exactly once for each. `current_version`
4289    /// is the version after the last change the group made where it went in, and the
4290    /// document's version where it did not. `index` is the position in the group, as sent,
4291    /// of the edit that kept it out, or the number of edits that went in.
4292    CodeEditGroupResult {
4293        request_id: u32,
4294        status: EditGroupStatus,
4295        base_version: u32,
4296        current_version: u32,
4297        index: u32,
4298    },
4299}
4300
4301/// A rectangle in physical pixels, for [`ScrollTarget`].
4302#[derive(Clone, Copy, Debug, Default, PartialEq)]
4303pub struct Rect {
4304    pub x_px: f64,
4305    pub y_px: f64,
4306    pub width_px: f64,
4307    pub height_px: f64,
4308}
4309
4310impl Rect {
4311    pub const fn new(x_px: f64, y_px: f64, width_px: f64, height_px: f64) -> Self {
4312        Self {
4313            x_px,
4314            y_px,
4315            width_px,
4316            height_px,
4317        }
4318    }
4319}
4320
4321/// A request to scroll a container to an offset, written by `Batch::scroll_to`.
4322///
4323/// An axis left `None` is not touched, so setting one axis never sends a stale value for
4324/// the other. The Renderer keeps each value inside the range the content allows, and
4325/// ignores the request while the user is dragging or flinging the container.
4326#[derive(Clone, Copy, Debug, Default, PartialEq)]
4327pub struct ScrollTo {
4328    pub x_px: Option<f64>,
4329    pub y_px: Option<f64>,
4330    /// In a lazy list, the item the offset is measured from. 0 everywhere else.
4331    pub item: u32,
4332    pub animate: bool,
4333}
4334
4335/// How a rectangle is brought into view along one axis: CSS's `block` and `inline` values,
4336/// and `Keep` for an axis that must not move.
4337#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4338#[repr(u16)]
4339pub enum ScrollAlign {
4340    /// The axis does not move.
4341    #[default]
4342    Keep = 0,
4343    Start = 1,
4344    Center = 2,
4345    End = 3,
4346    /// No movement when the rectangle is already in view, otherwise the smallest one that
4347    /// shows it. A rectangle larger than the viewport is aligned to its start.
4348    Nearest = 4,
4349}
4350
4351/// What a `ScrollIntoView` rectangle is measured from.
4352#[derive(Clone, Copy, Debug, PartialEq)]
4353pub enum ScrollTarget {
4354    /// The container's content: the origin is the content's top left at offset zero. Not
4355    /// for a lazy list, which has no content coordinates.
4356    Content(Rect),
4357    /// A descendant node of the container: the origin is that node's top left, and a
4358    /// rectangle with no width and no height means the whole node.
4359    Node(u32, Rect),
4360    /// An item of a lazy list by its index: the origin is the item's start, and a rectangle
4361    /// with no width and no height means the whole item.
4362    Item(u32, Rect),
4363}
4364
4365/// A request to bring a rectangle into view in one container, written by
4366/// `Batch::scroll_into_view`.
4367#[derive(Clone, Copy, Debug, PartialEq)]
4368pub struct ScrollIntoView {
4369    pub target: ScrollTarget,
4370    pub align_x: ScrollAlign,
4371    pub align_y: ScrollAlign,
4372    pub animate: bool,
4373}
4374
4375/// The bits of a `ScrollTo` record's flags word.
4376pub mod scroll_to_flags {
4377    pub const ANIMATE: u32 = 1;
4378    pub const X: u32 = 1 << 1;
4379    pub const Y: u32 = 1 << 2;
4380}
4381
4382/// The values of a `ScrollIntoView` record's target kind.
4383pub mod scroll_target_kind {
4384    pub const CONTENT: u16 = 0;
4385    pub const NODE: u16 = 1;
4386    pub const ITEM: u16 = 2;
4387}
4388
4389/// The bit of a `ScrollIntoView` record's flags.
4390pub mod scroll_into_view_flags {
4391    pub const ANIMATE: u16 = 1;
4392}
4393
4394/// The bits of a `ScrollChanged` event's flags word.
4395pub mod scroll_changed_flags {
4396    pub const SETTLED: u32 = 1;
4397    pub const CAN_SCROLL_BACKWARD: u32 = 1 << 1;
4398    pub const CAN_SCROLL_FORWARD: u32 = 1 << 2;
4399    /// Every bit that has a meaning. Any other bit set is a protocol error.
4400    pub const KNOWN: u32 = SETTLED | CAN_SCROLL_BACKWARD | CAN_SCROLL_FORWARD;
4401}
4402
4403pub const EVENT_SCHEMA: &[EventSchema] = &[
4404    EventSchema {
4405        name: "Clicked",
4406        tag: 1,
4407        payload: EventPayloadType::None,
4408    },
4409    EventSchema {
4410        name: "TextChanged",
4411        tag: 2,
4412        payload: EventPayloadType::Text,
4413    },
4414    EventSchema {
4415        name: "TextSubmitted",
4416        tag: 3,
4417        payload: EventPayloadType::Text,
4418    },
4419    EventSchema {
4420        name: "FocusLost",
4421        tag: 4,
4422        payload: EventPayloadType::None,
4423    },
4424    EventSchema {
4425        name: "ProtocolError",
4426        tag: 5,
4427        payload: EventPayloadType::ProtocolError,
4428    },
4429    EventSchema {
4430        name: "KeyDown",
4431        tag: 6,
4432        payload: EventPayloadType::KeyDown,
4433    },
4434    EventSchema {
4435        name: "RangeRequested",
4436        tag: 7,
4437        payload: EventPayloadType::Range,
4438    },
4439    EventSchema {
4440        name: "ValueChanged",
4441        // Tag 8 is the pointer event and 9 to 15 are reserved for the gesture events.
4442        tag: 16,
4443        payload: EventPayloadType::Double,
4444    },
4445    EventSchema {
4446        name: "WindowSizeChanged",
4447        tag: 17,
4448        payload: EventPayloadType::WindowSize,
4449    },
4450    // The three below address the Host itself rather than a node, so they carry no handler
4451    // and the Host answers them before it looks one up.
4452    EventSchema {
4453        name: "Resync",
4454        tag: 18,
4455        payload: EventPayloadType::None,
4456    },
4457    EventSchema {
4458        name: "LifecycleStart",
4459        tag: 19,
4460        payload: EventPayloadType::None,
4461    },
4462    EventSchema {
4463        name: "LifecycleStop",
4464        tag: 20,
4465        payload: EventPayloadType::None,
4466    },
4467    // Addressed to the Host rather than to a node, like the three above: it is a fact
4468    // about the window, not something that happened inside it.
4469    EventSchema {
4470        name: "DesignSystemResolved",
4471        tag: 21,
4472        payload: EventPayloadType::DesignSystem,
4473    },
4474    EventSchema {
4475        name: "FilesEntered",
4476        tag: 22,
4477        payload: EventPayloadType::None,
4478    },
4479    // One string for however many paths, which is the string convention unchanged. A
4480    // second way of carrying a list would be a second thing to keep in step.
4481    EventSchema {
4482        name: "FilesDropped",
4483        tag: 23,
4484        payload: EventPayloadType::Text,
4485    },
4486    // Both address the Host itself, like the resolved design system: a notification is not
4487    // in the tree, so there is no node and no handler to name.
4488    EventSchema {
4489        name: "NotificationActivated",
4490        tag: 24,
4491        payload: EventPayloadType::NotificationActivation,
4492    },
4493    EventSchema {
4494        name: "NotificationPermissionChanged",
4495        tag: 25,
4496        payload: EventPayloadType::NotificationPermission,
4497    },
4498    EventSchema {
4499        name: "CodeChanged",
4500        tag: 26,
4501        payload: EventPayloadType::CodeChange,
4502    },
4503    EventSchema {
4504        name: "CodeEditRejected",
4505        tag: 27,
4506        payload: EventPayloadType::CodeEditRejected,
4507    },
4508    EventSchema {
4509        name: "CodeHovered",
4510        tag: 28,
4511        payload: EventPayloadType::CodeHover,
4512    },
4513    EventSchema {
4514        name: "CodeSaveRequested",
4515        tag: 29,
4516        payload: EventPayloadType::CodeSave,
4517    },
4518    EventSchema {
4519        name: "DecorationActivated",
4520        tag: 30,
4521        payload: EventPayloadType::Decoration,
4522    },
4523    EventSchema {
4524        name: "AnimationEvent",
4525        tag: 31,
4526        payload: EventPayloadType::Animation,
4527    },
4528    // About the platform rather than a node, like the notification permission.
4529    EventSchema {
4530        name: "ReducedMotionChanged",
4531        tag: 32,
4532        payload: EventPayloadType::ReducedMotion,
4533    },
4534    // Tag 8 is the pointer event and 9 stays reserved. It is listed last because this table
4535    // is append only, not because its tag is the highest.
4536    EventSchema {
4537        name: "Pointer",
4538        tag: 8,
4539        payload: EventPayloadType::Pointer,
4540    },
4541    // Tags 26 to 32 are taken by events other requirements have numbered.
4542    EventSchema {
4543        name: "ZoomChanged",
4544        tag: 33,
4545        payload: EventPayloadType::Zoom,
4546    },
4547    EventSchema {
4548        name: "SelectionChanged",
4549        tag: 34,
4550        payload: EventPayloadType::CodeSelection,
4551    },
4552    EventSchema {
4553        name: "VisibleRangeChanged",
4554        tag: 35,
4555        payload: EventPayloadType::CodeVisibleRange,
4556    },
4557    EventSchema {
4558        name: "EditorFocusChanged",
4559        tag: 36,
4560        payload: EventPayloadType::CodeFocus,
4561    },
4562    EventSchema {
4563        name: "FoldChanged",
4564        tag: 37,
4565        payload: EventPayloadType::CodeFold,
4566    },
4567    EventSchema {
4568        name: "CompositionChanged",
4569        tag: 38,
4570        payload: EventPayloadType::CodeComposition,
4571    },
4572    EventSchema {
4573        name: "WindowCloseRequested",
4574        tag: 39,
4575        payload: EventPayloadType::None,
4576    },
4577    EventSchema {
4578        name: "WindowFocusChanged",
4579        tag: 40,
4580        payload: EventPayloadType::WindowFocus,
4581    },
4582    EventSchema {
4583        name: "WindowMoved",
4584        tag: 41,
4585        payload: EventPayloadType::WindowMove,
4586    },
4587    EventSchema {
4588        name: "WindowDrag",
4589        tag: 42,
4590        payload: EventPayloadType::WindowDrag,
4591    },
4592    EventSchema {
4593        name: "WindowDragOver",
4594        tag: 43,
4595        payload: EventPayloadType::WindowDragOver,
4596    },
4597    EventSchema {
4598        name: "WindowUnavailable",
4599        tag: 44,
4600        payload: EventPayloadType::None,
4601    },
4602    EventSchema {
4603        name: "WindowClosed",
4604        tag: 45,
4605        payload: EventPayloadType::None,
4606    },
4607    EventSchema {
4608        name: "WindowRequested",
4609        tag: 46,
4610        payload: EventPayloadType::WindowRequest,
4611    },
4612    EventSchema {
4613        name: "ApplicationReopen",
4614        tag: 47,
4615        payload: EventPayloadType::None,
4616    },
4617    EventSchema {
4618        name: "FocusGained",
4619        tag: 48,
4620        payload: EventPayloadType::None,
4621    },
4622    EventSchema {
4623        name: "KeyUp",
4624        tag: 49,
4625        payload: EventPayloadType::KeyDown,
4626    },
4627    // Like the notification events this addresses the Host itself: a dialog is not in the tree, so there is no
4628    // node and no handler to name, and the request number finds the callback.
4629    EventSchema {
4630        name: "FileDialogResult",
4631        tag: 50,
4632        payload: EventPayloadType::FileDialogResult,
4633    },
4634    // What became of a grouped edit of a code editor, reported to the editor's own handler.
4635    EventSchema {
4636        name: "CodeEditGroupResult",
4637        tag: 51,
4638        payload: EventPayloadType::CodeEditGroupResult,
4639    },
4640    // Tags 52 and 53 are held by events other requirements have numbered. Like a file
4641    // dialog's end, a clipboard read addresses the Host itself and is found by its request
4642    // number.
4643    EventSchema {
4644        name: "ClipboardText",
4645        tag: 56,
4646        payload: EventPayloadType::ClipboardText,
4647    },
4648    // The answer to a storage operation, and to a file operation on the web.
4649    EventSchema {
4650        name: "IoAnswer",
4651        tag: 57,
4652        payload: EventPayloadType::IoAnswer,
4653    },
4654    // Files a dialog, or a folder, handed the Renderer handles for.
4655    EventSchema {
4656        name: "FileHandles",
4657        tag: 58,
4658        payload: EventPayloadType::FileHandles,
4659    },
4660    // Android's files directory, which the Host cannot find on its own. A string, like a
4661    // text change, so it needs no payload type of its own.
4662    EventSchema {
4663        name: "StorageLocation",
4664        tag: 59,
4665        payload: EventPayloadType::Text,
4666    },
4667    // Where a scroll container is, reported to the node's own handler.
4668    EventSchema {
4669        name: "ScrollChanged",
4670        tag: 60,
4671        payload: EventPayloadType::ScrollChanged,
4672    },
4673    // 51 to 53 are numbered for events that are not here yet. Both of these carry one
4674    // `f64` of physical pixels, the shape `ValueChanged` has.
4675    EventSchema {
4676        name: "CodeMetrics",
4677        tag: 54,
4678        payload: EventPayloadType::Double,
4679    },
4680    EventSchema {
4681        name: "CodeScrolled",
4682        tag: 55,
4683        payload: EventPayloadType::Double,
4684    },
4685    // A node's size motion settled, reported to the node's own handler. 61 and 62 are
4686    // numbered for events that are not here yet.
4687    EventSchema {
4688        name: "MotionFinished",
4689        tag: 63,
4690        payload: EventPayloadType::MotionFinished,
4691    },
4692    // Where a code editor's caret is drawn,
4693    // and the answer to a question about where a position is.
4694    EventSchema {
4695        name: "CaretRect",
4696        tag: 64,
4697        payload: EventPayloadType::CodeCaretRect,
4698    },
4699    EventSchema {
4700        name: "PositionRect",
4701        tag: 65,
4702        payload: EventPayloadType::CodePositionRect,
4703    },
4704    // A pointer over a code editor, with the place in the text under it.
4705    EventSchema {
4706        name: "CodePointer",
4707        tag: 66,
4708        payload: EventPayloadType::CodePointer,
4709    },
4710    // About the platform rather than a node, like the reduced motion setting.
4711    EventSchema {
4712        name: "ColorSchemeChanged",
4713        tag: 52,
4714        payload: EventPayloadType::ColorScheme,
4715    },
4716    // Every selection of a code editor, to its selection listener.
4717    EventSchema {
4718        name: "SelectionsChanged",
4719        tag: 67,
4720        payload: EventPayloadType::CodeSelections,
4721    },
4722];
4723
4724/// One argument of a boundary operation.
4725///
4726/// The set is deliberately tiny, because only primitives, pointers and lengths cross the
4727/// boundary. Every platform binding can then be generated from the same list.
4728#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4729pub enum BoundaryParam {
4730    /// A readable byte range. It arrives as a direct byte buffer and a length on Android,
4731    /// as a pointer and a length on desktop.
4732    Bytes { name: &'static str },
4733    /// A 64-bit platform frame timestamp.
4734    Nanos { name: &'static str },
4735}
4736
4737impl BoundaryParam {
4738    pub const fn name(self) -> &'static str {
4739        match self {
4740            Self::Bytes { name } | Self::Nanos { name } => name,
4741        }
4742    }
4743}
4744
4745/// One logical boundary operation, stated without saying which side calls it.
4746///
4747/// `symbol` is the Host's C export. Every platform binding, the GraalVM function
4748/// declarations, the Android shims and the browser's imports and trampoline, is a rendering
4749/// of this table, which is why none of them is written by hand.
4750#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4751pub struct BoundaryOp {
4752    /// Direction-neutral name, in PascalCase.
4753    pub name: &'static str,
4754    pub symbol: &'static str,
4755    pub params: &'static [BoundaryParam],
4756    /// Whether the operation answers with a mutation batch.
4757    pub returns_batch: bool,
4758    /// Whether the call may skip the runtime's thread state transition on Android. That
4759    /// is only sound for a call that cannot run long, because the thread stays runnable
4760    /// and the garbage collector cannot suspend it while the call is in flight. A call
4761    /// that runs the VirtualDom can run long.
4762    pub fast: bool,
4763}
4764
4765pub const BOUNDARY_SCHEMA: &[BoundaryOp] = &[
4766    BoundaryOp {
4767        name: "Init",
4768        symbol: "compose_rust_host_init",
4769        params: &[BoundaryParam::Bytes { name: "handshake" }],
4770        returns_batch: true,
4771        fast: false,
4772    },
4773    BoundaryOp {
4774        name: "DispatchEvent",
4775        symbol: "compose_rust_host_dispatch_event",
4776        params: &[BoundaryParam::Bytes { name: "event" }],
4777        returns_batch: true,
4778        fast: false,
4779    },
4780    BoundaryOp {
4781        name: "RenderFrame",
4782        symbol: "compose_rust_host_render_frame",
4783        params: &[BoundaryParam::Nanos {
4784            name: "frameTimeNanos",
4785        }],
4786        returns_batch: true,
4787        fast: false,
4788    },
4789    BoundaryOp {
4790        name: "ReleaseBatch",
4791        symbol: "compose_rust_host_release_batch",
4792        params: &[],
4793        returns_batch: false,
4794        fast: true,
4795    },
4796    BoundaryOp {
4797        name: "Shutdown",
4798        symbol: "compose_rust_host_shutdown",
4799        params: &[],
4800        returns_batch: false,
4801        fast: false,
4802    },
4803];
4804
4805/// The JVM class the Android shims bind to. The Kotlin file is generated under the same
4806/// name, so the two sides cannot drift.
4807pub const ANDROID_BRIDGE_CLASS: &str = "dev/darkpyonix/composerust/ui/platform/HostBridge";
4808
4809// ---------------------------------------------------------------------------------------
4810// The browser's shared linear memory.
4811//
4812// One `WebAssembly.Memory` holds both modules' data. The Kotlin module defines and exports
4813// it, because a Kotlin/Wasm module has no way to import one, and the Rust module is linked
4814// with `--import-memory` so that the arena the Host writes is the arena the Renderer reads.
4815// Nothing below crosses the boundary as data: these are the addresses and offsets both
4816// generated halves are written against.
4817// ---------------------------------------------------------------------------------------
4818
4819/// The first address that belongs to the Rust module.
4820///
4821/// Below it is the Kotlin module's `kotlin.wasm.unsafe` allocator; at and above it are the
4822/// Rust module's data, stack and heap, which the linker is told with `--global-base`. Two
4823/// allocators handing out addresses in one memory is the hazard: an overlap does not crash,
4824/// it draws the wrong screen, so the Renderer checks which side of this line its own
4825/// addresses fall on before it makes the first boundary call.
4826pub const WEB_RUST_REGION_BASE: u32 = 4 * 1024 * 1024;
4827
4828/// One field of the `MutationBatch` record the Host writes into the shared memory.
4829#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4830pub struct WebBatchField {
4831    /// PascalCase, so the generated Kotlin constant reads as a name rather than an offset.
4832    pub name: &'static str,
4833    pub offset: u32,
4834    pub width: u32,
4835    pub description: &'static str,
4836}
4837
4838/// `MutationBatch` as a wasm32 target lays it out.
4839///
4840/// The offsets are written down rather than read from `offset_of!` because codegen runs on
4841/// the machine that generates, not the machine that runs: a 64-bit host makes the same
4842/// `#[repr(C)]` struct 24 bytes with an 8 byte pointer. The generated wasm glue asserts
4843/// this table against the real layout, so a compiler that disagrees stops that build
4844/// instead of producing a Renderer that reads the wrong four bytes.
4845pub const WEB_BATCH_FIELDS: &[WebBatchField] = &[
4846    WebBatchField {
4847        name: "Address",
4848        offset: 0,
4849        width: 4,
4850        description: "where the batch starts, as an address in the shared memory",
4851    },
4852    WebBatchField {
4853        name: "Length",
4854        offset: 4,
4855        width: 4,
4856        description: "the batch's length in bytes",
4857    },
4858    WebBatchField {
4859        name: "Result",
4860        offset: 8,
4861        width: 8,
4862        description: "the handler's synchronous result",
4863    },
4864];
4865
4866/// How long that record is, including the padding `#[repr(C)]` adds before the result.
4867pub const WEB_BATCH_BYTES: u32 = 16;
4868
4869/// How much room the Host lends the Renderer to encode one event into.
4870///
4871/// The same figure the Android connection allocates for the same job. An event that would
4872/// not fit is refused by the encoder rather than truncated.
4873pub const WEB_EVENT_BUFFER_BYTES: u32 = 4096;
4874
4875/// The largest event buffer the Host lends a browser Renderer.
4876///
4877/// An event that does not fit in the first buffer is encoded into a larger one the Host
4878/// lends on request, doubling each time, and this is where the doubling stops. An event
4879/// larger than this is refused rather than cut short: a single event that large is a bug
4880/// in whatever produced it, and lending without a ceiling would turn that bug into running
4881/// out of memory. The other platforms grow their own buffers to the same ceiling.
4882pub const WEB_EVENT_BUFFER_MAX_BYTES: u32 = 64 * 1024 * 1024;
4883
4884/// The Host export a browser Renderer borrows a larger event buffer from.
4885///
4886/// It is setup, like `compose_rust_host_web_start`, not a boundary operation: the
4887/// Renderer calls it only when an event outgrows the buffer it has, and keeps what it gets.
4888/// It goes through the trampoline like the boundary operations, in the slot after theirs.
4889pub const WEB_LEND_EVENT_BUFFER_SYMBOL: &str = "compose_rust_host_web_lend_event_buffer";
4890
4891/// Where the event buffer starts inside the block `compose_rust_host_web_start` reports.
4892///
4893/// The out record comes first, at offset zero, and the event buffer follows it.
4894pub const WEB_EVENT_BUFFER_OFFSET: u32 = WEB_BATCH_BYTES;
4895
4896/// The extra Host export a browser needs, because a page has no library loader.
4897///
4898/// It does what `JNI_OnLoad` does on Android, registering the root component and installing
4899/// the renderer API, and it answers with the address of the block above.
4900pub const WEB_START_SYMBOL: &str = "compose_rust_host_web_start";
4901
4902/// The wasm import module the Host's frame request comes from.
4903///
4904/// This direction has no JavaScript on it: the page hands Kotlin's exported function object
4905/// straight to the Rust instantiation, which the engine binds as a wasm-to-wasm call. It can,
4906/// because Rust is instantiated second and Kotlin's exports already exist by then.
4907pub const WEB_RENDERER_IMPORT_MODULE: &str = "compose_rust_renderer";
4908
4909/// The global the loader publishes the trampoline's table under.
4910///
4911/// The loader creates the table and the Renderer's generated `installHost` fills it with the
4912/// Host's exports. That body is a `@JsFun`, inlined into the import object module Kotlin
4913/// generates, so the only name it can reach the loader's table through is a property of the
4914/// global object. Nothing reads it on a call: a call goes through the table from wasm.
4915pub const WEB_TABLE_GLOBAL: &str = "__composeRustHostTable";
4916
4917/// The global the loader module publishes the compiled Host module under.
4918///
4919/// Besides the trampoline, compiling is all the page does, and the reason the page has to
4920/// do it at all is that compiling is asynchronous while the boundary is not: the fetch has
4921/// to have finished before the Renderer's `main` runs. Everything after it, the memory, the
4922/// Host's imports, the entry point and filling the table, is in the generated Kotlin,
4923/// because only the Renderer's own module can name them.
4924pub const WEB_MODULE_GLOBAL: &str = "__composeRustHostModule";