Some table improvements [More flexible tables] (#3473)

This commit is contained in:
PgBiel 2024-02-22 05:42:10 -03:00 committed by GitHub
parent 92a2f01b74
commit a8671962d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 154 additions and 33 deletions

View File

@ -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,
);

View File

@ -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))
}

View File

@ -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 {

View File

@ -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 {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -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[])

View File

@ -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,
@ -344,6 +344,22 @@
table.vline(x: 3)
)
---
// 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(

View File

@ -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(

View File

@ -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[])

View File

@ -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(