mod lazy;
mod widgets;
pub use crate::boundary::LaunchBuilder;
pub use crate::brush::{Brush, Stop, brush};
pub use crate::drawing::{DrawCommand, DrawList, DrawListBuilder};
pub use crate::message::{Message, show_message};
pub use crate::runtime::{application, launch};
pub use crate::schema::{
Alignment, Arrangement, ButtonKind, ButtonVariant, Color, ColorRole, ColorScheme, DesignSystem,
IconRole, Key, MaterialRole, MessageDuration, MotionRole, Paint, ShapeRole, SpaceRole,
TextAlign, TextOverflow, Theme, TypeRole, WindowHeightClass, WindowSizeClass,
};
pub use crate::spans::TextSpans;
pub use crate::theme::{ThemeHandle, current_theme};
pub fn theme() -> ThemeHandle {
ThemeHandle::default()
}
pub use crate::window::{NodeSize, WindowSize};
pub use crate::{
Cursor, FileDrop, KeyEvent, PointerEvent, PointerKind, PointerKinds, RangeRequest, UserSelect,
asset, pointer_button,
};
pub use lazy::{LazyColumn, LazyGrid, LazyList, LazyListScope, LazyRow};
pub use widgets::*;
use crate::runtime::composer::{EventCallback, OwnedAttr};
use crate::runtime::{group_guard, with_composer, with_composer_if_idle};
use crate::schema::WidgetKind;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Modifier {
observe_size: Option<i64>,
motion: Option<MotionRole>,
material: Option<MaterialRole>,
weight: Option<f32>,
width: Option<f32>,
height: Option<f32>,
padding: Option<f32>,
padding_role: Option<SpaceRole>,
background: Option<Paint>,
shape_role: Option<ShapeRole>,
corner_radius: Option<f32>,
border_width: Option<f32>,
border_color: Option<Paint>,
elevation: Option<f32>,
alpha: Option<f32>,
transform: Option<[f32; 8]>,
animated_alpha: Option<crate::Animated<f32>>,
animated_background: Option<crate::Animated<Paint>>,
animated_offset: Option<crate::Animated<(f32, f32)>>,
cursor: Option<crate::Cursor>,
user_select: Option<crate::UserSelect>,
fill_max_width: bool,
fill_max_height: bool,
}
#[allow(non_upper_case_globals)]
pub const Modifier: Modifier = Modifier::new();
impl Modifier {
pub const fn new() -> Self {
Self {
observe_size: None,
motion: None,
material: None,
weight: None,
width: None,
height: None,
padding: None,
padding_role: None,
background: None,
shape_role: None,
corner_radius: None,
border_width: None,
border_color: None,
elevation: None,
alpha: None,
transform: None,
animated_alpha: None,
animated_background: None,
animated_offset: None,
cursor: None,
user_select: None,
fill_max_width: false,
fill_max_height: false,
}
}
pub const fn weight(mut self, weight: f32) -> Self {
self.weight = Some(weight);
self
}
pub const fn width(mut self, dp: f32) -> Self {
self.width = Some(dp);
self
}
pub const fn height(mut self, dp: f32) -> Self {
self.height = Some(dp);
self
}
pub const fn size(self, width: f32, height: f32) -> Self {
self.width(width).height(height)
}
pub const fn width_if(mut self, dp: Option<f32>) -> Self {
self.width = dp;
self
}
pub const fn fill_max_width(mut self) -> Self {
self.fill_max_width = true;
self
}
pub const fn fill_max_height(mut self) -> Self {
self.fill_max_height = true;
self
}
pub const fn fill_max_size(self) -> Self {
self.fill_max_width().fill_max_height()
}
pub const fn fill_max_width_if(mut self, fill: bool) -> Self {
self.fill_max_width = fill;
self
}
pub const fn padding(mut self, dp: f32) -> Self {
self.padding = Some(dp);
self
}
pub const fn padding_role(mut self, role: SpaceRole) -> Self {
self.padding_role = Some(role);
self
}
pub const fn padding_role_if(mut self, role: Option<SpaceRole>) -> Self {
self.padding_role = role;
self
}
pub const fn background(mut self, paint: Paint) -> Self {
self.background = Some(paint);
self
}
pub const fn background_if(mut self, paint: Option<Paint>) -> Self {
self.background = paint;
self
}
pub const fn shape_role(mut self, role: ShapeRole) -> Self {
self.shape_role = Some(role);
self
}
pub const fn corner_radius(mut self, dp: f32) -> Self {
self.corner_radius = Some(dp);
self
}
pub const fn border(mut self, width: f32, paint: Paint) -> Self {
self.border_width = Some(width);
self.border_color = Some(paint);
self
}
pub const fn elevation(mut self, dp: f32) -> Self {
self.elevation = Some(dp);
self
}
pub const fn observe_size(mut self, token: i64) -> Self {
self.observe_size = Some(token);
self
}
pub const fn motion(mut self, role: MotionRole) -> Self {
self.motion = Some(role);
self
}
pub const fn material(mut self, role: MaterialRole) -> Self {
self.material = Some(role);
self
}
pub const fn alpha(mut self, alpha: f32) -> Self {
self.alpha = Some(alpha);
self
}
pub fn graphics_layer(mut self, layer: crate::GraphicsLayer) -> Self {
if let crate::schema::Modifier::Transform {
a,
b,
c,
d,
e,
f,
origin_x,
origin_y,
} = layer.transform()
{
self.transform = Some([a, b, c, d, e, f, origin_x, origin_y]);
}
self.alpha = Some(layer.alpha);
self
}
pub const fn animated_alpha(mut self, value: crate::Animated<f32>) -> Self {
self.animated_alpha = Some(value);
self
}
pub const fn animated_background(mut self, value: crate::Animated<Paint>) -> Self {
self.animated_background = Some(value);
self
}
pub const fn animated_offset(mut self, value: crate::Animated<(f32, f32)>) -> Self {
self.animated_offset = Some(value);
self
}
pub const fn cursor(mut self, cursor: crate::Cursor) -> Self {
self.cursor = Some(cursor);
self
}
pub const fn user_select(mut self, user_select: crate::UserSelect) -> Self {
self.user_select = Some(user_select);
self
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum Mods {
Container,
Standard,
Chip,
Scaffold,
None,
}
pub(crate) type Attrs = Vec<(&'static str, OwnedAttr)>;
fn float(value: Option<f32>) -> OwnedAttr {
value.map_or(OwnedAttr::None, |value| OwnedAttr::Float(f64::from(value)))
}
fn role(value: Option<impl Into<u16>>) -> OwnedAttr {
value.map_or(OwnedAttr::None, |value| {
OwnedAttr::Int(i64::from(value.into()))
})
}
fn paint(value: Option<Paint>) -> OwnedAttr {
value.map_or(OwnedAttr::None, |paint| {
OwnedAttr::Int(paint.to_bits() as i64)
})
}
pub(crate) fn role_or_zero(value: Option<impl Into<u16>>) -> OwnedAttr {
OwnedAttr::Int(value.map_or(0, |value| i64::from(value.into())))
}
pub(crate) fn dp_or_zero(value: Option<f32>) -> OwnedAttr {
OwnedAttr::Float(f64::from(value.unwrap_or(0.0)))
}
pub(crate) fn paint_or_zero(value: Option<Paint>) -> OwnedAttr {
OwnedAttr::Int(value.map_or(0, |paint| paint.to_bits() as i64))
}
pub(crate) fn optional_text(value: Option<String>) -> OwnedAttr {
value.map_or(OwnedAttr::None, OwnedAttr::Text)
}
pub(crate) fn push_modifiers(modifier: &Modifier, mods: Mods, out: &mut Attrs) {
let container = mods == Mods::Container;
let standard = container || mods == Mods::Standard;
let chip = standard || mods == Mods::Chip;
let scaffold = mods == Mods::Scaffold;
let mut push = |always: bool, name: &'static str, value: OwnedAttr| {
let set = !matches!(value, OwnedAttr::None | OwnedAttr::Bool(false));
if always || set {
out.push((name, value));
}
};
push(
container,
"observe_size",
modifier
.observe_size
.map_or(OwnedAttr::None, OwnedAttr::Int),
);
push(container, "motion", role(modifier.motion));
push(container || scaffold, "material", role(modifier.material));
push(chip, "weight", float(modifier.weight));
push(chip, "width", float(modifier.width));
push(chip, "height", float(modifier.height));
push(chip, "padding", float(modifier.padding));
push(chip, "padding_role", role(modifier.padding_role));
push(
standard || scaffold,
"background",
paint(if modifier.animated_background.is_some() {
None
} else {
modifier.background
}),
);
push(standard, "shape_role", role(modifier.shape_role));
push(standard, "corner_radius", float(modifier.corner_radius));
push(standard, "border_width", float(modifier.border_width));
push(standard, "border_color", paint(modifier.border_color));
push(standard, "elevation", float(modifier.elevation));
push(
chip,
"fill_max_width",
OwnedAttr::Bool(modifier.fill_max_width),
);
push(
standard,
"fill_max_height",
OwnedAttr::Bool(modifier.fill_max_height),
);
push(
false,
"alpha",
float(if modifier.animated_alpha.is_some() {
None
} else {
modifier.alpha
}),
);
push(
false,
"transform",
match (modifier.animated_offset, modifier.transform) {
(None, Some(values)) => OwnedAttr::Bytes(
values
.iter()
.flat_map(|value| value.to_le_bytes())
.collect(),
),
_ => OwnedAttr::None,
},
);
push(
false,
"cursor",
match modifier.cursor {
Some(crate::Cursor::Auto) | None => OwnedAttr::None,
Some(cursor) => OwnedAttr::Int(i64::from(u16::from(cursor))),
},
);
push(
false,
"user_select",
match modifier.user_select {
Some(crate::UserSelect::Auto) | None => OwnedAttr::None,
Some(select) => OwnedAttr::Int(i64::from(u16::from(select))),
},
);
push(
false,
"animated_alpha",
modifier.animated_alpha.map_or(OwnedAttr::None, |value| {
OwnedAttr::Bytes(crate::animation::animated_bytes(
&[value.target.to_bits()],
&value.spec,
))
}),
);
push(
false,
"animated_background",
modifier
.animated_background
.map_or(OwnedAttr::None, |value| {
let bits = value.target.to_bits();
OwnedAttr::Bytes(crate::animation::animated_bytes(
&[bits as u32, (bits >> 32) as u32],
&value.spec,
))
}),
);
push(
false,
"animated_offset",
modifier.animated_offset.map_or(OwnedAttr::None, |value| {
OwnedAttr::Bytes(crate::animation::animated_bytes(
&[value.target.0.to_bits(), value.target.1.to_bits()],
&value.spec,
))
}),
);
}
pub(crate) fn emit(
key: u64,
widget: WidgetKind,
fill: impl FnOnce(&mut Attrs),
listeners: Vec<(&'static str, EventCallback)>,
content: impl FnOnce(),
) {
let _group = group_guard(key);
emit_node(widget, fill, listeners, content);
}
pub(crate) fn emit_node(
widget: WidgetKind,
fill: impl FnOnce(&mut Attrs),
listeners: Vec<(&'static str, EventCallback)>,
content: impl FnOnce(),
) {
let node = with_composer_if_idle(|composer| {
if !composer.composing() {
return None;
}
let mut attrs = composer.take_attrs();
fill(&mut attrs);
Some(composer.update_node(widget, attrs, listeners))
})
.flatten();
let Some(node) = node else {
return;
};
with_composer(|composer| composer.push_node_context(node));
let _context = NodeContextGuard;
content();
}
struct NodeContextGuard;
impl Drop for NodeContextGuard {
fn drop(&mut self) {
with_composer_if_idle(|composer| composer.pop_node_context());
}
}
pub(crate) const fn widget_key(name: &str) -> u64 {
crate::runtime::composer::call_site(name, 0)
}