summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-17 18:56:50 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-17 23:00:30 +0100
commit980f898d553bec35bd94171d47fd86cb13e39b23 (patch)
tree50f5b983b3cf88b0e19a8766b9c3735612d2db36 /src/library
parent261f387535ebe3b784d69893027d8edac01e1ba9 (diff)
Automatic list numbering
Diffstat (limited to 'src/library')
-rw-r--r--src/library/list.rs77
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;