use crate::scene::Color; use crate::text::{TextSpan, TextStyle, TextWrap}; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct ElementId(u64); impl ElementId { pub const fn new(raw: u64) -> Self { Self(raw) } pub const fn raw(self) -> u64 { self.0 } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FlexDirection { Row, Column, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum CursorIcon { Default, Pointer, Text, } #[derive(Clone, Copy, Debug, PartialEq)] pub struct Edges { pub top: f32, pub right: f32, pub bottom: f32, pub left: f32, } impl Edges { pub const ZERO: Self = Self { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, }; pub const fn all(value: f32) -> Self { Self { top: value, right: value, bottom: value, left: value, } } pub const fn symmetric(horizontal: f32, vertical: f32) -> Self { Self { top: vertical, right: horizontal, bottom: vertical, left: horizontal, } } } #[derive(Clone, Debug, PartialEq)] pub struct Style { pub direction: FlexDirection, pub width: Option, pub height: Option, pub flex_grow: f32, pub gap: f32, pub padding: Edges, pub background: Option, pub pointer_events: bool, pub focusable: bool, pub cursor: Option, } impl Default for Style { fn default() -> Self { Self { direction: FlexDirection::Column, width: None, height: None, flex_grow: 0.0, gap: 0.0, padding: Edges::ZERO, background: None, pointer_events: true, focusable: false, cursor: None, } } } #[derive(Clone, Debug, PartialEq)] enum ElementContent { Container, Text(TextNode), } #[derive(Clone, Debug, PartialEq)] pub(crate) struct TextNode { pub spans: Vec, pub style: TextStyle, } #[derive(Clone, Debug, PartialEq)] pub struct Element { pub id: Option, pub style: Style, pub children: Vec, content: ElementContent, } impl Element { pub fn new() -> Self { Self { id: None, style: Style::default(), children: Vec::new(), content: ElementContent::Container, } } pub fn text(text: impl Into, style: TextStyle) -> Self { Self::spans([TextSpan::new(text)], style) } pub fn spans(spans: impl IntoIterator, style: TextStyle) -> Self { Self { id: None, style: Style::default(), children: Vec::new(), content: ElementContent::Text(TextNode { spans: spans.into_iter().collect(), style, }), } } pub fn paragraph(text: impl Into, style: TextStyle) -> Self { Self::text(text, style.with_wrap(TextWrap::Word)) } pub fn rich_paragraph(spans: impl IntoIterator, style: TextStyle) -> Self { Self::spans(spans, style.with_wrap(TextWrap::Word)) } pub fn row() -> Self { Self::new().direction(FlexDirection::Row) } pub fn column() -> Self { Self::new().direction(FlexDirection::Column) } pub fn direction(mut self, direction: FlexDirection) -> Self { self.style.direction = direction; self } pub fn width(mut self, width: f32) -> Self { self.style.width = Some(width); self } pub fn height(mut self, height: f32) -> Self { self.style.height = Some(height); self } pub fn flex(mut self, flex_grow: f32) -> Self { self.style.flex_grow = flex_grow.max(0.0); self } pub fn gap(mut self, gap: f32) -> Self { self.style.gap = gap.max(0.0); self } pub fn padding(mut self, padding: Edges) -> Self { self.style.padding = padding; self } pub fn background(mut self, color: Color) -> Self { self.style.background = Some(color); self } pub fn id(mut self, id: ElementId) -> Self { self.id = Some(id); self } pub fn pointer_events(mut self, pointer_events: bool) -> Self { self.style.pointer_events = pointer_events; self } pub fn focusable(mut self, focusable: bool) -> Self { self.style.focusable = focusable; self } pub fn cursor(mut self, cursor: CursorIcon) -> Self { self.style.cursor = Some(cursor); self } pub fn child(mut self, child: Element) -> Self { self.assert_container(); self.children.push(child); self } pub fn children(mut self, children: impl IntoIterator) -> Self { self.assert_container(); self.children.extend(children); self } pub(crate) fn text_node(&self) -> Option<&TextNode> { match &self.content { ElementContent::Text(text) => Some(text), ElementContent::Container => None, } } fn assert_container(&self) { assert!( matches!(self.content, ElementContent::Container), "text elements cannot contain children" ); } } impl Default for Element { fn default() -> Self { Self::new() } }