diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-17 18:56:50 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-17 23:00:30 +0100 |
| commit | 980f898d553bec35bd94171d47fd86cb13e39b23 (patch) | |
| tree | 50f5b983b3cf88b0e19a8766b9c3735612d2db36 /src/library | |
| parent | 261f387535ebe3b784d69893027d8edac01e1ba9 (diff) | |
Automatic list numbering
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/list.rs | 77 |
1 files changed, 58 insertions, 19 deletions
diff --git a/src/library/list.rs b/src/library/list.rs index 08ccfc18..1ec89da2 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -1,15 +1,27 @@ //! Unordered (bulleted) and ordered (numbered) lists. use super::prelude::*; -use super::{GridNode, TextNode, TrackSizing}; +use super::{GridNode, ParNode, TextNode, TrackSizing}; /// An unordered or ordered list. #[derive(Debug, Hash)] pub struct ListNode<const L: Labelling> { + /// The individual bulleted or numbered items. + pub items: Vec<ListItem>, + /// If true, there is paragraph spacing between the items, if false + /// there is list spacing between the items. + pub wide: bool, + /// Where the list starts. + pub start: usize, +} + +/// An item in a list. +#[derive(Debug, Clone, PartialEq, Hash)] +pub struct ListItem { /// The number of the item. pub number: Option<usize>, /// The node that produces the item's body. - pub child: LayoutNode, + pub body: LayoutNode, } #[class] @@ -18,28 +30,54 @@ impl<const L: Labelling> ListNode<L> { pub const LABEL_INDENT: Linear = Relative::new(0.0).into(); /// The space between the label and the body of each item. pub const BODY_INDENT: Linear = Relative::new(0.5).into(); + /// The spacing between the list items of a non-wide list. + pub const SPACING: Linear = Linear::zero(); fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { - Ok(args - .all()? - .into_iter() - .enumerate() - .map(|(i, child)| Template::show(Self { number: Some(1 + i), child })) - .sum()) + Ok(Template::show(Self { + items: args + .all()? + .into_iter() + .map(|body| ListItem { number: None, body }) + .collect(), + wide: args.named("wide")?.unwrap_or(false), + start: args.named("start")?.unwrap_or(0), + })) } } impl<const L: Labelling> Show for ListNode<L> { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> { + let mut children = vec![]; + let mut number = self.start; + + for item in &self.items { + number = item.number.unwrap_or(number); + + let label = match L { + UNORDERED => '•'.into(), + ORDERED | _ => format_eco!("{}.", number), + }; + + children.push(LayoutNode::default()); + children.push(Template::Text(label).pack()); + children.push(LayoutNode::default()); + children.push(item.body.clone()); + + number += 1; + } + let em = styles.get(TextNode::SIZE).abs; let label_indent = styles.get(Self::LABEL_INDENT).resolve(em); let body_indent = styles.get(Self::BODY_INDENT).resolve(em); - - let label = match L { - UNORDERED => '•'.into(), - ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)), + let leading = styles.get(ParNode::LEADING); + let spacing = if self.wide { + styles.get(ParNode::SPACING) + } else { + styles.get(Self::SPACING) }; + let gutter = (leading + spacing).resolve(em); Ok(Template::block(GridNode { tracks: Spec::with_x(vec![ TrackSizing::Linear(label_indent.into()), @@ -47,17 +85,18 @@ impl<const L: Labelling> Show for ListNode<L> { TrackSizing::Linear(body_indent.into()), TrackSizing::Auto, ]), - gutter: Spec::default(), - children: vec![ - LayoutNode::default(), - Template::Text(label).pack(), - LayoutNode::default(), - self.child.clone(), - ], + gutter: Spec::with_y(vec![TrackSizing::Linear(gutter.into())]), + children, })) } } +impl<const L: Labelling> From<ListItem> for ListNode<L> { + fn from(item: ListItem) -> Self { + Self { items: vec![item], wide: false, start: 1 } + } +} + /// How to label a list. pub type Labelling = usize; |
