Table fill closure

This commit is contained in:
Laurenz 2022-05-16 16:04:40 +02:00
parent bc1bc91a33
commit 6536e9e069
3 changed files with 54 additions and 14 deletions

View File

@ -14,12 +14,9 @@ pub struct TableNode {
#[node(showable)]
impl TableNode {
/// The primary cell fill color.
#[property(shorthand(fill))]
pub const PRIMARY: Option<Paint> = None;
/// The secondary cell fill color.
#[property(shorthand(fill))]
pub const SECONDARY: Option<Paint> = None;
/// How to fill the cells.
#[property(referenced)]
pub const FILL: Celled<Option<Paint>> = Celled::Value(None);
/// How to stroke the cells.
#[property(resolve, fold)]
pub const STROKE: Option<RawStroke> = Some(RawStroke::default());
@ -71,9 +68,8 @@ impl Show for TableNode {
}
}
fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
let primary = styles.get(Self::PRIMARY);
let secondary = styles.get(Self::SECONDARY);
fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
let fill = styles.get(Self::FILL);
let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default);
let padding = styles.get(Self::PADDING);
@ -92,13 +88,13 @@ impl Show for TableNode {
let x = i % cols;
let y = i / cols;
if let Some(fill) = [primary, secondary][(x + y) % 2] {
if let Some(fill) = fill.resolve(ctx, x, y)? {
child = child.filled(fill);
}
child
Ok(child)
})
.collect();
.collect::<TypResult<_>>()?;
Ok(Content::block(GridNode {
tracks: self.tracks.clone(),
@ -116,3 +112,43 @@ impl Show for TableNode {
Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
}
}
/// A value that can be configured per cell.
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Celled<T> {
/// A bare value, the same for all cells.
Value(T),
/// A closure mapping from cell coordinates to a value.
Func(Func, Span),
}
impl<T: Cast + Clone> Celled<T> {
/// Resolve the value based on the cell position.
pub fn resolve(&self, ctx: &mut Context, x: usize, y: usize) -> TypResult<T> {
Ok(match self {
Self::Value(value) => value.clone(),
Self::Func(func, span) => {
let args = Args::from_values(*span, [
Value::Int(x as i64),
Value::Int(y as i64),
]);
func.call(ctx, args)?.cast().at(*span)?
}
})
}
}
impl<T: Cast> Cast<Spanned<Value>> for Celled<T> {
fn is(value: &Spanned<Value>) -> bool {
matches!(&value.v, Value::Func(_)) || T::is(&value.v)
}
fn cast(value: Spanned<Value>) -> StrResult<Self> {
match value.v {
Value::Func(v) => Ok(Self::Func(v, value.span)),
v => T::cast(v)
.map(Self::Value)
.map_err(|msg| with_alternative(msg, "function")),
}
}
}

View File

@ -2,7 +2,7 @@
---
#set page(height: 70pt)
#set table(primary: rgb("aaa"), secondary: none)
#set table(fill: (x, y) => if even(x + y) { rgb("aaa") })
#table(
columns: (1fr,) * 3,
@ -16,3 +16,7 @@
---
// Ref: false
#table()
---
// Error: 14-19 expected color or none or function, found string
#table(fill: "hey")

View File

@ -26,7 +26,7 @@ Fourth
#set par(spacing: 100pt)
#set table(around: 5pt)
Hello
#table(columns: 4, secondary: silver)[A][B][C][D]
#table(columns: 4, fill: (x, y) => if odd(x + y) { silver })[A][B][C][D]
---
// While we're at it, test the larger block spacing wins.