#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EnumVariantSchema {
pub name: &'static str,
pub tag: u16,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FieldType {
Float,
U32,
U64,
Paint,
Role(&'static str),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FieldSlot {
FirstLow,
FirstHigh,
First,
SecondLow,
SecondHigh,
Second,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FieldSchema {
pub name: &'static str,
pub ty: FieldType,
pub slot: FieldSlot,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VariantSchema {
pub name: &'static str,
pub tag: u16,
pub fields: &'static [FieldSchema],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EventPayloadType {
None,
Text,
ProtocolError,
KeyDown,
Range,
Double,
WindowSize,
DesignSystem,
NotificationActivation,
NotificationPermission,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EventSchema {
pub name: &'static str,
pub tag: u16,
pub payload: EventPayloadType,
}
pub const SCHEMA_DESCRIPTOR: &str = concat!(
"compose-rust/v1;",
"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;",
"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;",
"modifiers=Empty,Padding,FillMaxWidth,FillMaxHeight,Width,Height,Size,Background,Clickable,PaddingRole,PaddingEach,Weight,Shape,ShapeRole,Border,Elevation,ObserveSize,Motion,Material;",
"keys=Enter;",
"events=Clicked,TextChanged,TextSubmitted,FocusLost,ProtocolError,KeyDown,RangeRequested,ValueChanged,WindowSizeChanged,DesignSystemResolved,FilesEntered,FilesDropped,NotificationActivated,NotificationPermissionChanged;",
"windowsizeclasses=Compact,Medium,Expanded;",
"windowheightclasses=Compact,Medium,Expanded;",
"commands=Create,SetProp,SetModifier,Insert,Move,Remove,SetText,AppendText,SetTheme,SetWindow,RegisterAsset,ReleaseAsset,ShowMessage,PostNotification,WithdrawNotification,RequestNotificationPermission"
);
const fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
let mut index = 0;
while index < bytes.len() {
hash ^= bytes[index] as u64;
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
index += 1;
}
hash
}
const fn hash_enum_schema(mut hash: u64, schema: &[EnumVariantSchema]) -> u64 {
let mut index = 0;
while index < schema.len() {
hash = hash_bytes(hash, schema[index].name.as_bytes());
hash = hash_bytes(hash, &schema[index].tag.to_le_bytes());
index += 1;
}
hash
}
const fn schema_hash() -> u64 {
let mut hash = hash_bytes(0xcbf2_9ce4_8422_2325_u64, SCHEMA_DESCRIPTOR.as_bytes());
hash = hash_bytes(hash, crate::extensions::SCHEMA_DESCRIPTOR.as_bytes());
hash = hash_enum_schema(hash, WIDGET_SCHEMA);
hash = hash_enum_schema(hash, PROPERTY_SCHEMA);
hash = hash_enum_schema(hash, KEY_SCHEMA);
hash = hash_enum_schema(hash, WINDOW_SIZE_CLASS_SCHEMA);
hash = hash_enum_schema(hash, WINDOW_HEIGHT_CLASS_SCHEMA);
let mut role_index = 0;
while role_index < ROLE_ENUM_SCHEMA.len() {
hash = hash_bytes(hash, ROLE_ENUM_SCHEMA[role_index].name.as_bytes());
hash = hash_enum_schema(hash, ROLE_ENUM_SCHEMA[role_index].variants);
role_index += 1;
}
let mut index = 0;
while index < MODIFIER_SCHEMA.len() {
let modifier = MODIFIER_SCHEMA[index];
hash = hash_bytes(hash, modifier.name.as_bytes());
hash = hash_bytes(hash, &modifier.tag.to_le_bytes());
let mut field_index = 0;
while field_index < modifier.fields.len() {
let field = modifier.fields[field_index];
hash = hash_bytes(hash, field.name.as_bytes());
hash = hash_bytes(
hash,
&[match field.ty {
FieldType::Float => 1,
FieldType::U32 => 2,
FieldType::U64 => 3,
FieldType::Paint => 4,
FieldType::Role(_) => 5,
}],
);
if let FieldType::Role(role) = field.ty {
hash = hash_bytes(hash, role.as_bytes());
}
hash = hash_bytes(
hash,
&[match field.slot {
FieldSlot::FirstLow => 0,
FieldSlot::FirstHigh => 1,
FieldSlot::First => 2,
FieldSlot::SecondLow => 3,
FieldSlot::SecondHigh => 4,
FieldSlot::Second => 5,
}],
);
field_index += 1;
}
index += 1;
}
index = 0;
while index < crate::drawing::DRAW_COMMAND_SCHEMA.len() {
let command = crate::drawing::DRAW_COMMAND_SCHEMA[index];
hash = hash_bytes(hash, command.name.as_bytes());
hash = hash_bytes(hash, &command.tag.to_le_bytes());
let mut field_index = 0;
while field_index < command.fields.len() {
let field = command.fields[field_index];
hash = hash_bytes(hash, field.name.as_bytes());
hash = hash_bytes(hash, &[field.word]);
hash = hash_bytes(
hash,
&[match field.ty {
crate::drawing::DrawFieldType::Float => 1,
crate::drawing::DrawFieldType::U32 => 2,
crate::drawing::DrawFieldType::Role(_) => 3,
}],
);
if let crate::drawing::DrawFieldType::Role(role) = field.ty {
hash = hash_bytes(hash, role.as_bytes());
}
field_index += 1;
}
index += 1;
}
index = 0;
while index < EVENT_SCHEMA.len() {
let event = EVENT_SCHEMA[index];
hash = hash_bytes(hash, event.name.as_bytes());
hash = hash_bytes(hash, &event.tag.to_le_bytes());
hash = hash_bytes(
hash,
&[match event.payload {
EventPayloadType::None => 0,
EventPayloadType::Text => 1,
EventPayloadType::ProtocolError => 2,
EventPayloadType::KeyDown => 3,
EventPayloadType::Range => 4,
EventPayloadType::Double => 5,
EventPayloadType::WindowSize => 6,
EventPayloadType::DesignSystem => 7,
EventPayloadType::NotificationActivation => 8,
EventPayloadType::NotificationPermission => 9,
}],
);
index += 1;
}
hash
}
pub const SCHEMA_HASH: u64 = schema_hash();
pub const PROTOCOL_VERSION: u16 = 1;
fn wire_name_eq(input: &str, schema_name: &str) -> bool {
input
.bytes()
.filter(|byte| *byte != b'_')
.map(|byte| byte.to_ascii_lowercase())
.eq(schema_name.bytes().map(|byte| byte.to_ascii_lowercase()))
}
macro_rules! define_wire_enum {
($schema:ident, $name:ident { $($variant:ident = $tag:literal),+ $(,)? }) => {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u16)]
pub enum $name {
$($variant = $tag),+
}
pub const $schema: &[EnumVariantSchema] = &[
$(EnumVariantSchema { name: stringify!($variant), tag: $tag }),+
];
impl From<$name> for u16 {
fn from(value: $name) -> Self {
value as Self
}
}
impl TryFrom<u16> for $name {
type Error = ();
fn try_from(value: u16) -> Result<Self, ()> {
match value {
$($tag => Ok(Self::$variant),)+
_ => Err(()),
}
}
}
impl $name {
#[allow(dead_code)]
#[doc(hidden)]
#[allow(clippy::result_unit_err)]
pub fn from_name(value: &str) -> Result<Self, ()> {
$(if wire_name_eq(value, stringify!($variant)) {
return Ok(Self::$variant);
})+
Err(())
}
}
};
}
crate::extensions::define_widget_schema_with_extensions!(define_wire_enum; WIDGET_SCHEMA, WidgetKind {
Column = 1,
Row = 2,
Box = 3,
Text = 4,
TextField = 5,
Button = 6,
Spacer = 7,
LazyColumn = 8,
ScrollColumn = 9,
Image = 10,
Icon = 11,
Checkbox = 12,
RadioButton = 13,
Switch = 14,
Slider = 15,
ProgressIndicator = 16,
Divider = 17,
Card = 18,
Surface = 19,
Dialog = 20,
Menu = 21,
Tabs = 22,
TopAppBar = 23,
LazyRow = 24,
Tooltip = 25,
Canvas = 26,
DatePicker = 27,
TimePicker = 28,
Dropdown = 29,
Navigation = 30,
NavigationItem = 31,
Sheet = 32,
Scaffold = 33,
ScaffoldSlot = 34,
LazyGrid = 35,
FileDropTarget = 36,
ScrollRow = 37,
Chip = 38,
FloatingAction = 39,
Badge = 40,
SelectionContainer = 41,
SplitPane = 42,
});
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum LoopMode {
#[default]
Renderer = 0,
Platform = 1,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Selection {
pub start: u32,
pub end: u32,
}
define_wire_enum!(KEY_SCHEMA, Key {
Enter = 1,
});
define_wire_enum!(WINDOW_SIZE_CLASS_SCHEMA, WindowSizeClass {
Compact = 0,
Medium = 1,
Expanded = 2,
});
impl WindowSizeClass {
pub const MEDIUM_MIN_WIDTH_DP: f32 = 600.0;
pub const EXPANDED_MIN_WIDTH_DP: f32 = 840.0;
pub fn from_width_dp(width_dp: f32) -> Self {
if width_dp >= Self::EXPANDED_MIN_WIDTH_DP {
Self::Expanded
} else if width_dp >= Self::MEDIUM_MIN_WIDTH_DP {
Self::Medium
} else {
Self::Compact
}
}
}
define_wire_enum!(WINDOW_HEIGHT_CLASS_SCHEMA, WindowHeightClass {
Compact = 0,
Medium = 1,
Expanded = 2,
});
impl WindowHeightClass {
pub const MEDIUM_MIN_HEIGHT_DP: f32 = 480.0;
pub const EXPANDED_MIN_HEIGHT_DP: f32 = 900.0;
pub fn from_height_dp(height_dp: f32) -> Self {
if height_dp >= Self::EXPANDED_MIN_HEIGHT_DP {
Self::Expanded
} else if height_dp >= Self::MEDIUM_MIN_HEIGHT_DP {
Self::Medium
} else {
Self::Compact
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RoleEnumSchema {
pub name: &'static str,
pub variants: &'static [EnumVariantSchema],
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(transparent)]
pub struct Color(pub u32);
impl Color {
pub const fn rgb(value: u32) -> Self {
Self(0xff00_0000 | (value & 0x00ff_ffff))
}
pub const fn argb(value: u32) -> Self {
Self(value)
}
pub const fn to_argb(self) -> u32 {
self.0
}
}
define_wire_enum!(COLOR_ROLE_SCHEMA, ColorRole {
Primary = 1,
OnPrimary = 2,
Secondary = 3,
OnSecondary = 4,
Surface = 5,
OnSurface = 6,
SurfaceVariant = 7,
OnSurfaceVariant = 8,
Background = 9,
OnBackground = 10,
Outline = 11,
OutlineVariant = 12,
Error = 13,
OnError = 14,
SurfaceContainer = 15,
Tertiary = 16,
OnTertiary = 17,
PrimaryContainer = 18,
OnPrimaryContainer = 19,
SecondaryContainer = 20,
OnSecondaryContainer = 21,
TertiaryContainer = 22,
OnTertiaryContainer = 23,
SyntaxKeyword = 24,
SyntaxString = 25,
SyntaxComment = 26,
SyntaxNumber = 27,
SyntaxConstant = 28,
SyntaxType = 29,
SyntaxFunction = 30,
SyntaxVariable = 31,
SyntaxProperty = 32,
SyntaxOperator = 33,
SyntaxPunctuation = 34,
SyntaxTag = 35,
SyntaxAttribute = 36,
SyntaxEscape = 37,
SyntaxMacro = 38,
DiffAdded = 39,
DiffRemoved = 40,
DiffModified = 41,
DiffAddedContainer = 42,
DiffRemovedContainer = 43,
DiffAddedEmphasis = 44,
DiffRemovedEmphasis = 45,
});
pub const COLOR_ROLE_COUNT: usize = 45;
define_wire_enum!(TYPE_ROLE_SCHEMA, TypeRole {
Display = 1,
Headline = 2,
Title = 3,
Subtitle = 4,
Body = 5,
BodyStrong = 6,
Label = 7,
Caption = 8,
Mono = 9,
});
define_wire_enum!(SHAPE_ROLE_SCHEMA, ShapeRole {
None = 1,
ExtraSmall = 2,
Small = 3,
Medium = 4,
Large = 5,
Full = 6,
});
define_wire_enum!(MOTION_ROLE_SCHEMA, MotionRole {
Instant = 1,
Quick = 2,
Standard = 3,
Slow = 4,
Emphasized = 5,
});
define_wire_enum!(MATERIAL_ROLE_SCHEMA, MaterialRole {
Thin = 1,
Regular = 2,
Thick = 3,
Chrome = 4,
});
define_wire_enum!(SPACE_ROLE_SCHEMA, SpaceRole {
None = 1,
Xs = 2,
Sm = 3,
Md = 4,
Lg = 5,
Xl = 6,
Xxl = 7,
});
define_wire_enum!(TEXT_ALIGN_SCHEMA, TextAlign {
Start = 1,
Center = 2,
End = 3,
Justify = 4,
});
define_wire_enum!(TEXT_OVERFLOW_SCHEMA, TextOverflow {
Clip = 1,
Ellipsis = 2,
Visible = 3,
});
define_wire_enum!(ARRANGEMENT_SCHEMA, Arrangement {
Start = 1,
Center = 2,
End = 3,
SpaceBetween = 4,
SpaceAround = 5,
SpaceEvenly = 6,
});
define_wire_enum!(ALIGNMENT_SCHEMA, Alignment {
TopStart = 1,
TopCenter = 2,
TopEnd = 3,
CenterStart = 4,
Center = 5,
CenterEnd = 6,
BottomStart = 7,
BottomCenter = 8,
BottomEnd = 9,
});
define_wire_enum!(BUTTON_VARIANT_SCHEMA, ButtonVariant {
Filled = 1,
Tonal = 2,
Outlined = 3,
Text = 4,
Operator = 5,
});
define_wire_enum!(ASSET_KIND_SCHEMA, AssetKind {
Png = 1,
Jpeg = 2,
Svg = 3,
VectorIcon = 4,
Font = 5,
Brush = 6,
});
define_wire_enum!(TILE_MODE_SCHEMA, TileMode {
Clamp = 1,
Repeat = 2,
Mirror = 3,
});
define_wire_enum!(ICON_ROLE_SCHEMA, IconRole {
Back = 1,
Forward = 2,
Close = 3,
Search = 4,
Add = 5,
Check = 6,
Settings = 7,
More = 8,
Home = 9,
List = 10,
Inbox = 11,
Menu = 12,
History = 13,
Send = 14,
Mic = 15,
Image = 16,
Video = 17,
Library = 18,
Sidebar = 19,
Compose = 20,
Collapse = 21,
});
define_wire_enum!(MESSAGE_DURATION_SCHEMA, MessageDuration {
Short = 1,
Long = 2,
});
define_wire_enum!(NOTIFICATION_IMPORTANCE_SCHEMA, NotificationImportance {
Normal = 1,
Urgent = 2,
});
define_wire_enum!(NOTIFICATION_PRESENTATION_SCHEMA, NotificationPresentation {
Always = 1,
WhenInactive = 2,
});
define_wire_enum!(NOTIFICATION_PERMISSION_SCHEMA, NotificationPermission {
NotDetermined = 1,
Granted = 2,
Denied = 3,
Unsupported = 4,
});
define_wire_enum!(DESIGN_SYSTEM_SCHEMA, DesignSystem {
Material3 = 1,
Cupertino = 2,
Fluent = 3,
Gnome = 4,
Breeze = 5,
Deepin = 6,
LiquidGlass = 7,
});
define_wire_enum!(COLOR_SCHEME_SCHEMA, ColorScheme {
Light = 1,
Dark = 2,
FollowSystem = 3,
});
define_wire_enum!(SLOT_ROLE_SCHEMA, SlotRole {
TopBar = 1,
BottomBar = 2,
FloatingAction = 3,
Content = 4,
});
pub const ROLE_ENUM_SCHEMA: &[RoleEnumSchema] = &[
RoleEnumSchema {
name: "ColorRole",
variants: COLOR_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "TypeRole",
variants: TYPE_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "ShapeRole",
variants: SHAPE_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "MotionRole",
variants: MOTION_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "TileMode",
variants: TILE_MODE_SCHEMA,
},
RoleEnumSchema {
name: "MaterialRole",
variants: MATERIAL_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "SpaceRole",
variants: SPACE_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "TextAlign",
variants: TEXT_ALIGN_SCHEMA,
},
RoleEnumSchema {
name: "TextOverflow",
variants: TEXT_OVERFLOW_SCHEMA,
},
RoleEnumSchema {
name: "Arrangement",
variants: ARRANGEMENT_SCHEMA,
},
RoleEnumSchema {
name: "Alignment",
variants: ALIGNMENT_SCHEMA,
},
RoleEnumSchema {
name: "ButtonVariant",
variants: BUTTON_VARIANT_SCHEMA,
},
RoleEnumSchema {
name: "DesignSystem",
variants: DESIGN_SYSTEM_SCHEMA,
},
RoleEnumSchema {
name: "ColorScheme",
variants: COLOR_SCHEME_SCHEMA,
},
RoleEnumSchema {
name: "AssetKind",
variants: ASSET_KIND_SCHEMA,
},
RoleEnumSchema {
name: "IconRole",
variants: ICON_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "MessageDuration",
variants: MESSAGE_DURATION_SCHEMA,
},
RoleEnumSchema {
name: "Chrome",
variants: CHROME_SCHEMA,
},
RoleEnumSchema {
name: "TitleBar",
variants: TITLE_BAR_SCHEMA,
},
RoleEnumSchema {
name: "SlotRole",
variants: SLOT_ROLE_SCHEMA,
},
RoleEnumSchema {
name: "NotificationImportance",
variants: NOTIFICATION_IMPORTANCE_SCHEMA,
},
RoleEnumSchema {
name: "NotificationPresentation",
variants: NOTIFICATION_PRESENTATION_SCHEMA,
},
RoleEnumSchema {
name: "NotificationPermission",
variants: NOTIFICATION_PERMISSION_SCHEMA,
},
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Paint {
Role(ColorRole),
Literal(Color),
Asset(u32),
}
const PAINT_KIND_ROLE: u64 = 1;
const PAINT_KIND_LITERAL: u64 = 2;
const PAINT_KIND_ASSET: u64 = 3;
impl Paint {
pub const fn to_bits(self) -> u64 {
match self {
Self::Role(role) => (PAINT_KIND_ROLE << 32) | role as u64,
Self::Literal(color) => (PAINT_KIND_LITERAL << 32) | color.0 as u64,
Self::Asset(id) => (PAINT_KIND_ASSET << 32) | id as u64,
}
}
pub fn from_bits(bits: u64) -> Option<Self> {
let value = bits as u32;
match bits >> 32 {
PAINT_KIND_ROLE => Some(Self::Role(
ColorRole::try_from(u16::try_from(value).ok()?).ok()?,
)),
PAINT_KIND_LITERAL => Some(Self::Literal(Color(value))),
PAINT_KIND_ASSET => Some(Self::Asset(value)),
_ => None,
}
}
}
define_wire_enum!(CHROME_SCHEMA, Chrome {
Modern = 1,
System = 2,
});
define_wire_enum!(TITLE_BAR_SCHEMA, TitleBar {
Normal = 1,
Simple = 2,
});
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Window {
pub chrome: Chrome,
pub title_bar: TitleBar,
pub title: &'static str,
pub icon: u32,
pub width: u16,
pub height: u16,
pub min_width: u16,
pub min_height: u16,
pub resizable: bool,
}
impl Window {
pub const fn new() -> Self {
Self {
chrome: Chrome::Modern,
title_bar: TitleBar::Normal,
title: "",
icon: 0,
width: 0,
height: 0,
min_width: 0,
min_height: 0,
resizable: true,
}
}
pub const fn with_title_bar(mut self, title_bar: TitleBar) -> Self {
self.title_bar = title_bar;
self
}
pub const fn with_title(mut self, title: &'static str) -> Self {
self.title = title;
self
}
pub const fn with_icon(mut self, asset_id: u32) -> Self {
self.icon = asset_id;
self
}
pub const fn with_chrome(mut self, chrome: Chrome) -> Self {
self.chrome = chrome;
self
}
pub const fn with_size(mut self, width: u16, height: u16) -> Self {
self.width = width;
self.height = height;
self
}
pub const fn with_min_size(mut self, width: u16, height: u16) -> Self {
self.min_width = width;
self.min_height = height;
self
}
pub const fn resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
}
impl Default for Window {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Theme {
pub design_system: DesignSystem,
pub fallback: DesignSystem,
pub color_scheme: ColorScheme,
pub adaptive: bool,
pub fonts: [u32; TYPE_ROLE_COUNT],
pub palette: Option<&'static crate::palette::Palette>,
}
pub const TYPE_ROLE_COUNT: usize = 9;
impl Theme {
pub const fn unified(design_system: DesignSystem) -> Self {
Self {
design_system,
fallback: design_system,
color_scheme: ColorScheme::FollowSystem,
adaptive: false,
fonts: [0; TYPE_ROLE_COUNT],
palette: None,
}
}
pub const fn adaptive(fallback: DesignSystem) -> Self {
Self {
design_system: fallback,
fallback,
color_scheme: ColorScheme::FollowSystem,
adaptive: true,
fonts: [0; TYPE_ROLE_COUNT],
palette: None,
}
}
pub const fn with_color_scheme(mut self, color_scheme: ColorScheme) -> Self {
self.color_scheme = color_scheme;
self
}
pub const fn with_font(mut self, role: TypeRole, asset: u32) -> Self {
self.fonts[role as usize - 1] = asset;
self
}
pub const fn with_palette(mut self, palette: &'static crate::palette::Palette) -> Self {
self.palette = Some(palette);
self
}
pub const fn font(&self, role: TypeRole) -> Option<u32> {
match self.fonts[role as usize - 1] {
0 => None,
asset => Some(asset),
}
}
}
impl Default for Theme {
fn default() -> Self {
Self::adaptive(DesignSystem::Material3)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Modifier {
Empty,
Padding(f32),
FillMaxWidth,
FillMaxHeight,
Width(f32),
Height(f32),
Size {
width: f32,
height: f32,
},
Background(Paint),
Clickable {
handler_id: u64,
},
PaddingRole(SpaceRole),
PaddingEach {
start: f32,
top: f32,
end: f32,
bottom: f32,
},
Weight(f32),
Shape {
top_start: f32,
top_end: f32,
bottom_end: f32,
bottom_start: f32,
},
ShapeRole(ShapeRole),
Border {
width: f32,
paint: Paint,
},
Elevation(f32),
ObserveSize {
token: u32,
},
Motion(MotionRole),
Material(MaterialRole),
}
const NO_FIELDS: &[FieldSchema] = &[];
const TOKEN_U32_FIELD: &[FieldSchema] = &[FieldSchema {
name: "token",
ty: FieldType::U32,
slot: FieldSlot::FirstLow,
}];
const VALUE_FLOAT_FIELD: &[FieldSchema] = &[FieldSchema {
name: "value",
ty: FieldType::Float,
slot: FieldSlot::FirstLow,
}];
const SIZE_FIELDS: &[FieldSchema] = &[
FieldSchema {
name: "width",
ty: FieldType::Float,
slot: FieldSlot::FirstLow,
},
FieldSchema {
name: "height",
ty: FieldType::Float,
slot: FieldSlot::SecondLow,
},
];
const PAINT_FIELD: &[FieldSchema] = &[FieldSchema {
name: "paint",
ty: FieldType::Paint,
slot: FieldSlot::First,
}];
const HANDLER_ID_FIELD: &[FieldSchema] = &[FieldSchema {
name: "handlerId",
ty: FieldType::U64,
slot: FieldSlot::First,
}];
const SPACE_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
name: "role",
ty: FieldType::Role("SpaceRole"),
slot: FieldSlot::FirstLow,
}];
const SHAPE_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
name: "role",
ty: FieldType::Role("ShapeRole"),
slot: FieldSlot::FirstLow,
}];
const MOTION_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
name: "role",
ty: FieldType::Role("MotionRole"),
slot: FieldSlot::FirstLow,
}];
const MATERIAL_ROLE_FIELD: &[FieldSchema] = &[FieldSchema {
name: "role",
ty: FieldType::Role("MaterialRole"),
slot: FieldSlot::FirstLow,
}];
const PADDING_EACH_FIELDS: &[FieldSchema] = &[
FieldSchema {
name: "start",
ty: FieldType::Float,
slot: FieldSlot::FirstLow,
},
FieldSchema {
name: "top",
ty: FieldType::Float,
slot: FieldSlot::FirstHigh,
},
FieldSchema {
name: "end",
ty: FieldType::Float,
slot: FieldSlot::SecondLow,
},
FieldSchema {
name: "bottom",
ty: FieldType::Float,
slot: FieldSlot::SecondHigh,
},
];
const SHAPE_FIELDS: &[FieldSchema] = &[
FieldSchema {
name: "topStart",
ty: FieldType::Float,
slot: FieldSlot::FirstLow,
},
FieldSchema {
name: "topEnd",
ty: FieldType::Float,
slot: FieldSlot::FirstHigh,
},
FieldSchema {
name: "bottomEnd",
ty: FieldType::Float,
slot: FieldSlot::SecondLow,
},
FieldSchema {
name: "bottomStart",
ty: FieldType::Float,
slot: FieldSlot::SecondHigh,
},
];
const BORDER_FIELDS: &[FieldSchema] = &[
FieldSchema {
name: "width",
ty: FieldType::Float,
slot: FieldSlot::FirstLow,
},
FieldSchema {
name: "paint",
ty: FieldType::Paint,
slot: FieldSlot::Second,
},
];
pub const MODIFIER_SCHEMA: &[VariantSchema] = &[
VariantSchema {
name: "Empty",
tag: 0,
fields: NO_FIELDS,
},
VariantSchema {
name: "Padding",
tag: 1,
fields: VALUE_FLOAT_FIELD,
},
VariantSchema {
name: "FillMaxWidth",
tag: 2,
fields: NO_FIELDS,
},
VariantSchema {
name: "FillMaxHeight",
tag: 3,
fields: NO_FIELDS,
},
VariantSchema {
name: "Width",
tag: 4,
fields: VALUE_FLOAT_FIELD,
},
VariantSchema {
name: "Height",
tag: 5,
fields: VALUE_FLOAT_FIELD,
},
VariantSchema {
name: "Size",
tag: 6,
fields: SIZE_FIELDS,
},
VariantSchema {
name: "Background",
tag: 7,
fields: PAINT_FIELD,
},
VariantSchema {
name: "Clickable",
tag: 8,
fields: HANDLER_ID_FIELD,
},
VariantSchema {
name: "PaddingRole",
tag: 9,
fields: SPACE_ROLE_FIELD,
},
VariantSchema {
name: "PaddingEach",
tag: 10,
fields: PADDING_EACH_FIELDS,
},
VariantSchema {
name: "Weight",
tag: 11,
fields: VALUE_FLOAT_FIELD,
},
VariantSchema {
name: "Shape",
tag: 12,
fields: SHAPE_FIELDS,
},
VariantSchema {
name: "ShapeRole",
tag: 13,
fields: SHAPE_ROLE_FIELD,
},
VariantSchema {
name: "Border",
tag: 14,
fields: BORDER_FIELDS,
},
VariantSchema {
name: "Elevation",
tag: 15,
fields: VALUE_FLOAT_FIELD,
},
VariantSchema {
name: "ObserveSize",
tag: 16,
fields: TOKEN_U32_FIELD,
},
VariantSchema {
name: "Motion",
tag: 17,
fields: MOTION_ROLE_FIELD,
},
VariantSchema {
name: "Material",
tag: 18,
fields: MATERIAL_ROLE_FIELD,
},
];
#[derive(Clone, Debug, PartialEq)]
pub struct ColumnProps {
pub enabled: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RowProps {
pub enabled: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BoxProps {
pub enabled: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextProps<'a> {
pub text: &'a str,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TextFieldProps<'a> {
pub placeholder: &'a str,
pub enabled: bool,
pub multiline: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ButtonProps<'a> {
pub text: &'a str,
pub enabled: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SpacerProps {
pub width: f32,
pub height: f32,
}
crate::extensions::define_property_schema_with_extensions!(define_wire_enum; PROPERTY_SCHEMA, PropertyKind {
Text = 1,
Placeholder = 2,
Enabled = 3,
Multiline = 4,
OnClick = 5,
OnValueChange = 6,
OnSubmit = 7,
OnFocusLost = 8,
OnKeyDown = 9,
ItemCount = 10,
ItemKey = 11,
OnRangeRequested = 12,
TypeRole = 13,
FontSize = 14,
FontWeight = 15,
LineHeight = 16,
LetterSpacing = 17,
Color = 18,
TextAlign = 19,
MaxLines = 20,
Overflow = 21,
Arrangement = 22,
Spacing = 23,
SpaceRole = 24,
Alignment = 25,
Variant = 26,
Asset = 28,
Checked = 32,
Steps = 33,
Determinate = 34,
Circular = 35,
Vertical = 36,
Open = 40,
OnDismiss = 41,
SelectedIndex = 42,
Commands = 50,
Value = 51,
Min = 52,
Max = 53,
Icon = 60,
Slot = 61,
Columns = 62,
MinColumnWidth = 63,
Spans = 64,
OnFilesEntered = 65,
OnFilesDropped = 66,
Section = 76,
Count = 80,
Collapsible = 90,
});
#[derive(Clone, Debug, PartialEq)]
pub enum EventPayload<'a> {
Clicked,
TextChanged(&'a str),
TextSubmitted(&'a str),
FocusLost,
ProtocolError {
code: u32,
message: &'a str,
},
KeyDown {
key: Key,
shift_key: bool,
ctrl_key: bool,
alt_key: bool,
meta_key: bool,
},
RangeRequested {
start: u32,
count: u32,
},
ValueChanged(f64),
WindowSizeChanged {
width_dp: f32,
height_dp: f32,
class: WindowSizeClass,
height_class: WindowHeightClass,
},
FilesEntered,
FilesDropped(&'a str),
DesignSystemResolved(DesignSystem),
Resync,
LifecycleStart,
LifecycleStop,
NotificationActivated {
action: u32,
key: &'a str,
},
NotificationPermissionChanged(NotificationPermission),
}
pub const EVENT_SCHEMA: &[EventSchema] = &[
EventSchema {
name: "Clicked",
tag: 1,
payload: EventPayloadType::None,
},
EventSchema {
name: "TextChanged",
tag: 2,
payload: EventPayloadType::Text,
},
EventSchema {
name: "TextSubmitted",
tag: 3,
payload: EventPayloadType::Text,
},
EventSchema {
name: "FocusLost",
tag: 4,
payload: EventPayloadType::None,
},
EventSchema {
name: "ProtocolError",
tag: 5,
payload: EventPayloadType::ProtocolError,
},
EventSchema {
name: "KeyDown",
tag: 6,
payload: EventPayloadType::KeyDown,
},
EventSchema {
name: "RangeRequested",
tag: 7,
payload: EventPayloadType::Range,
},
EventSchema {
name: "ValueChanged",
tag: 16,
payload: EventPayloadType::Double,
},
EventSchema {
name: "WindowSizeChanged",
tag: 17,
payload: EventPayloadType::WindowSize,
},
EventSchema {
name: "Resync",
tag: 18,
payload: EventPayloadType::None,
},
EventSchema {
name: "LifecycleStart",
tag: 19,
payload: EventPayloadType::None,
},
EventSchema {
name: "LifecycleStop",
tag: 20,
payload: EventPayloadType::None,
},
EventSchema {
name: "DesignSystemResolved",
tag: 21,
payload: EventPayloadType::DesignSystem,
},
EventSchema {
name: "FilesEntered",
tag: 22,
payload: EventPayloadType::None,
},
EventSchema {
name: "FilesDropped",
tag: 23,
payload: EventPayloadType::Text,
},
EventSchema {
name: "NotificationActivated",
tag: 24,
payload: EventPayloadType::NotificationActivation,
},
EventSchema {
name: "NotificationPermissionChanged",
tag: 25,
payload: EventPayloadType::NotificationPermission,
},
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BoundaryParam {
Bytes { name: &'static str },
Nanos { name: &'static str },
}
impl BoundaryParam {
pub const fn name(self) -> &'static str {
match self {
Self::Bytes { name } | Self::Nanos { name } => name,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BoundaryOp {
pub name: &'static str,
pub symbol: &'static str,
pub params: &'static [BoundaryParam],
pub returns_batch: bool,
pub fast: bool,
}
pub const BOUNDARY_SCHEMA: &[BoundaryOp] = &[
BoundaryOp {
name: "Init",
symbol: "compose_rust_host_init",
params: &[BoundaryParam::Bytes { name: "handshake" }],
returns_batch: true,
fast: false,
},
BoundaryOp {
name: "DispatchEvent",
symbol: "compose_rust_host_dispatch_event",
params: &[BoundaryParam::Bytes { name: "event" }],
returns_batch: true,
fast: false,
},
BoundaryOp {
name: "RenderFrame",
symbol: "compose_rust_host_render_frame",
params: &[BoundaryParam::Nanos {
name: "frameTimeNanos",
}],
returns_batch: true,
fast: false,
},
BoundaryOp {
name: "ReleaseBatch",
symbol: "compose_rust_host_release_batch",
params: &[],
returns_batch: false,
fast: true,
},
BoundaryOp {
name: "Shutdown",
symbol: "compose_rust_host_shutdown",
params: &[],
returns_batch: false,
fast: false,
},
];
pub const ANDROID_BRIDGE_CLASS: &str = "dev/darkpyonix/composerust/ui/platform/HostBridge";
pub const WEB_RUST_REGION_BASE: u32 = 4 * 1024 * 1024;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WebBatchField {
pub name: &'static str,
pub offset: u32,
pub width: u32,
pub description: &'static str,
}
pub const WEB_BATCH_FIELDS: &[WebBatchField] = &[
WebBatchField {
name: "Address",
offset: 0,
width: 4,
description: "where the batch starts, as an address in the shared memory",
},
WebBatchField {
name: "Length",
offset: 4,
width: 4,
description: "the batch's length in bytes",
},
WebBatchField {
name: "Result",
offset: 8,
width: 8,
description: "the handler's synchronous result",
},
];
pub const WEB_BATCH_BYTES: u32 = 16;
pub const WEB_EVENT_BUFFER_BYTES: u32 = 4096;
pub const WEB_EVENT_BUFFER_OFFSET: u32 = WEB_BATCH_BYTES;
pub const WEB_START_SYMBOL: &str = "compose_rust_host_web_start";
pub const WEB_RENDERER_IMPORT_MODULE: &str = "compose_rust_renderer";
pub const WEB_HOST_GLOBAL: &str = "__composeRustHost";
pub const WEB_MODULE_GLOBAL: &str = "__composeRustHostModule";