summaryrefslogtreecommitdiff
path: root/src/eval/layout.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-24 15:42:56 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-24 15:47:42 +0200
commit8fbb11fc05b3313bf102c1f23693290661d00863 (patch)
treea198db77338f1cc1304fe50f55020b08e22bca60 /src/eval/layout.rs
parente4ee14e54fb87961096856c7ea105435b7cc3c45 (diff)
Extract `model` module
Diffstat (limited to 'src/eval/layout.rs')
-rw-r--r--src/eval/layout.rs382
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)
- }
-}