diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 80be2e824..4b5feb0d6 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -18,7 +18,6 @@ use std::rc::Rc; use crate::cache::Cache; use crate::color::Color; use crate::diag::{Diag, DiagSet, Pass}; -use crate::geom::TrackSizing; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::loading::{FileHash, Loader}; use crate::parse::parse; @@ -245,6 +244,7 @@ impl Eval for Expr { fn eval(&self, ctx: &mut EvalContext) -> Self::Output { match *self { Self::None(_) => Value::None, + Self::Auto(_) => Value::Auto, Self::Bool(_, v) => Value::Bool(v), Self::Int(_, v) => Value::Int(v), Self::Float(_, v) => Value::Float(v), @@ -252,7 +252,6 @@ impl Eval for Expr { Self::Angle(_, v, unit) => Value::Angle(Angle::with_unit(v, unit)), Self::Percent(_, v) => Value::Relative(Relative::new(v / 100.0)), Self::Fractional(_, v) => Value::Fractional(Fractional::new(v)), - Self::Auto(_) => Value::TrackSizing(TrackSizing::Auto), Self::Color(_, v) => Value::Color(Color::Rgba(v)), Self::Str(_, ref v) => Value::Str(v.clone()), Self::Ident(ref v) => match ctx.scopes.get(&v) { diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 15c09e03a..b6bd5402c 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -11,8 +11,8 @@ pub fn pos(value: Value) -> Value { Length(v) => Length(v), Angle(v) => Angle(v), Relative(v) => Relative(v), - Fractional(v) => Fractional(v), Linear(v) => Linear(v), + Fractional(v) => Fractional(v), _ => Error, } } @@ -25,8 +25,8 @@ pub fn neg(value: Value) -> Value { Length(v) => Length(-v), Angle(v) => Angle(-v), Relative(v) => Relative(-v), - Fractional(v) => Fractional(-v), Linear(v) => Linear(-v), + Fractional(v) => Fractional(-v), _ => Error, } } @@ -34,29 +34,31 @@ pub fn neg(value: Value) -> Value { /// Compute the sum of two values. pub fn add(lhs: Value, rhs: Value) -> Value { match (lhs, rhs) { - // Math. (Int(a), Int(b)) => Int(a + b), (Int(a), Float(b)) => Float(a as f64 + b), (Float(a), Int(b)) => Float(a + b as f64), (Float(a), Float(b)) => Float(a + b), + (Angle(a), Angle(b)) => Angle(a + b), + (Length(a), Length(b)) => Length(a + b), (Length(a), Relative(b)) => Linear(a + b), (Length(a), Linear(b)) => Linear(a + b), + (Relative(a), Length(b)) => Linear(a + b), (Relative(a), Relative(b)) => Relative(a + b), (Relative(a), Linear(b)) => Linear(a + b), - (Fractional(a), Fractional(b)) => Fractional(a + b), + (Linear(a), Length(b)) => Linear(a + b), (Linear(a), Relative(b)) => Linear(a + b), (Linear(a), Linear(b)) => Linear(a + b), - // Collections. + (Fractional(a), Fractional(b)) => Fractional(a + b), + (Str(a), Str(b)) => Str(a + &b), (Array(a), Array(b)) => Array(concat(a, b)), (Dict(a), Dict(b)) => Dict(concat(a, b)), - // Templates. (Template(a), Template(b)) => Template(concat(a, b)), (Template(a), None) => Template(a), (None, Template(b)) => Template(b), @@ -80,17 +82,23 @@ pub fn sub(lhs: Value, rhs: Value) -> Value { (Int(a), Float(b)) => Float(a as f64 - b), (Float(a), Int(b)) => Float(a - b as f64), (Float(a), Float(b)) => Float(a - b), + (Angle(a), Angle(b)) => Angle(a - b), + (Length(a), Length(b)) => Length(a - b), (Length(a), Relative(b)) => Linear(a - b), (Length(a), Linear(b)) => Linear(a - b), + (Relative(a), Length(b)) => Linear(a - b), (Relative(a), Relative(b)) => Relative(a - b), (Relative(a), Linear(b)) => Linear(a - b), - (Fractional(a), Fractional(b)) => Fractional(a - b), + (Linear(a), Length(b)) => Linear(a - b), (Linear(a), Relative(b)) => Linear(a - b), (Linear(a), Linear(b)) => Linear(a - b), + + (Fractional(a), Fractional(b)) => Fractional(a - b), + _ => Error, } } @@ -102,27 +110,37 @@ pub fn mul(lhs: Value, rhs: Value) -> Value { (Int(a), Float(b)) => Float(a as f64 * b), (Float(a), Int(b)) => Float(a * b as f64), (Float(a), Float(b)) => Float(a * b), + (Length(a), Int(b)) => Length(a * b as f64), (Length(a), Float(b)) => Length(a * b), (Int(a), Length(b)) => Length(a as f64 * b), (Float(a), Length(b)) => Length(a * b), + (Angle(a), Int(b)) => Angle(a * b as f64), (Angle(a), Float(b)) => Angle(a * b), (Int(a), Angle(b)) => Angle(a as f64 * b), (Float(a), Angle(b)) => Angle(a * b), + (Relative(a), Int(b)) => Relative(a * b as f64), (Relative(a), Float(b)) => Relative(a * b), - (Fractional(a), Fractional(b)) => Fractional(a * b.get()), - (Fractional(a), Int(b)) => Fractional(a * b as f64), - (Fractional(a), Float(b)) => Fractional(a * b), - (Int(a), Relative(b)) => Relative(a as f64 * b), - (Int(a), Fractional(b)) => Fractional(a as f64 * b), (Float(a), Relative(b)) => Relative(a * b), - (Float(a), Fractional(b)) => Fractional(a * b), + (Int(a), Relative(b)) => Relative(a as f64 * b), + (Linear(a), Int(b)) => Linear(a * b as f64), (Linear(a), Float(b)) => Linear(a * b), (Int(a), Linear(b)) => Linear(a as f64 * b), (Float(a), Linear(b)) => Linear(a * b), + + (Float(a), Fractional(b)) => Fractional(a * b), + (Fractional(a), Int(b)) => Fractional(a * b as f64), + (Fractional(a), Float(b)) => Fractional(a * b), + (Int(a), Fractional(b)) => Fractional(a as f64 * b), + + (Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)), + (Int(a), Str(b)) => Str(b.repeat(a.max(0) as usize)), + (Array(a), Int(b)) => Array(repeat(a, b.max(0) as usize)), + (Int(a), Array(b)) => Array(repeat(b, a.max(0) as usize)), + _ => Error, } } @@ -134,20 +152,26 @@ pub fn div(lhs: Value, rhs: Value) -> Value { (Int(a), Float(b)) => Float(a as f64 / b), (Float(a), Int(b)) => Float(a / b as f64), (Float(a), Float(b)) => Float(a / b), + (Length(a), Int(b)) => Length(a / b as f64), (Length(a), Float(b)) => Length(a / b), (Length(a), Length(b)) => Float(a / b), + (Angle(a), Int(b)) => Angle(a / b as f64), (Angle(a), Float(b)) => Angle(a / b), (Angle(a), Angle(b)) => Float(a / b), + (Relative(a), Int(b)) => Relative(a / b as f64), (Relative(a), Float(b)) => Relative(a / b), (Relative(a), Relative(b)) => Float(a / b), - (Fractional(a), Fractional(b)) => Float(a.get() / b.get()), + (Fractional(a), Int(b)) => Fractional(a / b as f64), (Fractional(a), Float(b)) => Fractional(a / b), + (Fractional(a), Fractional(b)) => Float(a / b), + (Linear(a), Int(b)) => Linear(a / b as f64), (Linear(a), Float(b)) => Linear(a / b), + _ => Error, } } @@ -209,3 +233,9 @@ where a.extend(b); a } + +/// Repeat a vector `n` times. +fn repeat(vec: Vec, n: usize) -> Vec { + let len = n * vec.len(); + vec.into_iter().cycle().take(len).collect() +} diff --git a/src/eval/value.rs b/src/eval/value.rs index fa7993ede..51e451901 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -8,7 +8,7 @@ use std::rc::Rc; use super::EvalContext; use crate::color::{Color, RgbaColor}; use crate::exec::ExecContext; -use crate::geom::{Angle, Fractional, Length, Linear, Relative, TrackSizing}; +use crate::geom::{Angle, Fractional, Length, Linear, Relative}; use crate::syntax::{Expr, Span, Spanned, Tree}; /// A computational value. @@ -16,6 +16,8 @@ use crate::syntax::{Expr, Span, Spanned, Tree}; pub enum Value { /// The value that indicates the absence of a meaningful value. None, + /// A value that indicates some smart default behaviour. + Auto, /// A boolean: `true, false`. Bool(bool), /// An integer: `120`. @@ -28,12 +30,10 @@ pub enum Value { Angle(Angle), /// A relative value: `50%`. Relative(Relative), - /// A fractional value: `1fr`. - Fractional(Fractional), /// A combination of an absolute length and a relative value: `20% + 5cm`. Linear(Linear), - /// One of the units that can appear in a grid definition. - TrackSizing(TrackSizing), + /// A fractional value: `1fr`. + Fractional(Fractional), /// A color value: `#f79143ff`. Color(Color), /// A string: `"string"`. @@ -73,15 +73,15 @@ impl Value { pub fn type_name(&self) -> &'static str { match self { Self::None => "none", + Self::Auto => "auto", Self::Bool(_) => bool::TYPE_NAME, Self::Int(_) => i64::TYPE_NAME, Self::Float(_) => f64::TYPE_NAME, Self::Length(_) => Length::TYPE_NAME, Self::Angle(_) => Angle::TYPE_NAME, Self::Relative(_) => Relative::TYPE_NAME, - Self::Fractional(_) => Fractional::TYPE_NAME, Self::Linear(_) => Linear::TYPE_NAME, - Self::TrackSizing(_) => TrackSizing::TYPE_NAME, + Self::Fractional(_) => Fractional::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => String::TYPE_NAME, Self::Array(_) => ArrayValue::TYPE_NAME, @@ -100,14 +100,6 @@ impl Value { (&Self::Float(a), &Self::Int(b)) => a == b as f64, (&Self::Length(a), &Self::Linear(b)) => a == b.abs && b.rel.is_zero(), (&Self::Relative(a), &Self::Linear(b)) => a == b.rel && b.abs.is_zero(), - (&Self::Length(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b, - (&Self::Relative(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b, - (&Self::Linear(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b, - (&Self::Fractional(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b, - (&Self::TrackSizing(a), &Self::Length(b)) => TrackSizing::from(b) == a, - (&Self::TrackSizing(a), &Self::Relative(b)) => TrackSizing::from(b) == a, - (&Self::TrackSizing(a), &Self::Linear(b)) => TrackSizing::from(b) == a, - (&Self::TrackSizing(a), &Self::Fractional(b)) => TrackSizing::from(b) == a, (&Self::Linear(a), &Self::Length(b)) => a.abs == b && a.rel.is_zero(), (&Self::Linear(a), &Self::Relative(b)) => a.rel == b && a.abs.is_zero(), (Self::Array(a), Self::Array(b)) => { @@ -615,21 +607,13 @@ primitive! { primitive! { Length: "length", Value::Length } primitive! { Angle: "angle", Value::Angle } primitive! { Relative: "relative", Value::Relative } -primitive! { Fractional: "fractional", Value::Fractional } primitive! { Linear: "linear", Value::Linear, Value::Length(v) => v.into(), Value::Relative(v) => v.into(), } -primitive! { - TrackSizing: "GridUnit", - Value::TrackSizing, - Value::Length(v) => v.into(), - Value::Relative(v) => v.into(), - Value::Linear(v) => v.into(), - Value::Fractional(v) => v.into(), -} +primitive! { Fractional: "fractional", Value::Fractional } primitive! { Color: "color", Value::Color } primitive! { String: "string", Value::Str } primitive! { ArrayValue: "array", Value::Array } diff --git a/src/geom/gen.rs b/src/geom/gen.rs index c530ff2b1..075b73775 100644 --- a/src/geom/gen.rs +++ b/src/geom/gen.rs @@ -22,6 +22,14 @@ impl Gen { { Self { cross: value.clone(), main: value } } + + /// Convert to the specific representation. + pub fn to_spec(self, main: SpecAxis) -> Spec { + match main { + SpecAxis::Horizontal => Spec::new(self.main, self.cross), + SpecAxis::Vertical => Spec::new(self.cross, self.main), + } + } } impl Gen { @@ -32,6 +40,16 @@ impl Gen { cross: Length::zero(), } } + + /// Convert to a point. + 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 Get for Gen { @@ -52,17 +70,6 @@ impl Get for Gen { } } -impl Switch for Gen { - type Other = Spec; - - fn switch(self, main: SpecAxis) -> Self::Other { - match main { - SpecAxis::Horizontal => Spec::new(self.main, self.cross), - SpecAxis::Vertical => Spec::new(self.cross, self.main), - } - } -} - impl Debug for Gen { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Gen({:?}, {:?})", self.main, self.cross) @@ -88,17 +95,6 @@ impl GenAxis { } } -impl Switch for GenAxis { - type Other = SpecAxis; - - fn switch(self, main: SpecAxis) -> Self::Other { - match self { - Self::Main => main, - Self::Cross => main.other(), - } - } -} - impl Display for GenAxis { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.pad(match self { diff --git a/src/geom/gridu.rs b/src/geom/gridu.rs deleted file mode 100644 index 70fc17e4f..000000000 --- a/src/geom/gridu.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::*; - -/// An enum with the length that a grid cell may have. -#[derive(Copy, Clone, PartialEq, Hash)] -pub enum TrackSizing { - /// A length stated in absolute values and fractions of the parent's size. - Linear(Linear), - /// A length that is the fraction of the remaining free space in the parent. - Fractional(Fractional), - /// The cell will fit its contents. - Auto, -} - -impl TrackSizing { - pub fn is_zero(&self) -> bool { - match self { - Self::Linear(l) => l.is_zero(), - Self::Fractional(f) => f.is_zero(), - Self::Auto => false, - } - } - - pub fn preliminary_length(&self, resolve: Length) -> Length { - match self { - Self::Linear(l) => l.resolve(resolve), - _ => resolve, - } - } -} - -impl Display for TrackSizing { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Linear(x) => ::fmt(x, f), - Self::Fractional(x) => ::fmt(x, f), - Self::Auto => write!(f, "auto"), - } - } -} - -impl Debug for TrackSizing { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Linear(x) => ::fmt(x, f), - Self::Fractional(x) => ::fmt(x, f), - Self::Auto => write!(f, "auto"), - } - } -} - -impl From for TrackSizing { - fn from(abs: Length) -> Self { - Self::Linear(abs.into()) - } -} - -impl From for TrackSizing { - fn from(rel: Relative) -> Self { - Self::Linear(rel.into()) - } -} - -impl From for TrackSizing { - fn from(lin: Linear) -> Self { - Self::Linear(lin) - } -} - -impl From for TrackSizing { - fn from(fr: Fractional) -> Self { - Self::Fractional(fr) - } -} diff --git a/src/geom/mod.rs b/src/geom/mod.rs index fdc3980e7..25344f0e7 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -7,7 +7,6 @@ mod angle; mod dir; mod fr; mod gen; -mod gridu; mod length; mod linear; mod path; @@ -22,7 +21,6 @@ pub use angle::*; pub use dir::*; pub use fr::*; pub use gen::*; -pub use gridu::*; pub use length::*; pub use linear::*; pub use path::*; @@ -48,15 +46,3 @@ pub trait Get { /// Borrow the component for the specified index mutably. fn get_mut(&mut self, index: Index) -> &mut Self::Component; } - -/// Switch between the specific and generic representations of a type. -/// -/// The generic representation deals with main and cross axes while the specific -/// representation deals with horizontal and vertical axes. -pub trait Switch { - /// The type of the other version. - type Other; - - /// The other version of this type based on the current main axis. - fn switch(self, main: SpecAxis) -> Self::Other; -} diff --git a/src/geom/point.rs b/src/geom/point.rs index babbdfef4..8982dcea9 100644 --- a/src/geom/point.rs +++ b/src/geom/point.rs @@ -26,6 +26,14 @@ impl Point { pub fn splat(value: Length) -> Self { Self { x: value, y: value } } + + /// Convert to the generic representation. + pub fn to_gen(self, main: SpecAxis) -> Gen { + match main { + SpecAxis::Horizontal => Gen::new(self.y, self.x), + SpecAxis::Vertical => Gen::new(self.x, self.y), + } + } } impl Get for Point { @@ -46,17 +54,6 @@ impl Get for Point { } } -impl Switch for Point { - type Other = Gen; - - fn switch(self, main: SpecAxis) -> Self::Other { - match main { - SpecAxis::Horizontal => Gen::new(self.y, self.x), - SpecAxis::Vertical => Gen::new(self.x, self.y), - } - } -} - impl Debug for Point { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Point({:?}, {:?})", self.x, self.y) diff --git a/src/geom/size.rs b/src/geom/size.rs index e20c24afb..b859cb800 100644 --- a/src/geom/size.rs +++ b/src/geom/size.rs @@ -49,6 +49,14 @@ impl Size { pub fn to_point(self) -> Point { Point::new(self.width, self.height) } + + /// Convert to the generic representation. + pub fn to_gen(self, main: SpecAxis) -> Gen { + match main { + SpecAxis::Horizontal => Gen::new(self.height, self.width), + SpecAxis::Vertical => Gen::new(self.width, self.height), + } + } } impl Get for Size { @@ -69,17 +77,6 @@ impl Get for Size { } } -impl Switch for Size { - type Other = Gen; - - fn switch(self, main: SpecAxis) -> Self::Other { - match main { - SpecAxis::Horizontal => Gen::new(self.height, self.width), - SpecAxis::Vertical => Gen::new(self.width, self.height), - } - } -} - impl Debug for Size { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Size({:?}, {:?})", self.width, self.height) diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 713f0a16b..e7a2d056d 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -25,6 +25,14 @@ impl Spec { vertical: value, } } + + /// Convert to the generic representation. + pub fn to_gen(self, main: SpecAxis) -> Gen { + match main { + SpecAxis::Horizontal => Gen::new(self.vertical, self.horizontal), + SpecAxis::Vertical => Gen::new(self.horizontal, self.vertical), + } + } } impl Spec { @@ -65,17 +73,6 @@ impl Get for Spec { } } -impl Switch for Spec { - type Other = Gen; - - fn switch(self, main: SpecAxis) -> Self::Other { - match main { - SpecAxis::Horizontal => Gen::new(self.vertical, self.horizontal), - SpecAxis::Vertical => Gen::new(self.horizontal, self.vertical), - } - } -} - impl Debug for Spec { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Spec({:?}, {:?})", self.horizontal, self.vertical) @@ -101,14 +98,6 @@ impl SpecAxis { } } -impl Switch for SpecAxis { - type Other = GenAxis; - - fn switch(self, main: SpecAxis) -> Self::Other { - if self == main { GenAxis::Main } else { GenAxis::Cross } - } -} - impl Display for SpecAxis { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.pad(match self { diff --git a/src/layout/frame.rs b/src/layout/frame.rs index f1dc07e6f..6cecc7a34 100644 --- a/src/layout/frame.rs +++ b/src/layout/frame.rs @@ -35,14 +35,6 @@ impl Frame { self.push(pos + subpos, element); } } - - /// Translate the positions of all elements in the frame by adding the - /// argument to their position. - pub fn translate(&mut self, amount: Point) { - for (pos, _) in &mut self.elements { - *pos += amount; - } - } } /// The building block frames are composed of. diff --git a/src/layout/grid.rs b/src/layout/grid.rs index 9c4c2e0e3..3ba6c16bd 100644 --- a/src/layout/grid.rs +++ b/src/layout/grid.rs @@ -1,216 +1,128 @@ -use std::usize; - use super::*; -use crate::library::GridUnits; -/// A node that stacks its children. +/// A node that arranges its children in a grid. #[derive(Debug, Clone, PartialEq, Hash)] pub struct GridNode { - /// The `main` and `cross` directions of this stack. - /// - /// The children are stacked along the `main` direction. The `cross` - /// direction is required for aligning the children. - pub dir: Dir, - /// The nodes to be stacked. + /// The column (cross) direction of this stack. + pub column_dir: Dir, + /// The nodes to be arranged in a grid. pub children: Vec, - pub tracks: Gen, - pub gutter: Gen, + /// Defines sizing for rows and columns. + pub tracks: Gen, + /// Defines sizing of the gutter between rows and columns. + pub gutter: Gen, } impl Layout for GridNode { fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec { - let layout = GridLayouter::new(self, regions).layout(ctx); - layout + GridLayouter::new(self, regions.clone()).layout(ctx) } } -#[derive(Debug)] -enum GridItem<'a> { +impl From for AnyNode { + fn from(grid: GridNode) -> Self { + Self::new(grid) + } +} + +struct GridLayouter<'a> { + cross: SpecAxis, + main: SpecAxis, + cols: Vec, + rows: Vec, + cells: Vec>, + regions: Regions, + rrows: Vec<(usize, Option)>, + rcols: Vec, + finished: Vec, +} + +enum Cell<'a> { Node(&'a AnyNode), Gutter, } -#[derive(Debug)] -struct GridLayouter<'a> { - items: Vec>, - cols: Vec, - rows: Vec, - region: Regions, - dir: Dir, - rrows: Vec<(usize, Option)>, - rcols: Vec, - frames: Vec, -} - impl<'a> GridLayouter<'a> { - fn new( - grid: &'a GridNode, - regions: &Regions, - ) -> Self { - let mut items = vec![]; + fn new(grid: &'a GridNode, regions: Regions) -> Self { let mut col_sizes = vec![]; let mut row_sizes = vec![]; - let cols = grid.tracks.cross.0.len(); - // Create at least as many rows as specified and a row to fit every item. - let rows = if cols > 0 { - let res = grid - .tracks - .main - .0 - .len() - .max(grid.children.len() / cols + (grid.children.len() % cols).clamp(0, 1)); - res - } else { - 0 + let mut cells = vec![]; + + // A grid always needs to have at least one column. + let cols = grid.tracks.cross.0.len().max(1); + + // Create at least as many rows as specified and also at least as many + // as necessary to place each item. + let rows = { + let len = grid.children.len(); + let specified = grid.tracks.main.0.len(); + let necessary = len / cols + (len % cols).clamp(0, 1); + specified.max(necessary) }; - for (i, col_size) in grid.tracks.cross.0.iter().enumerate() { - let last = i == cols - 1; - col_sizes.push(*col_size); - - if !last { - let gutter = grid.gutter.cross.get(i); - col_sizes.push(gutter); + // Collect the track sizing for all columns, including gutter columns. + for i in 0 .. cols { + col_sizes.push(grid.tracks.cross.get(i)); + if i < cols - 1 { + col_sizes.push(grid.gutter.cross.get(i)); } } - for (i, row_size) in (0 .. rows).map(|i| (i, grid.tracks.main.get(i))) { - let last = i == rows - 1; - row_sizes.push(row_size); - - if !last { - let gutter = grid.gutter.main.get(i); - row_sizes.push(gutter); + // Collect the track sizing for all rows, including gutter rows. + for i in 0 .. rows { + row_sizes.push(grid.tracks.main.get(i)); + if i < rows - 1 { + row_sizes.push(grid.gutter.main.get(i)); } } + // Build up the matrix of cells, including gutter cells. for (i, item) in grid.children.iter().enumerate() { - if cols == 0 { - break; - } + cells.push(Cell::Node(item)); let row = i / cols; let col = i % cols; - items.push(GridItem::Node(item)); - - if col != cols - 1 { - // Push gutter - items.push(GridItem::Gutter); - } else if row != rows - 1 { - // Push gutter row. + if col < cols - 1 { + // Push gutter after each child. + cells.push(Cell::Gutter); + } else if row < rows - 1 { + // Except for the last child of each row. + // There we push a gutter row. for _ in 0 .. col_sizes.len() { - items.push(GridItem::Gutter); + cells.push(Cell::Gutter); } } } - // Fill the thing up - while items.len() < col_sizes.len() * row_sizes.len() { - items.push(GridItem::Gutter) + // Fill the thing up. + while cells.len() < col_sizes.len() * row_sizes.len() { + cells.push(Cell::Gutter) } - GridLayouter { + Self { + cross: grid.column_dir.axis(), + main: grid.column_dir.axis().other(), cols: col_sizes, rows: row_sizes, - region: regions.clone(), - dir: grid.dir, - items, + cells, + regions, rrows: vec![], rcols: vec![], - frames: vec![], + finished: vec![], } } - fn get(&self, x: usize, y: usize) -> &GridItem<'_> { - assert!(x < self.cols.len()); - assert!(y < self.rows.len()); - let row_cmp = y * self.cols.len(); - - self.items.get(row_cmp + x).unwrap() - } - - fn main(&self) -> SpecAxis { - self.dir.axis().other() - } - - fn cross(&self) -> SpecAxis { - self.dir.axis() - } - - fn finish_region(&mut self, ctx: &mut LayoutContext, total_frs: f64) { - let mut pos = Gen::splat(Length::zero()); - let pos2point = |mut pos: Gen| { - if !self.dir.is_positive() { - pos.cross = -pos.cross; - } - pos.switch(self.main()).to_point() - }; - let mut frame = Frame::new(Size::zero(), Length::zero()); - let mut total_cross = Length::zero(); - let mut total_main = Length::zero(); - - for (x, &w) in self.rcols.iter().enumerate() { - let total: Length = self.rrows.iter().filter_map(|(_, x)| *x).sum(); - let available = self.region.current.get(self.main()) - total; - total_cross += w; - - for (y, h) in self.rrows.iter() { - let element = self.get(x, *y); - let h = if let Some(len) = h { - *len - } else { - if let TrackSizing::Fractional(f) = self.rows[*y] { - if total_frs > 0.0 { - let res = available * (f.get() / total_frs); - if res.is_finite() { - res - } else { - Length::zero() - } - } else { - Length::zero() - } - } else { - unreachable!() - } - }; - if x == 0 { - total_main += h; - } - - if let GridItem::Node(n) = element { - let item = n.layout(ctx, &Regions::one(Gen::new(w, h).switch(self.main()).to_size(), Spec::splat(false))).remove(0); - frame.push_frame(pos2point(pos), item); - } - - pos.main += h; - } - pos.main = Length::zero(); - pos.cross += self.dir.factor() as f64 * w; - } - - if !self.dir.is_positive() { - frame.translate(Gen::new(total_cross, Length::zero()).switch(self.main()).to_point()); - } - - frame.size = Gen::new(total_cross, total_main).switch(self.main()).to_size(); - frame.baseline = frame.size.height; - - self.frames.push(frame); - - self.rrows.clear(); - self.region.next(); - } - fn layout(mut self, ctx: &mut LayoutContext) -> Vec { // Shrink area by linear sizing. - let mut available = self.region.current.get(self.cross()); + let mut available = self.regions.current.get(self.cross); available -= self .cols .iter() .filter_map(|x| match x { - TrackSizing::Linear(l) => Some(l.resolve(self.region.base.get(self.cross()))), + TrackSizing::Linear(l) => { + Some(l.resolve(self.regions.base.get(self.cross))) + } _ => None, }) .sum(); @@ -232,34 +144,31 @@ impl<'a> GridLayouter<'a> { let mut col_width = vec![]; - // For each of the auto columns, lay out all elements with `preliminary_length` - // rows and build max. + // For each of the auto columns, lay out all elements with + // `preliminary_length` rows and build max. for x in auto_columns { let mut max = Length::zero(); - for (y, row_height) in - self.rows.iter().enumerate().map(|(y, s)| { - (y, s.preliminary_length(self.region.base.get(self.main()))) - }) - { - let item = self.get(x, y); - let size = - Gen::new(self.region.current.get(self.cross()), row_height).switch(self.main()).to_size(); + + for (y, row) in self.rows.iter().enumerate() { + let mut size = self.regions.current; + if let TrackSizing::Linear(l) = row { + *size.get_mut(self.main) = + l.resolve(self.regions.base.get(self.main)); + } + let region = Regions::one(size, Spec::splat(false)); - match item { - GridItem::Node(n) => { - max = max.max( - n.layout(ctx, ®ion).first().unwrap().size.get(self.cross()), - ) - } - GridItem::Gutter => {} + if let Cell::Node(node) = self.get(x, y) { + let frame = node.layout(ctx, ®ion).remove(0); + max = max.max(frame.size.get(self.cross)) } } col_width.push((x, max)); } - // If accumulated auto column size exceeds available size, redistribute space - // proportionally amongst elements that exceed their size allocation. + // If accumulated auto column size exceeds available size, redistribute + // space proportionally amongst elements that exceed their size + // allocation. let mut total: Length = col_width.iter().map(|(_, x)| *x).sum(); if total > available { let alloc = available / col_width.len() as f64; @@ -288,23 +197,25 @@ impl<'a> GridLayouter<'a> { } // Build rcols - for (x, len) in col_width.into_iter().map(|(x, s)| (x, Some(s))).chain(std::iter::once((self.cols.len(), None))) { + for (x, len) in col_width + .into_iter() + .map(|(x, s)| (x, Some(s))) + .chain(std::iter::once((self.cols.len(), None))) + { for i in self.rcols.len() .. x { let len = match self.cols[i] { - TrackSizing::Linear(l) => l.resolve(self.region.base.get(self.cross())), + TrackSizing::Linear(l) => { + l.resolve(self.regions.base.get(self.cross)) + } TrackSizing::Fractional(f) => { if col_frac == 0.0 { Length::zero() } else { let res: Length = (available - total) * (f.get() / col_frac); - if res.is_finite() { - res - } else { - Length::zero() - } + if res.is_finite() { res } else { Length::zero() } } } - TrackSizing::Auto => unreachable!(), + TrackSizing::Auto => unreachable!("x is an auto track"), }; self.rcols.push(len); @@ -317,27 +228,23 @@ impl<'a> GridLayouter<'a> { // Determine non-`fr` row heights let mut total_frs = 0.0; - let mut current = self.region.current.get(self.main()); + let mut current = self.regions.current.get(self.main); - for y in 0..self.rows.len() { - let height = &self.rows[y]; - let resolved = match height { - TrackSizing::Linear(l) => Some(l.resolve(self.region.base.get(self.main()))), + for y in 0 .. self.rows.len() { + let resolved = match self.rows[y] { + TrackSizing::Linear(l) => { + Some(l.resolve(self.regions.base.get(self.main))) + } TrackSizing::Auto => { let mut max = Length::zero(); for (x, len) in self.rcols.iter().enumerate() { - let node = self.get(x, y); - if let GridItem::Node(node) = node { - let frames = node.layout( - ctx, - &Regions::one( - Gen::new(*len, current) - .switch(self.main()) - .to_size(), - Spec::splat(false), - ), + if let Cell::Node(node) = self.get(x, y) { + let regions = Regions::one( + Gen::new(*len, current).to_size(self.main), + Spec::splat(false), ); - max = max.max(frames.first().unwrap().size.get(self.main())); + let frame = node.layout(ctx, ®ions).remove(0); + max = max.max(frame.size.get(self.main)); } } Some(max) @@ -345,13 +252,13 @@ impl<'a> GridLayouter<'a> { TrackSizing::Fractional(f) => { total_frs += f.get(); None - }, + } }; if let Some(resolved) = resolved { - while !current.fits(resolved) && !self.region.in_full_last() { + while !current.fits(resolved) && !self.regions.in_full_last() { self.finish_region(ctx, total_frs); - current = self.region.current.get(self.main()); + current = self.regions.current.get(self.main); total_frs = 0.0; } current -= resolved; @@ -361,12 +268,91 @@ impl<'a> GridLayouter<'a> { } self.finish_region(ctx, total_frs); - self.frames + self.finished + } + + fn finish_region(&mut self, ctx: &mut LayoutContext, total_frs: f64) { + let mut pos = Gen::splat(Length::zero()); + let mut frame = Frame::new(Size::zero(), Length::zero()); + let mut total_cross = Length::zero(); + let mut total_main = Length::zero(); + + for (x, &w) in self.rcols.iter().enumerate() { + let total: Length = self.rrows.iter().filter_map(|(_, x)| *x).sum(); + let available = self.regions.current.get(self.main) - total; + total_cross += w; + + for (y, h) in self.rrows.iter() { + let element = self.get(x, *y); + let h = if let Some(len) = h { + *len + } else if let TrackSizing::Fractional(f) = self.rows[*y] { + if total_frs > 0.0 { + let res = available * (f.get() / total_frs); + if res.is_finite() { res } else { Length::zero() } + } else { + Length::zero() + } + } else { + unreachable!("non-fractional tracks are already resolved"); + }; + + if x == 0 { + total_main += h; + } + + if let Cell::Node(n) = element { + let regions = Regions::one( + Gen::new(w, h).to_size(self.main), + Spec::splat(false), + ); + let item = n.layout(ctx, ®ions).remove(0); + frame.push_frame(pos.to_point(self.main), item); + } + + pos.main += h; + } + pos.main = Length::zero(); + pos.cross += w; + } + + frame.size = Gen::new(total_cross, total_main).to_size(self.main); + frame.baseline = frame.size.height; + + self.rrows.clear(); + self.regions.next(); + self.finished.push(frame); + } + + fn get(&self, x: usize, y: usize) -> &Cell<'a> { + assert!(x < self.cols.len()); + assert!(y < self.rows.len()); + self.cells.get(y * self.cols.len() + x).unwrap() } } -impl From for AnyNode { - fn from(grid: GridNode) -> Self { - Self::new(grid) +/// A list of track sizing definitions. +#[derive(Default, Debug, Clone, PartialEq, Hash)] +pub struct Tracks(pub Vec); + +impl Tracks { + /// Get the sizing for the track at the given `idx`. + fn get(&self, idx: usize) -> TrackSizing { + self.0 + .get(idx) + .or(self.0.last()) + .copied() + .unwrap_or(TrackSizing::Auto) } } + +/// Defines how to size a grid cell along an axis. +#[derive(Debug, Copy, Clone, PartialEq, Hash)] +pub enum TrackSizing { + /// Fit the cell to its contents. + Auto, + /// A length stated in absolute values and fractions of the parent's size. + Linear(Linear), + /// A length that is the fraction of the remaining free space in the parent. + Fractional(Fractional), +} diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 410f53c66..e4c0708d5 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -29,24 +29,7 @@ pub enum StackChild { impl Layout for StackNode { fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec { - let mut layouter = StackLayouter::new(self.dirs, self.aspect, regions.clone()); - for child in &self.children { - match *child { - StackChild::Spacing(amount) => layouter.push_spacing(amount), - StackChild::Any(ref node, aligns) => { - let mut frames = node.layout(ctx, &layouter.regions).into_iter(); - if let Some(frame) = frames.next() { - layouter.push_frame(frame, aligns); - } - - for frame in frames { - layouter.finish_region(); - layouter.push_frame(frame, aligns); - } - } - } - } - layouter.finish() + StackLayouter::new(self, regions.clone()).layout(ctx) } } @@ -56,107 +39,154 @@ impl From for AnyNode { } } -struct StackLayouter { - dirs: Gen, - aspect: Option, +struct StackLayouter<'a> { + /// The directions of the stack. + stack: &'a StackNode, + /// The axis of the main direction. main: SpecAxis, + /// The region to layout into. regions: Regions, - finished: Vec, - frames: Vec<(Length, Frame, Gen)>, + /// Offset, alignment and frame for all children that fit into the current + /// region. The exact positions are not known yet. + frames: Vec<(Length, Gen, Frame)>, + /// The full size of `regions.current` that was available before we started + /// subtracting. full: Size, - size: Gen, + /// The generic size used by the frames for the current region. + used: Gen, + /// The alignment ruler for the current region. ruler: Align, + /// Finished frames for previous regions. + finished: Vec, } -impl StackLayouter { - fn new(dirs: Gen, aspect: Option, mut regions: Regions) -> Self { - if let Some(aspect) = aspect { +impl<'a> StackLayouter<'a> { + fn new(stack: &'a StackNode, mut regions: Regions) -> Self { + if let Some(aspect) = stack.aspect { regions.apply_aspect_ratio(aspect); } Self { - dirs, - aspect, - main: dirs.main.axis(), + stack, + main: stack.dirs.main.axis(), finished: vec![], frames: vec![], full: regions.current, - size: Gen::zero(), + used: Gen::zero(), ruler: Align::Start, regions, } } + fn layout(mut self, ctx: &mut LayoutContext) -> Vec { + for child in &self.stack.children { + match *child { + StackChild::Spacing(amount) => self.push_spacing(amount), + StackChild::Any(ref node, aligns) => { + let mut frames = node.layout(ctx, &self.regions).into_iter(); + if let Some(frame) = frames.next() { + self.push_frame(frame, aligns); + } + + for frame in frames { + self.finish_region(); + self.push_frame(frame, aligns); + } + } + } + } + + self.finish_region(); + self.finished + } + fn push_spacing(&mut self, amount: Length) { + // Cap the spacing to the remaining available space. let remaining = self.regions.current.get_mut(self.main); let capped = amount.min(*remaining); + + // Grow our size and shrink the available space in the region. + self.used.main += capped; *remaining -= capped; - self.size.main += capped; } fn push_frame(&mut self, frame: Frame, aligns: Gen) { + let size = frame.size; + + // Don't allow `Start` after `End` in the same region. if self.ruler > aligns.main { self.finish_region(); } - while !self.regions.current.fits(frame.size) && !self.regions.in_full_last() { + // Adjust the ruler. + self.ruler = aligns.main; + + // Find a fitting region. + while !self.regions.current.fits(size) && !self.regions.in_full_last() { self.finish_region(); } - let offset = self.size.main; - let size = frame.size.switch(self.main); - self.size.main += size.main; - self.size.cross.set_max(size.cross); - self.ruler = aligns.main; - *self.regions.current.get_mut(self.main) -= size.main; - self.frames.push((offset, frame, aligns)); + // Remember the frame with offset and alignment. + self.frames.push((self.used.main, aligns, frame)); + + // Grow our size and shrink available space in the region. + let gen = size.to_gen(self.main); + self.used.main += gen.main; + self.used.cross.set_max(gen.cross); + *self.regions.current.get_mut(self.main) -= gen.main; } fn finish_region(&mut self) { + let used = self.used.to_size(self.main); let fixed = self.regions.fixed; - let used = self.size.switch(self.main).to_size(); - let mut size = Size::new( + // Determine the stack's size dependening on whether the region is + // fixed. + let mut stack_size = Size::new( if fixed.horizontal { self.full.width } else { used.width }, if fixed.vertical { self.full.height } else { used.height }, ); - if let Some(aspect) = self.aspect { - let width = size + // Make sure the stack's size satisfies the aspect ratio. + if let Some(aspect) = self.stack.aspect { + let width = stack_size .width - .max(aspect.into_inner() * size.height) + .max(aspect.into_inner() * stack_size.height) .min(self.full.width) .min(aspect.into_inner() * self.full.height); - size = Size::new(width, width / aspect.into_inner()); + stack_size = Size::new(width, width / aspect.into_inner()); } - let mut output = Frame::new(size, size.height); + let mut output = Frame::new(stack_size, stack_size.height); let mut first = true; - let used = self.size; - let size = size.switch(self.main); - - for (offset, frame, aligns) in std::mem::take(&mut self.frames) { - let child = frame.size.switch(self.main); + // Place all frames. + for (offset, aligns, frame) in std::mem::take(&mut self.frames) { + let stack_size = stack_size.to_gen(self.main); + let child_size = frame.size.to_gen(self.main); // Align along the cross axis. - let cross = aligns - .cross - .resolve(self.dirs.cross, Length::zero() .. size.cross - child.cross); + let cross = aligns.cross.resolve( + self.stack.dirs.cross, + Length::zero() .. stack_size.cross - child_size.cross, + ); // Align along the main axis. let main = aligns.main.resolve( - self.dirs.main, - if self.dirs.main.is_positive() { - offset .. size.main - used.main + offset + self.stack.dirs.main, + if self.stack.dirs.main.is_positive() { + offset .. stack_size.main - self.used.main + offset } else { - let offset_with_self = offset + child.main; - used.main - offset_with_self .. size.main - offset_with_self + let offset_with_self = offset + child_size.main; + self.used.main - offset_with_self + .. stack_size.main - offset_with_self }, ); - let pos = Gen::new(cross, main).switch(self.main).to_point(); + let pos = Gen::new(cross, main).to_point(self.main); + + // The baseline of the stack is that of the first frame. if first { output.baseline = pos.y + frame.baseline; first = false; @@ -165,18 +195,15 @@ impl StackLayouter { output.push_frame(pos, frame); } - self.size = Gen::zero(); - self.ruler = Align::Start; + // Move on to the next region. self.regions.next(); - if let Some(aspect) = self.aspect { + if let Some(aspect) = self.stack.aspect { self.regions.apply_aspect_ratio(aspect); } + self.full = self.regions.current; + self.used = Gen::zero(); + self.ruler = Align::Start; self.finished.push(output); } - - fn finish(mut self) -> Vec { - self.finish_region(); - self.finished - } } diff --git a/src/library/grid.rs b/src/library/grid.rs index 0aa1fc9dc..cfe46bf46 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -1,76 +1,79 @@ -use crate::layout::GridNode; +use crate::layout::{GridNode, TrackSizing, Tracks}; use super::*; -/// `stack`: Stack children along an axis. +/// `grid`: Arrange children into a grid. /// /// # Positional parameters /// - Children: variadic, of type `template`. /// /// # Named parameters -/// - Column widths: `columns`, of type `Array`. -/// - Row widths: `rows`, of type `Array`. -/// - Gutter: `gutter-vertical` and `gutter-horizontal` for individual track axis or `gutter` for both, of type `Array` respectively. -/// - Stacking direction: `dir`, of type `direction`. +/// - Column sizing: `columns`, of type `tracks`. +/// - Row sizing: `rows`, of type `tracks`. +/// - Column direction: `column-dir`, of type `direction`. +/// - Gutter: `gutter`, shorthand for equal column and row gutter, of type `tracks`. +/// - Gutter for rows: `gutter-rows`, of type `tracks`. +/// - Gutter for columns: `gutter-columns`, of type `tracks`. /// /// # Return value /// A template that arranges its children along the specified grid cells. /// /// # Relevant types and constants +/// - Type `tracks` +/// - coerces from `array` of `track-sizing` +/// - Type `track-sizing` +/// - `auto` +// - coerces from `length` +// - coerces from `relative` +// - coerces from `linear` +// - coerces from `fractional` /// - Type `direction` /// - `ltr` /// - `rtl` /// - `ttb` /// - `btt` pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { - let cols = args.eat_named::(ctx, "columns").unwrap_or_default(); - let rows = args.eat_named::(ctx, "rows").unwrap_or_default(); - - let gutter = args.eat_named(ctx, "gutter"); - let gutter_vertical = args - .eat_named::(ctx, "gutter-col") - .or_else(|| gutter.clone()) - .unwrap_or_default(); - let gutter_horizontal = args - .eat_named::(ctx, "gutter-row") - .or(gutter) - .unwrap_or_default(); - - let dir = args.eat_named(ctx, "dir"); + let columns = args.eat_named::(ctx, "columns").unwrap_or_default(); + let rows = args.eat_named::(ctx, "rows").unwrap_or_default(); + let column_dir = args.eat_named(ctx, "column-dir"); + let gutter = args.eat_named::(ctx, "gutter").unwrap_or_default(); + let gutter_columns = args.eat_named::(ctx, "gutter-columns"); + let gutter_rows = args.eat_named::(ctx, "gutter-rows"); let children = args.eat_all::(ctx); Value::template("grid", move |ctx| { - let children = - children.iter().map(|child| ctx.exec_template_stack(child).into()).collect(); + let children = children + .iter() + .map(|child| ctx.exec_template_stack(child).into()) + .collect(); + ctx.push(GridNode { - dir: dir.unwrap_or_else(|| ctx.state.lang.dir), + column_dir: column_dir.unwrap_or(ctx.state.lang.dir), children, - gutter: Gen::new(gutter_vertical.clone(), gutter_horizontal.clone()), - tracks: Gen::new(cols.clone(), rows.clone()), + tracks: Gen::new(columns.clone(), rows.clone()), + gutter: Gen::new( + gutter_columns.as_ref().unwrap_or(&gutter).clone(), + gutter_rows.as_ref().unwrap_or(&gutter).clone(), + ), }) }) } -/// A list of [`GridUnit`]s. -#[derive(Default, Debug, Clone, PartialEq, Hash)] -pub struct GridUnits(pub Vec); - -impl GridUnits { - pub fn get(&self, index: usize) -> TrackSizing { - if self.0.is_empty() { - TrackSizing::Auto - } else { - *self.0.get(index).unwrap_or(self.0.last().unwrap()) - } - } -} - value! { - GridUnits: "array of fractional values, lengths, and the `auto` keyword", - Value::TrackSizing(value) => Self(vec![value]), + Tracks: "array of `auto`s, linears, and fractionals", + Value::Int(count) => Self(vec![TrackSizing::Auto; count.max(0) as usize]), Value::Array(values) => Self(values .into_iter() .filter_map(|v| v.cast().ok()) .collect() ), } + +value! { + TrackSizing: "`auto`, linear, or fractional", + Value::Auto => TrackSizing::Auto, + Value::Length(v) => TrackSizing::Linear(v.into()), + Value::Relative(v) => TrackSizing::Linear(v.into()), + Value::Linear(v) => TrackSizing::Linear(v), + Value::Fractional(v) => TrackSizing::Fractional(v), +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1f14f36f5..5ab5b2d8a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -314,11 +314,6 @@ fn primary(p: &mut Parser, atomic: bool) -> Option { Some(Token::For) => expr_for(p), Some(Token::Import) => expr_import(p), Some(Token::Include) => expr_include(p), - Some(Token::Auto) => { - let start = p.next_start(); - p.assert(Token::Auto); - Some(Expr::Auto(p.span(start))) - } // Nothing. _ => { @@ -334,6 +329,7 @@ fn literal(p: &mut Parser) -> Option { let expr = match p.peek()? { // Basic values. Token::None => Expr::None(span), + Token::Auto => Expr::Auto(span), Token::Bool(b) => Expr::Bool(span, b), Token::Int(i) => Expr::Int(span, i), Token::Float(f) => Expr::Float(span, f), diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index f82d0ae4b..f3ca25d95 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -350,6 +350,7 @@ impl<'s> Tokens<'s> { "and" => Token::And, "or" => Token::Or, "none" => Token::None, + "auto" => Token::Auto, "true" => Token::Bool(true), "false" => Token::Bool(false), id => keyword(id).unwrap_or(Token::Ident(id)), @@ -474,7 +475,6 @@ fn keyword(id: &str) -> Option> { "for" => Token::For, "in" => Token::In, "while" => Token::While, - "auto" => Token::Auto, "break" => Token::Break, "continue" => Token::Continue, "return" => Token::Return, @@ -759,12 +759,6 @@ mod tests { ("for", For), ("in", In), ("import", Import), - ("while", While), - ("break", Break), - ("continue", Continue), - ("using", Using), - ("auto", Auto), - ("return", Return), ]; for &(s, t) in &list { diff --git a/src/pretty.rs b/src/pretty.rs index 82e81ce64..1281e27b9 100644 --- a/src/pretty.rs +++ b/src/pretty.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Arguments, Write}; use crate::color::{Color, RgbaColor}; use crate::eval::*; -use crate::geom::{Angle, Fractional, Length, Linear, Relative, TrackSizing}; +use crate::geom::{Angle, Fractional, Length, Linear, Relative}; use crate::syntax::*; /// Pretty print an item and return the resulting string. @@ -451,24 +451,24 @@ impl Pretty for Ident { impl Pretty for Value { fn pretty(&self, p: &mut Printer) { match self { - Value::None => p.push_str("none"), - Value::Bool(v) => v.pretty(p), - Value::Int(v) => v.pretty(p), - Value::Float(v) => v.pretty(p), - Value::Length(v) => v.pretty(p), - Value::Angle(v) => v.pretty(p), - Value::Relative(v) => v.pretty(p), - Value::Fractional(v) => v.pretty(p), - Value::Linear(v) => v.pretty(p), - Value::TrackSizing(v) => v.pretty(p), - Value::Color(v) => v.pretty(p), - Value::Str(v) => v.pretty(p), - Value::Array(v) => v.pretty(p), - Value::Dict(v) => v.pretty(p), - Value::Template(v) => v.pretty(p), - Value::Func(v) => v.pretty(p), - Value::Any(v) => v.pretty(p), - Value::Error => p.push_str(""), + Self::None => p.push_str("none"), + Self::Auto => p.push_str("auto"), + Self::Bool(v) => v.pretty(p), + Self::Int(v) => v.pretty(p), + Self::Float(v) => v.pretty(p), + Self::Length(v) => v.pretty(p), + Self::Angle(v) => v.pretty(p), + Self::Relative(v) => v.pretty(p), + Self::Linear(v) => v.pretty(p), + Self::Fractional(v) => v.pretty(p), + Self::Color(v) => v.pretty(p), + Self::Str(v) => v.pretty(p), + Self::Array(v) => v.pretty(p), + Self::Dict(v) => v.pretty(p), + Self::Template(v) => v.pretty(p), + Self::Func(v) => v.pretty(p), + Self::Any(v) => v.pretty(p), + Self::Error => p.push_str(""), } } } @@ -579,9 +579,8 @@ pretty_display! { Length, Angle, Relative, - Fractional, Linear, - TrackSizing, + Fractional, RgbaColor, Color, AnyValue, diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 17e4a1966..24850d651 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -9,7 +9,7 @@ use crate::geom::{AngularUnit, LengthUnit}; pub enum Expr { /// The none literal: `none`. None(Span), - /// The `auto` constant. + /// The auto literal: `auto`. Auto(Span), /// A boolean literal: `true`, `false`. Bool(Span, bool), diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 26c01fbbc..2263f806f 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -76,6 +76,8 @@ pub enum Token<'s> { Or, /// The none literal: `none`. None, + /// The auto literal: `auto`. + Auto, /// The `let` keyword. Let, /// The `if` keyword. @@ -100,8 +102,6 @@ pub enum Token<'s> { Include, /// The `using` keyword. Using, - /// The `auto` keyword. - Auto, /// One or more whitespace characters. /// /// The contained `usize` denotes the number of newlines that were contained @@ -238,6 +238,7 @@ impl<'s> Token<'s> { Self::And => "operator `and`", Self::Or => "operator `or`", Self::None => "`none`", + Self::Auto => "`auto`", Self::Let => "keyword `let`", Self::If => "keyword `if`", Self::Else => "keyword `else`", @@ -250,7 +251,6 @@ impl<'s> Token<'s> { Self::Import => "keyword `import`", Self::Include => "keyword `include`", Self::Using => "keyword `using`", - Self::Auto => "keyword `auto`", Self::Space(_) => "space", Self::Text(_) => "text", Self::UnicodeEscape(_) => "unicode escape sequence", diff --git a/tests/ref/library/grid-table.png b/tests/ref/library/grid-table.png new file mode 100644 index 000000000..368376630 Binary files /dev/null and b/tests/ref/library/grid-table.png differ diff --git a/tests/ref/library/grid.png b/tests/ref/library/grid.png index 278ead89a..f7dda520c 100644 Binary files a/tests/ref/library/grid.png and b/tests/ref/library/grid.png differ diff --git a/tests/typ/library/grid-table.typ b/tests/typ/library/grid-table.typ new file mode 100644 index 000000000..277e695c4 --- /dev/null +++ b/tests/typ/library/grid-table.typ @@ -0,0 +1,29 @@ +// Test using the `grid` function to create a finance table. + +--- +#page(width: 12cm, height: 2.5cm) +#grid( + columns: 5, + gutter-columns: (2fr, 1fr, 1fr), + gutter-rows: 4 * (6pt,), + [*Quarter*], + [Expenditure], + [External Revenue], + [Financial ROI], + [_total_], + [*Q1*], + [173,472.57 \$], + [472,860.91 \$], + [51,286.84 \$], + [_350,675.18 \$_], + [*Q2*], + [93,382.12 \$], + [439,382.85 \$], + [-1,134.30 \$], + [_344,866.43 \$_], + [*Q3*], + [96,421.49 \$], + [238,583.54 \$], + [3,497.12 \$], + [_145,659.17 \$_], +) diff --git a/tests/typ/library/grid.typ b/tests/typ/library/grid.typ index dcc0797f1..afb43684f 100644 --- a/tests/typ/library/grid.typ +++ b/tests/typ/library/grid.typ @@ -1,8 +1,9 @@ // Test the `grid` function. --- -#page(width: 100pt, height: 140pt) #let rect(width, color) = rect(width: width, height: 2cm, fill: color) + +#page(width: 100pt, height: 140pt) #grid( columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%), rect(0.5cm, #2a631a), @@ -22,7 +23,6 @@ #grid() --- - #grid( columns: (auto, auto, 40%), gutter: (1fr,), @@ -31,49 +31,11 @@ rect(width: 100%, fill: #dddddd)[aaa], ) -#grid( - columns: (auto, auto, 40%), - gutter: (1fr,), - rect(fill: eastern)[dddaa aaa aaa], - rect(fill: conifer)[ccc], - rect(width: 100%, fill: #dddddd)[aaa], -) - - ---- - -#page(width: 12cm, height: 2.5cm) -#grid( - columns: (auto, auto, auto, auto, auto), - gutter-col: (2fr, 1fr, 1fr), - gutter-row: (6pt, 6pt, 6pt, 6pt), - [*Quarter*], - [Expenditure], - [External Revenue], - [Financial ROI], - [_total_], - [*Q1*], - [173,472.57 \$], - [472,860.91 \$], - [51,286.84 \$], - [_350,675.18 \$_], - [*Q2*], - [93,382.12 \$], - [439,382.85 \$], - [-1,134.30 \$], - [_344,866.43 \$_], - [*Q3*], - [96,421.49 \$], - [238,583.54 \$], - [3,497.12 \$], - [_145,659.17 \$_], -) - --- #page(height: 3cm, width: 2cm) #grid( - dir: ttb, columns: (1fr, 1cm, 1fr, 1fr), + column-dir: ttb, rows: (auto, 1fr), rect(height: 100%, fill: #222222)[foo], rect(height: 100%, fill: #547d0a)[bar], @@ -81,3 +43,12 @@ rect(height: 100%, fill: conifer)[baz], rect(height: 100%, width: 100%, fill: #547d0a)[bar], ) + +--- +#page(height: 3cm, margins: 0pt) +#align(center) +#grid( + columns: (1fr,), + rows: (1fr, auto, 2fr), + [], rect(width: 100%)[A bit more to the top], [], +) diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json index 0b0298fef..cdf92649a 100644 --- a/tools/support/typst.tmLanguage.json +++ b/tools/support/typst.tmLanguage.json @@ -259,6 +259,10 @@ "name": "constant.language.none.typst", "match": "\\bnone\\b" }, + { + "name": "constant.language.auto.typst", + "match": "\\bauto\\b" + }, { "name": "constant.language.boolean.typst", "match": "\\b(true|false)\\b"