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
|
//! Drawing and cofiguration actions composing layouts.
use std::fmt::{self, Display, Formatter};
use std::io::{self, Write};
use super::Layout;
use crate::size::Size2D;
use LayoutAction::*;
/// A layouting action.
#[derive(Clone)]
pub enum LayoutAction {
/// Move to an absolute position.
MoveAbsolute(Size2D),
/// Set the font by index and font size.
SetFont(usize, f32),
/// Write text starting at the current position.
WriteText(String),
/// Visualize a box for debugging purposes.
/// The arguments are position and size.
DebugBox(Size2D, Size2D),
}
impl LayoutAction {
/// Serialize this layout action into an easy-to-parse string representation.
pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
match self {
MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()),
SetFont(i, s) => write!(f, "f {} {}", i, s),
WriteText(s) => write!(f, "w {}", s),
DebugBox(p, s) => write!(
f,
"b {} {} {} {}",
p.x.to_pt(),
p.y.to_pt(),
s.x.to_pt(),
s.y.to_pt()
),
}
}
}
impl Display 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, s),
WriteText(s) => write!(f, "write \"{}\"", s),
DebugBox(p, s) => write!(f, "box {} {}", p, s),
}
}
}
debug_display!(LayoutAction);
/// 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. This is realized in the `add_box` 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)]
pub struct LayoutActionList {
pub origin: Size2D,
actions: Vec<LayoutAction>,
active_font: (usize, f32),
next_pos: Option<Size2D>,
next_font: Option<(usize, f32)>,
}
impl LayoutActionList {
/// Create a new action list.
pub fn new() -> LayoutActionList {
LayoutActionList {
actions: vec![],
origin: Size2D::zero(),
active_font: (std::usize::MAX, 0.0),
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),
DebugBox(pos, size) => self.actions.push(DebugBox(self.origin + pos, size)),
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: Size2D, layout: Layout) {
self.flush_position();
self.origin = position;
self.next_pos = Some(position);
if layout.debug_render {
self.actions.push(DebugBox(position, layout.dimensions));
}
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);
}
}
}
}
|