//! Layouting. mod background; mod fixed; mod frame; mod pad; mod par; mod shaping; mod stack; pub use background::*; pub use fixed::*; pub use frame::*; pub use pad::*; pub use par::*; pub use shaping::*; pub use stack::*; use std::any::Any; use std::fmt::{self, Debug, Formatter}; use crate::env::Env; use crate::geom::*; /// Layout a tree into a collection of frames. pub fn layout(env: &mut Env, tree: &Tree) -> Vec { tree.layout(&mut LayoutContext { env }) } /// A tree of layout nodes. #[derive(Debug, Clone, PartialEq)] pub struct Tree { /// Runs of pages with the same properties. pub runs: Vec, } impl Tree { /// Layout the tree into a collection of frames. pub fn layout(&self, ctx: &mut LayoutContext) -> Vec { self.runs.iter().flat_map(|run| run.layout(ctx)).collect() } } /// A run of pages that all have the same properties. #[derive(Debug, Clone, PartialEq)] pub struct PageRun { /// The size of each page. pub size: Size, /// The layout node that produces the actual pages (typically a /// [`StackNode`]). pub child: AnyNode, } impl PageRun { /// Layout the page run. pub fn layout(&self, ctx: &mut LayoutContext) -> Vec { // When one of the lengths is infinite the page fits its content along // that axis. let Size { width, height } = self.size; let fixed = Spec::new(width.is_finite(), height.is_finite()); let regions = Regions::repeat(self.size, fixed); self.child.layout(ctx, ®ions) } } /// A wrapper around a dynamic layouting node. pub struct AnyNode(Box); impl AnyNode { /// Create a new instance from any node that satisifies the required bounds. pub fn new(any: T) -> Self where T: Layout + Debug + Clone + PartialEq + 'static, { Self(Box::new(any)) } } impl Layout for AnyNode { fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec { self.0.layout(ctx, regions) } } impl Clone for AnyNode { fn clone(&self) -> Self { Self(self.0.dyn_clone()) } } impl PartialEq for AnyNode { fn eq(&self, other: &Self) -> bool { self.0.dyn_eq(other.0.as_ref()) } } impl Debug for AnyNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.0.fmt(f) } } trait Bounds: Layout + Debug + 'static { fn as_any(&self) -> &dyn Any; fn dyn_eq(&self, other: &dyn Bounds) -> bool; fn dyn_clone(&self) -> Box; } impl Bounds for T where T: Layout + Debug + PartialEq + Clone + 'static, { fn as_any(&self) -> &dyn Any { self } fn dyn_eq(&self, other: &dyn Bounds) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self == other } else { false } } fn dyn_clone(&self) -> Box { Box::new(self.clone()) } } /// Layout a node. pub trait Layout { /// Layout the node into the given regions. fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec; } /// The context for layouting. pub struct LayoutContext<'a> { /// The environment from which fonts are gathered. pub env: &'a mut Env, } /// A sequence of regions to layout into. #[derive(Debug, Clone, PartialEq)] pub struct Regions { /// The remaining size of the current region. pub current: Size, /// The base size for relative sizing. pub base: Size, /// A stack of followup regions. /// /// Note that this is a stack and not a queue! The size of the next region is /// `backlog.last()`. pub backlog: Vec, /// The final region that is repeated once the backlog is drained. pub last: Option, /// Whether layouting into these regions should produce frames of the exact /// size of `current` instead of shrinking to fit the content. /// /// This property is only handled by nodes that have the ability to control /// their own size. pub fixed: Spec, } impl Regions { /// Create a new region sequence with exactly one region. pub fn one(size: Size, fixed: Spec) -> Self { Self { current: size, base: size, backlog: vec![], last: None, fixed, } } /// Create a new sequence of same-size regions that repeats indefinitely. pub fn repeat(size: Size, fixed: Spec) -> Self { Self { current: size, base: size, backlog: vec![], last: Some(size), fixed, } } /// Map the size of all regions. pub fn map(&self, mut f: F) -> Self where F: FnMut(Size) -> Size, { Self { current: f(self.current), base: f(self.base), backlog: self.backlog.iter().copied().map(|s| f(s)).collect(), last: self.last.map(f), fixed: self.fixed, } } /// Whether `current` is a fully sized (untouched) copy of the last region. /// /// If this is true, calling `next()` will have no effect. pub fn in_full_last(&self) -> bool { self.backlog.is_empty() && self.last.map_or(true, |size| { self.current.is_nan() || size.is_nan() || self.current == size }) } /// Advance to the next region if there is any. pub fn next(&mut self) { if let Some(size) = self.backlog.pop().or(self.last) { self.current = size; self.base = size; } } /// Shrink `current` to ensure that the aspect ratio can be satisfied. pub fn apply_aspect_ratio(&mut self, aspect: f64) { let width = self.current.width.min(aspect * self.current.height); let height = width / aspect; self.current = Size::new(width, height); } }