summaryrefslogtreecommitdiff
path: root/library/src/structure
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/structure')
-rw-r--r--library/src/structure/document.rs48
-rw-r--r--library/src/structure/heading.rs62
-rw-r--r--library/src/structure/list.rs272
-rw-r--r--library/src/structure/mod.rs13
-rw-r--r--library/src/structure/reference.rs26
-rw-r--r--library/src/structure/table.rs136
6 files changed, 0 insertions, 557 deletions
diff --git a/library/src/structure/document.rs b/library/src/structure/document.rs
deleted file mode 100644
index e52c92ad..00000000
--- a/library/src/structure/document.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use crate::layout::{LayoutRoot, PageNode};
-use crate::prelude::*;
-
-/// The root node of the model.
-#[derive(Hash)]
-pub struct DocumentNode(pub StyleVec<PageNode>);
-
-#[node(LayoutRoot)]
-impl DocumentNode {
- /// The document's title.
- #[property(referenced)]
- pub const TITLE: Option<EcoString> = None;
-
- /// The document's author.
- #[property(referenced)]
- pub const AUTHOR: Option<EcoString> = None;
-}
-
-impl LayoutRoot for DocumentNode {
- /// Layout the document into a sequence of frames, one per page.
- fn layout_root(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Document> {
- let mut pages = vec![];
- for (page, map) in self.0.iter() {
- let number = 1 + pages.len();
- let fragment = page.layout(world, number, styles.chain(map))?;
- pages.extend(fragment);
- }
-
- Ok(Document {
- metadata: Metadata {
- title: styles.get(Self::TITLE).clone(),
- author: styles.get(Self::AUTHOR).clone(),
- },
- pages,
- })
- }
-}
-
-impl Debug for DocumentNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("Document ")?;
- self.0.fmt(f)
- }
-}
diff --git a/library/src/structure/heading.rs b/library/src/structure/heading.rs
deleted file mode 100644
index b251f27b..00000000
--- a/library/src/structure/heading.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use typst::font::FontWeight;
-
-use crate::layout::{BlockNode, VNode};
-use crate::prelude::*;
-use crate::text::{TextNode, TextSize};
-
-/// A section heading.
-#[derive(Debug, Hash)]
-pub struct HeadingNode {
- /// The logical nesting depth of the section, starting from one. In the
- /// default style, this controls the text size of the heading.
- pub level: NonZeroUsize,
- /// The heading's contents.
- pub body: Content,
-}
-
-#[node(Show, Finalize)]
-impl HeadingNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self {
- body: args.expect("body")?,
- level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "level" => Some(Value::Int(self.level.get() as i64)),
- "body" => Some(Value::Content(self.body.clone())),
- _ => None,
- }
- }
-}
-
-impl Show for HeadingNode {
- fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
- BlockNode(self.body.clone()).pack()
- }
-}
-
-impl Finalize for HeadingNode {
- fn finalize(&self, realized: Content) -> Content {
- let scale = match self.level.get() {
- 1 => 1.4,
- 2 => 1.2,
- _ => 1.0,
- };
-
- let size = Em::new(scale);
- let above = Em::new(if self.level.get() == 1 { 1.8 } else { 1.44 }) / scale;
- let below = Em::new(0.66) / scale;
-
- let mut map = StyleMap::new();
- map.set(TextNode::SIZE, TextSize(size.into()));
- map.set(TextNode::WEIGHT, FontWeight::BOLD);
- map.set(BlockNode::ABOVE, VNode::block_around(above.into()));
- map.set(BlockNode::BELOW, VNode::block_around(below.into()));
- map.set(BlockNode::STICKY, true);
- realized.styled_with_map(map)
- }
-}
diff --git a/library/src/structure/list.rs b/library/src/structure/list.rs
deleted file mode 100644
index b51284a8..00000000
--- a/library/src/structure/list.rs
+++ /dev/null
@@ -1,272 +0,0 @@
-use crate::base::NumberingPattern;
-use crate::layout::{BlockNode, GridNode, HNode, Spacing, TrackSizing};
-use crate::prelude::*;
-use crate::text::{ParNode, SpaceNode, TextNode};
-
-/// An unordered (bulleted) or ordered (numbered) list.
-#[derive(Debug, Hash)]
-pub struct ListNode<const L: ListKind = LIST> {
- /// If true, the items are separated by leading instead of list spacing.
- pub tight: bool,
- /// The individual bulleted or numbered items.
- pub items: StyleVec<ListItem>,
-}
-
-/// An ordered list.
-pub type EnumNode = ListNode<ENUM>;
-
-/// A description list.
-pub type DescNode = ListNode<DESC>;
-
-#[node(Layout)]
-impl<const L: ListKind> ListNode<L> {
- /// How the list is labelled.
- #[property(referenced)]
- pub const LABEL: Label = Label::Default;
- /// The indentation of each item's label.
- #[property(resolve)]
- pub const INDENT: Length = Length::zero();
- /// The space between the label and the body of each item.
- #[property(resolve)]
- pub const BODY_INDENT: Length = Em::new(match L {
- LIST | ENUM => 0.5,
- DESC | _ => 1.0,
- })
- .into();
- /// The spacing between the items of a wide (non-tight) list.
- pub const SPACING: Smart<Spacing> = Smart::Auto;
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let items = match L {
- LIST => args
- .all()?
- .into_iter()
- .map(|body| ListItem::List(Box::new(body)))
- .collect(),
- ENUM => {
- let mut number: usize = args.named("start")?.unwrap_or(1);
- args.all()?
- .into_iter()
- .map(|body| {
- let item = ListItem::Enum(Some(number), Box::new(body));
- number += 1;
- item
- })
- .collect()
- }
- DESC | _ => args
- .all()?
- .into_iter()
- .map(|item| ListItem::Desc(Box::new(item)))
- .collect(),
- };
-
- Ok(Self { tight: args.named("tight")?.unwrap_or(true), items }.pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "tight" => Some(Value::Bool(self.tight)),
- "items" => {
- Some(Value::Array(self.items.items().map(|item| item.encode()).collect()))
- }
- _ => None,
- }
- }
-}
-
-impl<const L: ListKind> Layout for ListNode<L> {
- fn layout(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- regions: &Regions,
- ) -> SourceResult<Fragment> {
- let mut cells = vec![];
- let mut number = 1;
-
- let label = styles.get(Self::LABEL);
- 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)
- };
-
- for (item, map) in self.items.iter() {
- if let &ListItem::Enum(Some(n), _) = item {
- number = n;
- }
-
- cells.push(Content::empty());
-
- let label = if L == LIST || L == ENUM {
- label.resolve(world, L, number)?.styled_with_map(map.clone())
- } else {
- Content::empty()
- };
-
- cells.push(label);
- cells.push(Content::empty());
-
- let body = match &item {
- ListItem::List(body) => body.as_ref().clone(),
- ListItem::Enum(_, body) => body.as_ref().clone(),
- ListItem::Desc(item) => Content::sequence(vec![
- HNode { amount: (-body_indent).into(), weak: false }.pack(),
- (item.term.clone() + TextNode::packed(':')).strong(),
- SpaceNode.pack(),
- item.body.clone(),
- ]),
- };
-
- cells.push(body.styled_with_map(map.clone()));
- number += 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(world, styles, regions)
- }
-}
-
-/// An item in a list.
-#[derive(Debug, Clone, Hash)]
-pub enum ListItem {
- /// An item of an unordered list.
- List(Box<Content>),
- /// An item of an ordered list.
- Enum(Option<usize>, Box<Content>),
- /// An item of a description list.
- Desc(Box<DescItem>),
-}
-
-impl ListItem {
- /// What kind of item this is.
- pub fn kind(&self) -> ListKind {
- match self {
- Self::List(_) => LIST,
- Self::Enum { .. } => ENUM,
- Self::Desc { .. } => DESC,
- }
- }
-
- /// Encode the item into a value.
- fn encode(&self) -> Value {
- match self {
- Self::List(body) => Value::Content(body.as_ref().clone()),
- Self::Enum(number, body) => Value::Dict(dict! {
- "number" => match *number {
- Some(n) => Value::Int(n as i64),
- None => Value::None,
- },
- "body" => Value::Content(body.as_ref().clone()),
- }),
- Self::Desc(item) => Value::Dict(dict! {
- "term" => Value::Content(item.term.clone()),
- "body" => Value::Content(item.body.clone()),
- }),
- }
- }
-}
-
-#[node]
-impl ListItem {}
-
-/// A description list item.
-#[derive(Debug, Clone, Hash)]
-pub struct DescItem {
- /// The term described by the list item.
- pub term: Content,
- /// The description of the term.
- pub body: Content,
-}
-
-castable! {
- DescItem,
- Expected: "dictionary with `term` and `body` keys",
- Value::Dict(dict) => {
- let term: Content = dict.get("term")?.clone().cast()?;
- let body: Content = dict.get("body")?.clone().cast()?;
- Self { term, body }
- },
-}
-
-/// How to label a list.
-pub type ListKind = usize;
-
-/// An unordered list.
-pub const LIST: ListKind = 0;
-
-/// An ordered list.
-pub const ENUM: ListKind = 1;
-
-/// A description list.
-pub const DESC: ListKind = 2;
-
-/// How to label a list or enumeration.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Label {
- /// The default labelling.
- Default,
- /// A pattern with prefix, numbering, lower / upper case and suffix.
- Pattern(NumberingPattern),
- /// Bare content.
- Content(Content),
- /// A closure mapping from an item number to a value.
- Func(Func, Span),
-}
-
-impl Label {
- /// Resolve the label based on the level.
- pub fn resolve(
- &self,
- world: Tracked<dyn World>,
- kind: ListKind,
- number: usize,
- ) -> SourceResult<Content> {
- Ok(match self {
- Self::Default => match kind {
- LIST => TextNode::packed('•'),
- ENUM => TextNode::packed(format_eco!("{}.", number)),
- DESC | _ => panic!("description lists don't have a label"),
- },
- Self::Pattern(pattern) => TextNode::packed(pattern.apply(number)),
- Self::Content(content) => content.clone(),
- Self::Func(func, span) => {
- let args = Args::new(*span, [Value::Int(number as i64)]);
- func.call_detached(world, args)?.display()
- }
- })
- }
-}
-
-impl Cast<Spanned<Value>> for Label {
- fn is(value: &Spanned<Value>) -> bool {
- matches!(&value.v, Value::Content(_) | Value::Func(_))
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- match value.v {
- Value::None => Ok(Self::Content(Content::empty())),
- Value::Str(v) => Ok(Self::Pattern(v.parse()?)),
- Value::Content(v) => Ok(Self::Content(v)),
- Value::Func(v) => Ok(Self::Func(v, value.span)),
- v => Err(format_eco!(
- "expected string, content or function, found {}",
- v.type_name(),
- )),
- }
- }
-}
diff --git a/library/src/structure/mod.rs b/library/src/structure/mod.rs
deleted file mode 100644
index a1c27eed..00000000
--- a/library/src/structure/mod.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! Document structuring.
-
-mod document;
-mod heading;
-mod list;
-mod reference;
-mod table;
-
-pub use self::document::*;
-pub use self::heading::*;
-pub use self::list::*;
-pub use self::reference::*;
-pub use self::table::*;
diff --git a/library/src/structure/reference.rs b/library/src/structure/reference.rs
deleted file mode 100644
index 948aa6f6..00000000
--- a/library/src/structure/reference.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use crate::prelude::*;
-use crate::text::TextNode;
-
-/// A reference to a label.
-#[derive(Debug, Hash)]
-pub struct RefNode(pub EcoString);
-
-#[node(Show)]
-impl RefNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.expect("target")?).pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "target" => Some(Value::Str(self.0.clone().into())),
- _ => None,
- }
- }
-}
-
-impl Show for RefNode {
- fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
- TextNode::packed(format_eco!("@{}", self.0))
- }
-}
diff --git a/library/src/structure/table.rs b/library/src/structure/table.rs
deleted file mode 100644
index bb900f3d..00000000
--- a/library/src/structure/table.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use crate::layout::{GridNode, TrackSizing, TrackSizings};
-use crate::prelude::*;
-
-/// A table of items.
-#[derive(Debug, Hash)]
-pub struct TableNode {
- /// Defines sizing for content rows and columns.
- pub tracks: Axes<Vec<TrackSizing>>,
- /// Defines sizing of gutter rows and columns between content.
- pub gutter: Axes<Vec<TrackSizing>>,
- /// The content to be arranged in the table.
- pub cells: Vec<Content>,
-}
-
-#[node(Layout)]
-impl TableNode {
- /// How to fill the cells.
- #[property(referenced)]
- pub const FILL: Celled<Option<Paint>> = Celled::Value(None);
- /// How to stroke the cells.
- #[property(resolve, fold)]
- pub const STROKE: Option<PartialStroke> = Some(PartialStroke::default());
- /// How much to pad the cells's content.
- pub const PADDING: Rel<Length> = Abs::pt(5.0).into();
-
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
- let TrackSizings(rows) = args.named("rows")?.unwrap_or_default();
- let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default();
- let column_gutter = args.named("column-gutter")?.map(|TrackSizings(v)| v);
- let row_gutter = args.named("row-gutter")?.map(|TrackSizings(v)| v);
- Ok(Self {
- tracks: Axes::new(columns, rows),
- gutter: Axes::new(
- column_gutter.unwrap_or_else(|| base_gutter.clone()),
- row_gutter.unwrap_or(base_gutter),
- ),
- cells: args.all()?,
- }
- .pack())
- }
-
- fn field(&self, name: &str) -> Option<Value> {
- match name {
- "cells" => Some(Value::Array(
- self.cells.iter().cloned().map(Value::Content).collect(),
- )),
- _ => None,
- }
- }
-}
-
-impl Layout for TableNode {
- fn layout(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- regions: &Regions,
- ) -> SourceResult<Fragment> {
- let fill = styles.get(Self::FILL);
- let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
- let padding = styles.get(Self::PADDING);
-
- let cols = self.tracks.x.len().max(1);
- let cells = self
- .cells
- .iter()
- .cloned()
- .enumerate()
- .map(|(i, child)| {
- let mut child = child.padded(Sides::splat(padding));
-
- if let Some(stroke) = stroke {
- child = child.stroked(stroke);
- }
-
- let x = i % cols;
- let y = i / cols;
- if let Some(fill) = fill.resolve(world, x, y)? {
- child = child.filled(fill);
- }
-
- Ok(child)
- })
- .collect::<SourceResult<_>>()?;
-
- GridNode {
- tracks: self.tracks.clone(),
- gutter: self.gutter.clone(),
- cells,
- }
- .layout(world, styles, regions)
- }
-}
-
-/// A value that can be configured per cell.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Celled<T> {
- /// A bare value, the same for all cells.
- Value(T),
- /// A closure mapping from cell coordinates to a value.
- Func(Func, Span),
-}
-
-impl<T: Cast + Clone> Celled<T> {
- /// Resolve the value based on the cell position.
- pub fn resolve(
- &self,
- world: Tracked<dyn World>,
- x: usize,
- y: usize,
- ) -> SourceResult<T> {
- Ok(match self {
- Self::Value(value) => value.clone(),
- Self::Func(func, span) => {
- let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
- func.call_detached(world, args)?.cast().at(*span)?
- }
- })
- }
-}
-
-impl<T: Cast> Cast<Spanned<Value>> for Celled<T> {
- fn is(value: &Spanned<Value>) -> bool {
- matches!(&value.v, Value::Func(_)) || T::is(&value.v)
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- match value.v {
- Value::Func(v) => Ok(Self::Func(v, value.span)),
- v => T::cast(v)
- .map(Self::Value)
- .map_err(|msg| with_alternative(msg, "function")),
- }
- }
-}