extern crate alloc;
mod column;
mod row;
mod text;
use alloc::vec::Vec;
use core::convert::Infallible;
use embedded_graphics::{
Drawable as _,
pixelcolor::Rgb565,
prelude::{DrawTarget, PixelColor, Point, Primitive as _, Size},
primitives::{PrimitiveStyle, Rectangle},
};
pub use column::*;
pub use row::*;
pub use text::*;
use crate::{Style, Theme, layout::BoxLayout, style::BoxStyle};
pub trait IntoElement {
type Element;
fn into_element(self) -> Self::Element;
}
#[derive(PartialEq, Eq)]
pub enum Element<'a, C = Rgb565, CE = Infallible>
where
C: PixelColor,
{
Column(Column<'a, C, CE>),
Row(Row<'a, C, CE>),
Text(Text<'a, C>),
Custom(CE),
}
impl<'a, C> Element<'a, C>
where
C: PixelColor,
{
#[allow(unused)]
pub(crate) const fn key(&self) -> Option<ElementKey> {
None
}
#[allow(unused)]
pub(crate) fn children(&self) -> Option<&[Self]> {
match self {
Element::Column(column) => Some(&column.children),
Element::Row(row) => Some(&row.children),
_ => None,
}
}
pub(crate) fn take_children(&mut self) -> Option<Vec<Self>> {
match self {
Element::Column(column) => Some(core::mem::take(&mut column.children)),
Element::Row(row) => Some(core::mem::take(&mut row.children)),
_ => None,
}
}
pub(crate) const fn box_style(&self) -> BoxStyle {
match self {
Element::Column(column) => column.style().box_style(),
Element::Row(row) => row.style().box_style(),
Element::Text(text) => text.style().box_style(),
Element::Custom(never) => match *never {},
}
}
pub(crate) fn draw<D>(
&self,
layout: &BoxLayout,
target: &mut D,
theme: &Theme<C>,
) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
{
match self {
Element::Column(column) => column.draw(layout, target),
Element::Row(row) => row.draw(layout, target),
Element::Text(text) => text.draw(layout, target, theme),
Element::Custom(_) => unimplemented!("custom elements are not implemented"),
}
}
}
pub(crate) fn draw_box<S, C, D>(
style: &Style<S, C>,
layout: &BoxLayout,
target: &mut D,
) -> Result<(), D::Error>
where
S: Default,
C: PixelColor,
D: DrawTarget<Color = C>,
{
if let Some(color) = style.background {
draw_filled_rectangle(layout.border, color, target)?;
}
let Some(color) = style.border_color else {
return Ok(());
};
let origin = layout.border.top_left;
let size = layout.border.size;
let top = style.border.top.min(size.height);
let right = style.border.right.min(size.width);
let bottom = style.border.bottom.min(size.height);
let left = style.border.left.min(size.width);
draw_filled_rectangle(Rectangle::new(origin, Size::new(size.width, top)), color, target)?;
draw_filled_rectangle(
Rectangle::new(
Point::new(
saturating_coordinate_add(origin.x, size.width.saturating_sub(right)),
origin.y,
),
Size::new(right, size.height),
),
color,
target,
)?;
draw_filled_rectangle(
Rectangle::new(
Point::new(
origin.x,
saturating_coordinate_add(origin.y, size.height.saturating_sub(bottom)),
),
Size::new(size.width, bottom),
),
color,
target,
)?;
draw_filled_rectangle(Rectangle::new(origin, Size::new(left, size.height)), color, target)
}
fn draw_filled_rectangle<C, D>(
rectangle: Rectangle,
color: C,
target: &mut D,
) -> Result<(), D::Error>
where
C: PixelColor,
D: DrawTarget<Color = C>,
{
if rectangle.size.width == 0 || rectangle.size.height == 0 {
return Ok(());
}
rectangle.into_styled(PrimitiveStyle::with_fill(color)).draw(target)
}
const fn saturating_coordinate_add(coordinate: i32, offset: u32) -> i32 {
coordinate.saturating_add(if offset > i32::MAX as u32 { i32::MAX } else { offset as i32 })
}
impl<'a, C> IntoElement for Element<'a, C>
where
C: PixelColor,
{
type Element = Self;
fn into_element(self) -> Self {
self
}
}
pub trait ParentElement<'a, C>
where
C: PixelColor,
{
fn extend(&mut self, elements: impl IntoIterator<Item = Element<'a, C>>);
fn child(mut self, child: impl IntoElement<Element = Element<'a, C>>) -> Self
where
Self: Sized,
{
self.extend(core::iter::once(child.into_element()));
self
}
fn children(
mut self,
children: impl IntoIterator<Item = impl IntoElement<Element = Element<'a, C>>>,
) -> Self
where
Self: Sized,
{
self.extend(children.into_iter().map(|child| child.into_element()));
self
}
}
#[derive(PartialEq, Eq)]
pub(crate) enum ElementKey {
Number(usize),
String(&'static str),
}
impl From<usize> for ElementKey {
fn from(value: usize) -> Self {
Self::Number(value)
}
}
impl From<&'static str> for ElementKey {
fn from(value: &'static str) -> Self {
Self::String(value)
}
}