//! Layouting.
mod background;
mod fixed;
mod frame;
mod node;
mod pad;
mod par;
mod shaping;
mod spacing;
mod stack;
mod text;
pub use background::*;
pub use fixed::*;
pub use frame::*;
pub use node::*;
pub use pad::*;
pub use par::*;
pub use shaping::*;
pub use spacing::*;
pub use stack::*;
pub use text::*;
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: Node,
}
impl PageRun {
/// Layout the page run.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec {
let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill));
self.child.layout(ctx, &areas).into_frames()
}
}
/// Layout a node.
pub trait Layout {
/// Layout the node into the given areas.
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment;
}
/// The context for layouting.
#[derive(Debug)]
pub struct LayoutContext<'a> {
/// The environment from which fonts are gathered.
pub env: &'a mut Env,
}
/// A sequence of areas to layout into.
#[derive(Debug, Clone, PartialEq)]
pub struct Areas {
/// The remaining size of the current area.
pub current: Size,
/// The full size the current area once had (used for relative sizing).
pub full: Size,
/// A stack of followup areas (the next area is the last element).
pub backlog: Vec,
/// The final area that is repeated when the backlog is empty.
pub last: Option,
/// Whether the frames resulting from layouting into this areas should be
/// shrunk to fit their content or expanded to fill the area.
///
/// This property is handled partially by the par layouter and fully by the
/// stack layouter.
pub expand: Spec,
/// The aspect ratio the resulting frame should respect.
///
/// This property is only handled by the stack layouter.
pub aspect: Option,
}
impl Areas {
/// Create a new sequence of areas that repeats `area` indefinitely.
pub fn repeat(size: Size, expand: Spec) -> Self {
Self {
current: size,
full: size,
backlog: vec![],
last: Some(size),
expand,
aspect: None,
}
}
/// Create a new length-1 sequence of areas with just one `area`.
pub fn once(size: Size, full: Size, expand: Spec) -> Self {
Self {
current: size,
full,
backlog: vec![],
last: None,
expand,
aspect: None,
}
}
/// Builder-style method for setting the aspect ratio.
pub fn with_aspect(mut self, aspect: Option) -> Self {
self.aspect = aspect;
self
}
/// Map all areas.
pub fn map(&self, mut f: F) -> Self
where
F: FnMut(Size) -> Size,
{
Self {
current: f(self.current),
full: f(self.full),
backlog: self.backlog.iter().copied().map(|s| f(s)).collect(),
last: self.last.map(f),
expand: self.expand,
aspect: self.aspect,
}
}
/// Advance to the next area if there is any.
pub fn next(&mut self) {
if let Some(size) = self.backlog.pop().or(self.last) {
self.current = size;
self.full = size;
}
}
/// Whether `current` is a fully sized (untouched) copy of the last area.
///
/// If this is false 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
})
}
}
/// Whether to expand or shrink a node along an axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Expand {
/// Fit the content.
Fit,
/// Fill the available space.
Fill,
}
impl Expand {
/// Resolve the expansion to either the `fit` or `fill` length.
///
/// Prefers `fit` if `fill` is infinite.
pub fn resolve(self, fit: Length, fill: Length) -> Length {
match self {
Self::Fill if fill.is_finite() => fill,
_ => fit,
}
}
}
/// The result of layouting a node.
#[derive(Debug, Clone, PartialEq)]
pub enum Fragment {
/// Spacing that should be added to the parent.
Spacing(Length),
/// A layout that should be added to and aligned in the parent.
Frame(Frame, LayoutAligns),
/// Multiple layouts.
Frames(Vec, LayoutAligns),
}
impl Fragment {
/// Return a reference to all frames contained in this variant (zero, one or
/// arbitrarily many).
pub fn frames(&self) -> &[Frame] {
match self {
Self::Spacing(_) => &[],
Self::Frame(frame, _) => std::slice::from_ref(frame),
Self::Frames(frames, _) => frames,
}
}
/// Return a mutable reference to all frames contained in this variant.
pub fn frames_mut(&mut self) -> &mut [Frame] {
match self {
Self::Spacing(_) => &mut [],
Self::Frame(frame, _) => std::slice::from_mut(frame),
Self::Frames(frames, _) => frames,
}
}
/// Return all frames contained in this varian.
pub fn into_frames(self) -> Vec {
match self {
Self::Spacing(_) => vec![],
Self::Frame(frame, _) => vec![frame],
Self::Frames(frames, _) => frames,
}
}
}