summaryrefslogtreecommitdiff
path: root/src/library/align.rs
blob: c16277f67d1c4f9a3ae43638a89309974ac25e76 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use super::prelude::*;
use super::ParNode;

/// `align`: Configure the alignment along the layouting axes.
pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
    let aligns: Spec<_> = args.expect("alignment")?;
    let body: Node = args.expect("body")?;

    let mut styles = Styles::new();
    if let Some(align) = aligns.x {
        styles.set(ParNode::ALIGN, align);
    }

    Ok(Value::block(
        body.into_block().styled(styles).aligned(aligns),
    ))
}

/// A node that aligns its child.
#[derive(Debug, Hash)]
pub struct AlignNode {
    /// How to align the node horizontally and vertically.
    pub aligns: Spec<Option<Align>>,
    /// The node to be aligned.
    pub child: PackedNode,
}

impl Layout for AlignNode {
    fn layout(
        &self,
        ctx: &mut LayoutContext,
        regions: &Regions,
    ) -> Vec<Constrained<Rc<Frame>>> {
        // The child only needs to expand along an axis if there's no alignment.
        let mut pod = regions.clone();
        pod.expand &= self.aligns.map_is_none();

        // Layout the child.
        let mut frames = self.child.layout(ctx, &pod);

        for ((current, base), Constrained { item: frame, cts }) in
            regions.iter().zip(&mut frames)
        {
            // Align in the target size. The target size depends on whether we
            // should expand.
            let target = regions.expand.select(current, frame.size);
            let default = Spec::new(Align::Left, Align::Top);
            let aligns = self.aligns.unwrap_or(default);
            Rc::make_mut(frame).resize(target, aligns);

            // Set constraints.
            cts.expand = regions.expand;
            cts.base = base.filter(cts.base.map_is_some());
            cts.exact = current.filter(regions.expand | cts.exact.map_is_some());
        }

        frames
    }
}

dynamic! {
    Align: "alignment",
}

castable! {
    Spec<Option<Align>>,
    Expected: "1d or 2d alignment",
    @align: Align => {
        let mut aligns = Spec::default();
        aligns.set(align.axis(), Some(*align));
        aligns
    },
    @aligns: Spec<Align> => aligns.map(Some),

}