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