summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPgBiel <9021226+PgBiel@users.noreply.github.com>2024-02-22 05:42:10 -0300
committerGitHub <noreply@github.com>2024-02-22 08:42:10 +0000
commita8671962d6cfde985c49e0ca8ebc27ed4c9118cc (patch)
treef9fb06446bb9e40065b6d31d4056ea4529b8b2e8
parent92a2f01b7488432df8bd6ed37b7565e39cd76f93 (diff)
Some table improvements [More flexible tables] (#3473)
-rw-r--r--crates/typst/src/layout/grid/layout.rs6
-rw-r--r--crates/typst/src/layout/grid/lines.rs45
-rw-r--r--crates/typst/src/layout/grid/mod.rs26
-rw-r--r--crates/typst/src/model/table.rs32
-rw-r--r--tests/ref/layout/grid-stroke.pngbin48961 -> 48899 bytes
-rw-r--r--tests/ref/layout/grid-styling.pngbin27200 -> 30784 bytes
-rw-r--r--tests/ref/layout/table.pngbin22446 -> 26146 bytes
-rw-r--r--tests/typ/layout/grid-cell.typ4
-rw-r--r--tests/typ/layout/grid-stroke.typ18
-rw-r--r--tests/typ/layout/grid-styling.typ26
-rw-r--r--tests/typ/layout/table-cell.typ4
-rw-r--r--tests/typ/layout/table.typ26
12 files changed, 154 insertions, 33 deletions
diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs
index 790aedac..543d736d 100644
--- a/crates/typst/src/layout/grid/layout.rs
+++ b/crates/typst/src/layout/grid/layout.rs
@@ -332,7 +332,7 @@ impl CellGrid {
items: I,
fill: &Celled<Option<Paint>>,
align: &Celled<Smart<Alignment>>,
- inset: Sides<Option<Rel<Length>>>,
+ inset: &Celled<Sides<Option<Rel<Length>>>>,
stroke: &ResolvedCelled<Sides<Option<Option<Arc<Stroke>>>>>,
engine: &mut Engine,
styles: StyleChain,
@@ -486,7 +486,7 @@ impl CellGrid {
y,
&fill.resolve(engine, x, y)?,
align.resolve(engine, x, y)?,
- inset,
+ inset.resolve(engine, x, y)?,
stroke.resolve(engine, styles, x, y)?,
styles,
);
@@ -572,7 +572,7 @@ impl CellGrid {
y,
&fill.resolve(engine, x, y)?,
align.resolve(engine, x, y)?,
- inset,
+ inset.resolve(engine, x, y)?,
stroke.resolve(engine, styles, x, y)?,
styles,
);
diff --git a/crates/typst/src/layout/grid/lines.rs b/crates/typst/src/layout/grid/lines.rs
index 9a4da26d..6eb43c9a 100644
--- a/crates/typst/src/layout/grid/lines.rs
+++ b/crates/typst/src/layout/grid/lines.rs
@@ -130,7 +130,7 @@ where
&CellGrid,
usize,
usize,
- Option<Arc<Stroke<Abs>>>,
+ Option<Option<Arc<Stroke<Abs>>>>,
) -> Option<(Arc<Stroke<Abs>>, StrokePriority)>
+ 'grid,
I: IntoIterator<Item = (usize, Abs)>,
@@ -204,7 +204,7 @@ where
// Get the expected line stroke at this track by folding the
// strokes of each user-specified line (with priority to the
// user-specified line specified last).
- let stroke = lines
+ let mut line_strokes = lines
.iter()
.filter(|line| {
line.position == expected_line_position
@@ -222,8 +222,15 @@ where
})
.unwrap_or_else(|| track >= gutter_factor * line.start)
})
- .map(|line| line.stroke.clone())
- .fold(None, |acc, line_stroke| line_stroke.fold(acc));
+ .map(|line| line.stroke.clone());
+
+ // Distinguish between unspecified stroke (None, if no lines
+ // were matched above) and specified stroke of None (Some(None),
+ // if some lines were matched and the one specified last had a
+ // stroke of None) by conditionally folding after 'next()'.
+ let line_stroke = line_strokes.next().map(|first_stroke| {
+ line_strokes.fold(first_stroke, |acc, line_stroke| line_stroke.fold(acc))
+ });
// The function shall determine if it is appropriate to draw
// the line at this position or not (i.e. whether or not it
@@ -240,7 +247,7 @@ where
// (which indicates, in the context of 'std::iter::from_fn', that
// our iterator isn't over yet, and this should be its next value).
if let Some((stroke, priority)) =
- line_stroke_at_track(grid, index, track, stroke)
+ line_stroke_at_track(grid, index, track, line_stroke)
{
// We should draw at this position. Let's check if we were
// already drawing in the previous position.
@@ -301,9 +308,10 @@ where
}
/// Returns the correct stroke with which to draw a vline right before column
-/// 'x' when going through row 'y', given the stroke of the user-specified line
-/// at this position, if any. Also returns the stroke's drawing priority, which
-/// depends on its source.
+/// `x` when going through row `y`, given the stroke of the user-specified line
+/// at this position, if any (note that a stroke of `None` is unspecified,
+/// while `Some(None)` means specified to remove any stroke at this position).
+/// Also returns the stroke's drawing priority, which depends on its source.
///
/// If the vline would go through a colspan, returns None (shouldn't be drawn).
/// If the one (when at the border) or two (otherwise) cells to the left and
@@ -317,12 +325,12 @@ where
/// stroke, as defined by user-specified lines (if any), is returned.
///
/// The priority associated with the returned stroke follows the rules
-/// described in the docs for 'generate_line_segment'.
+/// described in the docs for `generate_line_segment`.
pub(super) fn vline_stroke_at_row(
grid: &CellGrid,
x: usize,
y: usize,
- stroke: Option<Arc<Stroke<Abs>>>,
+ stroke: Option<Option<Arc<Stroke<Abs>>>>,
) -> Option<(Arc<Stroke<Abs>>, StrokePriority)> {
if x != 0 && x != grid.cols.len() {
// When the vline isn't at the border, we need to check if a colspan would
@@ -397,15 +405,16 @@ pub(super) fn vline_stroke_at_row(
// Fold the line stroke and folded cell strokes, if possible.
// Give priority to the explicit line stroke.
// Otherwise, use whichever of the two isn't 'none' or unspecified.
- let final_stroke = stroke.fold_or(cell_stroke);
+ let final_stroke = stroke.fold_or(Some(cell_stroke)).flatten();
final_stroke.zip(Some(priority))
}
-/// Returns the correct stroke with which to draw a hline on top of row 'y'
-/// when going through column 'x', given the stroke of the user-specified line
-/// at this position, if any. Also returns the stroke's drawing priority, which
-/// depends on its source.
+/// Returns the correct stroke with which to draw a hline on top of row `y`
+/// when going through column `x`, given the stroke of the user-specified line
+/// at this position, if any (note that a stroke of `None` is unspecified,
+/// while `Some(None)` means specified to remove any stroke at this position).
+/// Also returns the stroke's drawing priority, which depends on its source.
///
/// If the one (when at the border) or two (otherwise) cells above and below
/// the hline have bottom and top stroke overrides, respectively, then the
@@ -418,12 +427,12 @@ pub(super) fn vline_stroke_at_row(
/// defined by user-specified lines (if any), is directly returned.
///
/// The priority associated with the returned stroke follows the rules
-/// described in the docs for 'generate_line_segment'.
+/// described in the docs for `generate_line_segment`.
pub(super) fn hline_stroke_at_column(
grid: &CellGrid,
y: usize,
x: usize,
- stroke: Option<Arc<Stroke<Abs>>>,
+ stroke: Option<Option<Arc<Stroke<Abs>>>>,
) -> Option<(Arc<Stroke<Abs>>, StrokePriority)> {
// There are no rowspans yet, so no need to add a check here. The line will
// always be drawn, if it has a stroke.
@@ -505,7 +514,7 @@ pub(super) fn hline_stroke_at_column(
// Fold the line stroke and folded cell strokes, if possible.
// Give priority to the explicit line stroke.
// Otherwise, use whichever of the two isn't 'none' or unspecified.
- let final_stroke = stroke.fold_or(cell_stroke);
+ let final_stroke = stroke.fold_or(Some(cell_stroke)).flatten();
final_stroke.zip(Some(priority))
}
diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs
index 0ddecd84..fc884e80 100644
--- a/crates/typst/src/layout/grid/mod.rs
+++ b/crates/typst/src/layout/grid/mod.rs
@@ -10,7 +10,7 @@ use std::sync::Arc;
use ecow::eco_format;
use smallvec::{smallvec, SmallVec};
-use crate::diag::{SourceResult, StrResult, Trace, Tracepoint};
+use crate::diag::{bail, SourceResult, StrResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value,
@@ -19,6 +19,7 @@ use crate::layout::{
Abs, AlignElem, Alignment, Axes, Dir, Fragment, LayoutMultiple, Length,
OuterHAlignment, OuterVAlignment, Regions, Rel, Sides, Sizing,
};
+use crate::model::{TableCell, TableHLine, TableVLine};
use crate::syntax::Span;
use crate::text::TextElem;
use crate::util::NonZeroExt;
@@ -270,7 +271,7 @@ pub struct GridElem {
/// )
/// ```
#[fold]
- pub inset: Sides<Option<Rel<Length>>>,
+ pub inset: Celled<Sides<Option<Rel<Length>>>>,
/// The contents of the grid cells, plus any extra grid lines specified
/// with the [`grid.hline`]($grid.hline) and [`grid.vline`]($grid.vline)
@@ -353,7 +354,7 @@ impl LayoutMultiple for Packed<GridElem> {
items,
fill,
align,
- inset,
+ &inset,
&stroke,
engine,
styles,
@@ -395,7 +396,24 @@ cast! {
Self::VLine(vline) => vline.into_value(),
Self::Cell(cell) => cell.into_value(),
},
- v: Content => v.into(),
+ v: Content => {
+ if v.is::<TableCell>() {
+ bail!(
+ "cannot use `table.cell` as a grid cell; use `grid.cell` instead"
+ );
+ }
+ if v.is::<TableHLine>() {
+ bail!(
+ "cannot use `table.hline` as a grid line; use `grid.hline` instead"
+ );
+ }
+ if v.is::<TableVLine>() {
+ bail!(
+ "cannot use `table.vline` as a grid line; use `grid.vline` instead"
+ );
+ }
+ v.into()
+ }
}
impl From<Content> for GridChild {
diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs
index ae4dbce7..a51fd1e7 100644
--- a/crates/typst/src/model/table.rs
+++ b/crates/typst/src/model/table.rs
@@ -3,15 +3,16 @@ use std::sync::Arc;
use ecow::eco_format;
-use crate::diag::{SourceResult, Trace, Tracepoint};
+use crate::diag::{bail, SourceResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Content, Fold, Packed, Show, Smart, StyleChain,
};
use crate::layout::{
show_grid_cell, Abs, Alignment, Axes, Cell, CellGrid, Celled, Dir, Fragment,
- GridItem, GridLayouter, LayoutMultiple, Length, LinePosition, OuterHAlignment,
- OuterVAlignment, Regions, Rel, ResolvableCell, Sides, TrackSizings,
+ GridCell, GridHLine, GridItem, GridLayouter, GridVLine, LayoutMultiple, Length,
+ LinePosition, OuterHAlignment, OuterVAlignment, Regions, Rel, ResolvableCell, Sides,
+ TrackSizings,
};
use crate::model::Figurable;
use crate::syntax::Span;
@@ -200,8 +201,8 @@ pub struct TableElem {
/// )
/// ```
#[fold]
- #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
- pub inset: Sides<Option<Rel<Length>>>,
+ #[default(Celled::Value(Sides::splat(Some(Abs::pt(5.0).into()))))]
+ pub inset: Celled<Sides<Option<Rel<Length>>>>,
/// The contents of the table cells, plus any extra table lines specified
/// with the [`table.hline`]($table.hline) and
@@ -282,7 +283,7 @@ impl LayoutMultiple for Packed<TableElem> {
items,
fill,
align,
- inset,
+ &inset,
&stroke,
engine,
styles,
@@ -349,7 +350,24 @@ cast! {
Self::VLine(vline) => vline.into_value(),
Self::Cell(cell) => cell.into_value(),
},
- v: Content => v.into(),
+ v: Content => {
+ if v.is::<GridCell>() {
+ bail!(
+ "cannot use `grid.cell` as a table cell; use `table.cell` instead"
+ );
+ }
+ if v.is::<GridHLine>() {
+ bail!(
+ "cannot use `grid.hline` as a table line; use `table.hline` instead"
+ );
+ }
+ if v.is::<GridVLine>() {
+ bail!(
+ "cannot use `grid.vline` as a table line; use `table.vline` instead"
+ );
+ }
+ v.into()
+ }
}
impl From<Content> for TableChild {
diff --git a/tests/ref/layout/grid-stroke.png b/tests/ref/layout/grid-stroke.png
index c31519e6..0f0b562a 100644
--- a/tests/ref/layout/grid-stroke.png
+++ b/tests/ref/layout/grid-stroke.png
Binary files differ
diff --git a/tests/ref/layout/grid-styling.png b/tests/ref/layout/grid-styling.png
index c626d2e7..dc50dd90 100644
--- a/tests/ref/layout/grid-styling.png
+++ b/tests/ref/layout/grid-styling.png
Binary files differ
diff --git a/tests/ref/layout/table.png b/tests/ref/layout/table.png
index f2a9d104..ddd0e043 100644
--- a/tests/ref/layout/table.png
+++ b/tests/ref/layout/table.png
Binary files differ
diff --git a/tests/typ/layout/grid-cell.typ b/tests/typ/layout/grid-cell.typ
index 425d036c..a812fefc 100644
--- a/tests/typ/layout/grid-cell.typ
+++ b/tests/typ/layout/grid-cell.typ
@@ -127,3 +127,7 @@
[Jake], [49], [Epic]
)
}
+
+---
+// Error: 7-19 cannot use `table.cell` as a grid cell; use `grid.cell` instead
+#grid(table.cell[])
diff --git a/tests/typ/layout/grid-stroke.typ b/tests/typ/layout/grid-stroke.typ
index 87389ad5..51b810d9 100644
--- a/tests/typ/layout/grid-stroke.typ
+++ b/tests/typ/layout/grid-stroke.typ
@@ -139,7 +139,7 @@
---
// Line specification order priority
// The last line should be blue, not red.
-// The middle line should have disappeared.
+// The middle aqua line should be gone due to the 'none' override.
#grid(
columns: 2,
inset: 2pt,
@@ -345,6 +345,22 @@
)
---
+// Error: 7-20 cannot use `table.hline` as a grid line; use `grid.hline` instead
+#grid(table.hline())
+
+---
+// Error: 7-20 cannot use `table.vline` as a grid line; use `grid.vline` instead
+#grid(table.vline())
+
+---
+// Error: 8-20 cannot use `grid.hline` as a table line; use `table.hline` instead
+#table(grid.hline())
+
+---
+// Error: 8-20 cannot use `grid.vline` as a table line; use `table.vline` instead
+#table(grid.vline())
+
+---
// Error: 3:3-3:31 line cannot end before it starts
#grid(
columns: 3,
diff --git a/tests/typ/layout/grid-styling.typ b/tests/typ/layout/grid-styling.typ
index e076d0c4..f83c3cc4 100644
--- a/tests/typ/layout/grid-styling.typ
+++ b/tests/typ/layout/grid-styling.typ
@@ -88,6 +88,32 @@ a
[B],
)
+#grid(
+ columns: 3,
+ fill: (x, y) => (if y == 0 { aqua } else { orange }).darken(x * 15%),
+ inset: (x, y) => (left: if x == 0 { 0pt } else { 5pt }, right: if x == 0 { 5pt } else { 0pt }, y: if y == 0 { 0pt } else { 5pt }),
+ [A], [B], [C],
+ [A], [B], [C],
+)
+
+#grid(
+ columns: 3,
+ inset: (0pt, 5pt, 10pt),
+ fill: (x, _) => aqua.darken(x * 15%),
+ [A], [B], [C],
+)
+
+---
+// Test inset folding
+#set grid(inset: 10pt)
+#set grid(inset: (left: 0pt))
+
+#grid(
+ fill: red,
+ inset: (right: 0pt),
+ grid.cell(inset: (top: 0pt))[a]
+)
+
---
// Test interaction with gutters.
#grid(
diff --git a/tests/typ/layout/table-cell.typ b/tests/typ/layout/table-cell.typ
index d79298ae..cbe0b9f0 100644
--- a/tests/typ/layout/table-cell.typ
+++ b/tests/typ/layout/table-cell.typ
@@ -122,3 +122,7 @@
[Jake], [49], [Epic]
)
}
+
+---
+// Error: 8-19 cannot use `grid.cell` as a table cell; use `table.cell` instead
+#table(grid.cell[])
diff --git a/tests/typ/layout/table.typ b/tests/typ/layout/table.typ
index 1b250aa4..89a7dab5 100644
--- a/tests/typ/layout/table.typ
+++ b/tests/typ/layout/table.typ
@@ -61,6 +61,32 @@
[B],
)
+#table(
+ columns: 3,
+ fill: (x, y) => (if y == 0 { aqua } else { orange }).darken(x * 15%),
+ inset: (x, y) => (left: if x == 0 { 0pt } else { 5pt }, right: if x == 0 { 5pt } else { 0pt }, y: if y == 0 { 0pt } else { 5pt }),
+ [A], [B], [C],
+ [A], [B], [C],
+)
+
+#table(
+ columns: 3,
+ inset: (0pt, 5pt, 10pt),
+ fill: (x, _) => aqua.darken(x * 15%),
+ [A], [B], [C],
+)
+
+---
+// Test inset folding
+#set table(inset: 10pt)
+#set table(inset: (left: 0pt))
+
+#table(
+ fill: red,
+ inset: (right: 0pt),
+ table.cell(inset: (top: 0pt))[a]
+)
+
---
// Test interaction with gutters.
#table(