summaryrefslogtreecommitdiff
path: root/src/layout/actions.rs
blob: 317cff25cab5df2f63151e1c6998f66fc51e485e (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Drawing and configuration actions composing layouts.

use std::fmt::{self, Debug, Formatter};
use serde::ser::{Serialize, Serializer, SerializeTuple};
use toddle::query::FontIndex;

use crate::length::{Length, Size};
use super::Layout;
use self::LayoutAction::*;

/// A layouting action, which is the basic building block layouts are composed
/// of.
#[derive(Clone, PartialEq)]
pub enum LayoutAction {
    /// Move to an absolute position.
    MoveAbsolute(Size),
    /// Set the font given the index from the font loader and font size.
    SetFont(FontIndex, Length),
    /// Write text at the current position.
    WriteText(String),
    /// Visualize a box for debugging purposes.
    DebugBox(Size),
}

impl Serialize for LayoutAction {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        match self {
            LayoutAction::MoveAbsolute(pos) => {
                let mut tup = serializer.serialize_tuple(2)?;
                tup.serialize_element(&0u8)?;
                tup.serialize_element(&pos)?;
                tup.end()
            }
            LayoutAction::SetFont(index, size) => {
                let mut tup = serializer.serialize_tuple(4)?;
                tup.serialize_element(&1u8)?;
                tup.serialize_element(index)?;
                tup.serialize_element(size)?;
                tup.end()
            }
            LayoutAction::WriteText(text) => {
                let mut tup = serializer.serialize_tuple(2)?;
                tup.serialize_element(&2u8)?;
                tup.serialize_element(text)?;
                tup.end()
            }
            LayoutAction::DebugBox(size) => {
                let mut tup = serializer.serialize_tuple(2)?;
                tup.serialize_element(&3u8)?;
                tup.serialize_element(&size)?;
                tup.end()
            }
        }
    }
}

impl Debug for LayoutAction {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use LayoutAction::*;
        match self {
            MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y),
            SetFont(i, s) => write!(f, "font {}-{} {}", i.id, i.variant, s),
            WriteText(s) => write!(f, "write {:?}", s),
            DebugBox(s) => write!(f, "box {} {}", s.x, s.y),
        }
    }
}

/// A sequence of layouting actions.
///
/// The sequence of actions is optimized as the actions are added. For example,
/// a font changing option will only be added if the selected font is not
/// already active. All configuration actions (like moving, setting fonts, ...)
/// are only flushed when content is written.
///
/// Furthermore, the action list can translate absolute position into a
/// coordinate system with a different origin. This is realized in the
/// `add_layout` method, which allows a layout to be added at a position,
/// effectively translating all movement actions inside the layout by the
/// position.
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutActions {
    origin: Size,
    actions: Vec<LayoutAction>,
    active_font: (FontIndex, Length),
    next_pos: Option<Size>,
    next_font: Option<(FontIndex, Length)>,
}

impl LayoutActions {
    /// Create a new action list.
    pub fn new() -> LayoutActions {
        LayoutActions {
            actions: vec![],
            origin: Size::ZERO,
            active_font: (FontIndex::MAX, Length::ZERO),
            next_pos: None,
            next_font: None,
        }
    }

    /// Add an action to the list.
    pub fn add(&mut self, action: LayoutAction) {
        match action {
            MoveAbsolute(pos) => self.next_pos = Some(self.origin + pos),
            SetFont(index, size) => {
                self.next_font = Some((index, size));
            }

            _ => {
                self.flush_position();
                self.flush_font();

                self.actions.push(action);
            }
        }
    }

    /// Add a series of actions.
    pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item = LayoutAction> {
        for action in actions.into_iter() {
            self.add(action);
        }
    }

    /// Add a layout at a position. All move actions inside the layout are
    /// translated by the position.
    pub fn add_layout(&mut self, position: Size, layout: Layout) {
        self.flush_position();

        self.origin = position;
        self.next_pos = Some(position);

        self.extend(layout.actions);
    }

    /// Whether there are any actions in this list.
    pub fn is_empty(&self) -> bool {
        self.actions.is_empty()
    }

    /// Return the list of actions as a vector.
    pub fn into_vec(self) -> Vec<LayoutAction> {
        self.actions
    }

    /// Append a cached move action if one is cached.
    fn flush_position(&mut self) {
        if let Some(target) = self.next_pos.take() {
            self.actions.push(MoveAbsolute(target));
        }
    }

    /// Append a cached font-setting action if one is cached.
    fn flush_font(&mut self) {
        if let Some((index, size)) = self.next_font.take() {
            if (index, size) != self.active_font {
                self.actions.push(SetFont(index, size));
                self.active_font = (index, size);
            }
        }
    }
}