diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-02-07 12:10:52 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-02-07 12:10:52 +0100 |
| commit | 1d5bf56e585c659ed7975cdf6cdfc9c67c29998d (patch) | |
| tree | 7ebe6e2d99869c5d91b00dcf85c8b54b8865cb84 /library/src/layout/enum.rs | |
| parent | a0674990d3c318bb2e42e4d5fad66ea5477a54bf (diff) | |
Remove basics section
Diffstat (limited to 'library/src/layout/enum.rs')
| -rw-r--r-- | library/src/layout/enum.rs | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs new file mode 100644 index 00000000..b1b18680 --- /dev/null +++ b/library/src/layout/enum.rs @@ -0,0 +1,206 @@ +use std::str::FromStr; + +use crate::compute::{Numbering, NumberingPattern}; +use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing}; +use crate::prelude::*; + +/// # Numbered List +/// A numbered list. +/// +/// Displays a sequence of items vertically and numbers them consecutively. +/// +/// ## Example +/// ```example +/// Automatically numbered: +/// + Preparations +/// + Analysis +/// + Conclusions +/// +/// Manually numbered: +/// 2. What is the first step? +/// 5. I am confused. +/// + Moving on ... +/// +/// Function call. +/// #enum[First][Second] +/// ``` +/// +/// ## Syntax +/// This functions also has dedicated syntax: +/// +/// - Starting a line with a plus sign creates an automatically numbered +/// enumeration item. +/// - Start a line with a number followed by a dot creates an explicitly +/// numbered enumeration item. +/// +/// Enumeration items can contain multiple paragraphs and other block-level +/// content. All content that is indented more than an item's plus sign or dot +/// becomes part of that item. +/// +/// ## Parameters +/// - items: `Content` (positional, variadic) +/// The enumeration's children. +/// +/// When using the enum syntax, adjacent items are automatically collected +/// into enumerations, even through constructs like for loops. +/// +/// ```example +/// #for phase in ( +/// "Launch", +/// "Orbit", +/// "Descent", +/// ) [+ #phase] +/// ``` +/// +/// - start: `NonZeroUsize` (named) +/// Which number to start the enumeration with. +/// +/// ```example +/// #enum( +/// start: 3, +/// [Skipping], +/// [Ahead], +/// ) +/// ``` +/// +/// - tight: `bool` (named) +/// If this is `{false}`, the items are spaced apart with +/// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal +/// [leading]($func/par.leading) instead. This makes the enumeration more +/// compact, which can look better if the items are short. +/// +/// ```example +/// + If an enum has a lot of text, and +/// maybe other inline content, it +/// should not be tight anymore. +/// +/// + To make an enum wide, simply +/// insert a blank line between the +/// items. +/// ``` +/// +/// ## Category +/// layout +#[func] +#[capable(Layout)] +#[derive(Debug, Hash)] +pub struct EnumNode { + /// If true, the items are separated by leading instead of list spacing. + pub tight: bool, + /// The individual numbered items. + pub items: StyleVec<(Option<NonZeroUsize>, Content)>, +} + +#[node] +impl EnumNode { + /// How to number the enumeration. Accepts a + /// [numbering pattern or function]($func/numbering). + /// + /// ```example + /// #set enum(numbering: "(a)") + /// + /// + Different + /// + Numbering + /// + Style + /// ``` + #[property(referenced)] + pub const NUMBERING: Numbering = + Numbering::Pattern(NumberingPattern::from_str("1.").unwrap()); + + /// The indentation of each item's label. + #[property(resolve)] + pub const INDENT: Length = Length::zero(); + + /// The space between the numbering and the body of each item. + #[property(resolve)] + pub const BODY_INDENT: Length = Em::new(0.5).into(); + + /// The spacing between the items of a wide (non-tight) enumeration. + /// + /// If set to `{auto}` uses the spacing [below blocks]($func/block.below). + pub const SPACING: Smart<Spacing> = Smart::Auto; + + fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { + let mut number: NonZeroUsize = + args.named("start")?.unwrap_or(NonZeroUsize::new(1).unwrap()); + + Ok(Self { + tight: args.named("tight")?.unwrap_or(true), + items: args + .all()? + .into_iter() + .map(|body| { + let item = (Some(number), body); + number = number.saturating_add(1); + item + }) + .collect(), + } + .pack()) + } + + fn field(&self, name: &str) -> Option<Value> { + match name { + "tight" => Some(Value::Bool(self.tight)), + "items" => Some(Value::Array( + self.items + .items() + .map(|(number, body)| { + Value::Dict(dict! { + "number" => match *number { + Some(n) => Value::Int(n.get() as i64), + None => Value::None, + }, + "body" => Value::Content(body.clone()), + }) + }) + .collect(), + )), + _ => None, + } + } +} + +impl Layout for EnumNode { + fn layout( + &self, + vt: &mut Vt, + styles: StyleChain, + regions: Regions, + ) -> SourceResult<Fragment> { + let numbering = styles.get(Self::NUMBERING); + let indent = styles.get(Self::INDENT); + let body_indent = styles.get(Self::BODY_INDENT); + let gutter = if self.tight { + styles.get(ParNode::LEADING).into() + } else { + styles + .get(Self::SPACING) + .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount) + }; + + let mut cells = vec![]; + let mut number = NonZeroUsize::new(1).unwrap(); + for ((n, item), map) in self.items.iter() { + number = n.unwrap_or(number); + let resolved = numbering.apply(vt.world(), &[number])?.display(); + cells.push(Content::empty()); + cells.push(resolved.styled_with_map(map.clone())); + cells.push(Content::empty()); + cells.push(item.clone().styled_with_map(map.clone())); + number = number.saturating_add(1); + } + + GridNode { + tracks: Axes::with_x(vec![ + TrackSizing::Relative(indent.into()), + TrackSizing::Auto, + TrackSizing::Relative(body_indent.into()), + TrackSizing::Auto, + ]), + gutter: Axes::with_y(vec![gutter.into()]), + cells, + } + .layout(vt, styles, regions) + } +} |
