From 989d170dc7318ca3cbaa5b76760eb14f4e6a8605 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 28 Nov 2022 12:40:16 +0100 Subject: Fragments --- library/src/layout/align.rs | 16 +++++------ library/src/layout/columns.rs | 14 +++++----- library/src/layout/container.rs | 24 +++++++++-------- library/src/layout/flow.rs | 24 ++++++++--------- library/src/layout/grid.rs | 37 +++++++++++-------------- library/src/layout/mod.rs | 60 +++++++++-------------------------------- library/src/layout/pad.rs | 14 +++++----- library/src/layout/page.rs | 10 +++---- library/src/layout/place.rs | 14 +++++----- library/src/layout/stack.rs | 18 ++++++------- library/src/layout/transform.rs | 52 +++++++++++++++++++---------------- 11 files changed, 125 insertions(+), 158 deletions(-) (limited to 'library/src/layout') diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index 10a4a2ed..d8b6d92e 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -10,14 +10,14 @@ pub struct AlignNode { pub child: Content, } -#[node(LayoutBlock)] +#[node(Layout)] impl AlignNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let aligns: Axes> = args.find()?.unwrap_or_default(); let body: Content = args.expect("body")?; if let Axes { x: Some(x), y: None } = aligns { - if !body.has::() { + if !body.has::() || body.has::() { return Ok(body.styled(ParNode::ALIGN, HorizontalAlign(x))); } } @@ -26,13 +26,13 @@ impl AlignNode { } } -impl LayoutBlock for AlignNode { - fn layout_block( +impl Layout for AlignNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { // The child only needs to expand along an axis if there's no alignment. let mut pod = regions.clone(); pod.expand &= self.aligns.as_ref().map(Option::is_none); @@ -44,8 +44,8 @@ impl LayoutBlock for AlignNode { } // Layout the child. - let mut frames = self.child.layout_block(world, styles.chain(&map), &pod)?; - for (region, frame) in regions.iter().zip(&mut frames) { + let mut fragment = self.child.layout(world, styles.chain(&map), &pod)?; + for (region, frame) in regions.iter().zip(&mut fragment) { // Align in the target size. The target size depends on whether we // should expand. let target = regions.expand.select(region, frame.size()); @@ -57,6 +57,6 @@ impl LayoutBlock for AlignNode { frame.resize(target, aligns); } - Ok(frames) + Ok(fragment) } } diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index b18ba49f..257cc62f 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -11,7 +11,7 @@ pub struct ColumnsNode { pub child: Content, } -#[node(LayoutBlock)] +#[node(Layout)] impl ColumnsNode { /// The size of the gutter space between each column. #[property(resolve)] @@ -26,17 +26,17 @@ impl ColumnsNode { } } -impl LayoutBlock for ColumnsNode { - fn layout_block( +impl Layout for ColumnsNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { // Separating the infinite space into infinite columns does not make // much sense. if !regions.first.x.is_finite() { - return self.child.layout_block(world, styles, regions); + return self.child.layout(world, styles, regions); } // Determine the width of the gutter and each column. @@ -58,7 +58,7 @@ impl LayoutBlock for ColumnsNode { }; // Layout the children. - let mut frames = self.child.layout_block(world, styles, &pod)?.into_iter(); + let mut frames = self.child.layout(world, styles, &pod)?.into_iter(); let mut finished = vec![]; let dir = styles.get(TextNode::DIR); @@ -94,7 +94,7 @@ impl LayoutBlock for ColumnsNode { finished.push(output); } - Ok(finished) + Ok(Fragment::frames(finished)) } } diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index b299c0fc..1c1f8762 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -10,7 +10,7 @@ pub struct BoxNode { pub child: Content, } -#[node(LayoutInline)] +#[node(Layout, Inline)] impl BoxNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let width = args.named("width")?; @@ -20,13 +20,13 @@ impl BoxNode { } } -impl LayoutInline for BoxNode { - fn layout_inline( +impl Layout for BoxNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult { + ) -> SourceResult { // The "pod" is the region into which the child will be layouted. let pod = { // Resolve the sizing to a concrete size. @@ -47,21 +47,23 @@ impl LayoutInline for BoxNode { }; // Layout the child. - let mut frame = self.child.layout_inline(world, styles, &pod)?; + let mut frame = self.child.layout(world, styles, &pod)?.into_frame(); // Ensure frame size matches regions size if expansion is on. let target = regions.expand.select(regions.first, frame.size()); frame.resize(target, Align::LEFT_TOP); - Ok(frame) + Ok(Fragment::frame(frame)) } } +impl Inline for BoxNode {} + /// A block-level container that places content into a separate flow. #[derive(Debug, Hash)] pub struct BlockNode(pub Content); -#[node(LayoutBlock)] +#[node(Layout)] impl BlockNode { /// The spacing between the previous and this block. #[property(skip)] @@ -87,13 +89,13 @@ impl BlockNode { } } -impl LayoutBlock for BlockNode { - fn layout_block( +impl Layout for BlockNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { - self.0.layout_block(world, styles, regions) + ) -> SourceResult { + self.0.layout(world, styles, regions) } } diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index 3338da09..fd3e5fc7 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -11,23 +11,23 @@ use crate::text::ParNode; #[derive(Hash)] pub struct FlowNode(pub StyleVec); -#[node(LayoutBlock)] +#[node(Layout)] impl FlowNode {} -impl LayoutBlock for FlowNode { - fn layout_block( +impl Layout for FlowNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { let mut layouter = FlowLayouter::new(regions); for (child, map) in self.0.iter() { let styles = styles.chain(&map); if let Some(&node) = child.to::() { layouter.layout_spacing(node.amount, styles); - } else if child.has::() { + } else if child.has::() { layouter.layout_block(world, child, styles)?; } else if child.is::() { layouter.finish_region(); @@ -136,7 +136,7 @@ impl FlowLayouter { // aligned later. if let Some(placed) = block.to::() { if placed.out_of_flow() { - let frame = block.layout_block(world, styles, &self.regions)?.remove(0); + let frame = block.layout(world, styles, &self.regions)?.into_frame(); self.items.push(FlowItem::Placed(frame)); return Ok(()); } @@ -166,9 +166,9 @@ impl FlowLayouter { } // Layout the block itself. - let frames = block.layout_block(world, chained, &self.regions)?; - let len = frames.len(); - for (i, frame) in frames.into_iter().enumerate() { + let fragment = block.layout(world, chained, &self.regions)?; + let len = fragment.len(); + for (i, frame) in fragment.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. let size = frame.size(); self.used.y += size.y; @@ -234,8 +234,8 @@ impl FlowLayouter { self.finished.push(output); } - /// Finish layouting and return the resulting frames. - fn finish(mut self) -> Vec { + /// Finish layouting and return the resulting fragment. + fn finish(mut self) -> Fragment { if self.expand.y { while !self.regions.backlog.is_empty() { self.finish_region(); @@ -243,6 +243,6 @@ impl FlowLayouter { } self.finish_region(); - self.finished + Fragment::frames(self.finished) } } diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index 4cbef421..470b1f3b 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -13,7 +13,7 @@ pub struct GridNode { pub cells: Vec, } -#[node(LayoutBlock)] +#[node(Layout)] impl GridNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let TrackSizings(columns) = args.named("columns")?.unwrap_or_default(); @@ -33,13 +33,13 @@ impl GridNode { } } -impl LayoutBlock for GridNode { - fn layout_block( +impl Layout for GridNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { // Prepare grid layout by unifying content and gutter tracks. let layouter = GridLayouter::new( world, @@ -222,7 +222,7 @@ impl<'a> GridLayouter<'a> { } /// Determines the columns sizes and then layouts the grid row-by-row. - fn layout(mut self) -> SourceResult> { + fn layout(mut self) -> SourceResult { self.measure_columns()?; for y in 0..self.rows.len() { @@ -243,7 +243,7 @@ impl<'a> GridLayouter<'a> { } self.finish_region()?; - Ok(self.finished) + Ok(Fragment::frames(self.finished)) } /// Determine all column sizes. @@ -320,8 +320,7 @@ impl<'a> GridLayouter<'a> { v.resolve(self.styles).relative_to(self.regions.base.y); } - let frame = - cell.layout_block(self.world, self.styles, &pod)?.remove(0); + let frame = cell.layout(self.world, self.styles, &pod)?.into_frame(); resolved.set_max(frame.width()); } } @@ -391,7 +390,7 @@ impl<'a> GridLayouter<'a> { } let mut sizes = cell - .layout_block(self.world, self.styles, &pod)? + .layout(self.world, self.styles, &pod)? .into_iter() .map(|frame| frame.height()); @@ -429,9 +428,9 @@ impl<'a> GridLayouter<'a> { } // Layout into multiple regions. - let frames = self.layout_multi_row(&resolved, y)?; - let len = frames.len(); - for (i, frame) in frames.into_iter().enumerate() { + let fragment = self.layout_multi_row(&resolved, y)?; + let len = fragment.len(); + for (i, frame) in fragment.into_iter().enumerate() { self.push_row(frame); if i + 1 < len { self.finish_region()?; @@ -480,7 +479,7 @@ impl<'a> GridLayouter<'a> { .select(self.regions.base, size); let pod = Regions::one(size, base, Axes::splat(true)); - let frame = cell.layout_block(self.world, self.styles, &pod)?.remove(0); + let frame = cell.layout(self.world, self.styles, &pod)?.into_frame(); output.push_frame(pos, frame); } @@ -491,11 +490,7 @@ impl<'a> GridLayouter<'a> { } /// Layout a row spanning multiple regions. - fn layout_multi_row( - &mut self, - heights: &[Abs], - y: usize, - ) -> SourceResult> { + fn layout_multi_row(&mut self, heights: &[Abs], y: usize) -> SourceResult { // Prepare frames. let mut outputs: Vec<_> = heights .iter() @@ -520,8 +515,8 @@ impl<'a> GridLayouter<'a> { } // Push the layouted frames into the individual output frames. - let frames = cell.layout_block(self.world, self.styles, &pod)?; - for (output, frame) in outputs.iter_mut().zip(frames) { + let fragment = cell.layout(self.world, self.styles, &pod)?; + for (output, frame) in outputs.iter_mut().zip(fragment) { output.push_frame(pos, frame); } } @@ -529,7 +524,7 @@ impl<'a> GridLayouter<'a> { pos.x += rcol; } - Ok(outputs) + Ok(Fragment::frames(outputs)) } /// Push a row frame into the current region. diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index e63a072e..3481a6bd 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -29,7 +29,6 @@ use std::mem; use comemo::Tracked; use typed_arena::Arena; use typst::diag::SourceResult; -use typst::doc::Frame; use typst::geom::*; use typst::model::{ applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain, @@ -70,72 +69,37 @@ impl LayoutRoot for Content { } } -/// Block-level layout. +/// Layout into regions. #[capability] -pub trait LayoutBlock { +pub trait Layout { /// Layout into one frame per region. - fn layout_block( + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult>; + ) -> SourceResult; } -impl LayoutBlock for Content { +impl Layout for Content { #[comemo::memoize] - fn layout_block( + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { let scratch = Scratch::default(); let (realized, styles) = realize_block(world, &scratch, self, styles)?; let barrier = Style::Barrier(realized.id()); let styles = styles.chain_one(&barrier); - realized - .with::() - .unwrap() - .layout_block(world, styles, regions) + realized.with::().unwrap().layout(world, styles, regions) } } /// Inline-level layout. #[capability] -pub trait LayoutInline { - /// Layout into a single frame. - fn layout_inline( - &self, - world: Tracked, - styles: StyleChain, - regions: &Regions, - ) -> SourceResult; -} - -impl LayoutInline for Content { - #[comemo::memoize] - fn layout_inline( - &self, - world: Tracked, - styles: StyleChain, - regions: &Regions, - ) -> SourceResult { - assert!(regions.backlog.is_empty()); - assert!(regions.last.is_none()); - - if self.has::() && !applicable(self, styles) { - let barrier = Style::Barrier(self.id()); - let styles = styles.chain_one(&barrier); - return self - .with::() - .unwrap() - .layout_inline(world, styles, regions); - } - - Ok(self.layout_block(world, styles, regions)?.remove(0)) - } -} +pub trait Inline: Layout {} /// A sequence of regions to layout into. #[derive(Debug, Clone, Hash)] @@ -255,7 +219,7 @@ fn realize_block<'a>( content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<(Content, StyleChain<'a>)> { - if content.has::() && !applicable(content, styles) { + if content.has::() && !applicable(content, styles) { return Ok((content.clone(), styles)); } @@ -497,7 +461,7 @@ impl<'a> FlowBuilder<'a> { return true; } - if content.has::() { + if content.has::() { let is_tight_list = if let Some(node) = content.to::() { node.tight } else if let Some(node) = content.to::() { @@ -542,7 +506,7 @@ impl<'a> ParBuilder<'a> { || content.is::() || content.is::() || content.is::() - || content.has::() + || content.has::() { self.0.push(content.clone(), styles); return true; diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs index 4389d990..c688dd47 100644 --- a/library/src/layout/pad.rs +++ b/library/src/layout/pad.rs @@ -9,7 +9,7 @@ pub struct PadNode { pub child: Content, } -#[node(LayoutBlock)] +#[node(Layout)] impl PadNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let all = args.named("rest")?.or(args.find()?); @@ -25,19 +25,19 @@ impl PadNode { } } -impl LayoutBlock for PadNode { - fn layout_block( +impl Layout for PadNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { // Layout child into padded regions. let padding = self.padding.resolve(styles); let pod = regions.map(|size| shrink(size, padding)); - let mut frames = self.child.layout_block(world, styles, &pod)?; + let mut fragment = self.child.layout(world, styles, &pod)?; - for frame in &mut frames { + for frame in &mut fragment { // Apply the padding inversely such that the grown size padded // yields the frame's size. let padded = grow(frame.size(), padding); @@ -49,7 +49,7 @@ impl LayoutBlock for PadNode { frame.translate(offset); } - Ok(frames) + Ok(fragment) } } diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 42db02c3..9fe608ee 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -60,7 +60,7 @@ impl PageNode { world: Tracked, mut page: usize, styles: StyleChain, - ) -> SourceResult> { + ) -> SourceResult { // When one of the lengths is infinite the page fits its content along // that axis. let width = styles.get(Self::WIDTH).unwrap_or(Abs::inf()); @@ -97,7 +97,7 @@ impl PageNode { // Layout the child. let regions = Regions::repeat(size, size, size.map(Abs::is_finite)); - let mut frames = child.layout_block(world, styles, ®ions)?; + let mut fragment = child.layout(world, styles, ®ions)?; let header = styles.get(Self::HEADER); let footer = styles.get(Self::FOOTER); @@ -105,7 +105,7 @@ impl PageNode { let background = styles.get(Self::BACKGROUND); // Realize overlays. - for frame in &mut frames { + for frame in &mut fragment { let size = frame.size(); let pad = padding.resolve(styles).relative_to(size); let pw = size.x - pad.left - pad.right; @@ -118,7 +118,7 @@ impl PageNode { ] { if let Some(content) = marginal.resolve(world, page)? { let pod = Regions::one(area, area, Axes::splat(true)); - let sub = content.layout_block(world, styles, &pod)?.remove(0); + let sub = content.layout(world, styles, &pod)?.into_frame(); if std::ptr::eq(marginal, background) { frame.prepend_frame(pos, sub); } else { @@ -130,7 +130,7 @@ impl PageNode { page += 1; } - Ok(frames) + Ok(fragment) } } diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index af313073..215b5b9f 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -5,7 +5,7 @@ use crate::prelude::*; #[derive(Debug, Hash)] pub struct PlaceNode(pub Content); -#[node(LayoutBlock, Behave)] +#[node(Layout, Behave)] impl PlaceNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let aligns = args.find()?.unwrap_or(Axes::with_x(Some(GenAlign::Start))); @@ -16,13 +16,13 @@ impl PlaceNode { } } -impl LayoutBlock for PlaceNode { - fn layout_block( +impl Layout for PlaceNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { let out_of_flow = self.out_of_flow(); // The pod is the base area of the region because for absolute @@ -33,14 +33,14 @@ impl LayoutBlock for PlaceNode { Regions::one(regions.base, regions.base, expand) }; - let mut frames = self.0.layout_block(world, styles, &pod)?; + let mut frame = self.0.layout(world, styles, &pod)?.into_frame(); // If expansion is off, zero all sizes so that we don't take up any // space in our parent. Otherwise, respect the expand settings. let target = regions.expand.select(regions.first, Size::zero()); - frames[0].resize(target, Align::LEFT_TOP); + frame.resize(target, Align::LEFT_TOP); - Ok(frames) + Ok(Fragment::frame(frame)) } } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index c935d971..7de1d34a 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -15,7 +15,7 @@ pub struct StackNode { pub children: Vec, } -#[node(LayoutBlock)] +#[node(Layout)] impl StackNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { Ok(Self { @@ -27,13 +27,13 @@ impl StackNode { } } -impl LayoutBlock for StackNode { - fn layout_block( +impl Layout for StackNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult> { + ) -> SourceResult { let mut layouter = StackLayouter::new(self.dir, regions, styles); // Spacing to insert before the next block. @@ -196,9 +196,9 @@ impl<'a> StackLayouter<'a> { self.dir.start().into() }); - let frames = block.layout_block(world, styles, &self.regions)?; - let len = frames.len(); - for (i, frame) in frames.into_iter().enumerate() { + let fragment = block.layout(world, styles, &self.regions)?; + let len = fragment.len(); + for (i, frame) in fragment.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. let size = frame.size(); let size = match self.axis { @@ -276,9 +276,9 @@ impl<'a> StackLayouter<'a> { } /// Finish layouting and return the resulting frames. - fn finish(mut self) -> Vec { + fn finish(mut self) -> Fragment { self.finish_region(); - self.finished + Fragment::frames(self.finished) } } diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index f09b4e65..cfc4ac83 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -11,7 +11,7 @@ pub struct MoveNode { pub child: Content, } -#[node(LayoutInline)] +#[node(Layout, Inline)] impl MoveNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult { let dx = args.named("dx")?.unwrap_or_default(); @@ -24,21 +24,25 @@ impl MoveNode { } } -impl LayoutInline for MoveNode { - fn layout_inline( +impl Layout for MoveNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult { - let mut frame = self.child.layout_inline(world, styles, regions)?; - let delta = self.delta.resolve(styles); - let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); - frame.translate(delta.to_point()); - Ok(frame) + ) -> SourceResult { + let mut fragment = self.child.layout(world, styles, regions)?; + for frame in &mut fragment { + let delta = self.delta.resolve(styles); + let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); + frame.translate(delta.to_point()); + } + Ok(fragment) } } +impl Inline for MoveNode {} + /// Transform content without affecting layout. #[derive(Debug, Hash)] pub struct TransformNode { @@ -54,7 +58,7 @@ pub type RotateNode = TransformNode; /// Scale content without affecting layout. pub type ScaleNode = TransformNode; -#[node(LayoutInline)] +#[node(Layout, Inline)] impl TransformNode { /// The origin of the transformation. #[property(resolve)] @@ -78,26 +82,28 @@ impl TransformNode { } } -impl LayoutInline for TransformNode { - fn layout_inline( +impl Layout for TransformNode { + fn layout( &self, world: Tracked, styles: StyleChain, regions: &Regions, - ) -> SourceResult { - let mut frame = self.child.layout_inline(world, styles, regions)?; - - let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); - let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); - let transform = Transform::translate(x, y) - .pre_concat(self.transform) - .pre_concat(Transform::translate(-x, -y)); - frame.transform(transform); - - Ok(frame) + ) -> SourceResult { + let mut fragment = self.child.layout(world, styles, regions)?; + for frame in &mut fragment { + let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); + let transform = Transform::translate(x, y) + .pre_concat(self.transform) + .pre_concat(Transform::translate(-x, -y)); + frame.transform(transform); + } + Ok(fragment) } } +impl Inline for TransformNode {} + /// Kinds of transformations. /// /// The move transformation is handled separately. -- cgit v1.2.3