use std::fmt;
use std::marker::PhantomData;
pub(crate) mod tree;
pub use crate::handle::{AnyId, Id};
use crate::edit::Editor;
use tree::{Axis, Container, Content, Node};
pub mod kind {
#[derive(Debug)]
pub enum Text {}
#[derive(Debug)]
pub enum Button {}
#[derive(Debug)]
pub enum NavItem {}
#[derive(Debug)]
pub enum Row {}
#[derive(Debug)]
pub enum Column {}
#[derive(Debug)]
pub enum Stack {}
#[derive(Debug)]
pub enum Card {}
#[derive(Debug)]
pub enum Spacer {}
#[derive(Debug)]
pub enum TextBox {}
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::kind::Text {}
impl Sealed for super::kind::Button {}
impl Sealed for super::kind::NavItem {}
impl Sealed for super::kind::TextBox {}
}
pub trait HasText: sealed::Sealed {}
impl HasText for kind::Text {}
impl HasText for kind::Button {}
impl HasText for kind::NavItem {}
impl HasText for kind::TextBox {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Length {
Auto,
Fixed(f32),
Fill(f32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Align {
Start,
Center,
End,
Stretch,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Insets {
pub left: f32,
pub top: f32,
pub right: f32,
pub bottom: f32,
}
impl Insets {
pub const fn all(v: f32) -> Self {
Self {
left: v,
top: v,
right: v,
bottom: v,
}
}
pub const fn symmetric(horizontal: f32, vertical: f32) -> Self {
Self {
left: horizontal,
top: vertical,
right: horizontal,
bottom: vertical,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextStyle {
Caption,
#[default]
Body,
BodyStrong,
Subtitle,
Title,
}
impl TextStyle {
pub(crate) const ALL: [TextStyle; 5] = [
TextStyle::Caption,
TextStyle::Body,
TextStyle::BodyStrong,
TextStyle::Subtitle,
TextStyle::Title,
];
pub(crate) fn size(self) -> f32 {
match self {
TextStyle::Caption => 12.0,
TextStyle::Body | TextStyle::BodyStrong => 14.0,
TextStyle::Subtitle => 20.0,
TextStyle::Title => 28.0,
}
}
pub(crate) fn semibold(self) -> bool {
!matches!(self, TextStyle::Caption | TextStyle::Body)
}
}
pub struct Element<K> {
pub(crate) node: Node,
kind: PhantomData<fn() -> K>,
}
impl<K> Element<K> {
fn new(node: Node) -> Self {
Self {
node,
kind: PhantomData,
}
}
}
impl<K> fmt::Debug for Element<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Element").finish_non_exhaustive()
}
}
macro_rules! sizes {
() => {
#[must_use]
pub fn width(mut self, width: Length) -> Self {
self.node.width = width;
self
}
#[must_use]
pub fn height(mut self, height: Length) -> Self {
self.node.height = height;
self
}
};
}
macro_rules! container {
() => {
#[must_use]
pub fn child(mut self, child: impl Into<AnyId>) -> Self {
self.container_mut().children.push(child.into().0);
self
}
#[must_use]
pub fn children<I: IntoIterator<Item = A>, A: Into<AnyId>>(mut self, children: I) -> Self {
let list = &mut self.container_mut().children;
list.extend(children.into_iter().map(|c| c.into().0));
self
}
#[must_use]
pub fn gap(mut self, gap: f32) -> Self {
self.container_mut().gap = gap;
self
}
#[must_use]
pub fn padding(mut self, padding: Insets) -> Self {
self.container_mut().padding = padding;
self
}
#[must_use]
pub fn align(mut self, align: Align) -> Self {
self.container_mut().align = align;
self
}
fn container_mut(&mut self) -> &mut Container {
self.node
.container
.as_mut()
.expect("a container builder has a container")
}
};
}
macro_rules! element {
($builder:ident, $kind:ident) => {
impl From<$builder> for Element<kind::$kind> {
fn from(b: $builder) -> Self {
Element::new(b.node)
}
}
};
}
#[derive(Debug)]
pub struct Text {
node: Node,
}
impl Text {
pub fn new(text: impl Into<String>) -> Self {
Self {
node: Node::leaf(Content::Text {
text: text.into(),
style: TextStyle::Body,
}),
}
}
#[must_use]
pub fn style(mut self, style: TextStyle) -> Self {
if let Content::Text { style: s, .. } = &mut self.node.content {
*s = style;
}
self
}
sizes!();
}
#[derive(Debug)]
pub struct Button {
node: Node,
}
impl Button {
pub fn new(label: impl Into<String>) -> Self {
Self {
node: Node::leaf(Content::Button {
label: label.into(),
accent: false,
}),
}
}
#[must_use]
pub fn accent(mut self) -> Self {
if let Content::Button { accent, .. } = &mut self.node.content {
*accent = true;
}
self
}
sizes!();
}
#[derive(Debug)]
pub struct NavItem {
node: Node,
}
impl NavItem {
pub fn new(label: impl Into<String>) -> Self {
Self {
node: Node::leaf(Content::NavItem {
label: label.into(),
selected: false,
}),
}
}
#[must_use]
pub fn selected(mut self, on: bool) -> Self {
if let Content::NavItem { selected, .. } = &mut self.node.content {
*selected = on;
}
self
}
sizes!();
}
#[derive(Debug)]
pub struct Row {
node: Node,
}
impl Row {
pub fn new() -> Self {
Self {
node: Node::container(Content::Blank, Axis::Row, Align::Center),
}
}
sizes!();
container!();
}
impl Default for Row {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Column {
node: Node,
}
impl Column {
pub fn new() -> Self {
Self {
node: Node::container(Content::Blank, Axis::Column, Align::Stretch),
}
}
sizes!();
container!();
}
impl Default for Column {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Stack {
node: Node,
}
impl Stack {
pub fn new() -> Self {
Self {
node: Node::container(Content::Blank, Axis::Stack, Align::Stretch),
}
}
sizes!();
container!();
}
impl Default for Stack {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Card {
node: Node,
}
impl Card {
pub fn new() -> Self {
let mut node = Node::container(Content::Card, Axis::Stack, Align::Stretch);
if let Some(c) = &mut node.container {
c.padding = Insets::all(16.0);
}
Self { node }
}
sizes!();
container!();
}
impl Default for Card {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Spacer {
node: Node,
}
impl Spacer {
pub fn fill() -> Self {
let mut node = Node::leaf(Content::Blank);
node.width = Length::Fill(1.0);
node.height = Length::Fill(1.0);
Self { node }
}
pub fn fixed(size: f32) -> Self {
let mut node = Node::leaf(Content::Blank);
node.width = Length::Fixed(size);
node.height = Length::Fixed(size);
Self { node }
}
}
#[derive(Debug)]
pub struct TextBox {
node: Node,
}
impl TextBox {
pub fn new() -> Self {
Self {
node: Node::leaf(Content::TextBox {
edit: Editor::new(false, usize::MAX),
placeholder: String::new(),
scroll: 0.0,
}),
}
}
#[must_use]
pub fn placeholder(mut self, text: impl Into<String>) -> Self {
if let Content::TextBox { placeholder, .. } = &mut self.node.content {
*placeholder = text.into();
}
self
}
#[must_use]
pub fn multiline(mut self) -> Self {
if let Content::TextBox { edit, .. } = &mut self.node.content {
edit.set_multiline(true);
}
self
}
#[must_use]
pub fn max_length(mut self, chars: usize) -> Self {
if let Content::TextBox { edit, .. } = &mut self.node.content {
edit.set_max_chars(chars);
}
self
}
sizes!();
}
impl Default for TextBox {
fn default() -> Self {
Self::new()
}
}
element!(Text, Text);
element!(Button, Button);
element!(NavItem, NavItem);
element!(Row, Row);
element!(Column, Column);
element!(Stack, Stack);
element!(Card, Card);
element!(Spacer, Spacer);
element!(TextBox, TextBox);