use super::*;
use std::marker::PhantomData;
use zhc_utils::{
graphics::{Frame, Height, Justify, Size},
iter::Separate,
};
enum Spaced<E> {
Element(E),
Space,
}
pub struct VStack<E: Element, C: Class = NoClass> {
pub content: Vec<E>,
class: PhantomData<C>,
variable: VariableCell,
}
impl<E: Element, C: Class> VStack<E, C> {
pub fn new(content: Vec<E>) -> Self {
Self {
content,
class: PhantomData,
variable: VariableCell::fresh(),
}
}
}
impl<E: Element, C: Class> Element for VStack<E, C> {
fn solve_size(&mut self, stylesheet: &StyleSheet) {
let style = stylesheet.get::<C>();
let size = self
.content
.iter_mut()
.map(Spaced::Element)
.separate_with(|| Spaced::Space)
.fold(Size::ZERO, |size, element| match element {
Spaced::Element(element) => {
element.solve_size(stylesheet);
size.stack_vertical(element.get_size())
}
Spaced::Space => size.pad_bottom(style.spacing),
})
.pad(style.padding);
self.variable.set_size(size);
}
fn solve_frame(&mut self, stylesheet: &StyleSheet, available: Frame) {
let style = stylesheet.get::<C>();
let intrinsic = self.get_size();
let frame = match style.vjustify {
Justify::Pack => available.resize(&intrinsic, style.halign, style.valign),
Justify::Space | Justify::Spread => {
available.resize_horizontal(intrinsic.width, style.halign)
}
};
self.variable.set_frame(frame.clone());
let inner = frame.crop_around(style.padding);
let heights: Vec<Height> = self.content.iter().map(|e| e.get_size().height).collect();
let child_frames = match style.vjustify {
Justify::Pack => inner.pack_vertical(&heights, style.valign, style.spacing),
Justify::Space => inner.justify_vertical(&heights),
Justify::Spread => inner.spread_vertical(&heights),
};
for (element, child_frame) in self.content.iter_mut().zip(child_frames) {
element.solve_frame(stylesheet, child_frame);
}
}
fn get_size(&self) -> Size {
self.variable.get_size()
}
fn get_frame(&self) -> Frame {
self.variable.get_frame()
}
fn get_variable_cell(&self) -> VariableCell {
self.variable.clone()
}
}