diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:42:56 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:47:42 +0200 |
| commit | 8fbb11fc05b3313bf102c1f23693290661d00863 (patch) | |
| tree | a198db77338f1cc1304fe50f55020b08e22bca60 /src/eval/layout.rs | |
| parent | e4ee14e54fb87961096856c7ea105435b7cc3c45 (diff) | |
Extract `model` module
Diffstat (limited to 'src/eval/layout.rs')
| -rw-r--r-- | src/eval/layout.rs | 382 |
1 files changed, 0 insertions, 382 deletions
diff --git a/src/eval/layout.rs b/src/eval/layout.rs deleted file mode 100644 index 117c269a..00000000 --- a/src/eval/layout.rs +++ /dev/null @@ -1,382 +0,0 @@ -//! Layouting infrastructure. - -use std::any::Any; -use std::fmt::{self, Debug, Formatter}; -use std::hash::Hash; -use std::sync::Arc; - -use super::{Barrier, RawAlign, RawLength, Resolve, StyleChain}; -use crate::diag::TypResult; -use crate::frame::{Element, Frame, Geometry}; -use crate::geom::{Align, Length, Paint, Point, Relative, Sides, Size, Spec, Stroke}; -use crate::library::graphics::MoveNode; -use crate::library::layout::{AlignNode, PadNode}; -use crate::util::Prehashed; -use crate::Context; - -/// A node that can be layouted into a sequence of regions. -/// -/// Layout return one frame per used region alongside constraints that define -/// whether the result is reusable in other regions. -pub trait Layout: 'static { - /// Layout this node into the given regions, producing constrained frames. - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>>; - - /// Convert to a packed node. - fn pack(self) -> LayoutNode - where - Self: Debug + Hash + Sized + Sync + Send + 'static, - { - LayoutNode::new(self) - } -} - -/// A sequence of regions to layout into. -#[derive(Debug, Clone, Hash)] -pub struct Regions { - /// The (remaining) size of the first region. - pub first: Size, - /// The base size for relative sizing. - pub base: Size, - /// The height of followup regions. The width is the same for all regions. - pub backlog: Vec<Length>, - /// The height of the final region that is repeated once the backlog is - /// drained. The width is the same for all regions. - pub last: Option<Length>, - /// Whether nodes should expand to fill the regions instead of shrinking to - /// fit the content. - pub expand: Spec<bool>, -} - -impl Regions { - /// Create a new region sequence with exactly one region. - pub fn one(size: Size, base: Size, expand: Spec<bool>) -> Self { - Self { - first: size, - base, - backlog: vec![], - last: None, - expand, - } - } - - /// Create a new sequence of same-size regions that repeats indefinitely. - pub fn repeat(size: Size, base: Size, expand: Spec<bool>) -> Self { - Self { - first: size, - base, - backlog: vec![], - last: Some(size.y), - expand, - } - } - - /// Create new regions where all sizes are mapped with `f`. - /// - /// Note that since all regions must have the same width, the width returned - /// by `f` is ignored for the backlog and the final region. - pub fn map<F>(&self, mut f: F) -> Self - where - F: FnMut(Size) -> Size, - { - let x = self.first.x; - Self { - first: f(self.first), - base: f(self.base), - backlog: self.backlog.iter().map(|&y| f(Size::new(x, y)).y).collect(), - last: self.last.map(|y| f(Size::new(x, y)).y), - expand: self.expand, - } - } - - /// Whether the first region is full and a region break is called for. - pub fn is_full(&self) -> bool { - Length::zero().fits(self.first.y) && !self.in_last() - } - - /// Whether the first region is the last usable region. - /// - /// If this is true, calling `next()` will have no effect. - pub fn in_last(&self) -> bool { - self.backlog.len() == 0 && self.last.map_or(true, |height| self.first.y == height) - } - - /// Advance to the next region if there is any. - pub fn next(&mut self) { - if let Some(height) = (!self.backlog.is_empty()) - .then(|| self.backlog.remove(0)) - .or(self.last) - { - self.first.y = height; - self.base.y = height; - } - } - - /// An iterator that returns the sizes of the first and all following - /// regions, equivalently to what would be produced by calling - /// [`next()`](Self::next) repeatedly until all regions are exhausted. - /// This iterater may be infinite. - pub fn iter(&self) -> impl Iterator<Item = Size> + '_ { - let first = std::iter::once(self.first); - let backlog = self.backlog.iter(); - let last = self.last.iter().cycle(); - first.chain(backlog.chain(last).map(|&h| Size::new(self.first.x, h))) - } -} - -/// A type-erased layouting node with a precomputed hash. -#[derive(Clone, Hash)] -pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>); - -impl LayoutNode { - /// Pack any layoutable node. - pub fn new<T>(node: T) -> Self - where - T: Layout + Debug + Hash + Sync + Send + 'static, - { - Self(Arc::new(Prehashed::new(node))) - } - - /// Check whether the contained node is a specific layout node. - pub fn is<T: 'static>(&self) -> bool { - (**self.0).as_any().is::<T>() - } - - /// A barrier for the node. - pub fn barrier(&self) -> Barrier { - (**self.0).barrier() - } - - /// Try to downcast to a specific layout node. - pub fn downcast<T>(&self) -> Option<&T> - where - T: Layout + Debug + Hash + 'static, - { - (**self.0).as_any().downcast_ref() - } - - /// Force a size for this node. - pub fn sized(self, sizing: Spec<Option<Relative<RawLength>>>) -> Self { - if sizing.any(Option::is_some) { - SizedNode { sizing, child: self }.pack() - } else { - self - } - } - - /// Fill the frames resulting from a node. - pub fn filled(self, fill: Paint) -> Self { - FillNode { fill, child: self }.pack() - } - - /// Stroke the frames resulting from a node. - pub fn stroked(self, stroke: Stroke) -> Self { - StrokeNode { stroke, child: self }.pack() - } - - /// Set alignments for this node. - pub fn aligned(self, aligns: Spec<Option<RawAlign>>) -> Self { - if aligns.any(Option::is_some) { - AlignNode { aligns, child: self }.pack() - } else { - self - } - } - - /// Pad this node at the sides. - pub fn padded(self, padding: Sides<Relative<RawLength>>) -> Self { - if !padding.left.is_zero() - || !padding.top.is_zero() - || !padding.right.is_zero() - || !padding.bottom.is_zero() - { - PadNode { padding, child: self }.pack() - } else { - self - } - } - - /// Transform this node's contents without affecting layout. - pub fn moved(self, delta: Spec<Relative<RawLength>>) -> Self { - if delta.any(|r| !r.is_zero()) { - MoveNode { delta, child: self }.pack() - } else { - self - } - } -} - -impl Layout for LayoutNode { - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - ctx.query((self, regions, styles), |ctx, (node, regions, styles)| { - node.0.layout(ctx, regions, node.barrier().chain(&styles)) - }) - .clone() - } - - fn pack(self) -> LayoutNode { - self - } -} - -impl Default for LayoutNode { - fn default() -> Self { - EmptyNode.pack() - } -} - -impl Debug for LayoutNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl PartialEq for LayoutNode { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -trait Bounds: Layout + Debug + Sync + Send + 'static { - fn as_any(&self) -> &dyn Any; - fn barrier(&self) -> Barrier; -} - -impl<T> Bounds for T -where - T: Layout + Debug + Hash + Sync + Send + 'static, -{ - fn as_any(&self) -> &dyn Any { - self - } - - fn barrier(&self) -> Barrier { - Barrier::new::<T>() - } -} - -/// A layout node that produces an empty frame. -/// -/// The packed version of this is returned by [`PackedNode::default`]. -#[derive(Debug, Hash)] -struct EmptyNode; - -impl Layout for EmptyNode { - fn layout( - &self, - _: &mut Context, - regions: &Regions, - _: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - Ok(vec![Arc::new(Frame::new( - regions.expand.select(regions.first, Size::zero()), - ))]) - } -} - -/// Fix the size of a node. -#[derive(Debug, Hash)] -struct SizedNode { - /// How to size the node horizontally and vertically. - sizing: Spec<Option<Relative<RawLength>>>, - /// The node to be sized. - child: LayoutNode, -} - -impl Layout for SizedNode { - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - // The "pod" is the region into which the child will be layouted. - let pod = { - // Resolve the sizing to a concrete size. - let size = self - .sizing - .resolve(styles) - .zip(regions.base) - .map(|(s, b)| s.map(|v| v.relative_to(b))) - .unwrap_or(regions.first); - - // Select the appropriate base and expansion for the child depending - // on whether it is automatically or relatively sized. - let is_auto = self.sizing.map_is_none(); - let base = is_auto.select(regions.base, size); - let expand = regions.expand | !is_auto; - - Regions::one(size, base, expand) - }; - - // Layout the child. - let mut frames = self.child.layout(ctx, &pod, styles)?; - - // Ensure frame size matches regions size if expansion is on. - let frame = &mut frames[0]; - let target = regions.expand.select(regions.first, frame.size); - Arc::make_mut(frame).resize(target, Align::LEFT_TOP); - - Ok(frames) - } -} - -/// Fill the frames resulting from a node. -#[derive(Debug, Hash)] -struct FillNode { - /// How to fill the frames resulting from the `child`. - fill: Paint, - /// The node to fill. - child: LayoutNode, -} - -impl Layout for FillNode { - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - let mut frames = self.child.layout(ctx, regions, styles)?; - for frame in &mut frames { - let shape = Geometry::Rect(frame.size).filled(self.fill); - Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); - } - Ok(frames) - } -} - -/// Stroke the frames resulting from a node. -#[derive(Debug, Hash)] -struct StrokeNode { - /// How to stroke the frames resulting from the `child`. - stroke: Stroke, - /// The node to stroke. - child: LayoutNode, -} - -impl Layout for StrokeNode { - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - let mut frames = self.child.layout(ctx, regions, styles)?; - for frame in &mut frames { - let shape = Geometry::Rect(frame.size).stroked(self.stroke); - Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); - } - Ok(frames) - } -} |
