use embedded_graphics::{
draw_target::DrawTarget,
geometry::{Point, Size},
pixelcolor::PixelColor,
primitives::Rectangle,
};
use crate::{
DisplayTarget, StorageView, Style, TextStyle, Theme,
common::{NodeIndex, TextRange},
element::{CustomElement, DivStyle, NoCustomElement},
layout::{BorderBox, Layout},
style::BoxStyle,
};
#[cfg(feature = "flexbox")]
use crate::style::FlexItemStyle;
pub(crate) struct Node<C, CE>
where
C: PixelColor,
{
pub kind: NodeKind<C, CE>,
pub layout: Layout,
pub child: Option<NodeIndex>,
pub sibling: Option<NodeIndex>,
}
impl<C: PixelColor, CE> Node<C, CE> {
pub(crate) const fn box_style(&self) -> BoxStyle {
self.kind.box_style()
}
#[cfg(feature = "flexbox")]
pub(crate) const fn flex_item_style(&self) -> FlexItemStyle {
self.kind.flex_item_style()
}
pub(crate) fn draw<D>(
&self,
layout: &BorderBox,
theme: &Theme<C>,
target: &mut D,
) -> Result<(), D::Error>
where
CE: CustomElement<C>,
D: DrawTarget<Color = C>,
{
self.kind.draw(layout, theme, target)
}
pub(crate) const fn set_sibling(&mut self, sibling: NodeIndex) {
self.sibling = Some(sibling);
}
}
pub(crate) struct FrameTree<'frame, C, CE = NoCustomElement>
where
C: PixelColor,
{
pub root: Option<NodeIndex>,
pub storage: StorageView<'frame, C, CE>,
}
impl<'frame, C, CE> FrameTree<'frame, C, CE>
where
C: PixelColor,
{
pub(crate) const fn new(storage: StorageView<'frame, C, CE>) -> Self {
Self { root: None, storage }
}
#[allow(unused)]
pub(crate) fn len(&self) -> usize {
self.storage.nodes.len()
}
#[inline]
pub(crate) fn node(&self, index: NodeIndex) -> &Node<C, CE> {
&self.storage.nodes[index]
}
#[inline]
pub(crate) fn node_mut(&mut self, index: NodeIndex) -> &mut Node<C, CE> {
&mut self.storage.nodes[index]
}
pub(crate) fn draw<D>(&mut self, theme: &Theme<C>, target: &mut D) -> Result<(), D::Error>
where
CE: CustomElement<C>,
D: DisplayTarget<Color = C>,
{
let Some(root) = self.root else { return target.clear(theme.background) };
let viewport = target.bounding_box();
if self.needs_clear(viewport.size) {
if target.try_begin(viewport, theme.background) {
self.draw_subtree(root, Point::zero(), theme, target)?;
return target.flush();
}
target.clear(theme.background)?;
}
self.draw_adaptive(root, Point::zero(), theme.background, viewport, theme, target)
}
pub(crate) fn needs_clear(&self, viewport: Size) -> bool {
let Some(root) = self.root else {
return true;
};
let root = self.node(root);
if !root.kind.has_background() {
return true;
}
let root_bounds = root.layout.resolve(Point::zero()).border;
let viewport = Rectangle::new(Point::zero(), viewport);
root_bounds.intersection(&viewport) != viewport
}
fn draw_adaptive<D>(
&mut self,
index: NodeIndex,
parent_origin: Point,
inherited_background: C,
viewport: Rectangle,
theme: &Theme<C>,
target: &mut D,
) -> Result<(), D::Error>
where
CE: CustomElement<C>,
D: DisplayTarget<Color = C>,
{
let layout = self.node(index).layout.resolve(parent_origin);
let bounds = layout.border.intersection(&viewport);
if bounds.size != Size::zero() && target.try_begin(bounds, inherited_background) {
self.draw_subtree(index, parent_origin, theme, target)?;
target.flush()?;
return Ok(());
}
self.draw_node(index, &layout, theme, target)?;
let child_background = self.node(index).kind.background().unwrap_or(inherited_background);
let mut child = self.node(index).child;
while let Some(index) = child {
self.draw_adaptive(
index,
layout.content.top_left,
child_background,
viewport,
theme,
target,
)?;
child = self.node(index).sibling;
}
Ok(())
}
fn draw_node<D>(
&self,
index: NodeIndex,
layout: &BorderBox,
theme: &Theme<C>,
target: &mut D,
) -> Result<(), D::Error>
where
CE: CustomElement<C>,
D: DrawTarget<Color = C>,
{
let node = self.node(index);
if let NodeKind::Text(text) = &node.kind {
let content = text.content(self.storage.text);
text.draw(content, layout, theme, target)
} else {
node.draw(layout, theme, target)
}
}
fn draw_subtree<D>(
&mut self,
index: NodeIndex,
parent_origin: Point,
theme: &Theme<C>,
target: &mut D,
) -> Result<(), D::Error>
where
CE: CustomElement<C>,
D: DisplayTarget<Color = C>,
{
let layout = self.node(index).layout.resolve(parent_origin);
self.draw_node(index, &layout, theme, target)?;
let mut child = self.node(index).child;
while let Some(index) = child {
self.draw_subtree(index, layout.content.top_left, theme, target)?;
child = self.node(index).sibling;
}
Ok(())
}
}
pub(crate) enum NodeKind<C: PixelColor, CE> {
Div(Style<DivStyle, C>),
Text(TextNode<C>),
Custom(Style<(), C>, CE),
}
impl<C: PixelColor, CE> NodeKind<C, CE> {
pub(crate) const fn box_style(&self) -> BoxStyle {
match self {
Self::Div(style) => style.box_style(),
Self::Text(text) => text.style.box_style(),
Self::Custom(style, _) => style.box_style(),
}
}
#[cfg(feature = "flexbox")]
pub(crate) const fn flex_item_style(&self) -> FlexItemStyle {
match self {
Self::Div(style) => style.flex_item_style(),
Self::Text(text) => text.style.flex_item_style(),
Self::Custom(style, _) => style.flex_item_style(),
}
}
pub(crate) const fn background(&self) -> Option<C> {
match self {
Self::Div(style) => style.background,
Self::Text(text) => text.style.background,
Self::Custom(style, _) => style.background,
}
}
pub(crate) const fn has_background(&self) -> bool {
self.background().is_some()
}
}
pub(crate) struct TextNode<C> {
pub(crate) range: TextRange,
pub(crate) size: Size,
pub(crate) style: Style<TextStyle<C>, C>,
}
impl<C: PixelColor> TextNode<C> {
pub(crate) fn content<'a>(&self, storage: &'a [u8]) -> &'a str {
let end = self.range.offset + self.range.len;
unsafe { core::str::from_utf8_unchecked(&storage[self.range.offset..end]) }
}
}