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