anathema_default_widgets/
alignment.rs1use std::ops::ControlFlow;
2
3use anathema_geometry::{Pos, Size};
4use anathema_value_resolver::AttributeStorage;
5use anathema_widgets::error::Result;
6use anathema_widgets::layout::{Constraints, LayoutCtx, PositionCtx};
7use anathema_widgets::{LayoutForEach, PositionChildren, Widget, WidgetId};
8
9use crate::layout::alignment::{ALIGNMENT, Alignment};
10
11#[derive(Default)]
12pub struct Align;
13
14impl Widget for Align {
15 fn layout<'bp>(
16 &mut self,
17 mut children: LayoutForEach<'_, 'bp>,
18 constraints: Constraints,
19 _: WidgetId,
20 ctx: &mut LayoutCtx<'_, 'bp>,
21 ) -> Result<Size> {
22 _ = children.each(ctx, |ctx, widget, children| {
23 _ = widget.layout(children, constraints, ctx);
24 Ok(ControlFlow::Break(()))
25 })?;
26
27 Ok(constraints.max_size())
28 }
29
30 fn position<'bp>(
31 &mut self,
32 mut children: PositionChildren<'_, 'bp>,
33 id: WidgetId,
34 attribute_storage: &AttributeStorage<'bp>,
35 ctx: PositionCtx,
36 ) {
37 let attributes = attribute_storage.get(id);
38 let alignment = attributes.get_as::<Alignment>(ALIGNMENT).unwrap_or_default();
39
40 _ = children.each(|child, children| {
41 let width = ctx.inner_size.width as i32;
42 let height = ctx.inner_size.height as i32;
43 let child_width = child.size().width as i32;
44 let child_height = child.size().height as i32;
45
46 let child_offset = match alignment {
47 Alignment::TopLeft => Pos::ZERO,
48 Alignment::Top => Pos::new(width / 2 - child_width / 2, 0),
49 Alignment::TopRight => Pos::new(width - child_width, 0),
50 Alignment::Right => Pos::new(width - child_width, height / 2 - child_height / 2),
51 Alignment::BottomRight => Pos::new(width - child_width, height - child_height),
52 Alignment::Bottom => Pos::new(width / 2 - child_width / 2, height - child_height),
53 Alignment::BottomLeft => Pos::new(0, height - child_height),
54 Alignment::Left => Pos::new(0, height / 2 - child_height / 2),
55 Alignment::Centre => Pos::new(width / 2 - child_width / 2, height / 2 - child_height / 2),
56 };
57
58 child.position(children, ctx.pos + child_offset, attribute_storage, ctx.viewport);
59 ControlFlow::Break(())
60 });
61 }
62}
63
64#[cfg(test)]
65mod test {
66 use crate::testing::TestRunner;
67
68 #[test]
69 fn top_left() {
70 let tpl = "
71 align [alignment: 'top_left']
72 text 'x'
73 ";
74
75 let expected = "
76 ╔═══╗
77 ║x ║
78 ║ ║
79 ║ ║
80 ╚═══╝
81 ";
82
83 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
84 }
85
86 #[test]
87 fn top() {
88 let tpl = "
89 align [alignment: 'top']
90 text 'x'
91 ";
92
93 let expected = "
94 ╔═══╗
95 ║ x ║
96 ║ ║
97 ║ ║
98 ╚═══╝
99 ";
100
101 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
102 }
103
104 #[test]
105 fn top_right() {
106 let tpl = "
107 align [alignment: 'top_right']
108 text 'x'
109 ";
110
111 let expected = "
112 ╔═══╗
113 ║ x║
114 ║ ║
115 ║ ║
116 ╚═══╝
117 ";
118
119 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
120 }
121
122 #[test]
123 fn right() {
124 let tpl = "
125 align [alignment: 'right']
126 text 'x'
127 ";
128
129 let expected = "
130 ╔═══╗
131 ║ ║
132 ║ x║
133 ║ ║
134 ╚═══╝
135 ";
136
137 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
138 }
139
140 #[test]
141 fn bottom_right() {
142 let tpl = "
143 align [alignment: 'bottom_right']
144 text 'x'
145 ";
146
147 let expected = "
148 ╔═══╗
149 ║ ║
150 ║ ║
151 ║ x║
152 ╚═══╝
153 ";
154
155 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
156 }
157
158 #[test]
159 fn bottom() {
160 let tpl = "
161 align [alignment: 'bottom']
162 text 'x'
163 ";
164
165 let expected = "
166 ╔═══╗
167 ║ ║
168 ║ ║
169 ║ x ║
170 ╚═══╝
171 ";
172
173 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
174 }
175
176 #[test]
177 fn bottom_left() {
178 let tpl = "
179 align [alignment: 'bottom_left']
180 text 'x'
181 ";
182
183 let expected = "
184 ╔═══╗
185 ║ ║
186 ║ ║
187 ║x ║
188 ╚═══╝
189 ";
190
191 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
192 }
193
194 #[test]
195 fn left() {
196 let tpl = "
197 align [alignment: 'left']
198 text 'x'
199 ";
200
201 let expected = "
202 ╔═══╗
203 ║ ║
204 ║x ║
205 ║ ║
206 ╚═══╝
207 ";
208
209 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
210 }
211
212 #[test]
213 fn centre() {
214 let tpl = "
215 align [alignment: 'centre']
216 text 'x'
217 ";
218
219 let expected = "
220 ╔═══╗
221 ║ ║
222 ║ x ║
223 ║ ║
224 ╚═══╝
225 ";
226
227 TestRunner::new(tpl, (3, 3)).instance().render_assert(expected);
228 }
229}