use peniko::Color;
use crate::events::KeyInput;
use crate::style::{Length, Paint, Style, TextAlign, TextWrap, ThemedFn, Transition};
use crate::theme::Theme;
use crate::tokens::{ShadowToken, TextSize, Weight};
pub(crate) type TypeAheadFn<Msg> = Box<dyn Fn(&str) -> Option<Msg>>;
pub(crate) type KeyFn<Msg> = Box<dyn Fn(&KeyInput) -> Option<Msg>>;
pub(crate) type DragFn<Msg> = Box<dyn Fn(f32, f32) -> Option<Msg>>;
pub(crate) type SwipeFn<Msg> = Box<dyn Fn(SwipeDir) -> Msg>;
pub(crate) type InputFn<Msg> = Box<dyn Fn(&str) -> Msg>;
pub(crate) type FileDropFn<Msg> = Box<dyn Fn(&std::path::Path) -> Msg>;
pub(crate) type DropFn<Msg> = Box<dyn Fn(&str) -> Option<Msg>>;
pub(crate) type ContentFn = Box<dyn Fn(&Theme) -> Color>;
#[derive(Debug, Clone, PartialEq)]
pub enum Kind {
Box,
Text(String),
Rich(Vec<Span>),
Divider,
Path(PathData),
Input(InputData),
Image(ImageData),
}
pub struct VirtualData<Msg> {
pub(crate) count: usize,
pub(crate) row_height: f32,
pub(crate) variable: bool,
pub(crate) builder: std::rc::Rc<dyn Fn(usize) -> Element<Msg>>,
}
impl<Msg> Clone for VirtualData<Msg> {
fn clone(&self) -> Self {
Self {
count: self.count,
row_height: self.row_height,
variable: self.variable,
builder: std::rc::Rc::clone(&self.builder),
}
}
}
pub(crate) struct ResponsiveData<Msg> {
pub(crate) hint: (f32, f32),
pub(crate) f: std::rc::Rc<dyn Fn((f32, f32)) -> Element<Msg>>,
}
#[derive(Debug, Clone)]
pub struct ImageData {
pub image: peniko::ImageData,
}
impl PartialEq for ImageData {
fn eq(&self, other: &Self) -> bool {
self.image.data.id() == other.image.data.id()
&& self.image.width == other.image.width
&& self.image.height == other.image.height
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InputData {
pub value: String,
pub placeholder: String,
pub multiline: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct OpticalCorrection {
pub overshoot: bool,
pub center: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PathData {
pub path: std::sync::Arc<kurbo::BezPath>,
pub viewbox: (f64, f64),
pub stroke: Option<f64>,
pub optical: OpticalCorrection,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OverlayMode {
Open,
Toggle,
Hover {
delay_ms: f32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DrawerSide {
Left,
Right,
Top,
Bottom,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OverlayPlacement {
Below {
gap: f32,
},
BelowCenter {
gap: f32,
},
Center,
TopRight {
margin: f32,
},
Pointer {
gap: f32,
},
Edge {
side: DrawerSide,
},
RightStart {
gap: f32,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Overlay {
pub mode: OverlayMode,
pub placement: OverlayPlacement,
pub backdrop: bool,
pub trap_focus: bool,
}
impl Overlay {
pub fn menu() -> Self {
Self {
mode: OverlayMode::Toggle,
placement: OverlayPlacement::Below { gap: 4.0 },
backdrop: false,
trap_focus: false,
}
}
pub fn tooltip() -> Self {
Self {
mode: OverlayMode::Hover { delay_ms: 400.0 },
placement: OverlayPlacement::BelowCenter { gap: 6.0 },
backdrop: false,
trap_focus: false,
}
}
pub fn modal() -> Self {
Self {
mode: OverlayMode::Open,
placement: OverlayPlacement::Center,
backdrop: true,
trap_focus: true,
}
}
pub fn context() -> Self {
Self {
mode: OverlayMode::Open,
placement: OverlayPlacement::Pointer { gap: 2.0 },
backdrop: false,
trap_focus: false,
}
}
pub fn toasts() -> Self {
Self {
mode: OverlayMode::Open,
placement: OverlayPlacement::TopRight { margin: 16.0 },
backdrop: false,
trap_focus: false,
}
}
pub fn drawer(side: DrawerSide) -> Self {
Self {
mode: OverlayMode::Open,
placement: OverlayPlacement::Edge { side },
backdrop: true,
trap_focus: true,
}
}
pub fn submenu() -> Self {
Self {
mode: OverlayMode::Toggle,
placement: OverlayPlacement::RightStart { gap: 2.0 },
backdrop: false,
trap_focus: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Semantics {
Button,
Checkbox {
checked: bool,
mixed: bool,
},
Switch {
on: bool,
},
Radio {
selected: bool,
},
Slider {
value: f32,
min: f32,
max: f32,
},
TextInput {
multiline: bool,
},
ComboBox,
Dialog,
Tab {
selected: bool,
},
Alert,
Label,
Image,
Spinbutton {
value: f32,
min: f32,
max: f32,
},
Meter {
value: f32,
min: f32,
max: f32,
},
ProgressBar {
value: Option<f32>,
},
}
impl Semantics {
#[must_use]
pub fn aria_role(&self) -> &'static str {
crate::query::role_name(self)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Span {
pub(crate) text: String,
pub(crate) weight: Option<crate::tokens::Weight>,
pub(crate) color: Option<Color>,
pub(crate) size_px: Option<f32>,
pub(crate) family: Option<crate::tokens::FamilyRole>,
pub(crate) italic: bool,
}
pub fn span(text: impl Into<String>) -> Span {
Span {
text: text.into(),
weight: None,
color: None,
size_px: None,
family: None,
italic: false,
}
}
impl Span {
pub fn weight(mut self, weight: crate::tokens::Weight) -> Self {
self.weight = Some(weight);
self
}
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn size_px(mut self, px: f32) -> Self {
self.size_px = Some(px);
self
}
pub fn family(mut self, family: crate::tokens::FamilyRole) -> Self {
self.family = Some(family);
self
}
pub fn italic(mut self) -> Self {
self.italic = true;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SwipeDir {
Up,
Down,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cursor {
Default,
Pointer,
Text,
NotAllowed,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ExitAnim {
pub transition: Transition,
pub opacity_to: f32,
pub scale_to: f32,
pub translate_to: (f32, f32),
}
pub struct Element<Msg> {
pub(crate) kind: Kind,
pub(crate) style: Style,
pub(crate) children: Vec<Element<Msg>>,
pub(crate) key: Option<String>,
pub(crate) source: &'static std::panic::Location<'static>,
pub(crate) stack: bool,
pub(crate) focusable: bool,
pub(crate) autofocus: bool,
pub(crate) stick_bottom: bool,
pub(crate) cursor: Option<Cursor>,
pub(crate) disabled: bool,
pub(crate) on_click: Option<Msg>,
pub(crate) on_double_click: Option<Msg>,
pub(crate) on_right_click: Option<Msg>,
pub(crate) on_hover: Option<Msg>,
pub(crate) on_key: Option<KeyFn<Msg>>,
pub(crate) on_drag: Option<DragFn<Msg>>,
pub(crate) on_drag_end: Option<Msg>,
pub(crate) on_swipe: Option<SwipeFn<Msg>>,
pub(crate) on_input: Option<InputFn<Msg>>,
pub(crate) on_close: Option<Msg>,
pub(crate) on_file_drop: Option<FileDropFn<Msg>>,
pub(crate) drag_source: Option<String>,
pub(crate) on_drop: Option<DropFn<Msg>>,
pub(crate) overlay: Option<Overlay>,
pub(crate) spin: Option<f32>,
pub(crate) keyframes: Option<crate::style::Keyframes>,
pub(crate) virtual_rows: Option<VirtualData<Msg>>,
pub(crate) responsive: Option<ResponsiveData<Msg>>,
pub(crate) semantics: Option<Semantics>,
pub(crate) access_value: Option<String>,
pub(crate) live: bool,
pub(crate) selectable: bool,
pub(crate) enter: Option<crate::style::Transition>,
pub(crate) exit: Option<ExitAnim>,
pub(crate) animate_layout: bool,
pub(crate) on_type_ahead: Option<TypeAheadFn<Msg>>,
pub(crate) label: Option<String>,
pub(crate) themed: Option<ThemedFn>,
pub(crate) hover_style: Option<ThemedFn>,
pub(crate) active_style: Option<ThemedFn>,
pub(crate) focus_style: Option<ThemedFn>,
pub(crate) state_layer: Option<ContentFn>,
pub(crate) press_scale: bool,
pub(crate) invalid: bool,
pub(crate) transition: Option<Transition>,
}
impl<Msg> Element<Msg> {
#[track_caller]
fn new(kind: Kind) -> Self {
Self {
kind,
style: Style::default(),
children: Vec::new(),
key: None,
source: std::panic::Location::caller(),
stack: false,
focusable: false,
autofocus: false,
stick_bottom: false,
cursor: None,
disabled: false,
on_click: None,
on_double_click: None,
on_right_click: None,
on_hover: None,
on_key: None,
on_drag: None,
on_drag_end: None,
on_swipe: None,
on_input: None,
on_close: None,
on_file_drop: None,
drag_source: None,
on_drop: None,
overlay: None,
spin: None,
keyframes: None,
virtual_rows: None,
responsive: None,
semantics: None,
access_value: None,
live: false,
selectable: false,
enter: None,
exit: None,
animate_layout: false,
on_type_ahead: None,
label: None,
themed: None,
hover_style: None,
active_style: None,
focus_style: None,
state_layer: None,
press_scale: false,
invalid: false,
transition: None,
}
}
pub fn style(&self) -> &Style {
&self.style
}
pub fn children<M>(mut self, children: impl crate::IntoChildren<Msg, M>) -> Self {
self.children.extend(children.into_children());
self
}
pub fn child(mut self, child: impl Into<Element<Msg>>) -> Self {
self.children.push(child.into());
self
}
pub fn id(mut self, key: &str) -> Self {
self.key = Some(key.to_owned());
self
}
pub fn on_click(mut self, msg: Msg) -> Self {
self.on_click = Some(msg);
self.focusable = true;
if self.cursor.is_none() {
self.cursor = Some(Cursor::Pointer);
}
self
}
pub fn hover(mut self, f: impl Fn(Style) -> Style + 'static) -> Self {
self.hover_style = Some(Box::new(move |_, s| f(s)));
self
}
pub fn hover_themed(mut self, f: impl Fn(&Theme, Style) -> Style + 'static) -> Self {
self.hover_style = Some(Box::new(f));
self
}
pub fn active(mut self, f: impl Fn(Style) -> Style + 'static) -> Self {
self.active_style = Some(Box::new(move |_, s| f(s)));
self
}
pub fn active_themed(mut self, f: impl Fn(&Theme, Style) -> Style + 'static) -> Self {
self.active_style = Some(Box::new(f));
self
}
pub fn focus(mut self, f: impl Fn(Style) -> Style + 'static) -> Self {
self.focus_style = Some(Box::new(move |_, s| f(s)));
self
}
pub fn focus_themed(mut self, f: impl Fn(&Theme, Style) -> Style + 'static) -> Self {
self.focus_style = Some(Box::new(f));
self
}
pub fn state_layer(mut self, content: impl Fn(&Theme) -> Color + 'static) -> Self {
self.state_layer = Some(Box::new(content));
self
}
pub fn press_scale(mut self) -> Self {
self.press_scale = true;
self
}
pub fn invalid(mut self, invalid: bool) -> Self {
self.invalid = invalid;
self
}
pub fn themed(mut self, f: impl Fn(&Theme, Style) -> Style + 'static) -> Self {
self.themed = Some(match self.themed.take() {
Some(prev) => Box::new(move |t, s| f(t, prev(t, s))),
None => Box::new(f),
});
self
}
pub fn on_double_click(mut self, msg: Msg) -> Self {
self.on_double_click = Some(msg);
self
}
pub fn on_right_click(mut self, msg: Msg) -> Self {
self.on_right_click = Some(msg);
self
}
pub fn on_hover(mut self, msg: Msg) -> Self {
self.on_hover = Some(msg);
self
}
pub fn on_key(mut self, f: impl Fn(&KeyInput) -> Option<Msg> + 'static) -> Self {
self.on_key = Some(Box::new(f));
self.focusable = true;
self
}
pub fn on_drag(mut self, f: impl Fn(f32, f32) -> Option<Msg> + 'static) -> Self {
self.on_drag = Some(Box::new(f));
self
}
pub fn on_drag_end(mut self, msg: Msg) -> Self {
self.on_drag_end = Some(msg);
self
}
pub fn on_swipe(mut self, f: impl Fn(SwipeDir) -> Msg + 'static) -> Self {
self.on_swipe = Some(Box::new(f));
self
}
pub fn on_input(mut self, f: impl Fn(&str) -> Msg + 'static) -> Self {
self.on_input = Some(Box::new(f));
self
}
pub fn on_file_drop(mut self, f: impl Fn(&std::path::Path) -> Msg + 'static) -> Self {
self.on_file_drop = Some(Box::new(f));
self
}
pub fn drag_source(mut self, payload: impl Into<String>) -> Self {
self.drag_source = Some(payload.into());
self
}
pub fn on_drop(mut self, f: impl Fn(&str) -> Option<Msg> + 'static) -> Self {
self.on_drop = Some(Box::new(f));
self
}
pub fn overlay(mut self, overlay: Overlay) -> Self {
self.overlay = Some(overlay);
self
}
pub fn on_close(mut self, msg: Msg) -> Self {
self.on_close = Some(msg);
self
}
pub fn spin(mut self, period_ms: f32) -> Self {
self.spin = Some(period_ms);
self
}
pub fn optical_overshoot(mut self) -> Self {
if let Kind::Path(p) = &mut self.kind {
p.optical.overshoot = true;
}
self
}
pub fn optical_center(mut self) -> Self {
if let Kind::Path(p) = &mut self.kind {
p.optical.center = true;
}
self
}
pub fn keyframes(mut self, keyframes: crate::style::Keyframes) -> Self {
self.keyframes = Some(keyframes);
self
}
const MAX_VIRTUAL_ROWS: usize = 10_000_000;
pub fn virtual_rows(
mut self,
count: usize,
row_height: f32,
builder: impl Fn(usize) -> Element<Msg> + 'static,
) -> Self {
self.virtual_rows = Some(VirtualData {
count: count.min(Self::MAX_VIRTUAL_ROWS),
row_height,
variable: false,
builder: std::rc::Rc::new(builder),
});
self
}
pub fn virtual_rows_variable(
mut self,
count: usize,
estimated_height: f32,
builder: impl Fn(usize) -> Element<Msg> + 'static,
) -> Self {
self.virtual_rows = Some(VirtualData {
count: count.min(Self::MAX_VIRTUAL_ROWS),
row_height: estimated_height,
variable: true,
builder: std::rc::Rc::new(builder),
});
self
}
pub fn on_type_ahead(mut self, f: impl Fn(&str) -> Option<Msg> + 'static) -> Self {
self.on_type_ahead = Some(Box::new(f));
self
}
pub fn enter(mut self, transition: crate::style::Transition) -> Self {
self.enter = Some(crate::style::Transition {
opacity: true,
..transition
});
self
}
pub fn exit(mut self, transition: crate::style::Transition) -> Self {
self.exit = Some(ExitAnim {
transition: crate::style::Transition {
opacity: true,
..transition
},
opacity_to: 0.0,
scale_to: 1.0,
translate_to: (0.0, 0.0),
});
self
}
pub fn exit_to(mut self, opacity: f32, scale: f32, dx: f32, dy: f32) -> Self {
self.exit = Some(ExitAnim {
transition: crate::style::Transition::all()
.duration_ms(crate::tokens::MotionDuration::Base.exit_ms())
.easing(crate::tokens::EASE_EXIT),
opacity_to: opacity,
scale_to: scale,
translate_to: (dx, dy),
});
self
}
pub fn animate_layout(mut self) -> Self {
self.animate_layout = true;
self
}
pub fn selectable(mut self) -> Self {
self.selectable = true;
self
}
pub fn live(mut self) -> Self {
self.live = true;
self
}
pub fn semantics(mut self, semantics: Semantics) -> Self {
self.semantics = Some(semantics);
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn value(mut self, value: impl Into<String>) -> Self {
self.access_value = Some(value.into());
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn transition(mut self, t: Transition) -> Self {
self.transition = Some(t);
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = focusable;
self
}
pub fn autofocus(mut self) -> Self {
self.autofocus = true;
self.focusable = true;
self
}
pub fn cursor(mut self, cursor: Cursor) -> Self {
self.cursor = Some(cursor);
self
}
pub fn p(mut self, v: f32) -> Self {
self.style = self.style.p(v);
self
}
pub fn px(mut self, v: f32) -> Self {
self.style = self.style.px(v);
self
}
pub fn py(mut self, v: f32) -> Self {
self.style = self.style.py(v);
self
}
pub fn pt(mut self, v: f32) -> Self {
self.style = self.style.pt(v);
self
}
pub fn pr(mut self, v: f32) -> Self {
self.style = self.style.pr(v);
self
}
pub fn pb(mut self, v: f32) -> Self {
self.style = self.style.pb(v);
self
}
pub fn pl(mut self, v: f32) -> Self {
self.style = self.style.pl(v);
self
}
pub fn m(mut self, v: f32) -> Self {
self.style = self.style.m(v);
self
}
pub fn mx(mut self, v: f32) -> Self {
self.style = self.style.mx(v);
self
}
pub fn my(mut self, v: f32) -> Self {
self.style = self.style.my(v);
self
}
pub fn mt(mut self, v: f32) -> Self {
self.style = self.style.mt(v);
self
}
pub fn mr(mut self, v: f32) -> Self {
self.style = self.style.mr(v);
self
}
pub fn mb(mut self, v: f32) -> Self {
self.style = self.style.mb(v);
self
}
pub fn ml(mut self, v: f32) -> Self {
self.style = self.style.ml(v);
self
}
pub fn gap(mut self, v: f32) -> Self {
self.style = self.style.gap(v);
self
}
pub fn w(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.w(v);
self
}
pub fn h(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.h(v);
self
}
pub fn min_w(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.min_w(v);
self
}
pub fn max_w(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.max_w(v);
self
}
pub fn min_h(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.min_h(v);
self
}
pub fn max_h(mut self, v: impl Into<Length>) -> Self {
self.style = self.style.max_h(v);
self
}
pub fn measure(mut self, chars: f32) -> Self {
self.style = self.style.measure(chars);
self
}
pub fn w_ch(mut self, chars: f32) -> Self {
self.style = self.style.w_ch(chars);
self
}
pub fn min_w_ch(mut self, chars: f32) -> Self {
self.style = self.style.min_w_ch(chars);
self
}
pub fn max_w_ch(mut self, chars: f32) -> Self {
self.style = self.style.max_w_ch(chars);
self
}
pub fn w_full(mut self) -> Self {
self.style = self.style.w_full();
self
}
pub fn h_full(mut self) -> Self {
self.style = self.style.h_full();
self
}
pub fn grow(mut self) -> Self {
self.style = self.style.grow();
self
}
pub fn shrink0(mut self) -> Self {
self.style = self.style.shrink0();
self
}
pub fn items_start(mut self) -> Self {
self.style = self.style.items_start();
self
}
pub fn items_center(mut self) -> Self {
self.style = self.style.items_center();
self
}
pub fn items_end(mut self) -> Self {
self.style = self.style.items_end();
self
}
pub fn items_baseline(mut self) -> Self {
self.style = self.style.items_baseline();
self
}
pub fn self_start(mut self) -> Self {
self.style = self.style.self_start();
self
}
pub fn self_center(mut self) -> Self {
self.style = self.style.self_center();
self
}
pub fn self_end(mut self) -> Self {
self.style = self.style.self_end();
self
}
pub fn self_stretch(mut self) -> Self {
self.style = self.style.self_stretch();
self
}
pub fn justify_start(mut self) -> Self {
self.style = self.style.justify_start();
self
}
pub fn justify_center(mut self) -> Self {
self.style = self.style.justify_center();
self
}
pub fn justify_end(mut self) -> Self {
self.style = self.style.justify_end();
self
}
pub fn justify_between(mut self) -> Self {
self.style = self.style.justify_between();
self
}
pub fn wrap(mut self) -> Self {
self.style = self.style.wrap();
self
}
pub fn absolute(mut self) -> Self {
self.style = self.style.absolute();
self
}
pub fn top(mut self, v: f32) -> Self {
self.style = self.style.top(v);
self
}
pub fn right(mut self, v: f32) -> Self {
self.style = self.style.right(v);
self
}
pub fn bottom(mut self, v: f32) -> Self {
self.style = self.style.bottom(v);
self
}
pub fn left(mut self, v: f32) -> Self {
self.style = self.style.left(v);
self
}
pub fn overflow_hidden(mut self) -> Self {
self.style = self.style.overflow_hidden();
self
}
pub fn scroll_y(mut self) -> Self {
self.style = self.style.scroll_y();
self
}
pub fn scroll_x(mut self) -> Self {
self.style = self.style.scroll_x();
self
}
pub fn scroll_xy(mut self) -> Self {
self.style = self.style.scroll_xy();
self
}
pub fn sticky_top(mut self, offset: f32) -> Self {
self.style = self.style.sticky_top(offset);
self
}
pub fn sticky_bottom(mut self, offset: f32) -> Self {
self.style = self.style.sticky_bottom(offset);
self
}
pub fn sticky_left(mut self, offset: f32) -> Self {
self.style = self.style.sticky_left(offset);
self
}
pub fn sticky_right(mut self, offset: f32) -> Self {
self.style = self.style.sticky_right(offset);
self
}
pub fn stick_to_bottom(mut self) -> Self {
self.stick_bottom = true;
self
}
pub fn bg(mut self, paint: impl Into<Paint>) -> Self {
self.style = self.style.bg(paint);
self
}
pub fn border(mut self, width: f32, color: Color) -> Self {
self.style = self.style.border(width, color);
self
}
pub fn border_top(mut self, width: f32, color: Color) -> Self {
self.style = self.style.border_top(width, color);
self
}
pub fn border_right(mut self, width: f32, color: Color) -> Self {
self.style = self.style.border_right(width, color);
self
}
pub fn border_bottom(mut self, width: f32, color: Color) -> Self {
self.style = self.style.border_bottom(width, color);
self
}
pub fn border_left(mut self, width: f32, color: Color) -> Self {
self.style = self.style.border_left(width, color);
self
}
pub fn ring(mut self, width: f32, color: Color) -> Self {
self.style = self.style.ring(width, color);
self
}
pub fn rounded(mut self, r: f32) -> Self {
self.style = self.style.rounded(r);
self
}
pub fn rounded_full(mut self) -> Self {
self.style = self.style.rounded_full();
self
}
pub fn rounded_t(mut self, r: f32) -> Self {
self.style = self.style.rounded_t(r);
self
}
pub fn rounded_b(mut self, r: f32) -> Self {
self.style = self.style.rounded_b(r);
self
}
pub fn rounded_l(mut self, r: f32) -> Self {
self.style = self.style.rounded_l(r);
self
}
pub fn rounded_r(mut self, r: f32) -> Self {
self.style = self.style.rounded_r(r);
self
}
pub fn corners(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
self.style = self.style.corners(tl, tr, br, bl);
self
}
pub fn translate(mut self, x: f32, y: f32) -> Self {
self.style = self.style.translate(x, y);
self
}
pub fn rotate(mut self, degrees: f32) -> Self {
self.style = self.style.rotate(degrees);
self
}
pub fn skew(mut self, x_degrees: f32, y_degrees: f32) -> Self {
self.style = self.style.skew(x_degrees, y_degrees);
self
}
pub fn scale_xy(mut self, x: f32, y: f32) -> Self {
self.style = self.style.scale_xy(x, y);
self
}
pub fn transform_origin(mut self, fx: f32, fy: f32) -> Self {
self.style = self.style.transform_origin(fx, fy);
self
}
pub fn corner_smoothing(mut self, s: f32) -> Self {
self.style = self.style.corner_smoothing(s);
self
}
pub fn shadow(mut self, token: ShadowToken) -> Self {
self.style = self.style.shadow(token);
self
}
pub fn surface(self, role: crate::surface::Surface) -> Self {
self.themed(move |t, s| {
let mut s = role.bundle().apply(t, s).rounded(role.radius_px(&t.radius));
if t.elevation == crate::Elevation::Flat
&& matches!(
role,
crate::surface::Surface::Card | crate::surface::Surface::Raised
)
{
s.shadow_token = None;
}
s
})
}
pub fn opacity(mut self, v: f32) -> Self {
self.style = self.style.opacity(v);
self
}
pub fn trim(mut self, v: f32) -> Self {
self.style = self.style.trim(v);
self
}
pub fn backdrop_blur(mut self, radius: f32) -> Self {
self.style = self.style.backdrop_blur(radius);
self
}
pub fn element_filter(mut self, filter: crate::ElementFilter) -> Self {
self.style = self.style.element_filter(filter);
self
}
pub fn specular_edge(mut self, edge: crate::style::SpecularEdge) -> Self {
self.style = self.style.specular_edge(edge);
self
}
pub fn sheen(mut self, sheen: crate::style::Sheen) -> Self {
self.style = self.style.sheen(sheen);
self
}
pub fn adaptive_tint(mut self, adaptive: crate::style::AdaptiveTint) -> Self {
self.style = self.style.adaptive_tint(adaptive);
self
}
pub fn size(mut self, size: TextSize) -> Self {
self.style = self.style.size(size);
self
}
pub fn size_px(mut self, px: f32) -> Self {
self.style = self.style.size_px(px);
self
}
pub fn tracking(mut self, em: f32) -> Self {
self.style = self.style.tracking(em);
self
}
pub fn leading(mut self, multiple: f32) -> Self {
self.style = self.style.leading(multiple);
self
}
pub fn tabular(mut self) -> Self {
self.style = self.style.tabular();
self
}
pub fn proportional_nums(mut self) -> Self {
self.style = self.style.proportional_nums();
self
}
pub fn oldstyle_nums(mut self) -> Self {
self.style = self.style.oldstyle_nums();
self
}
pub fn lining_nums(mut self) -> Self {
self.style = self.style.lining_nums();
self
}
pub fn small_caps(mut self) -> Self {
self.style = self.style.small_caps();
self
}
pub fn ligatures(mut self, on: bool) -> Self {
self.style = self.style.ligatures(on);
self
}
pub fn fractions(mut self) -> Self {
self.style = self.style.fractions();
self
}
pub fn family(mut self, family: crate::tokens::FamilyRole) -> Self {
self.style = self.style.family(family);
self
}
pub fn weight(mut self, weight: Weight) -> Self {
self.style = self.style.weight(weight);
self
}
pub fn color(mut self, color: Color) -> Self {
self.style = self.style.color(color);
self
}
pub fn mono(mut self) -> Self {
self.style = self.style.mono();
self
}
pub fn truncate(mut self) -> Self {
self.style = self.style.truncate();
self
}
pub fn text_align(mut self, align: TextAlign) -> Self {
self.style = self.style.text_align(align);
self
}
pub fn balance(mut self) -> Self {
self.style = self.style.balance();
self
}
pub fn pretty(mut self) -> Self {
self.style = self.style.pretty();
self
}
pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
self.style = self.style.text_wrap(wrap);
self
}
pub fn optical(mut self, optical: crate::style::OpticalSizing) -> Self {
self.style = self.style.optical(optical);
self
}
pub fn optical_auto(mut self) -> Self {
self.style = self.style.optical_auto();
self
}
}
#[track_caller]
pub fn div<Msg>() -> Element<Msg> {
Element::new(Kind::Box)
}
#[track_caller]
pub fn row<Msg>() -> Element<Msg> {
Element::new(Kind::Box)
}
#[track_caller]
pub fn col<Msg>() -> Element<Msg> {
let mut el = Element::new(Kind::Box);
el.style.direction = crate::style::Direction::Column;
el
}
#[track_caller]
pub fn stack<Msg>() -> Element<Msg> {
let mut el = Element::new(Kind::Box);
el.style.display = crate::style::Display::Grid;
el.stack = true;
el
}
#[track_caller]
pub fn rich_text<Msg>(spans: impl IntoIterator<Item = Span>) -> Element<Msg> {
Element::new(Kind::Rich(spans.into_iter().collect()))
}
#[track_caller]
pub fn text<Msg>(content: impl Into<String>) -> Element<Msg> {
Element::new(Kind::Text(content.into()))
}
#[track_caller]
pub fn spacer<Msg>() -> Element<Msg> {
Element::new(Kind::Box).grow()
}
#[track_caller]
pub fn divider<Msg>() -> Element<Msg> {
Element::new(Kind::Divider).w_full().h(1.0).shrink0()
}
#[track_caller]
pub fn responsive<Msg>(f: impl Fn((f32, f32)) -> Element<Msg> + 'static) -> Element<Msg> {
responsive_hinted((0.0, 0.0), f)
}
#[track_caller]
pub fn responsive_hinted<Msg>(
hint: (f32, f32),
f: impl Fn((f32, f32)) -> Element<Msg> + 'static,
) -> Element<Msg> {
let mut el = Element::new(Kind::Box);
el.responsive = Some(ResponsiveData {
hint,
f: std::rc::Rc::new(f),
});
el
}
#[track_caller]
pub fn raw_input<Msg>(value: impl Into<String>, placeholder: impl Into<String>) -> Element<Msg> {
Element::new(Kind::Input(InputData {
value: value.into(),
placeholder: placeholder.into(),
multiline: false,
}))
.focusable(true)
.cursor(Cursor::Text)
}
#[track_caller]
pub fn raw_text_area<Msg>(
value: impl Into<String>,
placeholder: impl Into<String>,
) -> Element<Msg> {
Element::new(Kind::Input(InputData {
value: value.into(),
placeholder: placeholder.into(),
multiline: true,
}))
.focusable(true)
.cursor(Cursor::Text)
}
#[must_use]
pub fn image_payload(width: u32, height: u32, mut pixels: Vec<u8>) -> ImageData {
let row = width as usize * 4;
let rows = pixels
.len()
.checked_div(row)
.unwrap_or(0)
.min(height as usize);
pixels.truncate(row * rows);
#[expect(clippy::cast_possible_truncation, reason = "rows <= height: u32")]
let height = rows as u32;
ImageData {
image: peniko::ImageData {
data: pixels.into(),
format: peniko::ImageFormat::Rgba8,
alpha_type: peniko::ImageAlphaType::Alpha,
width,
height,
},
}
}
#[must_use]
pub fn image_from_data<Msg>(data: ImageData) -> Element<Msg> {
let (width, height) = (data.image.width, data.image.height);
#[expect(clippy::cast_precision_loss, reason = "image sizes fit in f32")]
Element::new(Kind::Image(data))
.w(width as f32)
.h(height as f32)
.shrink0()
}
#[track_caller]
pub fn image_rgba8<Msg>(width: u32, height: u32, pixels: Vec<u8>) -> Element<Msg> {
image_from_data(image_payload(width, height, pixels))
}
#[track_caller]
pub fn path<Msg>(bez: kurbo::BezPath, viewbox: (f64, f64), stroke: Option<f64>) -> Element<Msg> {
#[expect(clippy::cast_possible_truncation, reason = "viewbox sizes are small")]
Element::new(Kind::Path(PathData {
path: std::sync::Arc::new(bez),
viewbox,
stroke,
optical: OpticalCorrection::default(),
}))
.w(viewbox.0 as f32)
.h(viewbox.1 as f32)
.shrink0()
}
impl<Msg: 'static> Element<Msg> {
pub fn map<B: 'static>(self, f: impl Fn(Msg) -> B + Clone + 'static) -> Element<B> {
Element {
kind: self.kind,
style: self.style,
children: self
.children
.into_iter()
.map(|c| c.map(f.clone()))
.collect(),
key: self.key,
source: self.source,
stack: self.stack,
focusable: self.focusable,
autofocus: self.autofocus,
stick_bottom: self.stick_bottom,
cursor: self.cursor,
disabled: self.disabled,
on_click: self.on_click.map(&f),
on_double_click: self.on_double_click.map(&f),
on_right_click: self.on_right_click.map(&f),
on_hover: self.on_hover.map(&f),
on_key: self.on_key.map(|k| {
let f = f.clone();
Box::new(move |key: &KeyInput| k(key).map(&f)) as KeyFn<B>
}),
on_type_ahead: self.on_type_ahead.map(|k| {
let f = f.clone();
Box::new(move |buffer: &str| k(buffer).map(&f)) as TypeAheadFn<B>
}),
on_drag: self.on_drag.map(|d| {
let f = f.clone();
Box::new(move |x: f32, y: f32| d(x, y).map(&f)) as DragFn<B>
}),
on_drag_end: self.on_drag_end.map(&f),
on_swipe: self.on_swipe.map(|s| {
let f = f.clone();
Box::new(move |d: SwipeDir| f(s(d))) as SwipeFn<B>
}),
on_input: self.on_input.map(|i| {
let f = f.clone();
Box::new(move |s: &str| f(i(s))) as InputFn<B>
}),
on_close: self.on_close.map(&f),
on_file_drop: self.on_file_drop.map(|d| {
let f = f.clone();
Box::new(move |p: &std::path::Path| f(d(p))) as FileDropFn<B>
}),
drag_source: self.drag_source,
on_drop: self.on_drop.map(|d| {
let f = f.clone();
Box::new(move |payload: &str| d(payload).map(&f)) as DropFn<B>
}),
overlay: self.overlay,
spin: self.spin,
keyframes: self.keyframes,
virtual_rows: self.virtual_rows.map(|v| {
let f = f.clone();
let builder = v.builder;
VirtualData {
count: v.count,
row_height: v.row_height,
variable: v.variable,
builder: std::rc::Rc::new(move |i| builder(i).map(f.clone())),
}
}),
responsive: self.responsive.map(|r| {
let f = f.clone();
let inner = r.f;
ResponsiveData {
hint: r.hint,
f: std::rc::Rc::new(move |sz| inner(sz).map(f.clone())),
}
}),
semantics: self.semantics,
access_value: self.access_value,
live: self.live,
selectable: self.selectable,
enter: self.enter,
exit: self.exit,
animate_layout: self.animate_layout,
label: self.label,
themed: self.themed,
hover_style: self.hover_style,
active_style: self.active_style,
focus_style: self.focus_style,
state_layer: self.state_layer,
press_scale: self.press_scale,
invalid: self.invalid,
transition: self.transition,
}
}
}
impl<Msg> Element<Msg> {
pub fn grid_cols<T: Into<crate::style::GridTemplate>>(
mut self,
tracks: impl IntoIterator<Item = T>,
) -> Self {
self.style = self.style.grid_cols(tracks);
self
}
pub fn grid_rows<T: Into<crate::style::GridTemplate>>(
mut self,
tracks: impl IntoIterator<Item = T>,
) -> Self {
self.style = self.style.grid_rows(tracks);
self
}
pub fn grid_col(mut self, start: i16, span: u16) -> Self {
self.style = self.style.grid_col(start, span);
self
}
pub fn grid_row(mut self, start: i16, span: u16) -> Self {
self.style = self.style.grid_row(start, span);
self
}
pub fn grid_template_areas<R: AsRef<str>>(mut self, rows: impl IntoIterator<Item = R>) -> Self {
self.style = self.style.grid_template_areas(rows);
self
}
pub fn grid_area(mut self, name: impl Into<String>) -> Self {
self.style = self.style.grid_area(name);
self
}
pub fn grid_col_lines(mut self, start: impl Into<String>, end: impl Into<String>) -> Self {
self.style = self.style.grid_col_lines(start, end);
self
}
pub fn grid_row_lines(mut self, start: impl Into<String>, end: impl Into<String>) -> Self {
self.style = self.style.grid_row_lines(start, end);
self
}
pub fn grid_col_names<S: Into<String>>(mut self, names: impl IntoIterator<Item = S>) -> Self {
self.style = self.style.grid_col_names(names);
self
}
pub fn grid_row_names<S: Into<String>>(mut self, names: impl IntoIterator<Item = S>) -> Self {
self.style = self.style.grid_row_names(names);
self
}
}
#[cfg(test)]
mod surface_tests {
use super::div;
use crate::surface::Surface;
#[test]
fn element_surface_defers_to_resolution() {
let el = div::<()>().surface(Surface::Card);
assert!(el.style().fill.is_none());
}
}