pub mod flexbox;
pub mod text;
pub mod traits;
use crate::{
commands::RenderCommand,
elements::traits::KaolinElement,
style::sizing::{PreferredSize, SizingDimensions},
};
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use uuid::Uuid;
pub(crate) struct KaolinNode<Color>
where
Color: Default + Copy + PartialEq + crate::style::KaolinColor<Color>,
{
#[allow(dead_code)]
id: String,
growable_width: bool,
growable_height: bool,
shrinkable: bool,
element: Box<dyn KaolinElement<Color>>,
sizing: (SizingDimensions, SizingDimensions),
current_width: f64,
current_height: f64,
x: f64,
y: f64,
}
impl<Color> KaolinNode<Color>
where
Color: Default + Copy + PartialEq + crate::style::KaolinColor<Color>,
{
pub(crate) fn new(element: impl KaolinElement<Color> + 'static, id: Option<String>) -> Self {
let id = id.unwrap_or_else(|| Uuid::new_v4().to_string());
let (width, height) = element.get_sizing_dimensions();
KaolinNode {
id,
growable_width: element.default_growable_width(&width),
growable_height: element.default_growable_height(&height),
shrinkable: element.default_shrinkable(&width),
current_width: element.starting_width(&width),
current_height: element.starting_height(&height),
sizing: (width, height),
element: Box::new(element),
x: 0.0,
y: 0.0,
}
}
pub fn get_grow_factor(&self) -> (f64, f64) {
let w_factor = match self.sizing.0.preferred {
PreferredSize::Grow(factor) => factor.into(),
_ => 0.0, };
let h_factor = match self.sizing.1.preferred {
PreferredSize::Grow(factor) => factor.into(),
_ => 0.0, };
(w_factor, h_factor)
}
fn grow_width(&mut self, limit: f64) -> f64 {
let amount = self.sizing.0.clamped(self.current_width + limit) - self.current_width;
if self.growable_width && amount > 0.0 {
self.current_width += amount;
if amount != limit {
self.growable_width = false; }
amount
} else if self.shrinkable && amount < 0.0 {
self.current_width += amount;
if amount != limit {
self.shrinkable = false; }
amount
} else {
self.growable_width = false;
self.shrinkable = false;
0.0
}
}
fn grow_height(&mut self, limit: f64) -> f64 {
debug_assert!(limit > 0.0, "Height can only grow, never shrink");
let amount = self.sizing.1.clamped(self.current_height + limit) - self.current_height;
if self.growable_height && amount > 0.0 {
self.current_height += amount;
if amount != limit {
self.growable_height = false; }
amount
} else {
0.0
}
}
fn fit_height(&mut self, final_width: f64) {
let height = self.sizing.1;
self.current_height = height.clamped(self.element.fit_height_unbound(final_width));
}
pub fn set_position(&mut self, x: f64, y: f64) {
self.x = x;
self.y = y;
self.element
.propagate_position((x, y), (self.current_width, self.current_height));
}
pub fn render(&self) -> Box<dyn Iterator<Item = RenderCommand<Color>> + '_> {
self.element
.render((self.x, self.y), (self.current_width, self.current_height))
}
}
pub(crate) struct KaolinNodes<Color>
where
Color: Default + Copy + PartialEq + crate::style::KaolinColor<Color>,
{
pub(crate) nodes: Vec<KaolinNode<Color>>,
}
impl<Color> KaolinNodes<Color>
where
Color: Default + Copy + PartialEq + crate::style::KaolinColor<Color>,
{
fn new() -> Self {
KaolinNodes { nodes: Vec::new() }
}
fn push(&mut self, node: KaolinNode<Color>) {
self.nodes.push(node);
}
fn get_growable_children_w(&mut self) -> Vec<&mut KaolinNode<Color>> {
self.nodes.iter_mut().filter(|c| c.growable_width).collect()
}
fn get_growable_children_h(&mut self) -> Vec<&mut KaolinNode<Color>> {
self.nodes
.iter_mut()
.filter(|c| c.growable_height)
.collect()
}
fn get_shrinkable_children(&mut self) -> Vec<&mut KaolinNode<Color>> {
self.nodes.iter_mut().filter(|c| c.shrinkable).collect()
}
fn get_smallest_widths(children: &Vec<&mut KaolinNode<Color>>) -> (f64, f64) {
let mut smallest = f64::INFINITY;
let mut second_smallest = f64::INFINITY;
for node in children {
if node.current_width < smallest {
second_smallest = smallest;
smallest = node.current_width;
} else if node.current_width < second_smallest && node.current_width > smallest {
second_smallest = node.current_width;
}
}
(smallest, second_smallest)
}
fn get_smallest_heights(children: &Vec<&mut KaolinNode<Color>>) -> (f64, f64) {
let mut smallest = f64::INFINITY;
let mut second_smallest = f64::INFINITY;
for node in children {
if node.current_height < smallest {
second_smallest = smallest;
smallest = node.current_height;
} else if node.current_height < second_smallest && node.current_height > smallest {
second_smallest = node.current_height;
}
}
(smallest, second_smallest)
}
fn get_biggest_widths(children: &Vec<&mut KaolinNode<Color>>) -> (f64, f64) {
let mut biggest = 0.0;
let mut second_biggest = 0.0;
for node in children {
if node.current_width > biggest {
second_biggest = biggest;
biggest = node.current_width;
} else if node.current_width > second_biggest && node.current_width < biggest {
second_biggest = node.current_width;
}
}
(biggest, second_biggest)
}
fn get_cumulative_width(&self) -> f64 {
self.nodes.iter().map(|c| c.current_width).sum()
}
fn get_max_width(&self) -> f64 {
self.nodes
.iter()
.fold(0.0, |acc, c| acc.max(c.current_width))
}
fn get_cumulative_height(&self) -> f64 {
self.nodes.iter().map(|c| c.current_height).sum()
}
fn get_max_height(&self) -> f64 {
self.nodes
.iter()
.fold(0.0, |acc, c| acc.max(c.current_height))
}
fn nodes(&mut self) -> impl Iterator<Item = &mut KaolinNode<Color>> {
self.nodes.iter_mut()
}
pub fn propagate_width_growth(&mut self) {
self.nodes().for_each(|c| {
if let Some(container) = c.element.as_container() {
container.propagate_width_growth(c.current_width);
}
c.fit_height(c.current_width);
});
}
pub fn do_grow_height(&mut self) {
self.nodes.iter_mut().for_each(|node| {
if let Some(container) = node.element.as_container() {
container.propagate_height_growth(node.current_height);
}
});
}
pub fn render_nodes(&self) -> Box<dyn Iterator<Item = RenderCommand<Color>> + '_> {
Box::new(
self.nodes
.iter()
.flat_map(|node: &KaolinNode<Color>| node.render()),
)
}
pub fn gaps(&self) -> u32 {
if self.nodes.is_empty() {
return 0;
}
self.nodes.len() as u32 - 1
}
}