diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-09-27 15:11:56 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-09-27 22:23:26 +0200 |
| commit | ed0c80401782a6c42173f0bb123731b798a2cfe1 (patch) | |
| tree | b2069f302a2d4474a23cd69a5f0ab50ab5311643 | |
| parent | e10b3d838a75ea351f8477e07f2e1e647f4539dc (diff) | |
Add spacing capabilities to stack function
- Named argument `spacing` controls spacing between any two template arguments
- Arbitrary linears in the list can produce arbitrary spacing
| -rw-r--r-- | src/library/layout.rs | 98 | ||||
| -rw-r--r-- | src/library/text.rs | 55 | ||||
| -rw-r--r-- | tests/ref/layout/stack.png | bin | 318 -> 371 bytes | |||
| -rw-r--r-- | tests/typ/layout/stack.typ | 12 |
4 files changed, 101 insertions, 64 deletions
diff --git a/src/library/layout.rs b/src/library/layout.rs index e958f3a3..87cedd99 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -188,8 +188,22 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// `stack`: Stack children along an axis. pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + enum Child { + Spacing(Linear), + Any(Template), + } + + castable! { + Child: "linear or template", + Value::Length(v) => Self::Spacing(v.into()), + Value::Relative(v) => Self::Spacing(v.into()), + Value::Linear(v) => Self::Spacing(v), + Value::Template(v) => Self::Any(v), + } + let dir = args.named("dir")?; - let children: Vec<Template> = args.all().collect(); + let spacing = args.named("spacing")?; + let list: Vec<Child> = args.all().collect(); Ok(Value::Template(Template::from_block(move |state| { let mut dirs = Gen::new(None, dir).unwrap_or(state.dirs); @@ -208,10 +222,27 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { aligns = Gen::new(aligns.block, aligns.inline); } - let children = children - .iter() - .map(|child| StackChild::Any(child.to_stack(state).into(), aligns)) - .collect(); + let mut children = vec![]; + let mut delayed = None; + + // Build the list of stack children. + for child in &list { + match child { + Child::Spacing(v) => { + children.push(StackChild::Spacing(*v)); + delayed = None; + } + Child::Any(template) => { + if let Some(v) = delayed { + children.push(StackChild::Spacing(v)); + } + + let node = template.to_stack(state).into(); + children.push(StackChild::Any(node, aligns)); + delayed = spacing; + } + } + } StackNode { dirs, children } }))) @@ -219,27 +250,45 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// `grid`: Arrange children into a grid. pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + castable! { + Vec<TrackSizing>: "array of autos, linears, and fractionals", + Value::Int(count) => vec![TrackSizing::Auto; count.max(0) as usize], + Value::Array(values) => values + .into_iter() + .filter_map(|v| v.cast().ok()) + .collect(), + } + + castable! { + TrackSizing: "auto, linear, or fractional", + Value::Auto => Self::Auto, + Value::Length(v) => Self::Linear(v.into()), + Value::Relative(v) => Self::Linear(v.into()), + Value::Linear(v) => Self::Linear(v), + Value::Fractional(v) => Self::Fractional(v), + } + let columns = args.named("columns")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default(); + let tracks = Gen::new(columns, rows); + + let column_dir = args.named("column-dir")?; + let row_dir = args.named("row-dir")?; let gutter_columns = args.named("gutter-columns")?; let gutter_rows = args.named("gutter-rows")?; - let default = args + let gutter_default = args .named("gutter")? .map(|v| vec![TrackSizing::Linear(v)]) .unwrap_or_default(); - let column_dir = args.named("column-dir")?; - let row_dir = args.named("row-dir")?; - - let children: Vec<Template> = args.all().collect(); - - let tracks = Gen::new(columns, rows); let gutter = Gen::new( - gutter_columns.unwrap_or_else(|| default.clone()), - gutter_rows.unwrap_or(default), + gutter_columns.unwrap_or_else(|| gutter_default.clone()), + gutter_rows.unwrap_or(gutter_default), ); + let children: Vec<Template> = args.all().collect(); + Ok(Value::Template(Template::from_block(move |state| { // If the directions become aligned, try to fix up the direction which // is not user-defined. @@ -269,24 +318,3 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } }))) } - -/// Defines size of rows and columns in a grid. -type Tracks = Vec<TrackSizing>; - -castable! { - Tracks: "array of `auto`s, linears, and fractionals", - Value::Int(count) => vec![TrackSizing::Auto; count.max(0) as usize], - Value::Array(values) => values - .into_iter() - .filter_map(|v| v.cast().ok()) - .collect(), -} - -castable! { - 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/library/text.rs b/src/library/text.rs index b8b3afcd..0deda6e4 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -3,6 +3,33 @@ use crate::layout::{Decoration, LineDecoration, LineKind, Paint}; /// `font`: Configure the font. pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + struct FontDef(Rc<Vec<FontFamily>>); + struct FamilyDef(Rc<Vec<String>>); + + castable! { + FontDef: "font family or array of font families", + Value::Str(string) => Self(Rc::new(vec![FontFamily::Named(string.to_lowercase())])), + Value::Array(values) => Self(Rc::new( + values + .into_iter() + .filter_map(|v| v.cast().ok()) + .collect() + )), + @family: FontFamily => Self(Rc::new(vec![family.clone()])), + } + + castable! { + FamilyDef: "string or array of strings", + Value::Str(string) => Self(Rc::new(vec![string.to_lowercase()])), + Value::Array(values) => Self(Rc::new( + values + .into_iter() + .filter_map(|v| v.cast().ok()) + .map(|string: Str| string.to_lowercase()) + .collect() + )), + } + let list = args.named("family")?.or_else(|| { let families: Vec<_> = args.all().collect(); (!families.is_empty()).then(|| FontDef(Rc::new(families))) @@ -81,34 +108,6 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { }) } -struct FontDef(Rc<Vec<FontFamily>>); - -castable! { - FontDef: "font family or array of font families", - Value::Str(string) => Self(Rc::new(vec![FontFamily::Named(string.to_lowercase())])), - Value::Array(values) => Self(Rc::new( - values - .into_iter() - .filter_map(|v| v.cast().ok()) - .collect() - )), - @family: FontFamily => Self(Rc::new(vec![family.clone()])), -} - -struct FamilyDef(Rc<Vec<String>>); - -castable! { - FamilyDef: "string or array of strings", - Value::Str(string) => Self(Rc::new(vec![string.to_lowercase()])), - Value::Array(values) => Self(Rc::new( - values - .into_iter() - .filter_map(|v| v.cast().ok()) - .map(|string: Str| string.to_lowercase()) - .collect() - )), -} - /// `par`: Configure paragraphs. pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let par_spacing = args.named("spacing")?; diff --git a/tests/ref/layout/stack.png b/tests/ref/layout/stack.png Binary files differindex bc0eb48b..13438d07 100644 --- a/tests/ref/layout/stack.png +++ b/tests/ref/layout/stack.png diff --git a/tests/typ/layout/stack.typ b/tests/typ/layout/stack.typ index 384f574f..aee66d6a 100644 --- a/tests/typ/layout/stack.typ +++ b/tests/typ/layout/stack.typ @@ -24,7 +24,17 @@ #stack(dir: ltr, ..items) --- -// Test overflowing stack. +// Test spacing. +#page(width: 50pt, margins: 0pt) +#par(spacing: 5pt) + +#let x = square(length: 10pt, fill: eastern) +#stack(dir: rtl, spacing: 5pt, x, x, x) +#stack(dir: ltr, x, 20%, x, 20%, x) +#stack(dir: ltr, spacing: 5pt, x, x, 7pt, 3pt, x) + +--- +// Test overflow. #page(width: 50pt, height: 30pt, margins: 0pt) #box(stack( rect(width: 40pt, height: 20pt, fill: conifer), |
