use super::{EdgeLayout, UseLayout};
use crate::{
aspect_ratio::KnownAspectRatio,
bounds::Bounds,
edge::Edge,
state::{PreState, State},
Tick,
};
use leptos::prelude::*;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Layout {
pub outer: Memo<Bounds>,
pub top: Vec<Memo<Bounds>>,
pub right: Vec<Memo<Bounds>>,
pub bottom: Vec<Memo<Bounds>>,
pub left: Vec<Memo<Bounds>>,
pub inner: Memo<Bounds>,
pub x_width: Memo<f64>,
}
#[derive(Clone)]
pub struct DeferredRender {
edge: Edge,
bounds: Memo<Bounds>,
layout: UseLayout,
}
impl DeferredRender {
pub fn render<X: Tick, Y: Tick>(self, state: State<X, Y>) -> impl IntoView {
self.layout.render(self.edge, self.bounds, state)
}
}
impl Layout {
pub fn compose<X: Tick, Y: Tick>(
top: &[EdgeLayout<X>],
right: &[EdgeLayout<Y>],
bottom: &[EdgeLayout<X>],
left: &[EdgeLayout<Y>],
aspect_ratio: Memo<KnownAspectRatio>,
state: &PreState<X, Y>,
) -> (Layout, Vec<DeferredRender>) {
let top_heights = collect_heights(top, state);
let top_height = sum_sizes(top_heights.clone());
let bottom_heights = collect_heights(bottom, state);
let bottom_height = sum_sizes(bottom_heights.clone());
let inner_height =
KnownAspectRatio::inner_height_signal(aspect_ratio, top_height, bottom_height);
let (left_widths, left) = use_vertical(left, state, inner_height);
let left_width = sum_sizes(left_widths.clone());
let (right_widths, right) = use_vertical(right, state, inner_height);
let right_width = sum_sizes(right_widths.clone());
let avail_width =
KnownAspectRatio::inner_width_signal(aspect_ratio, left_width, right_width);
let outer = Memo::new(move |_| {
Bounds::new(
left_width.get() + avail_width.get() + right_width.get(),
top_height.get() + inner_height.get() + bottom_height.get(),
)
});
let inner = Memo::new(move |_| {
outer.get().shrink(
top_height.get(),
right_width.get(),
bottom_height.get(),
left_width.get(),
)
});
let top_bounds = Memo::new(move |_| {
let i = inner.get();
Bounds::from_points(i.left_x(), outer.get().top_y(), i.right_x(), i.top_y())
});
let right_bounds = Memo::new(move |_| {
let i = inner.get();
Bounds::from_points(i.right_x(), i.top_y(), outer.get().right_x(), i.bottom_y())
});
let bottom_bounds = Memo::new(move |_| {
let i = inner.get();
let bottom_y = outer.get().bottom_y();
Bounds::from_points(i.left_x(), i.bottom_y(), i.right_x(), bottom_y)
});
let left_bounds = Memo::new(move |_| {
let i = inner.get();
Bounds::from_points(outer.get().left_x(), i.top_y(), i.left_x(), i.bottom_y())
});
let data_len = state.data.len;
let x_width = Memo::new(move |_| inner.get().width() / data_len.get() as f64);
let layout = Layout {
outer,
top: option_bounds(Edge::Top, top_bounds, top_heights),
right: option_bounds(Edge::Right, right_bounds, right_widths),
bottom: option_bounds(Edge::Bottom, bottom_bounds, bottom_heights),
left: option_bounds(Edge::Left, left_bounds, left_widths),
inner,
x_width,
};
let vertical = |edge, bounds: &[Memo<Bounds>], items: Vec<_>| {
items
.into_iter()
.enumerate()
.map(move |(index, opt)| (edge, bounds[index], opt))
.collect::<Vec<_>>()
};
let horizontal = |edge: Edge, bounds: &[Memo<Bounds>], items: &[EdgeLayout<X>]| {
items
.iter()
.enumerate()
.map(|(index, opt)| {
(
edge,
bounds[index],
opt.to_horizontal_use(state, avail_width),
)
})
.collect::<Vec<_>>()
};
let deferred = vertical(Edge::Left, &layout.left, left)
.into_iter()
.chain(vertical(Edge::Right, &layout.right, right))
.chain(horizontal(Edge::Top, &layout.top, top))
.chain(horizontal(Edge::Bottom, &layout.bottom, bottom))
.map(|(edge, bounds, layout)| DeferredRender {
edge,
bounds,
layout,
})
.collect::<Vec<_>>();
(layout, deferred)
}
}
fn collect_heights<X: Tick, Y: Tick>(
items: &[EdgeLayout<X>],
state: &PreState<X, Y>,
) -> Vec<Signal<f64>> {
items
.iter()
.map(|c| c.fixed_height(state))
.collect::<Vec<_>>()
}
fn use_vertical<X: Tick, Y: Tick>(
items: &[EdgeLayout<Y>],
state: &PreState<X, Y>,
avail_height: Memo<f64>,
) -> (Vec<Signal<f64>>, Vec<UseLayout>) {
items
.iter()
.map(|c| {
let vert = c.to_vertical_use(state, avail_height);
(vert.width, vert.layout)
})
.unzip()
}
fn sum_sizes(sizes: Vec<Signal<f64>>) -> Memo<f64> {
Memo::new(move |_| sizes.iter().map(|opt| opt.get()).sum::<f64>())
}
fn option_bounds(edge: Edge, outer: Memo<Bounds>, sizes: Vec<Signal<f64>>) -> Vec<Memo<Bounds>> {
let mut seen = Vec::<Signal<f64>>::with_capacity(sizes.len());
sizes
.into_iter()
.map(|size| {
let prev = seen.clone();
seen.push(size);
Memo::new(move |_| {
let proximal = prev.iter().map(|s| s.get()).sum::<f64>();
let distal = proximal + size.get();
let outer = outer.get();
let width = outer.width();
let height = outer.height();
match edge {
Edge::Top => outer.shrink(height - distal, 0.0, proximal, 0.0),
Edge::Bottom => outer.shrink(proximal, 0.0, height - distal, 0.0),
Edge::Left => outer.shrink(0.0, proximal, 0.0, width - distal),
Edge::Right => outer.shrink(0.0, width - distal, 0.0, proximal),
}
})
})
.collect::<Vec<_>>()
}