diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-11-25 20:51:16 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-11-25 20:51:16 +0100 |
| commit | 393d74f9bb0d4c71a69108d5be261103c39f47f3 (patch) | |
| tree | a1d4219de2d8fbec1a16ac3760a95e0b7c9529c5 /src/library | |
| parent | 304d9dd1107504f3925c2593dd279ea6616defab (diff) | |
Layout improvements
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/align.rs | 25 | ||||
| -rw-r--r-- | src/library/flow.rs | 19 | ||||
| -rw-r--r-- | src/library/grid.rs | 36 | ||||
| -rw-r--r-- | src/library/image.rs | 37 | ||||
| -rw-r--r-- | src/library/par.rs | 16 | ||||
| -rw-r--r-- | src/library/shape.rs | 11 | ||||
| -rw-r--r-- | src/library/sized.rs | 8 | ||||
| -rw-r--r-- | src/library/spacing.rs | 4 | ||||
| -rw-r--r-- | src/library/stack.rs | 38 | ||||
| -rw-r--r-- | src/library/text.rs | 3 | ||||
| -rw-r--r-- | src/library/transform.rs | 8 |
11 files changed, 113 insertions, 92 deletions
diff --git a/src/library/align.rs b/src/library/align.rs index f788b325..6f079b7b 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -34,28 +34,29 @@ impl Layout for AlignNode { pod.expand.x &= self.aligns.x.is_none(); pod.expand.y &= self.aligns.y.is_none(); + // Layout the child. let mut frames = self.child.layout(ctx, &pod); - for (Constrained { item: frame, cts }, (current, _)) in + + for (Constrained { item: frame, cts }, (current, base)) in frames.iter_mut().zip(regions.iter()) { - let canvas = Size::new( + // The possibly larger size in which we align the frame. + let new = Size::new( if regions.expand.x { current.w } else { frame.size.w }, if regions.expand.y { current.h } else { frame.size.h }, ); let aligns = self.aligns.unwrap_or(Spec::new(Align::Left, Align::Top)); - let offset = Point::new( - aligns.x.resolve(canvas.w - frame.size.w), - aligns.y.resolve(canvas.h - frame.size.h), - ); - - let frame = Rc::make_mut(frame); - frame.size = canvas; - frame.baseline += offset.y; - frame.translate(offset); + Rc::make_mut(frame).resize(new, aligns); + // Set constraints. cts.expand = regions.expand; - cts.exact = current.to_spec().map(Some); + cts.base.x.and_set(Some(base.w)); + cts.base.y.and_set(Some(base.h)); + cts.exact = Spec::new( + regions.expand.x.then(|| current.w), + regions.expand.y.then(|| current.h), + ); } frames diff --git a/src/library/flow.rs b/src/library/flow.rs index 44ce6304..d30bce09 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -52,7 +52,7 @@ impl Layout for FlowNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - FlowLayouter::new(self, regions.clone()).layout(ctx) + FlowLayouter::new(self, regions).layout(ctx) } } @@ -80,7 +80,7 @@ struct FlowLayouter<'a> { children: &'a [FlowChild], /// Whether the flow should expand to fill the region. expand: Spec<bool>, - /// The region to layout into. + /// The regions to layout children into. regions: Regions, /// The full size of `regions.current` that was available before we started /// subtracting. @@ -109,15 +109,18 @@ enum FlowItem { impl<'a> FlowLayouter<'a> { /// Create a new flow layouter. - fn new(flow: &'a FlowNode, mut regions: Regions) -> Self { - // Disable vertical expansion for children. + fn new(flow: &'a FlowNode, regions: &Regions) -> Self { let expand = regions.expand; + let full = regions.current; + + // Disable vertical expansion for children. + let mut regions = regions.clone(); regions.expand.y = false; Self { children: &flow.children, expand, - full: regions.current, + full, regions, used: Size::zero(), fr: Fractional::zero(), @@ -129,6 +132,10 @@ impl<'a> FlowLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { for child in self.children { + if self.regions.is_full() { + self.finish_region(); + } + match *child { FlowChild::Spacing(Spacing::Linear(v)) => { self.layout_absolute(v); @@ -215,7 +222,7 @@ impl<'a> FlowLayouter<'a> { size.h = self.full.h; } - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut before = Length::zero(); let mut ruler = Align::Top; let mut first = true; diff --git a/src/library/grid.rs b/src/library/grid.rs index b48856eb..09bb3b3b 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -99,11 +99,11 @@ struct GridLayouter<'a> { cols: Vec<TrackSizing>, /// The row tracks including gutter tracks. rows: Vec<TrackSizing>, - /// The regions to layout into. + /// The regions to layout children into. regions: Regions, /// Resolved column sizes. rcols: Vec<Length>, - /// The full block size of the current region. + /// The full height of the current region. full: Length, /// The used-up size of the current region. The horizontal size is /// determined once after columns are resolved and not touched again. @@ -353,6 +353,12 @@ impl<'a> GridLayouter<'a> { /// Layout the grid row-by-row. fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { for y in 0 .. self.rows.len() { + // Skip to next region if current one is full, but only for content + // rows, not for gutter rows. + if y % 2 == 0 && self.regions.is_full() { + self.finish_region(ctx); + } + match self.rows[y] { TrackSizing::Auto => self.layout_auto_row(ctx, y), TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y), @@ -368,8 +374,8 @@ impl<'a> GridLayouter<'a> { self.finished } - /// Layout a row with automatic size along the block axis. Such a row may - /// break across multiple regions. + /// Layout a row with automatic height. Such a row may break across multiple + /// regions. fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) { let mut resolved: Vec<Length> = vec![]; @@ -388,10 +394,14 @@ impl<'a> GridLayouter<'a> { let mut sizes = node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h); + // For each region, we want to know the maximum height any + // column requires. for (target, size) in resolved.iter_mut().zip(&mut sizes) { target.set_max(size); } + // New heights are maximal by virtue of being new. Note that + // this extend only uses the rest of the sizes iterator. resolved.extend(sizes); } } @@ -431,16 +441,16 @@ impl<'a> GridLayouter<'a> { } } - /// Layout a row with linear sizing along the block axis. Such a row cannot - /// break across multiple regions, but it may force a region break. + /// Layout a row with linear height. Such a row cannot break across multiple + /// regions, but it may force a region break. fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) { let resolved = v.resolve(self.regions.base.h); let frame = self.layout_single_row(ctx, resolved, y); // Skip to fitting region. - let length = frame.size.h; - while !self.regions.current.h.fits(length) && !self.regions.in_full_last() { - self.cts.max.y = Some(self.used.h + length); + let height = frame.size.h; + while !self.regions.current.h.fits(height) && !self.regions.in_last() { + self.cts.max.y = Some(self.used.h + height); self.finish_region(ctx); // Don't skip multiple regions for gutter and don't push a row. @@ -452,14 +462,14 @@ impl<'a> GridLayouter<'a> { self.push_row(frame); } - /// Layout a row with a fixed size along the block axis and return its frame. + /// Layout a row with fixed height and return its frame. fn layout_single_row( &self, ctx: &mut LayoutContext, height: Length, y: usize, ) -> Frame { - let mut output = Frame::new(Size::new(self.used.w, height), height); + let mut output = Frame::new(Size::new(self.used.w, height)); let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { @@ -496,7 +506,7 @@ impl<'a> GridLayouter<'a> { // Prepare frames. let mut outputs: Vec<_> = heights .iter() - .map(|&h| Frame::new(Size::new(self.used.w, h), h)) + .map(|&h| Frame::new(Size::new(self.used.w, h))) .collect(); // Prepare regions. @@ -553,7 +563,7 @@ impl<'a> GridLayouter<'a> { } // The frame for the region. - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut pos = Point::zero(); // Place finished rows and layout fractional rows. diff --git a/src/library/image.rs b/src/library/image.rs index a421af60..185d033a 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -43,12 +43,12 @@ impl Layout for ImageNode { let &Regions { current, expand, .. } = regions; let img = ctx.images.get(self.id); - let pixel_w = img.width() as f64; - let pixel_h = img.height() as f64; + let pxw = img.width() as f64; + let pxh = img.height() as f64; - let region_ratio = current.w / current.h; - let pixel_ratio = pixel_w / pixel_h; - let wide = region_ratio < pixel_ratio; + let pixel_ratio = pxw / pxh; + let current_ratio = current.w / current.h; + let wide = pixel_ratio > current_ratio; // The space into which the image will be placed according to its fit. let canvas = if expand.x && expand.y { @@ -58,7 +58,7 @@ impl Layout for ImageNode { } else if current.h.is_finite() { Size::new(current.w.min(current.h * pixel_ratio), current.h) } else { - Size::new(Length::pt(pixel_w), Length::pt(pixel_h)) + Size::new(Length::pt(pxw), Length::pt(pxh)) }; // The actual size of the fitted image. @@ -73,26 +73,19 @@ impl Layout for ImageNode { ImageFit::Stretch => canvas, }; - // Position the image so that it is centered in the canvas. - let mut frame = Frame::new(canvas, canvas.h); - frame.push( - (canvas - size).to_point() / 2.0, - Element::Image(self.id, size), - ); + // First, place the image in a frame of exactly its size and then resize + // the frame to the canvas size, center aligning the image in the + // process. + let mut frame = Frame::new(size); + frame.push(Point::zero(), Element::Image(self.id, size)); + frame.resize(canvas, Spec::new(Align::Center, Align::Horizon)); - // Create a clipping group if the image mode is `cover`. + // Create a clipping group if the fit mode is "cover". if self.fit == ImageFit::Cover { - let mut wrapper = Frame::new(canvas, canvas.h); - wrapper.push( - Point::zero(), - Element::Group(Group::new(Rc::new(frame)).clips(true)), - ); - frame = wrapper; + frame.clip(); } - let mut cts = Constraints::new(regions.expand); - cts.exact = regions.current.to_spec().map(Some); - vec![frame.constrain(cts)] + vec![frame.constrain(Constraints::tight(regions))] } } diff --git a/src/library/par.rs b/src/library/par.rs index 7f3f1088..e09e4ad2 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -241,12 +241,17 @@ impl<'a> ParLayouter<'a> { starts.push((range.start, deco)); } ParChild::Undecorate => { - let (start, deco) = starts.pop().unwrap(); - decos.push((start .. range.end, deco)); + if let Some((start, deco)) = starts.pop() { + decos.push((start .. range.end, deco)); + } } } } + for (start, deco) in starts { + decos.push((start .. bidi.text.len(), deco)); + } + Self { align: par.align, leading: par.leading, @@ -307,7 +312,7 @@ impl<'a> ParLayouter<'a> { // If the line does not fit vertically, we start a new region. while !stack.regions.current.h.fits(line.size.h) { - if stack.regions.in_full_last() { + if stack.regions.in_last() { stack.overflowing = true; break; } @@ -487,8 +492,9 @@ impl<'a> LineLayout<'a> { let size = Size::new(self.size.w.max(width), self.size.h); let remaining = size.w - self.size.w; - let mut output = Frame::new(size, self.baseline); + let mut output = Frame::new(size); let mut offset = Length::zero(); + output.baseline = self.baseline; for (range, item) in self.reordered() { let mut position = |mut frame: Frame| { @@ -621,7 +627,7 @@ impl<'a> LineStack<'a> { self.cts.exact = self.full.to_spec().map(Some); } - let mut output = Frame::new(self.size, self.size.h); + let mut output = Frame::new(self.size); let mut offset = Length::zero(); let mut first = true; diff --git a/src/library/shape.rs b/src/library/shape.rs index 5d3504d0..36e25b3c 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -165,12 +165,16 @@ impl Layout for ShapeNode { if regions.expand.y { regions.current.h } else { default }, ); + // Don't overflow the region. + size.w = size.w.min(regions.current.w); + size.h = size.h.min(regions.current.h); + if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) { size.w = size.w.min(size.h); size.h = size.w; } - Frame::new(size, size.h) + Frame::new(size) }; // Add fill and/or stroke. @@ -197,9 +201,6 @@ impl Layout for ShapeNode { ); // Return tight constraints for now. - let mut cts = Constraints::new(regions.expand); - cts.exact = regions.current.to_spec().map(Some); - cts.base = regions.base.to_spec().map(Some); - vec![frame.constrain(cts)] + vec![frame.constrain(Constraints::tight(regions))] } } diff --git a/src/library/sized.rs b/src/library/sized.rs index 3e27ff2f..df150143 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -35,10 +35,6 @@ impl Layout for SizedNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - // Resolve width and height relative to the region's base. - let width = self.sizing.x.map(|w| w.resolve(regions.base.w)); - let height = self.sizing.y.map(|h| h.resolve(regions.base.h)); - // Generate constraints. let mut cts = Constraints::new(regions.expand); cts.set_base_if_linear(regions.base, self.sizing); @@ -56,6 +52,10 @@ impl Layout for SizedNode { cts.base.y = Some(regions.base.h); } + // Resolve width and height relative to the region's base. + let width = self.sizing.x.map(|w| w.resolve(regions.base.w)); + let height = self.sizing.y.map(|h| h.resolve(regions.base.h)); + // The "pod" is the region into which the child will be layouted. let pod = { let size = Size::new( diff --git a/src/library/spacing.rs b/src/library/spacing.rs index 26f1bee1..59911dc7 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -3,14 +3,14 @@ use super::prelude::*; /// `h`: Horizontal spacing. pub fn h(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let mut template = Template::new(); - template.spacing(GenAxis::Inline, args.expect("spacing")?); + template.spacing(SpecAxis::Horizontal, args.expect("spacing")?); Ok(Value::Template(template)) } /// `v`: Vertical spacing. pub fn v(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let mut template = Template::new(); - template.spacing(GenAxis::Block, args.expect("spacing")?); + template.spacing(SpecAxis::Vertical, args.expect("spacing")?); Ok(Value::Template(template)) } diff --git a/src/library/stack.rs b/src/library/stack.rs index d0d5225e..a6878bd6 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -66,7 +66,7 @@ impl Layout for StackNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - StackLayouter::new(self, regions.clone()).layout(ctx) + StackLayouter::new(self, regions).layout(ctx) } } @@ -96,7 +96,7 @@ struct StackLayouter<'a> { axis: SpecAxis, /// Whether the stack should expand to fill the region. expand: Spec<bool>, - /// The region to layout into. + /// The regions to layout children into. regions: Regions, /// The full size of `regions.current` that was available before we started /// subtracting. @@ -123,17 +123,21 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(stack: &'a StackNode, mut regions: Regions) -> Self { - // Disable expansion along the block axis for children. + fn new(stack: &'a StackNode, regions: &Regions) -> Self { let axis = stack.dir.axis(); let expand = regions.expand; + let full = regions.current; + + + // Disable expansion along the block axis for children. + let mut regions = regions.clone(); regions.expand.set(axis, false); Self { stack, axis, expand, - full: regions.current, + full, regions, used: Gen::zero(), fr: Fractional::zero(), @@ -145,6 +149,10 @@ impl<'a> StackLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { for child in &self.stack.children { + if self.regions.is_full() { + self.finish_region(); + } + match *child { StackChild::Spacing(Spacing::Linear(v)) => { self.layout_absolute(v); @@ -167,10 +175,10 @@ impl<'a> StackLayouter<'a> { fn layout_absolute(&mut self, amount: Linear) { // Resolve the linear, limiting it to the remaining available space. let remaining = self.regions.current.get_mut(self.axis); - let resolved = amount.resolve(self.full.get(self.axis)); + let resolved = amount.resolve(self.regions.base.get(self.axis)); let limited = resolved.min(*remaining); *remaining -= limited; - self.used.block += limited; + self.used.main += limited; self.items.push(StackItem::Absolute(resolved)); } @@ -187,9 +195,9 @@ impl<'a> StackLayouter<'a> { for (i, frame) in frames.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. let size = frame.item.size.to_gen(self.axis); - self.used.block += size.block; - self.used.inline.set_max(size.inline); - *self.regions.current.get_mut(self.axis) -= size.block; + self.used.main += size.main; + self.used.cross.set_max(size.cross); + *self.regions.current.get_mut(self.axis) -= size.main; self.items.push(StackItem::Frame(frame.item, align)); if i + 1 < len { @@ -210,13 +218,13 @@ impl<'a> StackLayouter<'a> { // Expand fully if there are fr spacings. let full = self.full.get(self.axis); - let remaining = full - self.used.block; + let remaining = full - self.used.main; if self.fr.get() > 0.0 && full.is_finite() { - self.used.block = full; + self.used.main = full; size.set(self.axis, full); } - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut before = Length::zero(); let mut ruler: Align = self.stack.dir.start().into(); @@ -239,11 +247,11 @@ impl<'a> StackLayouter<'a> { // Align along the block axis. let parent = size.get(self.axis); let child = frame.size.get(self.axis); - let block = ruler.resolve(parent - self.used.block) + let block = ruler.resolve(parent - self.used.main) + if self.stack.dir.is_positive() { before } else { - self.used.block - child - before + self.used.main - child - before }; let pos = Gen::new(Length::zero(), block).to_point(self.axis); diff --git a/src/library/text.rs b/src/library/text.rs index f1cd2b4d..aac99e8a 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -305,8 +305,9 @@ pub struct ShapedGlyph { impl<'a> ShapedText<'a> { /// Build the shaped text's frame. pub fn build(&self) -> Frame { - let mut frame = Frame::new(self.size, self.baseline); + let mut frame = Frame::new(self.size); let mut offset = Length::zero(); + frame.baseline = self.baseline; for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) { let pos = Point::new(offset, self.baseline); diff --git a/src/library/transform.rs b/src/library/transform.rs index 75df8067..207a8098 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -62,13 +62,7 @@ impl Layout for TransformNode { let transform = Transform::translation(x, y) .pre_concat(self.transform) .pre_concat(Transform::translation(-x, -y)); - - let mut wrapper = Frame::new(frame.size, frame.baseline); - wrapper.push( - Point::zero(), - Element::Group(Group::new(std::mem::take(frame)).transform(transform)), - ); - *frame = Rc::new(wrapper); + Rc::make_mut(frame).transform(transform); } frames |
