mod color;
mod types;
use std::panic::Location;
pub use color::Color;
pub use types::{
Align, Axis, FontWeight, IconName, InteractionState, Justify, Kind, Rect, Sides, Size, Source,
SurfaceRole, TextAlign, TextOverflow, TextRole, TextWrap,
};
use crate::anim::Timing;
use crate::layout::{LayoutCtx, LayoutFn, VirtualItems};
use crate::shader::ShaderBinding;
use crate::style::StyleProfile;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct El {
pub kind: Kind,
pub style_profile: StyleProfile,
pub key: Option<String>,
pub block_pointer: bool,
pub focusable: bool,
pub capture_keys: bool,
pub alpha_follows_focused_ancestor: bool,
pub source: Source,
pub axis: Axis,
pub gap: f32,
pub padding: Sides,
pub align: Align,
pub justify: Justify,
pub width: Size,
pub height: Size,
pub fill: Option<Color>,
pub stroke: Option<Color>,
pub stroke_width: f32,
pub radius: f32,
pub shadow: f32,
pub surface_role: SurfaceRole,
pub paint_overflow: Sides,
pub clip: bool,
pub scrollable: bool,
pub arrow_nav_siblings: bool,
pub tooltip: Option<String>,
pub shader_override: Option<ShaderBinding>,
pub layout_override: Option<LayoutFn>,
pub virtual_items: Option<VirtualItems>,
pub text: Option<String>,
pub text_color: Option<Color>,
pub text_align: TextAlign,
pub text_wrap: TextWrap,
pub text_overflow: TextOverflow,
pub text_role: TextRole,
pub text_max_lines: Option<usize>,
pub font_size: f32,
pub font_weight: FontWeight,
pub font_mono: bool,
pub text_italic: bool,
pub text_underline: bool,
pub text_strikethrough: bool,
pub text_link: Option<String>,
pub icon: Option<IconName>,
pub icon_stroke_width: f32,
pub children: Vec<El>,
pub opacity: f32,
pub translate: (f32, f32),
pub scale: f32,
pub animate: Option<Timing>,
pub computed_id: String,
}
impl Default for El {
fn default() -> Self {
Self {
kind: Kind::Group,
style_profile: StyleProfile::TextOnly,
key: None,
block_pointer: false,
focusable: false,
capture_keys: false,
alpha_follows_focused_ancestor: false,
source: Source::default(),
axis: Axis::Overlay,
gap: 0.0,
padding: Sides::zero(),
align: Align::Stretch,
justify: Justify::Start,
width: Size::Hug,
height: Size::Hug,
fill: None,
stroke: None,
stroke_width: 0.0,
radius: 0.0,
shadow: 0.0,
surface_role: SurfaceRole::None,
paint_overflow: Sides::zero(),
clip: false,
scrollable: false,
arrow_nav_siblings: false,
tooltip: None,
shader_override: None,
layout_override: None,
virtual_items: None,
text: None,
text_color: None,
text_align: TextAlign::Start,
text_wrap: TextWrap::NoWrap,
text_overflow: TextOverflow::Clip,
text_role: TextRole::Body,
text_max_lines: None,
font_size: crate::tokens::FONT_BASE,
font_weight: FontWeight::Regular,
font_mono: false,
text_italic: false,
text_underline: false,
text_strikethrough: false,
text_link: None,
icon: None,
icon_stroke_width: 2.0,
children: Vec::new(),
opacity: 1.0,
translate: (0.0, 0.0),
scale: 1.0,
animate: None,
computed_id: String::new(),
}
}
}
impl El {
pub fn new(kind: Kind) -> Self {
Self {
kind,
..Default::default()
}
}
pub fn key(mut self, k: impl Into<String>) -> Self {
self.key = Some(k.into());
self
}
pub fn block_pointer(mut self) -> Self {
self.block_pointer = true;
self
}
pub fn focusable(mut self) -> Self {
self.focusable = true;
self
}
pub fn capture_keys(mut self) -> Self {
self.capture_keys = true;
self.focusable = true;
self
}
pub fn alpha_follows_focused_ancestor(mut self) -> Self {
self.alpha_follows_focused_ancestor = true;
self
}
pub fn at(mut self, file: &'static str, line: u32) -> Self {
self.source = Source { file, line };
self
}
pub fn at_loc(mut self, loc: &'static Location<'static>) -> Self {
self.source = Source::from_caller(loc);
self
}
pub fn width(mut self, w: Size) -> Self {
self.width = w;
self
}
pub fn height(mut self, h: Size) -> Self {
self.height = h;
self
}
pub fn hug(mut self) -> Self {
self.width = Size::Hug;
self.height = Size::Hug;
self
}
pub fn fill_size(mut self) -> Self {
self.width = Size::Fill(1.0);
self.height = Size::Fill(1.0);
self
}
pub fn padding(mut self, p: impl Into<Sides>) -> Self {
self.padding = p.into();
self
}
pub fn gap(mut self, g: f32) -> Self {
self.gap = g;
self
}
pub fn align(mut self, a: Align) -> Self {
self.align = a;
self
}
pub fn justify(mut self, j: Justify) -> Self {
self.justify = j;
self
}
pub fn fill(mut self, c: Color) -> Self {
self.fill = Some(c);
self
}
pub fn stroke(mut self, c: Color) -> Self {
self.stroke = Some(c);
if self.stroke_width == 0.0 {
self.stroke_width = 1.0;
}
self
}
pub fn stroke_width(mut self, w: f32) -> Self {
self.stroke_width = w;
self
}
pub fn radius(mut self, r: f32) -> Self {
self.radius = r;
self
}
pub fn shadow(mut self, s: f32) -> Self {
self.shadow = s;
self
}
pub fn surface_role(mut self, role: SurfaceRole) -> Self {
self.surface_role = role;
self
}
pub fn paint_overflow(mut self, outset: impl Into<Sides>) -> Self {
self.paint_overflow = outset.into();
self
}
pub fn clip(mut self) -> Self {
self.clip = true;
self
}
pub fn scrollable(mut self) -> Self {
self.scrollable = true;
self
}
pub fn arrow_nav_siblings(mut self) -> Self {
self.arrow_nav_siblings = true;
self
}
pub fn tooltip(mut self, text: impl Into<String>) -> Self {
self.tooltip = Some(text.into());
self
}
pub fn opacity(mut self, v: f32) -> Self {
self.opacity = v.clamp(0.0, 1.0);
self
}
pub fn translate(mut self, x: f32, y: f32) -> Self {
self.translate = (x, y);
self
}
pub fn scale(mut self, v: f32) -> Self {
self.scale = v.max(0.0);
self
}
pub fn animate(mut self, timing: Timing) -> Self {
self.animate = Some(timing);
self
}
pub fn shader(mut self, binding: ShaderBinding) -> Self {
self.shader_override = Some(binding);
self
}
pub fn layout<F>(mut self, f: F) -> Self
where
F: Fn(LayoutCtx) -> Vec<Rect> + Send + Sync + 'static,
{
self.layout_override = Some(LayoutFn::new(f));
self
}
pub fn text(mut self, t: impl Into<String>) -> Self {
self.text = Some(t.into());
self
}
pub fn text_color(mut self, c: Color) -> Self {
self.text_color = Some(c);
self
}
pub fn text_align(mut self, align: TextAlign) -> Self {
self.text_align = align;
self
}
pub fn center_text(self) -> Self {
self.text_align(TextAlign::Center)
}
pub fn end_text(self) -> Self {
self.text_align(TextAlign::End)
}
pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
self.text_wrap = wrap;
self
}
pub fn wrap_text(self) -> Self {
self.text_wrap(TextWrap::Wrap)
}
pub fn nowrap_text(self) -> Self {
self.text_wrap(TextWrap::NoWrap)
}
pub fn text_overflow(mut self, overflow: TextOverflow) -> Self {
self.text_overflow = overflow;
self
}
pub fn ellipsis(self) -> Self {
self.text_overflow(TextOverflow::Ellipsis)
}
pub fn max_lines(mut self, lines: usize) -> Self {
self.text_max_lines = Some(lines.max(1));
self
}
pub fn font_size(mut self, s: f32) -> Self {
self.font_size = s;
self
}
pub fn font_weight(mut self, w: FontWeight) -> Self {
self.font_weight = w;
self
}
pub fn icon_name(mut self, name: IconName) -> Self {
self.icon = Some(name);
self
}
pub fn icon_stroke_width(mut self, width: f32) -> Self {
self.icon_stroke_width = width.max(0.25);
self
}
pub fn icon_size(mut self, size: f32) -> Self {
let size = size.max(1.0);
self.font_size = size;
self.width = Size::Fixed(size);
self.height = Size::Fixed(size);
self
}
pub fn mono(mut self) -> Self {
self.font_mono = true;
self
}
pub fn italic(mut self) -> Self {
self.text_italic = true;
self
}
pub fn underline(mut self) -> Self {
self.text_underline = true;
self
}
pub fn strikethrough(mut self) -> Self {
self.text_strikethrough = true;
self
}
pub fn code(mut self) -> Self {
self.text_role = TextRole::Code;
self.font_size = crate::tokens::FONT_SM;
self.font_weight = FontWeight::Regular;
self.font_mono = true;
self.text_color = Some(crate::tokens::TEXT_FOREGROUND);
self
}
pub fn link(mut self, url: impl Into<String>) -> Self {
self.text_link = Some(url.into());
self
}
pub fn child(mut self, c: impl Into<El>) -> Self {
self.children.push(c.into());
self
}
pub fn children<I, E>(mut self, cs: I) -> Self
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
self.children.extend(cs.into_iter().map(Into::into));
self
}
pub fn style_profile(mut self, p: StyleProfile) -> Self {
self.style_profile = p;
self
}
pub fn axis(mut self, a: Axis) -> Self {
self.axis = a;
self
}
}
#[track_caller]
pub fn column<I, E>(children: I) -> El
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
El::new(Kind::Group)
.at_loc(Location::caller())
.children(children)
.axis(Axis::Column)
}
#[track_caller]
pub fn row<I, E>(children: I) -> El
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
El::new(Kind::Group)
.at_loc(Location::caller())
.children(children)
.axis(Axis::Row)
}
#[track_caller]
pub fn stack<I, E>(children: I) -> El
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
El::new(Kind::Group)
.at_loc(Location::caller())
.children(children)
.axis(Axis::Overlay)
}
#[track_caller]
pub fn scroll<I, E>(children: I) -> El
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
El::new(Kind::Scroll)
.at_loc(Location::caller())
.children(children)
.axis(Axis::Column)
.width(Size::Fill(1.0))
.height(Size::Fill(1.0))
.clip()
.scrollable()
}
#[track_caller]
pub fn text_runs<I, E>(children: I) -> El
where
I: IntoIterator<Item = E>,
E: Into<El>,
{
El::new(Kind::Inlines)
.at_loc(Location::caller())
.axis(Axis::Column)
.align(Align::Start)
.width(Size::Fill(1.0))
.children(children)
}
#[track_caller]
pub fn hard_break() -> El {
El::new(Kind::HardBreak)
.at_loc(Location::caller())
.width(Size::Hug)
.height(Size::Hug)
}
#[track_caller]
pub fn virtual_list<F>(count: usize, row_height: f32, build_row: F) -> El
where
F: Fn(usize) -> El + Send + Sync + 'static,
{
let mut el = El::new(Kind::VirtualList)
.at_loc(Location::caller())
.axis(Axis::Column)
.align(Align::Stretch)
.width(Size::Fill(1.0))
.height(Size::Fill(1.0))
.clip()
.scrollable();
el.virtual_items = Some(VirtualItems::new(count, row_height, build_row));
el
}
#[track_caller]
pub fn spacer() -> El {
El::new(Kind::Spacer)
.at_loc(Location::caller())
.width(Size::Fill(1.0))
.height(Size::Fill(1.0))
}
#[track_caller]
pub fn divider() -> El {
El::new(Kind::Divider)
.at_loc(Location::caller())
.height(Size::Fixed(1.0))
.width(Size::Fill(1.0))
.fill(crate::tokens::BORDER)
}
impl From<&str> for El {
fn from(s: &str) -> Self {
crate::widgets::text::text(s)
}
}
impl From<String> for El {
fn from(s: String) -> Self {
crate::widgets::text::text(s)
}
}
impl From<&String> for El {
fn from(s: &String) -> Self {
crate::widgets::text::text(s.as_str())
}
}