From 21585e03cfcc47ad283c162e4a2959ea5f8fbd6f Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:52:28 -0300 Subject: [PATCH] Table cell `x` and `y` fields [More Flexible Tables Pt.2b] (#3050) --- crates/typst/src/layout/grid/layout.rs | 255 ++++++++++++++++++++++--- crates/typst/src/layout/grid/mod.rs | 184 ++++++++++++++++-- crates/typst/src/model/bibliography.rs | 22 ++- crates/typst/src/model/table.rs | 126 ++++++++++-- tests/ref/layout/grid-cell.png | Bin 39178 -> 45433 bytes tests/ref/layout/grid-positioning.png | Bin 0 -> 55048 bytes tests/ref/layout/table-cell.png | Bin 41893 -> 49018 bytes tests/typ/layout/grid-cell.typ | 22 +++ tests/typ/layout/grid-positioning.typ | 223 +++++++++++++++++++++ tests/typ/layout/table-cell.typ | 22 +++ 10 files changed, 784 insertions(+), 70 deletions(-) create mode 100644 tests/ref/layout/grid-positioning.png create mode 100644 tests/typ/layout/grid-positioning.typ diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs index a7e259d01..b2490d1ea 100644 --- a/crates/typst/src/layout/grid/layout.rs +++ b/crates/typst/src/layout/grid/layout.rs @@ -1,4 +1,8 @@ -use crate::diag::{bail, At, SourceResult, StrResult}; +use ecow::eco_format; + +use crate::diag::{ + bail, At, Hint, HintedStrResult, HintedString, SourceResult, StrResult, +}; use crate::engine::Engine; use crate::foundations::{ Array, CastInfo, Content, FromValue, Func, IntoValue, Reflect, Resolve, Smart, @@ -83,6 +87,7 @@ impl FromValue for Celled { } /// Represents a cell in CellGrid, to be laid out by GridLayouter. +#[derive(Clone)] pub struct Cell { /// The cell's body. pub body: Content, @@ -123,6 +128,15 @@ pub trait ResolvableCell { inset: Sides>, styles: StyleChain, ) -> Cell; + + /// Returns this cell's column override. + fn x(&self, styles: StyleChain) -> Smart; + + /// Returns this cell's row override. + fn y(&self, styles: StyleChain) -> Smart; + + /// The cell's span, for errors. + fn span(&self) -> Span; } /// A grid of cells, including the columns, rows, and cell data. @@ -200,12 +214,12 @@ impl CellGrid { Self { cols, rows, cells, has_gutter, is_rtl } } - /// Resolves all cells in the grid before creating it. - /// Allows them to keep track of their final properties and adjust their - /// fields accordingly. + /// Resolves and positions all cells in the grid before creating it. + /// Allows them to keep track of their final properties and positions + /// and adjust their fields accordingly. /// Cells must implement Clone as they will be owned. Additionally, they - /// must implement Default in order to fill the last row of the grid with - /// empty cells, if it is not completely filled. + /// must implement Default in order to fill positions in the grid which + /// weren't explicitly specified by the user with empty cells. #[allow(clippy::too_many_arguments)] pub fn resolve( tracks: Axes<&[Sizing]>, @@ -216,38 +230,129 @@ impl CellGrid { inset: Sides>, engine: &mut Engine, styles: StyleChain, + span: Span, ) -> SourceResult { // Number of content columns: Always at least one. let c = tracks.x.len().max(1); - // If not all columns in the last row have cells, we will add empty - // cells and complete the row so that those positions are susceptible - // to show rules and receive grid styling. - // We apply '% c' twice so that 'cells_remaining' is zero when - // the last row is already filled (then 'cell_count % c' would be zero). - let cell_count = cells.len(); - let cells_remaining = (c - cell_count % c) % c; - let cells = cells - .iter() - .cloned() - .chain(std::iter::repeat_with(T::default).take(cells_remaining)) + // We can't just use the cell's index in the 'cells' vector to + // determine its automatic position, since cells could have arbitrary + // positions, so the position of a cell in 'cells' can differ from its + // final position in 'resolved_cells' (see below). + // Therefore, we use a counter, 'auto_index', to determine the position + // of the next cell with (x: auto, y: auto). It is only stepped when + // a cell with (x: auto, y: auto), usually the vast majority, is found. + let mut auto_index = 0; + + // We have to rebuild the grid to account for arbitrary positions. + // Create at least 'cells.len()' positions, since there will be at + // least 'cells.len()' cells, even though some of them might be placed + // in arbitrary positions and thus cause the grid to expand. + // Additionally, make sure we allocate up to the next multiple of 'c', + // since each row will have 'c' cells, even if the last few cells + // weren't explicitly specified by the user. + // We apply '% c' twice so that the amount of cells potentially missing + // is zero when 'cells.len()' is already a multiple of 'c' (thus + // 'cells.len() % c' would be zero). + let Some(cell_count) = cells.len().checked_add((c - cells.len() % c) % c) else { + bail!(span, "too many cells were given") + }; + let mut resolved_cells: Vec> = Vec::with_capacity(cell_count); + for cell in cells.iter().cloned() { + let cell_span = cell.span(); + // Let's calculate the cell's final position based on its + // requested position. + let resolved_index = { + let cell_x = cell.x(styles); + let cell_y = cell.y(styles); + resolve_cell_position(cell_x, cell_y, &resolved_cells, &mut auto_index, c) + .at(cell_span)? + }; + let x = resolved_index % c; + let y = resolved_index / c; + + // Let's resolve the cell so it can determine its own fields + // based on its final position. + let cell = cell.resolve_cell( + x, + y, + &fill.resolve(engine, x, y)?, + align.resolve(engine, x, y)?, + inset, + styles, + ); + + if resolved_index >= resolved_cells.len() { + // Ensure the length of the vector of resolved cells is always + // a multiple of 'c' by pushing full rows every time. Here, we + // add enough absent positions (later converted to empty cells) + // to ensure the last row in the new vector length is + // completely filled. This is necessary so that those + // positions, even if not explicitly used at the end, are + // eventually susceptible to show rules and receive grid + // styling, as they will be resolved as empty cells in a second + // loop below. + let Some(new_len) = resolved_index + .checked_add(1) + .and_then(|new_len| new_len.checked_add((c - new_len % c) % c)) + else { + bail!(cell_span, "cell position too large") + }; + + // Here, the cell needs to be placed in a position which + // doesn't exist yet in the grid (out of bounds). We will add + // enough absent positions for this to be possible. They must + // be absent as no cells actually occupy them (they can be + // overridden later); however, if no cells occupy them as we + // finish building the grid, then such positions will be + // replaced by empty cells. + resolved_cells.resize(new_len, None); + } + + // The vector is large enough to contain the cell, so we can just + // index it directly to access the position it will be placed in. + // However, we still need to ensure we won't try to place a cell + // where there already is one. + let slot = &mut resolved_cells[resolved_index]; + if slot.is_some() { + bail!( + cell_span, + "attempted to place a second cell at column {x}, row {y}"; + hint: "try specifying your cells in a different order" + ); + } + + *slot = Some(cell); + } + + // Replace absent entries by resolved empty cells, and produce a vector + // of 'Cell' from 'Option' (final step). + let resolved_cells = resolved_cells + .into_iter() .enumerate() .map(|(i, cell)| { - let x = i % c; - let y = i / c; + if let Some(cell) = cell { + Ok(cell) + } else { + let x = i % c; + let y = i / c; - Ok(cell.resolve_cell( - x, - y, - &fill.resolve(engine, x, y)?, - align.resolve(engine, x, y)?, - inset, - styles, - )) + // Ensure all absent entries are affected by show rules and + // grid styling by turning them into resolved empty cells. + let new_cell = T::default().resolve_cell( + x, + y, + &fill.resolve(engine, x, y)?, + align.resolve(engine, x, y)?, + inset, + styles, + ); + Ok(new_cell) + } }) - .collect::>>()?; + .collect::>>()?; - Ok(Self::new(tracks, gutter, cells, styles)) + Ok(Self::new(tracks, gutter, resolved_cells, styles)) } /// Get the content of the cell in column `x` and row `y`. @@ -278,6 +383,98 @@ impl CellGrid { } } +/// Given a cell's requested x and y, the vector with the resolved cell +/// positions, the `auto_index` counter (determines the position of the next +/// `(auto, auto)` cell) and the amount of columns in the grid, returns the +/// final index of this cell in the vector of resolved cells. +fn resolve_cell_position( + cell_x: Smart, + cell_y: Smart, + resolved_cells: &[Option], + auto_index: &mut usize, + columns: usize, +) -> HintedStrResult { + // Translates a (x, y) position to the equivalent index in the final cell vector. + // Errors if the position would be too large. + let cell_index = |x, y: usize| { + y.checked_mul(columns) + .and_then(|row_index| row_index.checked_add(x)) + .ok_or_else(|| HintedString::from(eco_format!("cell position too large"))) + }; + match (cell_x, cell_y) { + // Fully automatic cell positioning. The cell did not + // request a coordinate. + (Smart::Auto, Smart::Auto) => { + // Let's find the first available position starting from the + // automatic position counter, searching in row-major order. + let mut resolved_index = *auto_index; + while let Some(Some(_)) = resolved_cells.get(resolved_index) { + // Skip any non-absent cell positions (`Some(None)`) to + // determine where this cell will be placed. An out of bounds + // position (thus `None`) is also a valid new position (only + // requires expanding the vector). + resolved_index += 1; + } + + // Ensure the next cell with automatic position will be + // placed after this one (maybe not immediately after). + *auto_index = resolved_index + 1; + + Ok(resolved_index) + } + // Cell has chosen at least its column. + (Smart::Custom(cell_x), cell_y) => { + if cell_x >= columns { + return Err(HintedString::from(eco_format!( + "cell could not be placed at invalid column {cell_x}" + ))); + } + if let Smart::Custom(cell_y) = cell_y { + // Cell has chosen its exact position. + cell_index(cell_x, cell_y) + } else { + // Cell has only chosen its column. + // Let's find the first row which has that column available. + let mut resolved_y = 0; + while let Some(Some(_)) = + resolved_cells.get(cell_index(cell_x, resolved_y)?) + { + // Try each row until either we reach an absent position + // (`Some(None)`) or an out of bounds position (`None`), + // in which case we'd create a new row to place this cell in. + resolved_y += 1; + } + cell_index(cell_x, resolved_y) + } + } + // Cell has only chosen its row, not its column. + (Smart::Auto, Smart::Custom(cell_y)) => { + // Let's find the first column which has that row available. + let first_row_pos = cell_index(0, cell_y)?; + let last_row_pos = first_row_pos + .checked_add(columns) + .ok_or_else(|| eco_format!("cell position too large"))?; + + (first_row_pos..last_row_pos) + .find(|possible_index| { + // Much like in the previous cases, we skip any occupied + // positions until we either reach an absent position + // (`Some(None)`) or an out of bounds position (`None`), + // in which case we can just expand the vector enough to + // place this cell. In either case, we found an available + // position. + !matches!(resolved_cells.get(*possible_index), Some(Some(_))) + }) + .ok_or_else(|| { + eco_format!( + "cell could not be placed in row {cell_y} because it was full" + ) + }) + .hint("try specifying your cells in a different order") + } + } +} + /// Performs grid layout. pub struct GridLayouter<'a> { /// The grid of cells. diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs index ab758e984..6ed9cce58 100644 --- a/crates/typst/src/layout/grid/mod.rs +++ b/crates/typst/src/layout/grid/mod.rs @@ -4,18 +4,19 @@ pub use self::layout::{Cell, CellGrid, Celled, GridLayouter, ResolvableCell}; use std::num::NonZeroUsize; +use ecow::eco_format; use smallvec::{smallvec, SmallVec}; -use crate::diag::{SourceResult, StrResult}; +use crate::diag::{SourceResult, StrResult, Trace, Tracepoint}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, scope, Array, Content, Fold, NativeElement, Packed, Show, Smart, - StyleChain, Value, + cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value, }; use crate::layout::{ Abs, AlignElem, Alignment, Axes, Fragment, Layout, Length, Regions, Rel, Sides, Sizing, }; +use crate::syntax::Span; use crate::visualize::{Paint, Stroke}; /// Arranges content in a grid. @@ -60,7 +61,8 @@ use crate::visualize::{Paint, Stroke}; /// appearance options to depend on a cell's position (column and row), you may /// specify a function to `fill` or `align` of the form /// `(column, row) => value`. You may also use a show rule on -/// [`grid.cell`]($grid.cell) - see that element's examples for more information. +/// [`grid.cell`]($grid.cell) - see that element's examples or the examples +/// below for more information. /// /// # Examples /// The example below demonstrates the different track sizing options. @@ -97,6 +99,61 @@ use crate::visualize::{Paint, Stroke}; /// ..range(25).map(str) /// ) /// ``` +/// +/// Additionally, you can use [`grid.cell`]($grid.cell) in various ways to +/// not only style each cell based on its position and other fields, but also +/// to determine the cell's preferential position in the table. +/// +/// ```example +/// #set page(width: auto) +/// #show grid.cell: it => { +/// if it.y == 0 { +/// // The first row's text must be white and bold. +/// set text(white) +/// strong(it) +/// } else { +/// // For the second row and beyond, we will show the day number for each +/// // cell. +/// +/// // In general, a cell's index is given by cell.x + columns * cell.y. +/// // Days start in the second grid row, so we subtract 1 row. +/// // But the first day is day 1, not day 0, so we add 1. +/// let day = it.x + 7 * (it.y - 1) + 1 +/// if day <= 31 { +/// // Place the day's number at the top left of the cell. +/// // Only if the day is valid for this month (not 32 or higher). +/// place(top + left, dx: 2pt, dy: 2pt, text(8pt, red.darken(40%))[#day]) +/// } +/// it +/// } +/// } +/// +/// #grid( +/// fill: (x, y) => if y == 0 { gray.darken(50%) }, +/// columns: (30pt,) * 7, +/// rows: (auto, 30pt), +/// // Events will be written at the bottom of each day square. +/// align: bottom, +/// inset: 5pt, +/// stroke: (thickness: 0.5pt, dash: "densely-dotted"), +/// +/// [Sun], [Mon], [Tue], [Wed], [Thu], [Fri], [Sat], +/// +/// // This event will occur on the first Friday (sixth column). +/// grid.cell(x: 5, fill: yellow.darken(10%))[Call], +/// +/// // This event will occur every Monday (second column). +/// // We have to repeat it 5 times so it occurs every week. +/// ..(grid.cell(x: 1, fill: red.lighten(50%))[Meet],) * 5, +/// +/// // This event will occur at day 19. +/// grid.cell(x: 4, y: 3, fill: orange.lighten(25%))[Talk], +/// +/// // These events will occur at the second week, where available. +/// grid.cell(y: 2, fill: aqua)[Chat], +/// grid.cell(y: 2, fill: aqua)[Walk], +/// ) +/// ``` #[elem(scope, Layout)] pub struct GridElem { /// The column sizes. @@ -213,7 +270,7 @@ pub struct GridElem { /// /// The cells are populated in row-major order. #[variadic] - pub children: Vec, + pub children: Vec>, } #[scope] @@ -241,6 +298,8 @@ impl Layout for Packed { let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice()); let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice()); + // Use trace to link back to the grid when a specific cell errors + let tracepoint = || Tracepoint::Call(Some(eco_format!("grid"))); let grid = CellGrid::resolve( tracks, gutter, @@ -250,7 +309,9 @@ impl Layout for Packed { inset, engine, styles, - )?; + self.span(), + ) + .trace(engine.world, tracepoint, self.span())?; let layouter = GridLayouter::new(&grid, &stroke, regions, styles, self.span()); @@ -290,12 +351,84 @@ cast! { /// [G], grid.cell(inset: 0pt)[H] /// ) /// ``` +/// +/// You may also apply a show rule on `grid.cell` to style all cells at once, +/// which allows you, for example, to apply styles based on a cell's position: +/// +/// ```example +/// #show grid.cell: it => { +/// if it.y == 0 { +/// // First row is bold +/// strong(it) +/// } else if it.x == 1 { +/// // Second column is italicized +/// // (except at the first row) +/// emph(it) +/// } else { +/// // Remaining cells aren't changed +/// it +/// } +/// } +/// +/// #grid( +/// columns: 3, +/// gutter: 3pt, +/// [Name], [Age], [Info], +/// [John], [52], [Nice], +/// [Mary], [50], [Cool], +/// [Jake], [49], [Epic] +/// ) +/// ``` #[elem(name = "cell", title = "Grid Cell", Show)] pub struct GridCell { /// The cell's body. #[required] body: Content, + /// The cell's column (zero-indexed). + /// This field may be used in show rules to style a cell depending on its + /// column. + /// + /// You may override this field to pick in which column the cell must + /// be placed. If no row (`y`) is chosen, the cell will be placed in the + /// first row (starting at row 0) with that column available (or a new row + /// if none). If both `x` and `y` are chosen, however, the cell will be + /// placed in that exact position. An error is raised if that position is + /// not available (thus, it is usually wise to specify cells with a custom + /// position before cells with automatic positions). + /// + /// ```example + /// #grid( + /// columns: 4, + /// rows: 2.5em, + /// fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + /// align: center + horizon, + /// inset: 3pt, + /// grid.cell(x: 2, y: 2)[3], + /// [1], grid.cell(x: 3)[4], [2], + /// ) + /// ``` + x: Smart, + + /// The cell's row (zero-indexed). + /// This field may be used in show rules to style a cell depending on its + /// row. + /// + /// You may override this field to pick in which row the cell must be + /// placed. If no column (`x`) is chosen, the cell will be placed in the + /// first column (starting at column 0) available in the chosen row. If all + /// columns in the chosen row are already occupied, an error is raised. + /// + /// ```example + /// #grid( + /// columns: 2, + /// fill: (x, y) => if calc.odd(x + y) { gray.lighten(40%) }, + /// inset: 1pt, + /// [A], grid.cell(y: 1)[B], grid.cell(y: 1)[C], grid.cell(y: 2)[D] + /// ) + /// ``` + y: Smart, + /// The cell's fill override. fill: Smart>, @@ -311,39 +444,54 @@ cast! { v: Content => v.into(), } -impl Default for GridCell { +impl Default for Packed { fn default() -> Self { - Self::new(Content::default()) + Packed::new(GridCell::new(Content::default())) } } -impl ResolvableCell for GridCell { +impl ResolvableCell for Packed { fn resolve_cell( mut self, - _: usize, - _: usize, + x: usize, + y: usize, fill: &Option, align: Smart, inset: Sides>, styles: StyleChain, ) -> Cell { - let fill = self.fill(styles).unwrap_or_else(|| fill.clone()); - self.push_fill(Smart::Custom(fill.clone())); - self.push_align(match align { + let cell = &mut *self; + let fill = cell.fill(styles).unwrap_or_else(|| fill.clone()); + cell.push_x(Smart::Custom(x)); + cell.push_y(Smart::Custom(y)); + cell.push_fill(Smart::Custom(fill.clone())); + cell.push_align(match align { Smart::Custom(align) => { - Smart::Custom(self.align(styles).map_or(align, |inner| inner.fold(align))) + Smart::Custom(cell.align(styles).map_or(align, |inner| inner.fold(align))) } // Don't fold if the grid is using outer alignment. Use the // cell's alignment instead (which, in the end, will fold with // the outer alignment when it is effectively displayed). - Smart::Auto => self.align(styles), + Smart::Auto => cell.align(styles), }); - self.push_inset(Smart::Custom( - self.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some), + cell.push_inset(Smart::Custom( + cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some), )); Cell { body: self.pack(), fill } } + + fn x(&self, styles: StyleChain) -> Smart { + (**self).x(styles) + } + + fn y(&self, styles: StyleChain) -> Smart { + (**self).y(styles) + } + + fn span(&self) -> Span { + Packed::span(self) + } } impl Show for Packed { diff --git a/crates/typst/src/model/bibliography.rs b/crates/typst/src/model/bibliography.rs index 81caf9537..94681fe1d 100644 --- a/crates/typst/src/model/bibliography.rs +++ b/crates/typst/src/model/bibliography.rs @@ -240,8 +240,13 @@ impl Show for Packed { if references.iter().any(|(prefix, _)| prefix.is_some()) { let mut cells = vec![]; for (prefix, reference) in references { - cells.push(GridCell::new(prefix.clone().unwrap_or_default())); - cells.push(GridCell::new(reference.clone())); + cells.push( + Packed::new(GridCell::new(prefix.clone().unwrap_or_default())) + .spanned(span), + ); + cells.push( + Packed::new(GridCell::new(reference.clone())).spanned(span), + ); } seq.push(VElem::new(row_gutter).with_weakness(3).pack()); @@ -945,11 +950,14 @@ impl ElemRenderer<'_> { if let Some(prefix) = suf_prefix { const COLUMN_GUTTER: Em = Em::new(0.65); - content = GridElem::new(vec![GridCell::new(prefix), GridCell::new(content)]) - .with_columns(TrackSizings(smallvec![Sizing::Auto; 2])) - .with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()])) - .pack() - .spanned(self.span); + content = GridElem::new(vec![ + Packed::new(GridCell::new(prefix)).spanned(self.span), + Packed::new(GridCell::new(content)).spanned(self.span), + ]) + .with_columns(TrackSizings(smallvec![Sizing::Auto; 2])) + .with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()])) + .pack() + .spanned(self.span); } match elem.display { diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs index ef0d3f917..413a375b4 100644 --- a/crates/typst/src/model/table.rs +++ b/crates/typst/src/model/table.rs @@ -1,13 +1,16 @@ -use crate::diag::SourceResult; +use ecow::eco_format; + +use crate::diag::{SourceResult, Trace, Tracepoint}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, scope, Content, Fold, NativeElement, Packed, Show, Smart, StyleChain, + cast, elem, scope, Content, Fold, Packed, Show, Smart, StyleChain, }; use crate::layout::{ show_grid_cell, Abs, Alignment, Axes, Cell, CellGrid, Celled, Fragment, GridLayouter, Layout, Length, Regions, Rel, ResolvableCell, Sides, TrackSizings, }; use crate::model::Figurable; +use crate::syntax::Span; use crate::text::{Lang, LocalName, Region}; use crate::visualize::{Paint, Stroke}; @@ -29,6 +32,8 @@ use crate::visualize::{Paint, Stroke}; /// [figure]($figure). /// /// # Example +/// +/// The example below demonstrates some of the most common table options. /// ```example /// #table( /// columns: (1fr, auto, auto), @@ -47,6 +52,40 @@ use crate::visualize::{Paint, Stroke}; /// [$a$: edge length] /// ) /// ``` +/// +/// Much like with grids, you can use [`table.cell`]($table.cell) to customize +/// the appearance and the position of each cell. +/// +/// ```example +/// #set page(width: auto) +/// #show table.cell: it => { +/// if it.x == 0 or it.y == 0 { +/// set text(white) +/// strong(it) +/// } else if it.body == [] { +/// // Replace empty cells with 'N/A' +/// pad(rest: it.inset)[_N/A_] +/// } else { +/// it +/// } +/// } +/// +/// #table( +/// fill: (x, y) => if x == 0 or y == 0 { gray.darken(50%) }, +/// columns: 4, +/// [], [Exam 1], [Exam 2], [Exam 3], +/// ..([John], [Mary], [Jake], [Robert]).map(table.cell.with(x: 0)), +/// +/// // Mary got grade A on Exam 3. +/// table.cell(x: 3, y: 2, fill: green)[A], +/// +/// // Everyone got grade A on Exam 2. +/// ..(table.cell(x: 2, fill: green)[A],) * 4, +/// +/// // Robert got grade B on other exams. +/// ..(table.cell(y: 4, fill: aqua)[B],) * 2, +/// ) +/// ``` #[elem(scope, Layout, LocalName, Figurable)] pub struct TableElem { /// The column sizes. See the [grid documentation]($grid) for more @@ -157,7 +196,7 @@ pub struct TableElem { /// The contents of the table cells. #[variadic] - pub children: Vec, + pub children: Vec>, } #[scope] @@ -185,6 +224,8 @@ impl Layout for Packed { let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice()); let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice()); + // Use trace to link back to the table when a specific cell errors + let tracepoint = || Tracepoint::Call(Some(eco_format!("table"))); let grid = CellGrid::resolve( tracks, gutter, @@ -194,7 +235,9 @@ impl Layout for Packed { inset, engine, styles, - )?; + self.span(), + ) + .trace(engine.world, tracepoint, self.span())?; let layouter = GridLayouter::new(&grid, &stroke, regions, styles, self.span()); @@ -259,12 +302,48 @@ impl Figurable for Packed {} /// [M.], table.cell(inset: 0pt)[Player] /// ) /// ``` +/// +/// You may also apply a show rule on `table.cell` to style all cells at once, +/// which allows you, for example, to apply styles based on a cell's position: +/// +/// ```example +/// #show table.cell: it => { +/// if it.y == 0 { +/// // First row is bold +/// strong(it) +/// } else if it.x == 1 { +/// // Second column is italicized +/// // (except at the first row) +/// emph(it) +/// } else { +/// // Remaining cells aren't changed +/// it +/// } +/// } +/// +/// #table( +/// columns: 3, +/// gutter: 3pt, +/// [Name], [Age], [Info], +/// [John], [52], [Nice], +/// [Mary], [50], [Cool], +/// [Jake], [49], [Epic] +/// ) +/// ``` #[elem(name = "cell", title = "Table Cell", Show)] pub struct TableCell { /// The cell's body. #[required] body: Content, + /// The cell's column (zero-indexed). + /// Functions identically to the `x` field in [`grid.cell`]($grid.cell). + x: Smart, + + /// The cell's row (zero-indexed). + /// Functions identically to the `y` field in [`grid.cell`]($grid.cell). + y: Smart, + /// The cell's fill override. fill: Smart>, @@ -280,39 +359,54 @@ cast! { v: Content => v.into(), } -impl Default for TableCell { +impl Default for Packed { fn default() -> Self { - Self::new(Content::default()) + Packed::new(TableCell::new(Content::default())) } } -impl ResolvableCell for TableCell { +impl ResolvableCell for Packed { fn resolve_cell( mut self, - _: usize, - _: usize, + x: usize, + y: usize, fill: &Option, align: Smart, inset: Sides>, styles: StyleChain, ) -> Cell { - let fill = self.fill(styles).unwrap_or_else(|| fill.clone()); - self.push_fill(Smart::Custom(fill.clone())); - self.push_align(match align { + let cell = &mut *self; + let fill = cell.fill(styles).unwrap_or_else(|| fill.clone()); + cell.push_x(Smart::Custom(x)); + cell.push_y(Smart::Custom(y)); + cell.push_fill(Smart::Custom(fill.clone())); + cell.push_align(match align { Smart::Custom(align) => { - Smart::Custom(self.align(styles).map_or(align, |inner| inner.fold(align))) + Smart::Custom(cell.align(styles).map_or(align, |inner| inner.fold(align))) } // Don't fold if the table is using outer alignment. Use the // cell's alignment instead (which, in the end, will fold with // the outer alignment when it is effectively displayed). - Smart::Auto => self.align(styles), + Smart::Auto => cell.align(styles), }); - self.push_inset(Smart::Custom( - self.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some), + cell.push_inset(Smart::Custom( + cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some), )); Cell { body: self.pack(), fill } } + + fn x(&self, styles: StyleChain) -> Smart { + (**self).x(styles) + } + + fn y(&self, styles: StyleChain) -> Smart { + (**self).y(styles) + } + + fn span(&self) -> Span { + Packed::span(self) + } } impl Show for Packed { diff --git a/tests/ref/layout/grid-cell.png b/tests/ref/layout/grid-cell.png index fb68312366c07430039a3cc8f95a6185e04de736..07508b40013fa96cb0bb6d369f493f067d55cd62 100644 GIT binary patch delta 6932 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 U@$J(PVA5ppboFyt=akR{06`#3)c^nh diff --git a/tests/ref/layout/grid-positioning.png b/tests/ref/layout/grid-positioning.png new file mode 100644 index 0000000000000000000000000000000000000000..5d60c8b71bbe87989b3e1414fa3d876451c7bcec GIT binary patch literal 55048 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 aW5z^Uzcml3$@=Ffo6^hI@>R0%;Qs}r@eUIJ delta 2377 zcmZ{mc`zH=9>+b>AWbU~N^}jPtzA&H6hXCGwUnUtHPpV7UYgp4T4TGVG-!#|5>*tf zqKHze)V}q$VvjA?7LAwt$GbCc?t3$5<~QH(@67ql_sp5koC6l;^KQ=cQ+&sqc{n*Z zIJ^=Cc{w;ryeUZR6THtl>t3mK^rc5-(WDHCVuDw3B6&j_c1$Yg}~p#{*#PCV|^ z`#fwoY+OSSO#(SLuEyv4)-N&9n@Rc!n{Be&w9f@F>FM9r``(2$7=>X%X3e(W^v~7R z8VPT1FBl)mkN7WrMHW(4Z=6TXs51`tgVvyO_AT-w=Trk@@e6i4Yf4^16JLgU^Hu!n z@P~V(U(4U5e8P`d@8}79=R=dh8t2boOQ^Q;y#7;6fFh~cN3*w#;++C4wrDZ6vatyY zp_>pLa`9Hv4SuuBYGqCBX`AdoLEd-rKTI-eY(J$trBXNO)9Pg*CX*g3-f6gxDW_G! zCt;S!vH5u0g+M-V?0YAMVW9@DUBWXXKa5`PXnwlNp;gub>bDAl0~pRD^m?M0G<+Ly z(WqdN{^=7)>tTV_RN<0Nv*U%ZkeCM{4(}t#Qo&VK!o0YHpOMU+9yN0&b9eo>omJ>N zhG{~d{CKK@gDYg__s?%zV7PV>@Xkb>agipb1E1Gjqa@PR5=wdeqE7_l=^H=@2&q0h ze_Tk`zphJ{^=n>pEgdZEc4F|`n;TOtxS{TQxDaT`TJ}qtMW=A|G&bbK%)_C9PRrp- z;ps8le5of%mxuK}CQMCC1d?tcy#4%&RDc4J+(B$!LmC~nvt9?Q*fefy z>dv@RnP7x26@rTWPr?7U{97$60>}dp6ab++RoZ9}K-7s(N4WC9|9=_F&Yf$SIK@6W zSr;f*1JfhL=&Y6po8A@+0NpgV|kuYP*U6PA_8T}A^nFD6c4q^C# z{otBVTnOJxhzy_FS<5P0a)n~h&G=L3AQW!RbeAA|QbA5`J%K%;+T>%U+oRh^CVjeD z1P|b)zjhz!58{Z#AF(12JW4jCBKEi9Vh1a28rTt?L_CVp4=%%(m*i{tIh(@PXEcaW z7UGB!`jte8nc)@>`9^NhMxPMi_lL=hpXK?%QTf7Xa=)b+$G@tCTc6yc>MCoE~*tiPA4eHj7p1Q%|6-E~58Y z6=ROpCFdrIyjvHsR-!0Ks%-ZC`1Umg%g2IYO-2dI(pO5`F;Y2Cj2A6;dq83vt;*Iu zlb6(8P>DfbFfMwt8pphSb~SQ@{zZw32$16bq4D-xQYS_p1kZ^BC&kzBcY4=VjvnA& zbm$nrjYN?`Pj*M4kL7zyAYMgBG~ z8gpjRYR(V}`%%2tSfP}NoOts5UN=vapUhT0%`wL8>1vbRopAxvj0WLgtm}sll%ECQ z>MiKkSNu1|G-Yr8=v$j?kZSX61#0;h*m$n%kHB+M!Jyn4U`nXpA#IG}f)Cv*ufIYM zRFBx(Xw*s*BT*C4b!&n_UCVwWXU<)FpmbWrfDDC+G)an;nyY`6H1o1HBkKh;%?_cH zf+n(%q*9S&z;JkCF4F&QQ$JUQr1SD|cuq7}KXM*73>;vBAMIyy(;&~srIicSLOyA9 zD9sD%#K$3ZbD0*YJy;BWQb8wP5;W06kMX9N+Ww(dz!^bw~oz|cy;VgtMxLu^J;|7oyYwuxr9 t*?az%;NaTwd { + if it.y == 0 { + strong(it) + } else if it.x == 1 { + emph(it) + } else { + it + } + } + grid( + columns: 3, + gutter: 3pt, + [Name], [Age], [Info], + [John], [52], [Nice], + [Mary], [50], [Cool], + [Jake], [49], [Epic] + ) +} diff --git a/tests/typ/layout/grid-positioning.typ b/tests/typ/layout/grid-positioning.typ new file mode 100644 index 000000000..ca71cb372 --- /dev/null +++ b/tests/typ/layout/grid-positioning.typ @@ -0,0 +1,223 @@ +// Test cell positioning in grids. + +--- +#{ + show grid.cell: it => (it.x, it.y) + grid( + columns: 2, + inset: 5pt, + fill: aqua, + gutter: 3pt, + [Hello], [World], + [Sweet], [Home] + ) +} +#{ + show table.cell: it => pad(rest: it.inset)[#(it.x, it.y)] + table( + columns: 2, + gutter: 3pt, + [Hello], [World], + [Sweet], [Home] + ) +} + +--- +// Positioning cells in a different order than they appear +#grid( + columns: 2, + [A], [B], + grid.cell(x: 1, y: 2)[C], grid.cell(x: 0, y: 2)[D], + grid.cell(x: 1, y: 1)[E], grid.cell(x: 0, y: 1)[F], +) + +--- +// Creating more rows by positioning out of bounds +#grid( + columns: 3, + rows: 1.5em, + inset: 5pt, + fill: (x, y) => if (x, y) == (0, 0) { blue } else if (x, y) == (2, 3) { red } else { green }, + [A], + grid.cell(x: 2, y: 3)[B] +) + +#table( + columns: (3em, 1em, 3em), + rows: 1.5em, + inset: (top: 0pt, bottom: 0pt, rest: 5pt), + fill: (x, y) => if (x, y) == (0, 0) { blue } else if (x, y) == (2, 3) { red } else { green }, + align: (x, y) => (left, center, right).at(x), + [A], + table.cell(x: 2, y: 3)[B] +) + +--- +// Error: 3:3-3:42 attempted to place a second cell at column 0, row 0 +// Hint: 3:3-3:42 try specifying your cells in a different order +#grid( + [A], + grid.cell(x: 0, y: 0)[This shall error] +) + +--- +// Error: 3:3-3:43 attempted to place a second cell at column 0, row 0 +// Hint: 3:3-3:43 try specifying your cells in a different order +#table( + [A], + table.cell(x: 0, y: 0)[This shall error] +) + +--- +// Automatic position cell skips custom position cell +#grid( + grid.cell(x: 0, y: 0)[This shall not error], + [A] +) + +--- +// Error: 4:3-4:36 cell could not be placed at invalid column 2 +#grid( + columns: 2, + [A], + grid.cell(x: 2)[This shall error] +) + +--- +// Partial positioning +#grid( + columns: 3, + rows: 1.5em, + inset: 5pt, + fill: aqua, + [A], grid.cell(y: 1, fill: green)[B], [C], grid.cell(x: auto, y: 1, fill: green)[D], [E], + grid.cell(y: 2, fill: green)[F], grid.cell(x: 0, fill: orange)[G], grid.cell(x: 0, y: auto, fill: orange)[H], + grid.cell(x: 1, fill: orange)[I] +) + +#table( + columns: 3, + rows: 1.5em, + inset: 5pt, + fill: aqua, + [A], table.cell(y: 1, fill: green)[B], [C], table.cell(x: auto, y: 1, fill: green)[D], [E], + table.cell(y: 2, fill: green)[F], table.cell(x: 0, fill: orange)[G], table.cell(x: 0, y: auto, fill: orange)[H], + table.cell(x: 1, fill: orange)[I] +) + +--- +// Error: 4:3-4:21 cell could not be placed in row 0 because it was full +// Hint: 4:3-4:21 try specifying your cells in a different order +#grid( + columns: 2, + [A], [B], + grid.cell(y: 0)[C] +) + +--- +// Error: 4:3-4:22 cell could not be placed in row 0 because it was full +// Hint: 4:3-4:22 try specifying your cells in a different order +#table( + columns: 2, + [A], [B], + table.cell(y: 0)[C] +) + +--- +// Doc example 1 +#set page(width: auto) +#show grid.cell: it => { + if it.y == 0 { + set text(white) + strong(it) + } else { + // For the second row and beyond, we will write the day number for each + // cell. + + // In general, a cell's index is given by cell.x + columns * cell.y. + // Days start in the second grid row, so we subtract 1 row. + // But the first day is day 1, not day 0, so we add 1. + let day = it.x + 7 * (it.y - 1) + 1 + if day <= 31 { + // Place the day's number at the top left of the cell. + // Only if the day is valid for this month (not 32 or higher). + place(top + left, dx: 2pt, dy: 2pt, text(8pt, red.darken(40%))[#day]) + } + it + } +} + +#grid( + fill: (x, y) => if y == 0 { gray.darken(50%) }, + columns: (30pt,) * 7, + rows: (auto, 30pt), + // Events will be written at the bottom of each day square. + align: bottom, + inset: 5pt, + stroke: (thickness: 0.5pt, dash: "densely-dotted"), + + [Sun], [Mon], [Tue], [Wed], [Thu], [Fri], [Sat], + + // This event will occur on the first Friday (sixth column). + grid.cell(x: 5, fill: yellow.darken(10%))[Call], + + // This event will occur every Monday (second column). + // We have to repeat it 5 times so it occurs every week. + ..(grid.cell(x: 1, fill: red.lighten(50%))[Meet],) * 5, + + // This event will occur at day 19. + grid.cell(x: 4, y: 3, fill: orange.lighten(25%))[Talk], + + // These events will occur at the second week, where available. + grid.cell(y: 2, fill: aqua)[Chat], + grid.cell(y: 2, fill: aqua)[Walk], +) + +--- +// Doc example 2 +#set page(width: auto) +#show table.cell: it => { + if it.x == 0 or it.y == 0 { + set text(white) + strong(it) + } else if it.body == [] { + // Replace empty cells with 'N/A' + pad(rest: it.inset)[_N/A_] + } else { + it + } +} + +#table( + fill: (x, y) => if x == 0 or y == 0 { gray.darken(50%) }, + columns: 4, + [], [Exam 1], [Exam 2], [Exam 3], + ..([John], [Mary], [Jake], [Robert]).map(table.cell.with(x: 0)), + + // Mary got grade A on Exam 3. + table.cell(x: 3, y: 2, fill: green)[A], + + // Everyone got grade A on Exam 2. + ..(table.cell(x: 2, fill: green)[A],) * 4, + + // Robert got grade B on other exams. + ..(table.cell(y: 4, fill: aqua)[B],) * 2, +) + +--- +// Error: 5:3-5:39 cell position too large +#grid( + columns: 3, + rows: 2em, + fill: (x, y) => if calc.odd(x + y) { red.lighten(50%) } else { green }, + grid.cell(y: 6148914691236517206)[a], +) + +--- +// Error: 5:3-5:46 cell position too large +#table( + columns: 3, + rows: 2em, + fill: (x, y) => if calc.odd(x + y) { red.lighten(50%) } else { green }, + table.cell(x: 2, y: 6148914691236517206)[a], +) diff --git a/tests/typ/layout/table-cell.typ b/tests/typ/layout/table-cell.typ index a4d3bba47..d79298aea 100644 --- a/tests/typ/layout/table-cell.typ +++ b/tests/typ/layout/table-cell.typ @@ -100,3 +100,25 @@ [John], [Dog] ) } + +--- +// Style based on position +#{ + show table.cell: it => { + if it.y == 0 { + strong(it) + } else if it.x == 1 { + emph(it) + } else { + it + } + } + table( + columns: 3, + gutter: 3pt, + [Name], [Age], [Info], + [John], [52], [Nice], + [Mary], [50], [Cool], + [Jake], [49], [Epic] + ) +}