diff options
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/align.rs | 27 | ||||
| -rw-r--r-- | src/library/flow.rs | 37 | ||||
| -rw-r--r-- | src/library/grid.rs | 85 | ||||
| -rw-r--r-- | src/library/image.rs | 14 | ||||
| -rw-r--r-- | src/library/mod.rs | 21 | ||||
| -rw-r--r-- | src/library/pad.rs | 40 | ||||
| -rw-r--r-- | src/library/page.rs | 8 | ||||
| -rw-r--r-- | src/library/par.rs | 58 | ||||
| -rw-r--r-- | src/library/shape.rs | 22 | ||||
| -rw-r--r-- | src/library/sized.rs | 56 | ||||
| -rw-r--r-- | src/library/stack.rs | 12 | ||||
| -rw-r--r-- | src/library/transform.rs | 3 |
12 files changed, 164 insertions, 219 deletions
diff --git a/src/library/align.rs b/src/library/align.rs index 6f079b7b..7ad5a2d4 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -29,10 +29,9 @@ impl Layout for AlignNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - // Along axes with specified alignment, the child doesn't need to expand. + // The child only needs to expand along an axis if there's no alignment. let mut pod = regions.clone(); - pod.expand.x &= self.aligns.x.is_none(); - pod.expand.y &= self.aligns.y.is_none(); + pod.expand &= self.aligns.map_is_none(); // Layout the child. let mut frames = self.child.layout(ctx, &pod); @@ -40,23 +39,17 @@ impl Layout for AlignNode { for (Constrained { item: frame, cts }, (current, base)) in frames.iter_mut().zip(regions.iter()) { - // 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)); - Rc::make_mut(frame).resize(new, aligns); + // Align in the target size. The target size depends on whether we + // should expand. + let target = regions.expand.select(current, frame.size); + let default = Spec::new(Align::Left, Align::Top); + let aligns = self.aligns.unwrap_or(default); + Rc::make_mut(frame).resize(target, aligns); // Set constraints. cts.expand = regions.expand; - 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), - ); + cts.base = base.filter(cts.base.map_is_some()); + cts.exact = current.filter(regions.expand); } frames diff --git a/src/library/flow.rs b/src/library/flow.rs index d30bce09..e105c596 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -157,10 +157,10 @@ impl<'a> FlowLayouter<'a> { /// Layout absolute spacing. fn layout_absolute(&mut self, amount: Linear) { // Resolve the linear, limiting it to the remaining available space. - let resolved = amount.resolve(self.full.h); - let limited = resolved.min(self.regions.current.h); - self.regions.current.h -= limited; - self.used.h += limited; + let resolved = amount.resolve(self.full.y); + let limited = resolved.min(self.regions.current.y); + self.regions.current.y -= limited; + self.used.y += limited; self.items.push(FlowItem::Absolute(resolved)); } @@ -195,9 +195,9 @@ impl<'a> FlowLayouter<'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; - self.used.h += size.h; - self.used.w.set_max(size.w); - self.regions.current.h -= size.h; + self.used.y += size.y; + self.used.x.set_max(size.x); + self.regions.current.y -= size.y; self.items.push(FlowItem::Frame(frame.item, aligns)); if i + 1 < len { @@ -210,16 +210,13 @@ impl<'a> FlowLayouter<'a> { fn finish_region(&mut self) { // Determine the size of the flow in this region dependening on whether // the region expands. - let mut size = Size::new( - if self.expand.x { self.full.w } else { self.used.w }, - if self.expand.y { self.full.h } else { self.used.h }, - ); + let mut size = self.expand.select(self.full, self.used); // Account for fractional spacing in the size calculation. - let remaining = self.full.h - self.used.h; - if self.fr.get() > 0.0 && self.full.h.is_finite() { - self.used.h = self.full.h; - size.h = self.full.h; + let remaining = self.full.y - self.used.y; + if self.fr.get() > 0.0 && self.full.y.is_finite() { + self.used.y = self.full.y; + size.y = self.full.y; } let mut output = Frame::new(size); @@ -243,10 +240,10 @@ impl<'a> FlowLayouter<'a> { ruler = ruler.max(aligns.y); // Align horizontally and vertically. - let x = aligns.x.resolve(size.w - frame.size.w); - let y = before + ruler.resolve(size.h - self.used.h); + let x = aligns.x.resolve(size.x - frame.size.x); + let y = before + ruler.resolve(size.y - self.used.y); let pos = Point::new(x, y); - before += frame.size.h; + before += frame.size.y; // The baseline of the flow is that of the first frame. if first { @@ -261,8 +258,8 @@ impl<'a> FlowLayouter<'a> { // Generate tight constraints for now. let mut cts = Constraints::new(self.expand); - cts.exact = self.full.to_spec().map(Some); - cts.base = self.regions.base.to_spec().map(Some); + cts.exact = self.full.map(Some); + cts.base = self.regions.base.map(Some); // Advance to the next region. self.regions.next(); diff --git a/src/library/grid.rs b/src/library/grid.rs index 09bb3b3b..9dd156da 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -178,7 +178,7 @@ impl<'a> GridLayouter<'a> { rcols: vec![Length::zero(); cols.len()], cols, rows, - full: regions.current.h, + full: regions.current.y, regions, used: Size::zero(), fr: Fractional::zero(), @@ -220,7 +220,7 @@ impl<'a> GridLayouter<'a> { case = Case::Fitting; } TrackSizing::Linear(v) => { - let resolved = v.resolve(self.regions.base.w); + let resolved = v.resolve(self.regions.base.x); *rcol = resolved; linear += resolved; } @@ -232,7 +232,7 @@ impl<'a> GridLayouter<'a> { } // Size that is not used by fixed-size columns. - let available = self.regions.current.w - linear; + let available = self.regions.current.x - linear; if available >= Length::zero() { // Determine size of auto columns. let (auto, count) = self.measure_auto_columns(ctx, available); @@ -254,18 +254,18 @@ impl<'a> GridLayouter<'a> { } // Children could depend on base. - self.cts.base = self.regions.base.to_spec().map(Some); + self.cts.base = self.regions.base.map(Some); // Set constraints depending on the case we hit. match case { Case::PurelyLinear => {} - Case::Fitting => self.cts.min.x = Some(self.used.w), - Case::Exact => self.cts.exact.x = Some(self.regions.current.w), + Case::Fitting => self.cts.min.x = Some(self.used.x), + Case::Exact => self.cts.exact.x = Some(self.regions.current.x), Case::Overflowing => self.cts.max.x = Some(linear), } // Sum up the resolved column sizes once here. - self.used.w = self.rcols.iter().sum(); + self.used.x = self.rcols.iter().sum(); } /// Measure the size that is available to auto columns. @@ -287,7 +287,7 @@ impl<'a> GridLayouter<'a> { let mut resolved = Length::zero(); for y in 0 .. self.rows.len() { if let Some(node) = self.cell(x, y) { - let size = Size::new(available, self.regions.base.h); + let size = Size::new(available, self.regions.base.y); let mut pod = Regions::one(size, self.regions.base, Spec::splat(false)); @@ -295,11 +295,11 @@ impl<'a> GridLayouter<'a> { // base, for auto it's already correct and for fr we could // only guess anyway. if let TrackSizing::Linear(v) = self.rows[y] { - pod.base.h = v.resolve(self.regions.base.h); + pod.base.y = v.resolve(self.regions.base.y); } let frame = node.layout(ctx, &pod).remove(0).item; - resolved.set_max(frame.size.w); + resolved.set_max(frame.size.x); } } @@ -382,17 +382,15 @@ impl<'a> GridLayouter<'a> { // Determine the size for each region of the row. for (x, &rcol) in self.rcols.iter().enumerate() { if let Some(node) = self.cell(x, y) { + // All widths should be `rcol` except the base for auto columns. let mut pod = self.regions.clone(); - pod.mutate(|size| size.w = rcol); - - // Set the horizontal base back to the parent region's base for - // auto columns. + pod.mutate(|size| size.x = rcol); if self.cols[x] == TrackSizing::Auto { - pod.base.w = self.regions.base.w; + pod.base.x = self.regions.base.x; } let mut sizes = - node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h); + node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.y); // For each region, we want to know the maximum height any // column requires. @@ -425,7 +423,7 @@ impl<'a> GridLayouter<'a> { for (target, (current, _)) in resolved[.. len - 1].iter_mut().zip(self.regions.iter()) { - target.set_max(current.h); + target.set_max(current.y); } } @@ -444,13 +442,13 @@ impl<'a> GridLayouter<'a> { /// 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 resolved = v.resolve(self.regions.base.y); let frame = self.layout_single_row(ctx, resolved, y); // Skip to fitting region. - 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); + let height = frame.size.y; + while !self.regions.current.y.fits(height) && !self.regions.in_last() { + self.cts.max.y = Some(self.used.y + height); self.finish_region(ctx); // Don't skip multiple regions for gutter and don't push a row. @@ -469,21 +467,18 @@ impl<'a> GridLayouter<'a> { height: Length, y: usize, ) -> Frame { - let mut output = Frame::new(Size::new(self.used.w, height)); + let mut output = Frame::new(Size::new(self.used.x, height)); let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { if let Some(node) = self.cell(x, y) { let size = Size::new(rcol, height); - // Set the base to the size for non-auto rows. - let mut base = self.regions.base; - if self.cols[x] != TrackSizing::Auto { - base.w = size.w; - } - if self.rows[y] != TrackSizing::Auto { - base.h = size.h; - } + // Set the base to the region's base for auto rows and to the + // size for linear and fractional rows. + let base = Spec::new(self.cols[x], self.rows[y]) + .map(|s| s == TrackSizing::Auto) + .select(self.regions.base, size); let pod = Regions::one(size, base, Spec::splat(true)); let frame = node.layout(ctx, &pod).remove(0); @@ -506,15 +501,15 @@ impl<'a> GridLayouter<'a> { // Prepare frames. let mut outputs: Vec<_> = heights .iter() - .map(|&h| Frame::new(Size::new(self.used.w, h))) + .map(|&h| Frame::new(Size::new(self.used.x, h))) .collect(); // Prepare regions. - let size = Size::new(self.used.w, heights[0]); + let size = Size::new(self.used.x, heights[0]); let mut pod = Regions::one(size, self.regions.base, Spec::splat(true)); pod.backlog = heights[1 ..] .iter() - .map(|&h| Size::new(self.used.w, h)) + .map(|&h| Size::new(self.used.x, h)) .collect::<Vec<_>>() .into_iter(); @@ -522,12 +517,10 @@ impl<'a> GridLayouter<'a> { let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { if let Some(node) = self.cell(x, y) { - pod.mutate(|size| size.w = rcol); - - // Set the horizontal base back to the parent region's base for - // auto columns. + // All widths should be `rcol` except the base for auto columns. + pod.mutate(|size| size.x = rcol); if self.cols[x] == TrackSizing::Auto { - pod.base.w = self.regions.base.w; + pod.base.x = self.regions.base.x; } // Push the layouted frames into the individual output frames. @@ -545,8 +538,8 @@ impl<'a> GridLayouter<'a> { /// Push a row frame into the current region. fn push_row(&mut self, frame: Frame) { - self.regions.current.h -= frame.size.h; - self.used.h += frame.size.h; + self.regions.current.y -= frame.size.y; + self.used.y += frame.size.y; self.lrows.push(Row::Frame(frame)); } @@ -556,10 +549,10 @@ impl<'a> GridLayouter<'a> { // there are fr rows. let mut size = self.used; if self.fr.get() > 0.0 && self.full.is_finite() { - size.h = self.full; + size.y = self.full; self.cts.exact.y = Some(self.full); } else { - self.cts.min.y = Some(size.h); + self.cts.min.y = Some(size.y); } // The frame for the region. @@ -571,20 +564,20 @@ impl<'a> GridLayouter<'a> { let frame = match row { Row::Frame(frame) => frame, Row::Fr(v, y) => { - let remaining = self.full - self.used.h; + let remaining = self.full - self.used.y; let height = v.resolve(self.fr, remaining); self.layout_single_row(ctx, height, y) } }; - let height = frame.size.h; + let height = frame.size.y; output.merge_frame(pos, frame); pos.y += height; } self.regions.next(); - self.full = self.regions.current.h; - self.used.h = Length::zero(); + self.full = self.regions.current.y; + self.used.y = Length::zero(); self.fr = Fractional::zero(); self.finished.push(output.constrain(self.cts)); self.cts = Constraints::new(self.expand); diff --git a/src/library/image.rs b/src/library/image.rs index 185d033a..92580f6e 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -47,16 +47,16 @@ impl Layout for ImageNode { let pxh = img.height() as f64; let pixel_ratio = pxw / pxh; - let current_ratio = current.w / current.h; + let current_ratio = current.x / current.y; 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 { current - } else if expand.x || (wide && current.w.is_finite()) { - Size::new(current.w, current.h.min(current.w.safe_div(pixel_ratio))) - } else if current.h.is_finite() { - Size::new(current.w.min(current.h * pixel_ratio), current.h) + } else if expand.x || (wide && current.x.is_finite()) { + Size::new(current.x, current.y.min(current.x.safe_div(pixel_ratio))) + } else if current.y.is_finite() { + Size::new(current.x.min(current.y * pixel_ratio), current.y) } else { Size::new(Length::pt(pxw), Length::pt(pxh)) }; @@ -65,9 +65,9 @@ impl Layout for ImageNode { let size = match self.fit { ImageFit::Contain | ImageFit::Cover => { if wide == (self.fit == ImageFit::Contain) { - Size::new(canvas.w, canvas.w / pixel_ratio) + Size::new(canvas.x, canvas.x / pixel_ratio) } else { - Size::new(canvas.h * pixel_ratio, canvas.h) + Size::new(canvas.y * pixel_ratio, canvas.y) } } ImageFit::Stretch => canvas, diff --git a/src/library/mod.rs b/src/library/mod.rs index 4d730a7e..c953a76e 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -144,15 +144,6 @@ dynamic! { } dynamic! { - Spec<Option<Align>>: "2d alignment", - @align: Align => { - let mut aligns = Spec::default(); - aligns.set(align.axis(), Some(*align)); - aligns - }, -} - -dynamic! { FontFamily: "font family", Value::Str(string) => Self::Named(string.to_lowercase()), } @@ -162,3 +153,15 @@ castable! { Expected: "color", Value::Color(color) => Paint::Solid(color), } + +castable! { + Spec<Option<Align>>, + Expected: "1d or 2d alignment", + @align: Align => { + let mut aligns = Spec::default(); + aligns.set(align.axis(), Some(*align)); + aligns + }, + @aligns: Spec<Align> => aligns.map(Some), + +} diff --git a/src/library/pad.rs b/src/library/pad.rs index 7604af40..ce7f4150 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -54,29 +54,17 @@ impl Layout for PadNode { frame.baseline += offset.y; frame.translate(offset); - // Set exact and base constraints if the child had them. - cts.exact.x.and_set(Some(current.w)); - cts.exact.y.and_set(Some(current.h)); - cts.base.x.and_set(Some(base.w)); - cts.base.y.and_set(Some(base.h)); - - // Also set base constraints if the padding is relative. - if self.padding.left.is_relative() || self.padding.right.is_relative() { - cts.base.x = Some(base.w); - } - - if self.padding.top.is_relative() || self.padding.bottom.is_relative() { - cts.base.y = Some(base.h); - } + // Set exact and base constraints if the child had them. Also set + // base if our padding is relative. + let is_rel = self.padding.sum_by_axis().map(Linear::is_relative); + cts.exact = current.filter(cts.exact.map_is_some()); + cts.base = base.filter(is_rel | cts.base.map_is_some()); // Inflate min and max contraints by the padding. for spec in [&mut cts.min, &mut cts.max] { - if let Some(x) = spec.x.as_mut() { - *x += padding.size().w; - } - if let Some(y) = spec.y.as_mut() { - *y += padding.size().h; - } + spec.as_mut() + .zip(padding.sum_by_axis()) + .map(|(s, p)| s.as_mut().map(|v| *v += p)); } } @@ -86,7 +74,7 @@ impl Layout for PadNode { /// Shrink a size by padding relative to the size itself. fn shrink(size: Size, padding: Sides<Linear>) -> Size { - size - padding.resolve(size).size() + size - padding.resolve(size).sum_by_axis() } /// Grow a size by padding relative to the grown size. @@ -109,12 +97,6 @@ fn shrink(size: Size, padding: Sides<Linear>) -> Size { /// <=> (1 - p.rel) * w = s + p.abs /// <=> w = (s + p.abs) / (1 - p.rel) fn grow(size: Size, padding: Sides<Linear>) -> Size { - fn solve_axis(length: Length, padding: Linear) -> Length { - (length + padding.abs).safe_div(1.0 - padding.rel.get()) - } - - Size::new( - solve_axis(size.w, padding.left + padding.right), - solve_axis(size.h, padding.top + padding.bottom), - ) + size.zip(padding.sum_by_axis()) + .map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get())) } diff --git a/src/library/page.rs b/src/library/page.rs index 1ac21fac..0289401a 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -30,16 +30,16 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { if let Some(width) = width { page.class = PaperClass::Custom; - page.size.w = width; + page.size.x = width; } if flip.unwrap_or(false) { - std::mem::swap(&mut page.size.w, &mut page.size.h); + std::mem::swap(&mut page.size.x, &mut page.size.y); } if let Some(height) = height { page.class = PaperClass::Custom; - page.size.h = height; + page.size.y = height; } if let Some(margins) = margins { @@ -95,7 +95,7 @@ impl PageNode { 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 expand = self.size.map(Length::is_finite); let regions = Regions::repeat(self.size, self.size, expand); // Layout the child. diff --git a/src/library/par.rs b/src/library/par.rs index e09e4ad2..5f900dff 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -208,7 +208,7 @@ impl<'a> ParLayouter<'a> { for (range, child) in par.ranges().zip(&par.children) { match *child { ParChild::Spacing(Spacing::Linear(v)) => { - let resolved = v.resolve(regions.current.w); + let resolved = v.resolve(regions.current.x); items.push(ParItem::Absolute(resolved)); ranges.push(range); } @@ -230,7 +230,7 @@ impl<'a> ParLayouter<'a> { } } ParChild::Node(ref node) => { - let size = Size::new(regions.current.w, regions.base.h); + let size = Size::new(regions.current.x, regions.base.y); let expand = Spec::splat(false); let pod = Regions::one(size, regions.base, expand); let frame = node.layout(ctx, &pod).remove(0); @@ -292,26 +292,26 @@ impl<'a> ParLayouter<'a> { // fit the line will yield the same line break. Therefore, // the width of the region must not fit the width of the // tried line. - if !stack.regions.current.w.fits(line.size.w) { - stack.cts.max.x.set_min(line.size.w); + if !stack.regions.current.x.fits(line.size.x) { + stack.cts.max.x.set_min(line.size.x); } // Same as above, but for height. - if !stack.regions.current.h.fits(line.size.h) { - let too_large = stack.size.h + self.leading + line.size.h; + if !stack.regions.current.y.fits(line.size.y) { + let too_large = stack.size.y + self.leading + line.size.y; stack.cts.max.y.set_min(too_large); } stack.push(last_line); - stack.cts.min.y = Some(stack.size.h); + stack.cts.min.y = Some(stack.size.y); start = last_end; line = LineLayout::new(ctx, &self, start .. end); } } // If the line does not fit vertically, we start a new region. - while !stack.regions.current.h.fits(line.size.h) { + while !stack.regions.current.y.fits(line.size.y) { if stack.regions.in_last() { stack.overflowing = true; break; @@ -320,7 +320,7 @@ impl<'a> ParLayouter<'a> { // Again, the line must not fit. It would if the space taken up // plus the line height would fit, therefore the constraint // below. - let too_large = stack.size.h + self.leading + line.size.h; + let too_large = stack.size.y + self.leading + line.size.y; stack.cts.max.y.set_min(too_large); stack.finish_region(ctx); @@ -329,7 +329,7 @@ impl<'a> ParLayouter<'a> { // If the line does not fit horizontally or we have a mandatory // line break (i.e. due to "\n"), we push the line into the // stack. - if mandatory || !stack.regions.current.w.fits(line.size.w) { + if mandatory || !stack.regions.current.x.fits(line.size.x) { start = end; last = None; @@ -339,23 +339,23 @@ impl<'a> ParLayouter<'a> { // paragraph, we want to force an empty line. if mandatory && end == self.bidi.text.len() { let line = LineLayout::new(ctx, &self, end .. end); - if stack.regions.current.h.fits(line.size.h) { + if stack.regions.current.y.fits(line.size.y) { stack.push(line); } } - stack.cts.min.y = Some(stack.size.h); + stack.cts.min.y = Some(stack.size.y); } else { // Otherwise, the line fits both horizontally and vertically // and we remember it. - stack.cts.min.x.set_max(line.size.w); + stack.cts.min.x.set_max(line.size.x); last = Some((line, end)); } } if let Some((line, _)) = last { stack.push(line); - stack.cts.min.y = Some(stack.size.h); + stack.cts.min.y = Some(stack.size.y); } stack.finish(ctx) @@ -467,9 +467,9 @@ impl<'a> LineLayout<'a> { ParItem::Fractional(v) => fr += v, ParItem::Text(ShapedText { size, baseline, .. }) | ParItem::Frame(Frame { size, baseline, .. }) => { - width += size.w; + width += size.x; top.set_max(baseline); - bottom.set_max(size.h - baseline); + bottom.set_max(size.y - baseline); } } } @@ -489,8 +489,8 @@ impl<'a> LineLayout<'a> { /// Build the line's frame. fn build(&self, ctx: &LayoutContext, width: Length) -> Frame { - let size = Size::new(self.size.w.max(width), self.size.h); - let remaining = size.w - self.size.w; + let size = Size::new(self.size.x.max(width), self.size.y); + let remaining = size.x - self.size.x; let mut output = Frame::new(size); let mut offset = Length::zero(); @@ -507,7 +507,7 @@ impl<'a> LineLayout<'a> { let x = offset + self.par.align.resolve(remaining); let y = self.baseline - frame.baseline; - offset += frame.size.w; + offset += frame.size.x; // Add to the line's frame. output.merge_frame(Point::new(x, y), frame); @@ -602,12 +602,12 @@ impl<'a> LineStack<'a> { /// Push a new line into the stack. fn push(&mut self, line: LineLayout<'a>) { - self.regions.current.h -= line.size.h + self.leading; + self.regions.current.y -= line.size.y + self.leading; - self.size.w.set_max(line.size.w); - self.size.h += line.size.h; + self.size.x.set_max(line.size.x); + self.size.y += line.size.y; if !self.lines.is_empty() { - self.size.h += self.leading; + self.size.y += self.leading; } self.fractional |= !line.fr.is_zero(); @@ -617,14 +617,14 @@ impl<'a> LineStack<'a> { /// Finish the frame for one region. fn finish_region(&mut self, ctx: &LayoutContext) { if self.regions.expand.x || self.fractional { - self.size.w = self.regions.current.w; - self.cts.exact.x = Some(self.regions.current.w); + self.size.x = self.regions.current.x; + self.cts.exact.x = Some(self.regions.current.x); } if self.overflowing { self.cts.min.y = None; self.cts.max.y = None; - self.cts.exact = self.full.to_spec().map(Some); + self.cts.exact = self.full.map(Some); } let mut output = Frame::new(self.size); @@ -632,7 +632,7 @@ impl<'a> LineStack<'a> { let mut first = true; for line in self.lines.drain(..) { - let frame = line.build(ctx, self.size.w); + let frame = line.build(ctx, self.size.x); let pos = Point::with_y(offset); if first { @@ -640,7 +640,7 @@ impl<'a> LineStack<'a> { first = false; } - offset += frame.size.h + self.leading; + offset += frame.size.y + self.leading; output.merge_frame(pos, frame); } @@ -648,7 +648,7 @@ impl<'a> LineStack<'a> { self.regions.next(); self.full = self.regions.current; self.cts = Constraints::new(self.regions.expand); - self.cts.base = self.regions.base.to_spec().map(Some); + self.cts.base = self.regions.base.map(Some); self.size = Size::zero(); } diff --git a/src/library/shape.rs b/src/library/shape.rs index 36e25b3c..208ca2a3 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -138,8 +138,8 @@ impl Layout for ShapeNode { // the result is really a square or circle. let size = frames[0].item.size; let mut pod = regions.clone(); - pod.current.w = size.w.max(size.h).min(pod.current.w); - pod.current.h = pod.current.w; + pod.current.x = size.x.max(size.y).min(pod.current.x); + pod.current.y = pod.current.x; pod.expand = Spec::splat(true); frames = node.layout(ctx, &pod); } @@ -153,7 +153,7 @@ impl Layout for ShapeNode { let default = Length::pt(30.0); let mut size = Size::new( if regions.expand.x { - regions.current.w + regions.current.x } else { // For rectangle and ellipse, the default shape is a bit // wider than high. @@ -162,16 +162,16 @@ impl Layout for ShapeNode { ShapeKind::Rect | ShapeKind::Ellipse => 1.5 * default, } }, - if regions.expand.y { regions.current.h } else { default }, + if regions.expand.y { regions.current.y } else { default }, ); // Don't overflow the region. - size.w = size.w.min(regions.current.w); - size.h = size.h.min(regions.current.h); + size.x = size.x.min(regions.current.x); + size.y = size.y.min(regions.current.y); if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) { - size.w = size.w.min(size.h); - size.h = size.w; + size.x = size.x.min(size.y); + size.y = size.x; } Frame::new(size) @@ -194,11 +194,7 @@ impl Layout for ShapeNode { } // Ensure frame size matches regions size if expansion is on. - let expand = regions.expand; - frame.size = Size::new( - if expand.x { regions.current.w } else { frame.size.w }, - if expand.y { regions.current.h } else { frame.size.h }, - ); + frame.size = regions.expand.select(regions.current, frame.size); // Return tight constraints for now. vec![frame.constrain(Constraints::tight(regions))] diff --git a/src/library/sized.rs b/src/library/sized.rs index df150143..9b2cdf22 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -35,50 +35,36 @@ impl Layout for SizedNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - // Generate constraints. - let mut cts = Constraints::new(regions.expand); - cts.set_base_if_linear(regions.base, self.sizing); - - // Set tight exact and base constraints if the child is - // automatically sized since we don't know what the child might do. - if self.sizing.x.is_none() { - cts.exact.x = Some(regions.current.w); - cts.base.x = Some(regions.base.w); - } - - // Same here. - if self.sizing.y.is_none() { - cts.exact.y = Some(regions.current.h); - 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)); + let is_auto = self.sizing.map_is_none(); + let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative)); // The "pod" is the region into which the child will be layouted. let pod = { - let size = Size::new( - width.unwrap_or(regions.current.w), - height.unwrap_or(regions.current.h), - ); + // Resolve the sizing to a concrete size. + let size = self + .sizing + .zip(regions.base) + .map(|(s, b)| s.map(|v| v.resolve(b))) + .unwrap_or(regions.current); - let base = Size::new( - if width.is_some() { size.w } else { regions.base.w }, - if height.is_some() { size.h } else { regions.base.h }, - ); + // Select the appropriate base and expansion for the child depending + // on whether it is automatically or linearly sized. + let base = is_auto.select(regions.base, size); + let expand = regions.expand | !is_auto; - let expand = Spec::new( - width.is_some() || regions.expand.x, - height.is_some() || regions.expand.y, - ); - - // TODO: Allow multiple regions if only width is set. Regions::one(size, base, expand) }; let mut frames = self.child.layout(ctx, &pod); - frames[0].cts = cts; + + // Set base & exact constraints if the child is automatically sized + // since we don't know what the child might do. Also set base if our + // sizing is relative. + let frame = &mut frames[0]; + frame.cts = Constraints::new(regions.expand); + frame.cts.exact = regions.current.filter(is_auto); + frame.cts.base = regions.base.filter(is_auto | is_rel); + frames } } diff --git a/src/library/stack.rs b/src/library/stack.rs index a6878bd6..91f1ef62 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -128,7 +128,6 @@ impl<'a> StackLayouter<'a> { 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); @@ -210,11 +209,8 @@ impl<'a> StackLayouter<'a> { fn finish_region(&mut self) { // Determine the size of the stack in this region dependening on whether // the region expands. - let used = self.used.to_size(self.axis); - let mut size = Size::new( - if self.expand.x { self.full.w } else { used.w }, - if self.expand.y { self.full.h } else { used.h }, - ); + let used = self.used.to_spec(self.axis); + let mut size = self.expand.select(self.full, used); // Expand fully if there are fr spacings. let full = self.full.get(self.axis); @@ -263,8 +259,8 @@ impl<'a> StackLayouter<'a> { // Generate tight constraints for now. let mut cts = Constraints::new(self.expand); - cts.exact = self.full.to_spec().map(Some); - cts.base = self.regions.base.to_spec().map(Some); + cts.exact = self.full.map(Some); + cts.base = self.regions.base.map(Some); // Advance to the next region. self.regions.next(); diff --git a/src/library/transform.rs b/src/library/transform.rs index 207a8098..c8b48666 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -57,8 +57,7 @@ impl Layout for TransformNode { let mut frames = self.child.layout(ctx, regions); for Constrained { item: frame, .. } in frames.iter_mut() { - let x = self.origin.x.resolve(frame.size.w); - let y = self.origin.y.resolve(frame.size.h); + let Spec { x, y } = self.origin.zip(frame.size).map(|(o, s)| o.resolve(s)); let transform = Transform::translation(x, y) .pre_concat(self.transform) .pre_concat(Transform::translation(-x, -y)); |
