diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-11-16 10:41:30 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-11-16 10:41:30 +0100 |
| commit | 0e0f340502beada1cd9ee23857f48b91a0d11a90 (patch) | |
| tree | e4e4087260720adf4bab57edeac6a3d3cb5f14cf /src/library | |
| parent | bc118634aca5de415d211cab38c4eaa3d3cca25f (diff) | |
Revert page and inline levels
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/container.rs | 3 | ||||
| -rw-r--r-- | src/library/document.rs | 16 | ||||
| -rw-r--r-- | src/library/flow.rs | 10 | ||||
| -rw-r--r-- | src/library/grid.rs | 8 | ||||
| -rw-r--r-- | src/library/image.rs | 23 | ||||
| -rw-r--r-- | src/library/mod.rs | 2 | ||||
| -rw-r--r-- | src/library/pad.rs | 6 | ||||
| -rw-r--r-- | src/library/page.rs | 20 | ||||
| -rw-r--r-- | src/library/par.rs | 17 | ||||
| -rw-r--r-- | src/library/shape.rs | 44 | ||||
| -rw-r--r-- | src/library/stack.rs | 8 | ||||
| -rw-r--r-- | src/library/transform.rs | 40 |
12 files changed, 136 insertions, 61 deletions
diff --git a/src/library/container.rs b/src/library/container.rs index 4b52139f..2bcbbd19 100644 --- a/src/library/container.rs +++ b/src/library/container.rs @@ -5,14 +5,13 @@ use super::{ShapeKind, ShapeNode}; pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let width = args.named("width")?; let height = args.named("height")?; - let fill = args.named("fill")?; let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_inline(move |style| { ShapeNode { shape: ShapeKind::Rect, width, height, - fill: fill.map(Paint::Color), + fill: None, child: Some(body.to_flow(style).pack()), } }))) diff --git a/src/library/document.rs b/src/library/document.rs new file mode 100644 index 00000000..fe01d2df --- /dev/null +++ b/src/library/document.rs @@ -0,0 +1,16 @@ +use super::prelude::*; +use super::PageNode; + +/// The root layout node, a document consisting of top-level page runs. +#[derive(Debug, Hash)] +pub struct DocumentNode { + /// The page runs. + pub pages: Vec<PageNode>, +} + +impl DocumentNode { + /// Layout the document into a sequence of frames, one per page. + pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { + self.pages.iter().flat_map(|node| node.layout(ctx)).collect() + } +} diff --git a/src/library/flow.rs b/src/library/flow.rs index cc53d520..c41fb62c 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -48,7 +48,7 @@ pub struct FlowNode { pub children: Vec<FlowChild>, } -impl BlockLevel for FlowNode { +impl Layout for FlowNode { fn layout( &self, ctx: &mut LayoutContext, @@ -63,8 +63,8 @@ impl BlockLevel for FlowNode { pub enum FlowChild { /// Vertical spacing between other children. Spacing(Spacing), - /// Any block node and how to align it in the flow. - Node(BlockNode, Align), + /// A node and how to align it in the flow. + Node(PackedNode, Align), } impl Debug for FlowChild { @@ -157,8 +157,8 @@ impl<'a> FlowLayouter<'a> { self.items.push(FlowItem::Absolute(resolved)); } - /// Layout a block node. - fn layout_node(&mut self, ctx: &mut LayoutContext, node: &BlockNode, align: Align) { + /// Layout a node. + fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode, align: Align) { let frames = node.layout(ctx, &self.regions); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { diff --git a/src/library/grid.rs b/src/library/grid.rs index bafd639c..692ed210 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -58,7 +58,7 @@ pub struct GridNode { /// Defines sizing of gutter rows and columns between content. pub gutter: Spec<Vec<TrackSizing>>, /// The nodes to be arranged in a grid. - pub children: Vec<BlockNode>, + pub children: Vec<PackedNode>, } /// Defines how to size a grid cell along an axis. @@ -72,7 +72,7 @@ pub enum TrackSizing { Fractional(Fractional), } -impl BlockLevel for GridNode { +impl Layout for GridNode { fn layout( &self, ctx: &mut LayoutContext, @@ -92,7 +92,7 @@ impl BlockLevel for GridNode { /// Performs grid layout. struct GridLayouter<'a> { /// The children of the grid. - children: &'a [BlockNode], + children: &'a [PackedNode], /// Whether the grid should expand to fill the region. expand: Spec<bool>, /// The column tracks including gutter tracks. @@ -591,7 +591,7 @@ impl<'a> GridLayouter<'a> { /// /// Returns `None` if it's a gutter cell. #[track_caller] - fn cell(&self, x: usize, y: usize) -> Option<&'a BlockNode> { + fn cell(&self, x: usize, y: usize) -> Option<&'a PackedNode> { assert!(x < self.cols.len()); assert!(y < self.rows.len()); diff --git a/src/library/image.rs b/src/library/image.rs index b51e4e70..f93d4b54 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -36,23 +36,31 @@ pub struct ImageNode { pub height: Option<Linear>, } -impl InlineLevel for ImageNode { - fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { +impl Layout for ImageNode { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>> { let img = ctx.images.get(self.id); let pixel_size = Spec::new(img.width() as f64, img.height() as f64); let pixel_ratio = pixel_size.x / pixel_size.y; - let width = self.width.map(|w| w.resolve(base.w)); - let height = self.height.map(|w| w.resolve(base.h)); + let width = self.width.map(|w| w.resolve(regions.base.w)); + let height = self.height.map(|w| w.resolve(regions.base.h)); + + let mut cts = Constraints::new(regions.expand); + cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); let size = match (width, height) { (Some(width), Some(height)) => Size::new(width, height), (Some(width), None) => Size::new(width, width / pixel_ratio), (None, Some(height)) => Size::new(height * pixel_ratio, height), (None, None) => { - if space.is_finite() { + cts.exact.x = Some(regions.current.w); + if regions.current.w.is_finite() { // Fit to width. - Size::new(space, space / pixel_ratio) + Size::new(regions.current.w, regions.current.w / pixel_ratio) } else { // Unbounded width, we have to make up something, // so it is 1pt per pixel. @@ -63,6 +71,7 @@ impl InlineLevel for ImageNode { let mut frame = Frame::new(size, size.h); frame.push(Point::zero(), Element::Image(self.id, size)); - frame + + vec![frame.constrain(cts)] } } diff --git a/src/library/mod.rs b/src/library/mod.rs index 775fb51f..f11e05f3 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -6,6 +6,7 @@ mod align; mod container; mod deco; +mod document; mod flow; mod grid; mod image; @@ -36,6 +37,7 @@ pub use self::image::*; pub use align::*; pub use container::*; pub use deco::*; +pub use document::*; pub use flow::*; pub use grid::*; pub use pad::*; diff --git a/src/library/pad.rs b/src/library/pad.rs index d17a4ca2..88f8c562 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -16,7 +16,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { bottom.or(all).unwrap_or_default(), ); - Ok(Value::Template(Template::from_block(move |style| { + Ok(Value::Template(Template::from_inline(move |style| { PadNode { padding, child: body.to_flow(style).pack(), @@ -30,10 +30,10 @@ pub struct PadNode { /// The amount of padding. pub padding: Sides<Linear>, /// The child node whose sides to pad. - pub child: BlockNode, + pub child: PackedNode, } -impl BlockLevel for PadNode { +impl Layout for PadNode { fn layout( &self, ctx: &mut LayoutContext, diff --git a/src/library/page.rs b/src/library/page.rs index 16e6283d..a5935002 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -73,3 +73,23 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> { template.pagebreak(true); Ok(Value::Template(template)) } + +/// Layouts its children onto one or multiple pages. +#[derive(Debug, Hash)] +pub struct PageNode { + /// The size of the page. + pub size: Size, + /// The node that produces the actual pages. + pub child: PackedNode, +} + +impl PageNode { + /// Layout the page run into a sequence of frames, one per page. + pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { + // When one of the lengths is infinite the page fits its content along + // that axis. + let expand = self.size.to_spec().map(Length::is_finite); + let regions = Regions::repeat(self.size, self.size, expand); + self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect() + } +} diff --git a/src/library/par.rs b/src/library/par.rs index 216b7d41..c8befc2d 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -8,7 +8,7 @@ use xi_unicode::LineBreakIterator; use super::prelude::*; use super::{shape, Decoration, ShapedText, Spacing}; use crate::style::TextStyle; -use crate::util::{EcoString, RangeExt, SliceExt}; +use crate::util::{EcoString, RangeExt, RcExt, SliceExt}; /// `par`: Configure paragraphs. pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { @@ -63,7 +63,7 @@ pub struct ParNode { pub children: Vec<ParChild>, } -impl BlockLevel for ParNode { +impl Layout for ParNode { fn layout( &self, ctx: &mut LayoutContext, @@ -77,7 +77,7 @@ impl BlockLevel for ParNode { // Prepare paragraph layout by building a representation on which we can // do line breaking without layouting each and every line from scratch. - let layouter = ParLayouter::new(self, ctx, regions, bidi); + let layouter = ParLayouter::new(self, ctx, regions.clone(), bidi); // Find suitable linebreaks. layouter.layout(ctx, regions.clone()) @@ -126,7 +126,7 @@ pub enum ParChild { /// A run of text and how to align it in its line. Text(EcoString, Align, Rc<TextStyle>), /// Any child node and how to align it in its line. - Node(InlineNode, Align), + Node(PackedNode, Align), /// A decoration that applies until a matching `Undecorate`. Decorate(Decoration), /// The end of a decoration. @@ -182,9 +182,12 @@ impl<'a> ParLayouter<'a> { fn new( par: &'a ParNode, ctx: &mut LayoutContext, - regions: &Regions, + mut regions: Regions, bidi: BidiInfo<'a>, ) -> Self { + // Disable expansion for children. + regions.expand = Spec::splat(false); + let mut items = vec![]; let mut ranges = vec![]; let mut starts = vec![]; @@ -216,8 +219,8 @@ impl<'a> ParLayouter<'a> { } } ParChild::Node(ref node, align) => { - let frame = node.layout(ctx, regions.current.w, regions.base); - items.push(ParItem::Frame(frame, align)); + let frame = node.layout(ctx, ®ions).remove(0); + items.push(ParItem::Frame(Rc::take(frame.item), align)); ranges.push(range); } ParChild::Decorate(ref deco) => { diff --git a/src/library/shape.rs b/src/library/shape.rs index 5be26aa4..c64dedb3 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -94,7 +94,7 @@ pub struct ShapeNode { /// How to fill the shape, if at all. pub fill: Option<Paint>, /// The child node to place into the shape, if any. - pub child: Option<BlockNode>, + pub child: Option<PackedNode>, } /// The type of a shape. @@ -110,15 +110,36 @@ pub enum ShapeKind { Ellipse, } -impl InlineLevel for ShapeNode { - fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { +impl Layout for ShapeNode { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>> { // Resolve width and height relative to the region's base. - let width = self.width.map(|w| w.resolve(base.w)); - let height = self.height.map(|h| h.resolve(base.h)); + let width = self.width.map(|w| w.resolve(regions.base.w)); + let height = self.height.map(|h| h.resolve(regions.base.h)); + + // Generate constraints. + let mut cts = Constraints::new(regions.expand); + cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); + + // Set tight exact and base constraints if the child is + // automatically sized since we don't know what the child might do. + if self.width.is_none() { + cts.exact.x = Some(regions.current.w); + cts.base.x = Some(regions.base.w); + } + + // Same here. + if self.height.is_none() { + cts.exact.y = Some(regions.current.h); + cts.base.y = Some(regions.base.h); + } // Layout. let mut frame = if let Some(child) = &self.child { - let mut node: &dyn BlockLevel = child; + let mut node: &dyn Layout = child; let padded; if matches!(self.shape, ShapeKind::Circle | ShapeKind::Ellipse) { @@ -133,11 +154,14 @@ impl InlineLevel for ShapeNode { // The "pod" is the region into which the child will be layouted. let mut pod = { - let size = Size::new(width.unwrap_or(space), height.unwrap_or(base.h)); + let size = Size::new( + width.unwrap_or(regions.current.w), + height.unwrap_or(regions.base.h), + ); let base = Size::new( - if width.is_some() { size.w } else { base.w }, - if height.is_some() { size.h } else { base.h }, + if width.is_some() { size.w } else { regions.base.w }, + if height.is_some() { size.h } else { regions.base.h }, ); let expand = Spec::new(width.is_some(), height.is_some()); @@ -180,6 +204,6 @@ impl InlineLevel for ShapeNode { frame.prepend(pos, Element::Geometry(geometry, fill)); } - frame + vec![frame.constrain(cts)] } } diff --git a/src/library/stack.rs b/src/library/stack.rs index fe1deda0..46825939 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -60,7 +60,7 @@ pub struct StackNode { pub children: Vec<StackChild>, } -impl BlockLevel for StackNode { +impl Layout for StackNode { fn layout( &self, ctx: &mut LayoutContext, @@ -76,7 +76,7 @@ pub enum StackChild { /// Spacing between other nodes. Spacing(Spacing), /// Any block node and how to align it in the stack. - Node(BlockNode), + Node(PackedNode), } impl Debug for StackChild { @@ -174,8 +174,8 @@ impl<'a> StackLayouter<'a> { self.items.push(StackItem::Absolute(resolved)); } - /// Layout a block node. - fn layout_node(&mut self, ctx: &mut LayoutContext, node: &BlockNode) { + /// Layout a node. + fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode) { let frames = node.layout(ctx, &self.regions); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { diff --git a/src/library/transform.rs b/src/library/transform.rs index b7e5e36c..20d2bc1d 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -1,5 +1,4 @@ use super::prelude::*; -use super::{ShapeKind, ShapeNode}; /// `move`: Move content without affecting layout. pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { @@ -10,13 +9,7 @@ pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { Ok(Value::Template(Template::from_inline(move |style| { MoveNode { offset: Spec::new(x, y), - child: ShapeNode { - shape: ShapeKind::Rect, - width: None, - height: None, - fill: None, - child: Some(body.to_flow(style).pack()), - }, + child: body.to_flow(style).pack(), } }))) } @@ -24,21 +17,30 @@ pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { #[derive(Debug, Hash)] struct MoveNode { offset: Spec<Option<Linear>>, - child: ShapeNode, + child: PackedNode, } -impl InlineLevel for MoveNode { - fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { - let offset = Point::new( - self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(), - self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(), - ); +impl Layout for MoveNode { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>> { + let mut frames = self.child.layout(ctx, regions); - let mut frame = self.child.layout(ctx, space, base); - for (point, _) in &mut frame.children { - *point += offset; + for (Constrained { item: frame, .. }, (_, base)) in + frames.iter_mut().zip(regions.iter()) + { + let offset = Point::new( + self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(), + self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(), + ); + + for (point, _) in &mut Rc::make_mut(frame).children { + *point += offset; + } } - frame + frames } } |
