From e6857f810e8868d95ebe78753568016b6dea12ca Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 8 Dec 2022 18:44:31 +0100 Subject: [PATCH] Copyable regions --- library/src/basics/list.rs | 2 +- library/src/basics/table.rs | 2 +- library/src/layout/align.rs | 4 ++-- library/src/layout/columns.rs | 16 ++++++++------- library/src/layout/container.rs | 6 +++--- library/src/layout/flow.rs | 15 +++++++------- library/src/layout/grid.rs | 16 +++++++-------- library/src/layout/hide.rs | 2 +- library/src/layout/mod.rs | 35 ++++++++++++++++++++------------- library/src/layout/pad.rs | 8 +++++--- library/src/layout/page.rs | 4 ++-- library/src/layout/par.rs | 4 ++-- library/src/layout/place.rs | 4 ++-- library/src/layout/repeat.rs | 2 +- library/src/layout/stack.rs | 8 ++++---- library/src/layout/transform.rs | 4 ++-- library/src/math/mod.rs | 2 +- library/src/shared/ext.rs | 4 ++-- library/src/visualize/image.rs | 4 ++-- library/src/visualize/line.rs | 2 +- library/src/visualize/shape.rs | 6 +++--- 21 files changed, 80 insertions(+), 70 deletions(-) diff --git a/library/src/basics/list.rs b/library/src/basics/list.rs index 16c2ba646..bcd8ef1c7 100644 --- a/library/src/basics/list.rs +++ b/library/src/basics/list.rs @@ -81,7 +81,7 @@ impl Layout for ListNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut cells = vec![]; let mut number = NonZeroUsize::new(1).unwrap(); diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs index 38ee57a01..448ad1bd2 100644 --- a/library/src/basics/table.rs +++ b/library/src/basics/table.rs @@ -55,7 +55,7 @@ impl Layout for TableNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let fill = styles.get(Self::FILL); let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default); diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index 42bf86e17..d7f571cbd 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -31,7 +31,7 @@ impl Layout for AlignNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { // The child only needs to expand along an axis if there's no alignment. let mut pod = regions.clone(); @@ -44,7 +44,7 @@ impl Layout for AlignNode { } // Layout the child. - let mut fragment = self.child.layout(vt, styles.chain(&map), &pod)?; + let mut fragment = self.child.layout(vt, 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. diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index a5d5aff52..e16302d95 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -31,7 +31,7 @@ impl Layout for ColumnsNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { // Separating the infinite space into infinite columns does not make // much sense. @@ -44,21 +44,23 @@ impl Layout for ColumnsNode { let gutter = styles.get(Self::GUTTER).relative_to(regions.base.x); let width = (regions.first.x - gutter * (columns - 1) as f64) / columns as f64; + let backlog: Vec<_> = std::iter::once(®ions.first.y) + .chain(regions.backlog) + .flat_map(|&height| std::iter::repeat(height).take(columns)) + .skip(1) + .collect(); + // Create the pod regions. let pod = Regions { first: Size::new(width, regions.first.y), base: Size::new(width, regions.base.y), - backlog: std::iter::once(®ions.first.y) - .chain(regions.backlog.as_slice()) - .flat_map(|&height| std::iter::repeat(height).take(columns)) - .skip(1) - .collect(), + backlog: &backlog, last: regions.last, expand: Axes::new(true, regions.expand.y), }; // Layout the children. - let mut frames = self.child.layout(vt, styles, &pod)?.into_iter(); + let mut frames = self.child.layout(vt, styles, pod)?.into_iter(); let mut finished = vec![]; let dir = styles.get(TextNode::DIR); diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index 5790aae69..762a4bd55 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -25,7 +25,7 @@ impl Layout for BoxNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { // The "pod" is the region into which the child will be layouted. let pod = { @@ -47,7 +47,7 @@ impl Layout for BoxNode { }; // Layout the child. - let mut frame = self.child.layout(vt, styles, &pod)?.into_frame(); + let mut frame = self.child.layout(vt, styles, pod)?.into_frame(); // Ensure frame size matches regions size if expansion is on. let target = regions.expand.select(regions.first, frame.size()); @@ -97,7 +97,7 @@ impl Layout for BlockNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { self.0.layout(vt, styles, regions) } diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index 2994d9c60..0b6454a07 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -22,7 +22,7 @@ impl Layout for FlowNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut layouter = FlowLayouter::new(regions, self.1); @@ -55,11 +55,11 @@ impl Debug for FlowNode { } /// Performs flow layout. -struct FlowLayouter { +struct FlowLayouter<'a> { /// Whether this is a root page-level flow. root: bool, /// The regions to layout children into. - regions: Regions, + regions: Regions<'a>, /// Whether the flow should expand to fill the region. expand: Axes, /// The full size of `regions.size` that was available before we started @@ -88,14 +88,13 @@ enum FlowItem { Placed(Frame), } -impl FlowLayouter { +impl<'a> FlowLayouter<'a> { /// Create a new flow layouter. - fn new(regions: &Regions, root: bool) -> Self { + fn new(mut regions: Regions<'a>, root: bool) -> Self { let expand = regions.expand; let full = regions.first; // Disable vertical expansion for children. - let mut regions = regions.clone(); regions.expand.y = false; Self { @@ -166,7 +165,7 @@ impl FlowLayouter { // aligned later. if let Some(placed) = block.to::() { if placed.out_of_flow() { - let frame = block.layout(vt, styles, &self.regions)?.into_frame(); + let frame = block.layout(vt, styles, self.regions)?.into_frame(); self.layout_item(FlowItem::Placed(frame)); return Ok(()); } @@ -187,7 +186,7 @@ impl FlowLayouter { // Layout the block itself. let sticky = styles.get(BlockNode::STICKY); - let fragment = block.layout(vt, styles, &self.regions)?; + let fragment = block.layout(vt, styles, self.regions)?; for frame in fragment { self.layout_item(FlowItem::Frame(frame, aligns, sticky)); } diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index ab1894237..9ae780f1c 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -38,7 +38,7 @@ impl Layout for GridNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { // Prepare grid layout by unifying content and gutter tracks. let layouter = GridLayouter::new( @@ -120,7 +120,7 @@ struct GridLayouter<'a, 'v> { /// The row tracks including gutter tracks. rows: Vec, /// The regions to layout children into. - regions: Regions, + regions: Regions<'a>, /// The inherited styles. styles: StyleChain<'a>, /// Resolved column sizes. @@ -156,7 +156,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { tracks: Axes<&[TrackSizing]>, gutter: Axes<&[TrackSizing]>, cells: &'a [Content], - regions: &Regions, + regions: Regions<'a>, styles: StyleChain<'a>, ) -> Self { let mut cols = vec![]; @@ -318,7 +318,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { v.resolve(self.styles).relative_to(self.regions.base.y); } - let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame(); + let frame = cell.layout(self.vt, self.styles, pod)?.into_frame(); resolved.set_max(frame.width()); } } @@ -395,7 +395,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { } let mut sizes = cell - .layout(self.vt, self.styles, &pod)? + .layout(self.vt, self.styles, pod)? .into_iter() .map(|frame| frame.height()); @@ -483,7 +483,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { .select(self.regions.base, size); let pod = Regions::one(size, base, Axes::splat(true)); - let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame(); + let frame = cell.layout(self.vt, self.styles, pod)?.into_frame(); output.push_frame(pos, frame); } @@ -504,7 +504,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { // Prepare regions. let size = Size::new(self.used.x, heights[0]); let mut pod = Regions::one(size, self.regions.base, Axes::splat(true)); - pod.backlog = heights[1..].to_vec(); + pod.backlog = &heights[1..]; // Layout the row. let mut pos = Point::zero(); @@ -519,7 +519,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { } // Push the layouted frames into the individual output frames. - let fragment = cell.layout(self.vt, self.styles, &pod)?; + let fragment = cell.layout(self.vt, self.styles, pod)?; for (output, frame) in outputs.iter_mut().zip(fragment) { output.push_frame(pos, frame); } diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs index 136dae2f9..f6ce21d58 100644 --- a/library/src/layout/hide.rs +++ b/library/src/layout/hide.rs @@ -16,7 +16,7 @@ impl Layout for HideNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut fragment = self.0.layout(vt, styles, regions)?; for frame in &mut fragment { diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index 30c424bcc..00b1f9bec 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -90,7 +90,7 @@ pub trait Layout { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult; } @@ -99,7 +99,7 @@ impl Layout for Content { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { #[comemo::memoize] fn cached( @@ -108,7 +108,7 @@ impl Layout for Content { provider: TrackedMut, introspector: Tracked, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut vt = Vt { world, provider, introspector }; let scratch = Scratch::default(); @@ -137,14 +137,14 @@ impl Layout for Content { pub trait Inline: Layout {} /// A sequence of regions to layout into. -#[derive(Debug, Clone, Hash)] -pub struct Regions { +#[derive(Debug, Copy, Clone, Hash)] +pub struct Regions<'a> { /// 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, + pub backlog: &'a [Abs], /// 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, @@ -153,13 +153,13 @@ pub struct Regions { pub expand: Axes, } -impl Regions { +impl<'a> Regions<'a> { /// Create a new region sequence with exactly one region. pub fn one(size: Size, base: Size, expand: Axes) -> Self { Self { first: size, base, - backlog: vec![], + backlog: &[], last: None, expand, } @@ -170,7 +170,7 @@ impl Regions { Self { first: size, base, - backlog: vec![], + backlog: &[], last: Some(size.y), expand, } @@ -180,15 +180,17 @@ impl Regions { /// /// 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(&self, mut f: F) -> Self + pub fn map<'v, F>(&self, backlog: &'v mut Vec, mut f: F) -> Regions<'v> where F: FnMut(Size) -> Size, { let x = self.first.x; - Self { + backlog.clear(); + backlog.extend(self.backlog.iter().map(|&y| f(Size::new(x, y)).y)); + Regions { first: f(self.first), base: f(self.base), - backlog: self.backlog.iter().map(|&y| f(Size::new(x, y)).y).collect(), + backlog, last: self.last.map(|y| f(Size::new(x, y)).y), expand: self.expand, } @@ -208,8 +210,13 @@ impl Regions { /// 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)) + if let Some(height) = self + .backlog + .split_first() + .map(|(first, tail)| { + self.backlog = tail; + *first + }) .or(self.last) { self.first.y = height; diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs index 5b36c8c35..d73574609 100644 --- a/library/src/layout/pad.rs +++ b/library/src/layout/pad.rs @@ -30,12 +30,14 @@ impl Layout for PadNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { + let mut backlog = vec![]; + // Layout child into padded regions. let padding = self.padding.resolve(styles); - let pod = regions.map(|size| shrink(size, padding)); - let mut fragment = self.child.layout(vt, styles, &pod)?; + let pod = regions.map(&mut backlog, |size| shrink(size, padding)); + let mut fragment = self.child.layout(vt, styles, pod)?; for frame in &mut fragment { // Apply the padding inversely such that the grown size padded diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index dee77abd1..e9e27e354 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -97,7 +97,7 @@ impl PageNode { // Layout the child. let regions = Regions::repeat(size, size, size.map(Abs::is_finite)); - let mut fragment = child.layout(vt, styles, ®ions)?; + let mut fragment = child.layout(vt, styles, regions)?; let header = styles.get(Self::HEADER); let footer = styles.get(Self::FOOTER); @@ -118,7 +118,7 @@ impl PageNode { ] { if let Some(content) = marginal.resolve(vt, page)? { let pod = Regions::one(area, area, Axes::splat(true)); - let sub = content.layout(vt, styles, &pod)?.into_frame(); + let sub = content.layout(vt, styles, pod)?.into_frame(); if std::ptr::eq(marginal, background) { frame.prepend_frame(pos, sub); } else { diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index 78e9b2a3c..c0e7c6c90 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -538,7 +538,7 @@ fn prepare<'a>( } else { let size = Size::new(width, base.y); let pod = Regions::one(size, base, Axes::splat(false)); - let mut frame = inline.layout(vt, styles, &pod)?.into_frame(); + let mut frame = inline.layout(vt, styles, pod)?.into_frame(); frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); items.push(Item::Frame(frame)); } @@ -1125,7 +1125,7 @@ fn commit( let fill = Fr::one().share(fr, remaining); let size = Size::new(fill, base.y); let pod = Regions::one(size, base, Axes::new(false, false)); - let frame = repeat.layout(vt, *styles, &pod)?.into_frame(); + let frame = repeat.layout(vt, *styles, pod)?.into_frame(); let width = frame.width(); let count = (fill / width).floor(); let remaining = fill % width; diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index f95aff6a2..2e2c81a1d 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -21,7 +21,7 @@ impl Layout for PlaceNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let out_of_flow = self.out_of_flow(); @@ -33,7 +33,7 @@ impl Layout for PlaceNode { Regions::one(regions.base, regions.base, expand) }; - let mut frame = self.0.layout(vt, styles, &pod)?.into_frame(); + let mut frame = self.0.layout(vt, 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. diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs index a0fceee90..6e0ce39ff 100644 --- a/library/src/layout/repeat.rs +++ b/library/src/layout/repeat.rs @@ -16,7 +16,7 @@ impl Layout for RepeatNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { self.0.layout(vt, styles, regions) } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index 5d0b072ff..6432f4acc 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -31,7 +31,7 @@ impl Layout for StackNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut layouter = StackLayouter::new(self.dir, regions, styles); @@ -94,7 +94,7 @@ struct StackLayouter<'a> { /// The axis of the stacking direction. axis: Axis, /// The regions to layout children into. - regions: Regions, + regions: Regions<'a>, /// The inherited styles. styles: StyleChain<'a>, /// Whether the stack itself should expand to fill the region. @@ -124,7 +124,7 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(dir: Dir, regions: &Regions, styles: StyleChain<'a>) -> Self { + fn new(dir: Dir, regions: Regions<'a>, styles: StyleChain<'a>) -> Self { let axis = dir.axis(); let expand = regions.expand; let full = regions.first; @@ -195,7 +195,7 @@ impl<'a> StackLayouter<'a> { self.dir.start().into() }); - let fragment = block.layout(vt, styles, &self.regions)?; + let fragment = block.layout(vt, 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. diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index 16ee33165..5ccb5686c 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -29,7 +29,7 @@ impl Layout for MoveNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { @@ -87,7 +87,7 @@ impl Layout for TransformNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index 4eb72e23b..83cc62aa7 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -57,7 +57,7 @@ impl Layout for MathNode { &self, vt: &mut Vt, styles: StyleChain, - _: &Regions, + _: Regions, ) -> SourceResult { let mut t = Texifier::new(styles); self.texify(&mut t)?; diff --git a/library/src/shared/ext.rs b/library/src/shared/ext.rs index 4fd1f161a..3c20c90f5 100644 --- a/library/src/shared/ext.rs +++ b/library/src/shared/ext.rs @@ -114,7 +114,7 @@ impl Layout for FillNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { @@ -142,7 +142,7 @@ impl Layout for StrokeNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs index 48764c373..205c8a7d4 100644 --- a/library/src/visualize/image.rs +++ b/library/src/visualize/image.rs @@ -41,14 +41,14 @@ impl Layout for ImageNode { &self, _: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let pxw = self.0.width() as f64; let pxh = self.0.height() as f64; let px_ratio = pxw / pxh; // Find out whether the image is wider or taller than the target size. - let &Regions { first, expand, .. } = regions; + let Regions { first, expand, .. } = regions; let region_ratio = first.x / first.y; let wide = px_ratio > region_ratio; diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs index 505ad4df3..0875fafcb 100644 --- a/library/src/visualize/line.rs +++ b/library/src/visualize/line.rs @@ -41,7 +41,7 @@ impl Layout for LineNode { &self, _: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let stroke = styles.get(Self::STROKE).unwrap_or_default(); diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs index acb5a9e91..4517380a5 100644 --- a/library/src/visualize/shape.rs +++ b/library/src/visualize/shape.rs @@ -76,7 +76,7 @@ impl Layout for ShapeNode { &self, vt: &mut Vt, styles: StyleChain, - regions: &Regions, + regions: Regions, ) -> SourceResult { let mut frame; if let Some(child) = &self.0 { @@ -89,7 +89,7 @@ impl Layout for ShapeNode { let child = child.clone().padded(inset.map(|side| side.map(Length::from))); let mut pod = Regions::one(regions.first, regions.base, regions.expand); - frame = child.layout(vt, styles, &pod)?.into_frame(); + frame = child.layout(vt, styles, pod)?.into_frame(); // Relayout with full expansion into square region to make sure // the result is really a square or circle. @@ -105,7 +105,7 @@ impl Layout for ShapeNode { pod.first = Size::splat(length); pod.expand = Axes::splat(true); - frame = child.layout(vt, styles, &pod)?.into_frame(); + frame = child.layout(vt, styles, pod)?.into_frame(); } } else { // The default size that a shape takes on if it has no child and