use super::anchors::{Align, Side, is_out_band};
use super::ir::{Bbox, GridRule, PlacedNode};
use super::primitives;
use crate::error::Error;
use crate::resolve::{AttrMap, ResolvedValue};
pub fn reserve_bands(
children: &mut [PlacedNode],
flow_indices: &[usize],
reserve_indices: &[usize],
flow_bbox: Bbox,
grid_rules: &mut [GridRule],
gap: f64,
) -> Bbox {
let mut top: Vec<usize> = Vec::new();
let mut bottom: Vec<usize> = Vec::new();
for &i in reserve_indices {
match side(&children[i].attrs) {
Side::Bottom => bottom.push(i),
_ => top.push(i),
}
}
let band = |idxs: &[usize]| -> f64 { idxs.iter().map(|&i| children[i].bbox.h() + gap).sum() };
let top_band = band(&top);
let bottom_band = band(&bottom);
let content_h = flow_bbox.h();
let title_w = reserve_indices
.iter()
.map(|&i| children[i].bbox.w())
.fold(0.0_f64, f64::max);
let total_w = flow_bbox.w().max(title_w);
let total_h = top_band + content_h + bottom_band;
let content_dy = -total_h / 2.0 + top_band + content_h / 2.0;
for &i in flow_indices {
children[i].cy += content_dy;
}
for seg in grid_rules.iter_mut() {
seg.1 += content_dy;
seg.3 += content_dy;
}
let mut cursor = -total_h / 2.0;
for &i in &top {
let h = children[i].bbox.h();
place(&mut children[i], cursor + h / 2.0, total_w);
cursor += h + gap;
}
let mut cursor = total_h / 2.0;
for &i in &bottom {
let h = children[i].bbox.h();
place(&mut children[i], cursor - h / 2.0, total_w);
cursor -= h + gap;
}
Bbox::centered(total_w, total_h)
}
pub fn place_out_bands(
children: &mut [PlacedNode],
frame: Bbox,
gap: f64,
) -> Result<(Bbox, bool), Error> {
let mut top: Vec<usize> = Vec::new();
let mut bottom: Vec<usize> = Vec::new();
for (i, c) in children.iter().enumerate() {
if !is_out_band(&c.attrs) {
continue;
}
match side(&c.attrs) {
Side::Bottom => bottom.push(i),
_ => top.push(i),
}
}
if top.is_empty() && bottom.is_empty() {
return Ok((frame, false));
}
let mut footprint = frame;
let mut cursor = frame.min_y;
for &i in &top {
cursor = place_out(
&mut children[i],
cursor,
-1.0,
gap,
frame.w(),
&mut footprint,
)?;
}
let mut cursor = frame.max_y;
for &i in &bottom {
cursor = place_out(
&mut children[i],
cursor,
1.0,
gap,
frame.w(),
&mut footprint,
)?;
}
Ok((footprint, true))
}
fn place_out(
node: &mut PlacedNode,
edge: f64,
dir: f64,
gap: f64,
band_w: f64,
footprint: &mut Bbox,
) -> Result<f64, Error> {
let (t, r, b, l) = primitives::margin(&node.attrs, node.span)?;
let drawn = node.bbox;
node.bbox = drawn.expand(t, r, b, l);
let h = node.bbox.h();
place(node, edge + dir * (gap + h / 2.0), band_w);
*footprint = footprint.union(node.bbox.shifted(node.cx, node.cy));
node.bbox = drawn;
Ok(edge + dir * (gap + h))
}
fn place(node: &mut PlacedNode, band_cy: f64, total_w: f64) {
let w = node.bbox.w();
let center_x = (node.bbox.min_x + node.bbox.max_x) / 2.0;
let center_y = (node.bbox.min_y + node.bbox.max_y) / 2.0;
let target_x = match align(&node.attrs) {
Align::Start => -total_w / 2.0 + w / 2.0,
Align::Center => 0.0,
Align::End => total_w / 2.0 - w / 2.0,
};
node.cx = target_x - center_x;
node.cy = band_cy - center_y;
}
fn side(attrs: &AttrMap) -> Side {
match attrs.get("side").and_then(ident).and_then(Side::parse) {
Some(Side::Bottom) => Side::Bottom,
_ => Side::Top,
}
}
fn align(attrs: &AttrMap) -> Align {
attrs
.get("align")
.and_then(ident)
.and_then(Align::parse)
.unwrap_or(Align::Center)
}
fn ident(v: &ResolvedValue) -> Option<&str> {
match v {
ResolvedValue::Ident(s) => Some(s.as_str()),
_ => None,
}
}