summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/function.rs4
-rw-r--r--src/eval/mod.rs4
-rw-r--r--src/eval/ops.rs18
-rw-r--r--src/export/pdf.rs22
-rw-r--r--src/frame.rs18
-rw-r--r--src/geom/gen.rs15
-rw-r--r--src/geom/linear.rs2
-rw-r--r--src/geom/mod.rs2
-rw-r--r--src/geom/path.rs10
-rw-r--r--src/geom/scalar.rs2
-rw-r--r--src/geom/sides.rs20
-rw-r--r--src/geom/size.rs131
-rw-r--r--src/geom/spec.rs219
-rw-r--r--src/layout/constraints.rs18
-rw-r--r--src/layout/regions.rs2
-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
-rw-r--r--src/util/mod.rs9
28 files changed, 403 insertions, 476 deletions
diff --git a/src/eval/function.rs b/src/eval/function.rs
index c83d8b2b..931a90a0 100644
--- a/src/eval/function.rs
+++ b/src/eval/function.rs
@@ -226,7 +226,3 @@ impl Debug for Arg {
Debug::fmt(&self.value.v, f)
}
}
-
-dynamic! {
- Args: "arguments",
-}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 6b28e5ab..a0c31e98 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -494,6 +494,10 @@ impl Eval for ClosureExpr {
// Put the remaining arguments into the sink.
if let Some(sink) = &sink {
+ dynamic! {
+ Args: "arguments",
+ }
+
ctx.scopes.def_mut(sink, args.take());
}
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 6fac354d..ede1230f 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
use super::{Dynamic, Value};
use crate::diag::StrResult;
-use crate::geom::{Align, Get, Spec};
+use crate::geom::{Align, Spec, SpecAxis};
use crate::util::EcoString;
use Value::*;
@@ -94,14 +94,18 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
if let (Some(&a), Some(&b)) =
(a.downcast::<Align>(), b.downcast::<Align>())
{
- if a.axis() == b.axis() {
- return Err(format!("cannot add two {:?} alignments", a.axis()));
+ dynamic! {
+ Spec<Align>: "2d alignment",
}
- let mut aligns = Spec::default();
- aligns.set(a.axis(), Some(a));
- aligns.set(b.axis(), Some(b));
- return Ok(Dyn(Dynamic::new(aligns)));
+ return if a.axis() != b.axis() {
+ Ok(Dyn(Dynamic::new(match a.axis() {
+ SpecAxis::Horizontal => Spec { x: a, y: b },
+ SpecAxis::Vertical => Spec { x: b, y: a },
+ })))
+ } else {
+ Err(format!("cannot add two {:?} alignments", a.axis()))
+ };
}
}
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index b9312c0a..94386c99 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -266,8 +266,8 @@ impl<'a> PdfExporter<'a> {
let mut page_writer = self.writer.page(page_id);
page_writer.parent(page_tree_ref);
- let w = page.size.w.to_f32();
- let h = page.size.h.to_f32();
+ let w = page.size.x.to_f32();
+ let h = page.size.y.to_f32();
page_writer.media_box(Rect::new(0.0, 0.0, w, h));
page_writer.contents(content_id);
@@ -366,7 +366,7 @@ impl<'a> PageExporter<'a> {
fn export(mut self, frame: &Frame) -> Page {
// Make the coordinate system start at the top-left.
- self.bottom = frame.size.h.to_f32();
+ self.bottom = frame.size.y.to_f32();
self.content.transform([1.0, 0.0, 0.0, -1.0, 0.0, self.bottom]);
self.write_frame(&frame);
Page {
@@ -397,8 +397,8 @@ impl<'a> PageExporter<'a> {
self.transform(translation.pre_concat(group.transform));
if group.clips {
- let w = group.frame.size.w.to_f32();
- let h = group.frame.size.h.to_f32();
+ let w = group.frame.size.x.to_f32();
+ let h = group.frame.size.y.to_f32();
self.content.move_to(0.0, 0.0);
self.content.line_to(w, 0.0);
self.content.line_to(w, h);
@@ -471,8 +471,8 @@ impl<'a> PageExporter<'a> {
match shape.geometry {
Geometry::Rect(size) => {
- let w = size.w.to_f32();
- let h = size.h.to_f32();
+ let w = size.x.to_f32();
+ let h = size.y.to_f32();
if w > 0.0 && h > 0.0 {
self.content.rect(x, y, w, h);
}
@@ -533,8 +533,8 @@ impl<'a> PageExporter<'a> {
fn write_image(&mut self, x: f32, y: f32, id: ImageId, size: Size) {
self.image_map.insert(id);
let name = format_eco!("Im{}", self.image_map.map(id));
- let w = size.w.to_f32();
- let h = size.h.to_f32();
+ let w = size.x.to_f32();
+ let h = size.y.to_f32();
self.content.save_state();
self.content.transform([w, 0.0, 0.0, -h, x, y + h]);
self.content.x_object(Name(name.as_bytes()));
@@ -550,8 +550,8 @@ impl<'a> PageExporter<'a> {
// Compute the bounding box of the transformed link.
for point in [
pos,
- pos + Point::with_x(size.w),
- pos + Point::with_y(size.h),
+ pos + Point::with_x(size.x),
+ pos + Point::with_y(size.y),
pos + size.to_point(),
] {
let t = point.transform(self.state.transform);
diff --git a/src/frame.rs b/src/frame.rs
index ec10fe96..4eea0578 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -25,7 +25,7 @@ impl Frame {
#[track_caller]
pub fn new(size: Size) -> Self {
assert!(size.is_finite());
- Self { size, baseline: size.h, elements: vec![] }
+ Self { size, baseline: size.y, elements: vec![] }
}
/// Add an element at a position in the background.
@@ -58,13 +58,15 @@ impl Frame {
/// Resize the frame to a new size, distributing new space according to the
/// given alignments.
pub fn resize(&mut self, new: Size, aligns: Spec<Align>) {
- let offset = Point::new(
- aligns.x.resolve(new.w - self.size.w),
- aligns.y.resolve(new.h - self.size.h),
- );
- self.size = new;
- self.baseline += offset.y;
- self.translate(offset);
+ if self.size != new {
+ let offset = Point::new(
+ aligns.x.resolve(new.x - self.size.x),
+ aligns.y.resolve(new.y - self.size.y),
+ );
+ self.size = new;
+ self.baseline += offset.y;
+ self.translate(offset);
+ }
}
/// Move the contents of the frame by an offset.
diff --git a/src/geom/gen.rs b/src/geom/gen.rs
index 5232139b..fc4c56e7 100644
--- a/src/geom/gen.rs
+++ b/src/geom/gen.rs
@@ -53,21 +53,6 @@ impl Gen<Length> {
pub fn to_point(self, main: SpecAxis) -> Point {
self.to_spec(main).to_point()
}
-
- /// Convert to a size.
- pub fn to_size(self, main: SpecAxis) -> Size {
- self.to_spec(main).to_size()
- }
-}
-
-impl<T> Gen<Option<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Gen<T>) -> Gen<T> {
- Gen {
- cross: self.cross.unwrap_or(other.cross),
- main: self.main.unwrap_or(other.main),
- }
- }
}
impl<T> Get<GenAxis> for Gen<T> {
diff --git a/src/geom/linear.rs b/src/geom/linear.rs
index 4a0def2f..77923c43 100644
--- a/src/geom/linear.rs
+++ b/src/geom/linear.rs
@@ -42,7 +42,7 @@ impl Linear {
}
/// Whether there is a linear component.
- pub fn is_relative(&self) -> bool {
+ pub fn is_relative(self) -> bool {
!self.rel.is_zero()
}
}
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index b0c75d25..2f722f16 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -16,7 +16,6 @@ mod point;
mod relative;
mod scalar;
mod sides;
-mod size;
mod spec;
mod transform;
@@ -34,7 +33,6 @@ pub use point::*;
pub use relative::*;
pub use scalar::*;
pub use sides::*;
-pub use size::*;
pub use spec::*;
pub use transform::*;
diff --git a/src/geom/path.rs b/src/geom/path.rs
index 20519cb3..87e20dd1 100644
--- a/src/geom/path.rs
+++ b/src/geom/path.rs
@@ -26,9 +26,9 @@ impl Path {
let point = Point::new;
let mut path = Self::new();
path.move_to(point(z, z));
- path.line_to(point(size.w, z));
- path.line_to(point(size.w, size.h));
- path.line_to(point(z, size.h));
+ path.line_to(point(size.x, z));
+ path.line_to(point(size.x, size.y));
+ path.line_to(point(z, size.y));
path.close_path();
path
}
@@ -37,8 +37,8 @@ impl Path {
pub fn ellipse(size: Size) -> Self {
// https://stackoverflow.com/a/2007782
let z = Length::zero();
- let rx = size.w / 2.0;
- let ry = size.h / 2.0;
+ let rx = size.x / 2.0;
+ let ry = size.y / 2.0;
let m = 0.551784;
let mx = m * rx;
let my = m * ry;
diff --git a/src/geom/scalar.rs b/src/geom/scalar.rs
index 6536e23a..066246a9 100644
--- a/src/geom/scalar.rs
+++ b/src/geom/scalar.rs
@@ -2,7 +2,7 @@ use super::*;
/// A 64-bit float that implements `Eq`, `Ord` and `Hash`.
///
-/// Panics if its `NaN` during any of those operations.
+/// Panics if it's `NaN` during any of those operations.
#[derive(Default, Copy, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Scalar(pub f64);
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index 83407ad6..c0929fc7 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -33,11 +33,13 @@ impl<T> Sides<T> {
}
}
-impl Sides<Length> {
- /// A size with `left` and `right` summed into `width`, and `top` and
- /// `bottom` summed into `height`.
- pub fn size(self) -> Size {
- Size::new(self.left + self.right, self.top + self.bottom)
+impl<T> Sides<T>
+where
+ T: Add,
+{
+ /// Sums up `left` and `right` into `x`, and `top` and `bottom` into `y`.
+ pub fn sum_by_axis(self) -> Spec<T::Output> {
+ Spec::new(self.left + self.right, self.top + self.bottom)
}
}
@@ -45,10 +47,10 @@ impl Sides<Linear> {
/// Resolve the linear sides relative to the given `size`.
pub fn resolve(self, size: Size) -> Sides<Length> {
Sides {
- left: self.left.resolve(size.w),
- top: self.top.resolve(size.h),
- right: self.right.resolve(size.w),
- bottom: self.bottom.resolve(size.h),
+ left: self.left.resolve(size.x),
+ top: self.top.resolve(size.y),
+ right: self.right.resolve(size.x),
+ bottom: self.bottom.resolve(size.y),
}
}
}
diff --git a/src/geom/size.rs b/src/geom/size.rs
deleted file mode 100644
index 1c049425..00000000
--- a/src/geom/size.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use super::*;
-
-/// A size in 2D.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
-pub struct Size {
- /// The width.
- pub w: Length,
- /// The height.
- pub h: Length,
-}
-
-impl Size {
- /// The zero size.
- pub const fn zero() -> Self {
- Self { w: Length::zero(), h: Length::zero() }
- }
-
- /// Create a new size from width and height.
- pub const fn new(w: Length, h: Length) -> Self {
- Self { w, h }
- }
-
- /// Create an instance with two equal components.
- pub const fn splat(v: Length) -> Self {
- Self { w: v, h: v }
- }
-
- /// Whether the other size fits into this one (smaller width and height).
- pub fn fits(self, other: Self) -> bool {
- self.w.fits(other.w) && self.h.fits(other.h)
- }
-
- /// Whether both components are finite.
- pub fn is_finite(self) -> bool {
- self.w.is_finite() && self.h.is_finite()
- }
-
- /// Whether any of the two components is infinite.
- pub fn is_infinite(self) -> bool {
- self.w.is_infinite() || self.h.is_infinite()
- }
-
- /// Convert to a point.
- pub const fn to_point(self) -> Point {
- Point::new(self.w, self.h)
- }
-
- /// Convert to a Spec.
- pub const fn to_spec(self) -> Spec<Length> {
- Spec::new(self.w, self.h)
- }
-
- /// Convert to the generic representation.
- pub const fn to_gen(self, main: SpecAxis) -> Gen<Length> {
- match main {
- SpecAxis::Horizontal => Gen::new(self.h, self.w),
- SpecAxis::Vertical => Gen::new(self.w, self.h),
- }
- }
-}
-
-impl Get<SpecAxis> for Size {
- type Component = Length;
-
- fn get(self, axis: SpecAxis) -> Length {
- match axis {
- SpecAxis::Horizontal => self.w,
- SpecAxis::Vertical => self.h,
- }
- }
-
- fn get_mut(&mut self, axis: SpecAxis) -> &mut Length {
- match axis {
- SpecAxis::Horizontal => &mut self.w,
- SpecAxis::Vertical => &mut self.h,
- }
- }
-}
-
-impl Debug for Size {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Size({:?}, {:?})", self.w, self.h)
- }
-}
-
-impl Neg for Size {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self { w: -self.w, h: -self.h }
- }
-}
-
-impl Add for Size {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self { w: self.w + other.w, h: self.h + other.h }
- }
-}
-
-sub_impl!(Size - Size -> Size);
-
-impl Mul<f64> for Size {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self { w: self.w * other, h: self.h * other }
- }
-}
-
-impl Mul<Size> for f64 {
- type Output = Size;
-
- fn mul(self, other: Size) -> Size {
- other * self
- }
-}
-
-impl Div<f64> for Size {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self { w: self.w / other, h: self.h / other }
- }
-}
-
-assign_impl!(Size -= Size);
-assign_impl!(Size += Size);
-assign_impl!(Size *= f64);
-assign_impl!(Size /= f64);
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index 608643d8..640d6231 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -1,7 +1,10 @@
+use std::any::Any;
+use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
+
use super::*;
/// A container with a horizontal and vertical component.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Spec<T> {
/// The horizontal component.
pub x: T,
@@ -31,9 +34,18 @@ impl<T> Spec<T> {
Spec { x: f(self.x), y: f(self.y) }
}
+ /// Convert from `&Spec<T>` to `Spec<&T>`.
+ pub fn as_ref(&self) -> Spec<&T> {
+ Spec { x: &self.x, y: &self.y }
+ }
+
+ /// Convert from `&mut Spec<T>` to `Spec<&mut T>`.
+ pub fn as_mut(&mut self) -> Spec<&mut T> {
+ Spec { x: &mut self.x, y: &mut self.y }
+ }
+
/// Zip two instances into an instance over a tuple.
- pub fn zip<U>(self, other: impl Into<Spec<U>>) -> Spec<(T, U)> {
- let other = other.into();
+ pub fn zip<U>(self, other: Spec<U>) -> Spec<(T, U)> {
Spec {
x: (self.x, other.x),
y: (self.y, other.y),
@@ -56,6 +68,14 @@ impl<T> Spec<T> {
f(&self.x) && f(&self.y)
}
+ /// Filter the individual fields with a mask.
+ pub fn filter(self, mask: Spec<bool>) -> Spec<Option<T>> {
+ Spec {
+ x: if mask.x { Some(self.x) } else { None },
+ y: if mask.y { Some(self.y) } else { None },
+ }
+ }
+
/// Convert to the generic representation.
pub fn to_gen(self, main: SpecAxis) -> Gen<T> {
match main {
@@ -65,39 +85,6 @@ impl<T> Spec<T> {
}
}
-impl From<Size> for Spec<Length> {
- fn from(size: Size) -> Self {
- size.to_spec()
- }
-}
-
-impl Spec<Length> {
- /// The zero value.
- pub fn zero() -> Self {
- Self { x: Length::zero(), y: Length::zero() }
- }
-
- /// Convert to a point.
- pub fn to_point(self) -> Point {
- Point::new(self.x, self.y)
- }
-
- /// Convert to a size.
- pub fn to_size(self) -> Size {
- Size::new(self.x, self.y)
- }
-}
-
-impl<T> Spec<Option<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
- Spec {
- x: self.x.unwrap_or(other.x),
- y: self.y.unwrap_or(other.y),
- }
- }
-}
-
impl<T> Get<SpecAxis> for Spec<T> {
type Component = T;
@@ -116,9 +103,20 @@ impl<T> Get<SpecAxis> for Spec<T> {
}
}
-impl<T: Debug> Debug for Spec<T> {
+impl<T> Debug for Spec<T>
+where
+ T: Debug + 'static,
+{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Spec({:?}, {:?})", self.x, self.y)
+ if let Spec { x: Some(x), y: Some(y) } =
+ self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<Align>())
+ {
+ write!(f, "{:?}-{:?}", x, y)
+ } else if (&self.x as &dyn Any).is::<Length>() {
+ write!(f, "Size({:?}, {:?})", self.x, self.y)
+ } else {
+ write!(f, "Spec({:?}, {:?})", self.x, self.y)
+ }
}
}
@@ -159,3 +157,148 @@ impl Debug for SpecAxis {
})
}
}
+
+/// A size in 2D.
+pub type Size = Spec<Length>;
+
+impl Size {
+ /// The zero value.
+ pub fn zero() -> Self {
+ Self { x: Length::zero(), y: Length::zero() }
+ }
+
+ /// Whether the other size fits into this one (smaller width and height).
+ pub fn fits(self, other: Self) -> bool {
+ self.x.fits(other.x) && self.y.fits(other.y)
+ }
+
+ /// Whether both components are finite.
+ pub fn is_finite(self) -> bool {
+ self.x.is_finite() && self.y.is_finite()
+ }
+
+ /// Whether any of the two components is infinite.
+ pub fn is_infinite(self) -> bool {
+ self.x.is_infinite() || self.y.is_infinite()
+ }
+
+ /// Convert to a point.
+ pub fn to_point(self) -> Point {
+ Point::new(self.x, self.y)
+ }
+}
+
+impl Neg for Size {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ Self { x: -self.x, y: -self.y }
+ }
+}
+
+impl Add for Size {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ Self { x: self.x + other.x, y: self.y + other.y }
+ }
+}
+
+sub_impl!(Size - Size -> Size);
+
+impl Mul<f64> for Size {
+ type Output = Self;
+
+ fn mul(self, other: f64) -> Self {
+ Self { x: self.x * other, y: self.y * other }
+ }
+}
+
+impl Mul<Size> for f64 {
+ type Output = Size;
+
+ fn mul(self, other: Size) -> Size {
+ other * self
+ }
+}
+
+impl Div<f64> for Size {
+ type Output = Self;
+
+ fn div(self, other: f64) -> Self {
+ Self { x: self.x / other, y: self.y / other }
+ }
+}
+
+assign_impl!(Size -= Size);
+assign_impl!(Size += Size);
+assign_impl!(Size *= f64);
+assign_impl!(Size /= f64);
+
+impl<T> Spec<Option<T>> {
+ /// Whether the individual fields are some.
+ pub fn map_is_some(&self) -> Spec<bool> {
+ self.as_ref().map(Option::is_some)
+ }
+
+ /// Whether the individual fields are none.
+ pub fn map_is_none(&self) -> Spec<bool> {
+ self.as_ref().map(Option::is_none)
+ }
+
+ /// Unwrap the individual fields.
+ pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
+ Spec {
+ x: self.x.unwrap_or(other.x),
+ y: self.y.unwrap_or(other.y),
+ }
+ }
+}
+
+impl Spec<bool> {
+ /// Select `t.x` if `self.x` is true and `f.x` otherwise and same for `y`.
+ pub fn select<T>(self, t: Spec<T>, f: Spec<T>) -> Spec<T> {
+ Spec {
+ x: if self.x { t.x } else { f.x },
+ y: if self.y { t.y } else { f.y },
+ }
+ }
+}
+
+impl Not for Spec<bool> {
+ type Output = Self;
+
+ fn not(self) -> Self::Output {
+ Self { x: !self.x, y: !self.y }
+ }
+}
+
+impl BitOr for Spec<bool> {
+ type Output = Self;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self { x: self.x | rhs.x, y: self.y | rhs.y }
+ }
+}
+
+impl BitAnd for Spec<bool> {
+ type Output = Self;
+
+ fn bitand(self, rhs: Self) -> Self::Output {
+ Self { x: self.x & rhs.x, y: self.y & rhs.y }
+ }
+}
+
+impl BitOrAssign for Spec<bool> {
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.x |= rhs.x;
+ self.y |= rhs.y;
+ }
+}
+
+impl BitAndAssign for Spec<bool> {
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.x &= rhs.x;
+ self.y &= rhs.y;
+ }
+}
diff --git a/src/layout/constraints.rs b/src/layout/constraints.rs
index b72254d7..0d772cea 100644
--- a/src/layout/constraints.rs
+++ b/src/layout/constraints.rs
@@ -2,7 +2,7 @@ use std::rc::Rc;
use super::Regions;
use crate::frame::Frame;
-use crate::geom::{Length, Linear, Size, Spec};
+use crate::geom::{Length, Size, Spec};
/// Constrain a frame with constraints.
pub trait Constrain {
@@ -65,8 +65,8 @@ impl Constraints {
Self {
min: Spec::default(),
max: Spec::default(),
- exact: regions.current.to_spec().map(Some),
- base: regions.base.to_spec().map(Some),
+ exact: regions.current.map(Some),
+ base: regions.base.map(Some),
expand: regions.expand,
}
}
@@ -80,18 +80,6 @@ impl Constraints {
&& verify(self.exact, current, Length::approx_eq)
&& verify(self.base, base, Length::approx_eq)
}
-
- /// Set the appropriate base constraints for linear width and height sizing.
- pub fn set_base_if_linear(&mut self, base: Size, sizing: Spec<Option<Linear>>) {
- // The full sizes need to be equal if there is a relative component in
- // the sizes.
- if sizing.x.map_or(false, |l| l.is_relative()) {
- self.base.x = Some(base.w);
- }
- if sizing.y.map_or(false, |l| l.is_relative()) {
- self.base.y = Some(base.h);
- }
- }
}
/// Verify a single constraint.
diff --git a/src/layout/regions.rs b/src/layout/regions.rs
index 0ef92543..66e6da17 100644
--- a/src/layout/regions.rs
+++ b/src/layout/regions.rs
@@ -51,7 +51,7 @@ impl Regions {
/// Whether the current region is full and a region break is called for.
pub fn is_full(&self) -> bool {
- Length::zero().fits(self.current.h) && !self.in_last()
+ Length::zero().fits(self.current.y) && !self.in_last()
}
/// Whether `current` is the last usable region.
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));
diff --git a/src/util/mod.rs b/src/util/mod.rs
index 8b74aed6..6fc1fb59 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -27,9 +27,6 @@ impl BoolExt for bool {
/// Additional methods for options.
pub trait OptionExt<T> {
- /// Replace `self` with `other` if `self` is `Some`.
- fn and_set(&mut self, other: Option<T>);
-
/// Sets `other` as the value if `self` is `None` or if it contains a value
/// larger than `other`.
fn set_min(&mut self, other: T)
@@ -44,12 +41,6 @@ pub trait OptionExt<T> {
}
impl<T> OptionExt<T> for Option<T> {
- fn and_set(&mut self, other: Option<T>) {
- if self.is_some() {
- *self = other;
- }
- }
-
fn set_min(&mut self, other: T)
where
T: Ord,