typst_library/layout/
stack.rs

1use std::fmt::{self, Debug, Formatter};
2
3use crate::diag::SourceResult;
4use crate::engine::Engine;
5use crate::foundations::{cast, elem, Content, NativeElement, Packed, Show, StyleChain};
6use crate::layout::{BlockElem, Dir, Spacing};
7
8/// Arranges content and spacing horizontally or vertically.
9///
10/// The stack places a list of items along an axis, with optional spacing
11/// between each item.
12///
13/// # Example
14/// ```example
15/// #stack(
16///   dir: ttb,
17///   rect(width: 40pt),
18///   rect(width: 120pt),
19///   rect(width: 90pt),
20/// )
21/// ```
22#[elem(Show)]
23pub struct StackElem {
24    /// The direction along which the items are stacked. Possible values are:
25    ///
26    /// - `{ltr}`: Left to right.
27    /// - `{rtl}`: Right to left.
28    /// - `{ttb}`: Top to bottom.
29    /// - `{btt}`: Bottom to top.
30    ///
31    /// You can use the `start` and `end` methods to obtain the initial and
32    /// final points (respectively) of a direction, as `alignment`. You can also
33    /// use the `axis` method to determine whether a direction is
34    /// `{"horizontal"}` or `{"vertical"}`. The `inv` method returns a
35    /// direction's inverse direction.
36    ///
37    /// For example, `{ttb.start()}` is `top`, `{ttb.end()}` is `bottom`,
38    /// `{ttb.axis()}` is `{"vertical"}` and `{ttb.inv()}` is equal to `btt`.
39    #[default(Dir::TTB)]
40    pub dir: Dir,
41
42    /// Spacing to insert between items where no explicit spacing was provided.
43    pub spacing: Option<Spacing>,
44
45    /// The children to stack along the axis.
46    #[variadic]
47    pub children: Vec<StackChild>,
48}
49
50impl Show for Packed<StackElem> {
51    fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
52        Ok(BlockElem::multi_layouter(self.clone(), engine.routines.layout_stack)
53            .pack()
54            .spanned(self.span()))
55    }
56}
57
58/// A child of a stack element.
59#[derive(Clone, PartialEq, Hash)]
60pub enum StackChild {
61    /// Spacing between other children.
62    Spacing(Spacing),
63    /// Arbitrary block-level content.
64    Block(Content),
65}
66
67impl Debug for StackChild {
68    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
69        match self {
70            Self::Spacing(kind) => kind.fmt(f),
71            Self::Block(block) => block.fmt(f),
72        }
73    }
74}
75
76cast! {
77    StackChild,
78    self => match self {
79        Self::Spacing(spacing) => spacing.into_value(),
80        Self::Block(content) => content.into_value(),
81    },
82    v: Spacing => Self::Spacing(v),
83    v: Content => Self::Block(v),
84}