mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
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
This commit is contained in:
parent
e10b3d838a
commit
ed0c804017
@ -188,8 +188,22 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
/// `stack`: Stack children along an axis.
|
/// `stack`: Stack children along an axis.
|
||||||
pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
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 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| {
|
Ok(Value::Template(Template::from_block(move |state| {
|
||||||
let mut dirs = Gen::new(None, dir).unwrap_or(state.dirs);
|
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);
|
aligns = Gen::new(aligns.block, aligns.inline);
|
||||||
}
|
}
|
||||||
|
|
||||||
let children = children
|
let mut children = vec![];
|
||||||
.iter()
|
let mut delayed = None;
|
||||||
.map(|child| StackChild::Any(child.to_stack(state).into(), aligns))
|
|
||||||
.collect();
|
// 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 }
|
StackNode { dirs, children }
|
||||||
})))
|
})))
|
||||||
@ -219,27 +250,45 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
|||||||
|
|
||||||
/// `grid`: Arrange children into a grid.
|
/// `grid`: Arrange children into a grid.
|
||||||
pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
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 columns = args.named("columns")?.unwrap_or_default();
|
||||||
let rows = args.named("rows")?.unwrap_or_default();
|
let rows = args.named("rows")?.unwrap_or_default();
|
||||||
|
let tracks = Gen::new(columns, rows);
|
||||||
let gutter_columns = args.named("gutter-columns")?;
|
|
||||||
let gutter_rows = args.named("gutter-rows")?;
|
|
||||||
let default = args
|
|
||||||
.named("gutter")?
|
|
||||||
.map(|v| vec![TrackSizing::Linear(v)])
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let column_dir = args.named("column-dir")?;
|
let column_dir = args.named("column-dir")?;
|
||||||
let row_dir = args.named("row-dir")?;
|
let row_dir = args.named("row-dir")?;
|
||||||
|
|
||||||
let children: Vec<Template> = args.all().collect();
|
let gutter_columns = args.named("gutter-columns")?;
|
||||||
|
let gutter_rows = args.named("gutter-rows")?;
|
||||||
|
let gutter_default = args
|
||||||
|
.named("gutter")?
|
||||||
|
.map(|v| vec![TrackSizing::Linear(v)])
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let tracks = Gen::new(columns, rows);
|
|
||||||
let gutter = Gen::new(
|
let gutter = Gen::new(
|
||||||
gutter_columns.unwrap_or_else(|| default.clone()),
|
gutter_columns.unwrap_or_else(|| gutter_default.clone()),
|
||||||
gutter_rows.unwrap_or(default),
|
gutter_rows.unwrap_or(gutter_default),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let children: Vec<Template> = args.all().collect();
|
||||||
|
|
||||||
Ok(Value::Template(Template::from_block(move |state| {
|
Ok(Value::Template(Template::from_block(move |state| {
|
||||||
// If the directions become aligned, try to fix up the direction which
|
// If the directions become aligned, try to fix up the direction which
|
||||||
// is not user-defined.
|
// 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),
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,33 @@ use crate::layout::{Decoration, LineDecoration, LineKind, Paint};
|
|||||||
|
|
||||||
/// `font`: Configure the font.
|
/// `font`: Configure the font.
|
||||||
pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
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 list = args.named("family")?.or_else(|| {
|
||||||
let families: Vec<_> = args.all().collect();
|
let families: Vec<_> = args.all().collect();
|
||||||
(!families.is_empty()).then(|| FontDef(Rc::new(families)))
|
(!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.
|
/// `par`: Configure paragraphs.
|
||||||
pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let par_spacing = args.named("spacing")?;
|
let par_spacing = args.named("spacing")?;
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 318 B After Width: | Height: | Size: 371 B |
@ -24,7 +24,17 @@
|
|||||||
#stack(dir: ltr, ..items)
|
#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)
|
#page(width: 50pt, height: 30pt, margins: 0pt)
|
||||||
#box(stack(
|
#box(stack(
|
||||||
rect(width: 40pt, height: 20pt, fill: conifer),
|
rect(width: 40pt, height: 20pt, fill: conifer),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user