summaryrefslogtreecommitdiff
path: root/src/library/spacing.rs
blob: 5ae25a926481c5e2101ba170e6f79c7c3a86aa1b (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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use crate::length::ScaleLength;
use crate::layout::SpacingKind;

use super::*;
use self::ContentKind::*;

function! {
    /// `line.break`, `n`: Ends the current line.
    #[derive(Debug, Default, Clone, PartialEq)]
    pub struct LineBreakFunc;

    parse(default)
    layout(self, ctx, f) { vec![BreakLine] }
}

function! {
    /// `par.break`: Ends the current paragraph.
    ///
    /// self has the same effect as two subsequent newlines.
    #[derive(Debug, Default, Clone, PartialEq)]
    pub struct ParBreakFunc;

    parse(default)
    layout(self, ctx, f) { vec![BreakParagraph] }
}

function! {
    /// `page.break`: Ends the current page.
    #[derive(Debug, Default, Clone, PartialEq)]
    pub struct PageBreakFunc;

    parse(default)
    layout(self, ctx, f) { vec![BreakPage] }
}

function! {
    /// `word.spacing`, `line.spacing`, `par.spacing`: The spacing between
    /// words, lines or paragraphs as a multiple of the font size.
    #[derive(Debug, Clone, PartialEq)]
    pub struct ContentSpacingFunc {
        body: Option<SyntaxModel>,
        content: ContentKind,
        spacing: Option<f64>,
    }

    type Meta = ContentKind;

    parse(header, body, state, f, meta) {
        ContentSpacingFunc {
            body: body!(opt: body, state, f),
            content: meta,
            spacing: header.args.pos.get::<f64>(&mut f.diagnostics)
                .map(|num| num as f64)
                .or_missing(&mut f.diagnostics, header.name.span, "spacing"),
        }
    }

    layout(self, ctx, f) {
        styled(&self.body, ctx, self.spacing, |t, s| match self.content {
            Word => t.word_spacing_scale = s,
            Line => t.line_spacing_scale = s,
            Paragraph => t.paragraph_spacing_scale = s,
        })
    }
}

/// The different kinds of content that can be spaced. Used as a metadata type
/// for the [`ContentSpacingFunc`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[allow(missing_docs)]
pub enum ContentKind {
    Word,
    Line,
    Paragraph,
}

function! {
    /// `spacing`, `h`, `v`: Adds spacing along an axis.
    #[derive(Debug, Clone, PartialEq)]
    pub struct SpacingFunc {
        spacing: Option<(AxisKey, ScaleLength)>,
    }

    type Meta = Option<SpecificAxis>;

    parse(header, body, state, f, meta) {
        body!(nope: body, f);
        SpacingFunc {
            spacing: if let Some(axis) = meta {
                header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
                    .map(|s| (AxisKey::Specific(axis), s))
            } else {
                header.args.key.get_with_key::<AxisKey, ScaleLength>(&mut f.diagnostics)
            }.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
        }
    }

    layout(self, ctx, f) {
        if let Some((axis, spacing)) = self.spacing {
            let axis = axis.to_generic(ctx.axes);
            let spacing = spacing.scaled(ctx.style.text.font_size());
            vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
        } else {
            vec![]
        }
    }
}