summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs27
-rw-r--r--src/library/flow.rs37
-rw-r--r--src/library/grid.rs85
-rw-r--r--src/library/image.rs14
-rw-r--r--src/library/mod.rs21
-rw-r--r--src/library/pad.rs40
-rw-r--r--src/library/page.rs8
-rw-r--r--src/library/par.rs58
-rw-r--r--src/library/shape.rs22
-rw-r--r--src/library/sized.rs56
-rw-r--r--src/library/stack.rs12
-rw-r--r--src/library/transform.rs3
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));