embedded_layout/align/vertical/
mod.rs1use crate::align::{Alignment, VerticalAlignment};
5use embedded_graphics::{geometry::AnchorPoint, primitives::Rectangle};
6
7#[derive(Copy, Clone, Default)]
9pub struct NoAlignment;
10impl VerticalAlignment for NoAlignment {}
11
12impl Alignment for NoAlignment {
13    #[inline]
14    fn align_with_offset(&self, _object: Rectangle, _reference: Rectangle, _offset: i32) -> i32 {
15        0
16    }
17}
18
19#[derive(Copy, Clone, Default)]
24pub struct Center;
25impl VerticalAlignment for Center {}
26
27impl Alignment for Center {
28    #[inline]
29    fn align_with_offset(&self, object: Rectangle, reference: Rectangle, offset: i32) -> i32 {
30        reference.anchor_point(AnchorPoint::Center).y - object.anchor_point(AnchorPoint::Center).y
31            + offset
32    }
33}
34
35#[derive(Copy, Clone, Default)]
37pub struct Top;
38impl VerticalAlignment for Top {}
39
40impl Alignment for Top {
41    #[inline]
42    fn align_with_offset(&self, object: Rectangle, reference: Rectangle, offset: i32) -> i32 {
43        reference.top_left.y - object.top_left.y + offset
44    }
45}
46
47#[derive(Copy, Clone, Default)]
49pub struct Bottom;
50impl VerticalAlignment for Bottom {}
51
52impl Alignment for Bottom {
53    #[inline]
54    fn align_with_offset(&self, object: Rectangle, reference: Rectangle, offset: i32) -> i32 {
55        reference.anchor_point(AnchorPoint::BottomRight).y
56            - object.anchor_point(AnchorPoint::BottomRight).y
57            + offset
58    }
59}
60
61#[derive(Copy, Clone, Default)]
63pub struct TopToBottom;
64impl VerticalAlignment for TopToBottom {}
65
66impl Alignment for TopToBottom {
67    #[inline]
68    fn align_with_offset(&self, object: Rectangle, reference: Rectangle, offset: i32) -> i32 {
69        let offset = if object.size.height == 0 {
70            offset
71        } else {
72            offset + 1
73        };
74        reference.anchor_point(AnchorPoint::BottomRight).y - object.top_left.y + offset
75    }
76}
77
78#[derive(Copy, Clone, Default)]
80pub struct BottomToTop;
81impl VerticalAlignment for BottomToTop {}
82
83impl Alignment for BottomToTop {
84    #[inline]
85    fn align_with_offset(&self, object: Rectangle, reference: Rectangle, offset: i32) -> i32 {
86        let offset = if object.size.height == 0 {
87            offset
88        } else {
89            offset - 1
90        };
91        reference.top_left.y - object.anchor_point(AnchorPoint::BottomRight).y + offset
92    }
93}
94
95#[cfg(test)]
96mod test {
97    use crate::prelude::*;
98    use embedded_graphics::{
99        geometry::{AnchorPoint, Point},
100        prelude::Size,
101        primitives::Rectangle,
102    };
103
104    #[test]
105    fn test_center() {
106        fn check_center_alignment(source: Rectangle, reference: Rectangle, result: Rectangle) {
107            let center_of_reference = reference.top_left + reference.size() / 2;
108            let center_of_result = result.top_left + result.size() / 2;
109
110            assert_eq!(result.size(), source.size());
112
113            assert_eq!(center_of_result.y, center_of_reference.y);
115
116            assert_eq!(result.top_left.x, source.top_left.x);
118        }
119
120        let rect1 = Rectangle::with_corners(Point::new(0, 0), Point::new(10, 10));
121        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
122
123        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::Center);
124        check_center_alignment(rect1, rect2, result);
125
126        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::Center);
129        check_center_alignment(rect2, rect1, result);
130    }
131
132    #[test]
133    fn test_top() {
134        fn check_top_alignment(source: Rectangle, reference: Rectangle, result: Rectangle) {
135            assert_eq!(result.size(), source.size());
137
138            assert_eq!(result.top_left.y, reference.top_left.y);
140
141            assert_eq!(result.top_left.x, source.top_left.x);
143        }
144
145        let rect1 = Rectangle::with_corners(Point::new(0, 0), Point::new(10, 10));
146        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
147
148        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::Top);
149        check_top_alignment(rect1, rect2, result);
150
151        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::Top);
153        check_top_alignment(rect2, rect1, result);
154    }
155
156    #[test]
157    fn test_bottom() {
158        fn check_bottom_alignment(source: Rectangle, reference: Rectangle, result: Rectangle) {
159            assert_eq!(result.size(), source.size());
161
162            assert_eq!(
164                result.anchor_point(AnchorPoint::BottomRight).y,
165                reference.anchor_point(AnchorPoint::BottomRight).y
166            );
167
168            assert_eq!(
170                result.anchor_point(AnchorPoint::BottomRight).x,
171                source.anchor_point(AnchorPoint::BottomRight).x
172            );
173        }
174
175        let rect1 = Rectangle::with_corners(Point::new(0, 0), Point::new(10, 10));
176        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
177
178        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::Bottom);
179        check_bottom_alignment(rect1, rect2, result);
180
181        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::Bottom);
183        check_bottom_alignment(rect2, rect1, result);
184    }
185
186    #[test]
187    fn test_top_to_bottom() {
188        let rect1 = Rectangle::with_corners(Point::new(0, 0), Point::new(10, 10));
189        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
190
191        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::TopToBottom);
192        assert_eq!(result.size(), rect1.size());
194
195        assert_eq!(
197            result.top_left.y,
198            rect2.anchor_point(AnchorPoint::BottomRight).y + 1
199        );
200
201        assert_eq!(
203            result.anchor_point(AnchorPoint::BottomRight).x,
204            rect1.anchor_point(AnchorPoint::BottomRight).x
205        );
206
207        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::TopToBottom);
209        assert_eq!(result.size(), rect2.size());
211
212        assert_eq!(
214            result.top_left.y,
215            rect1.anchor_point(AnchorPoint::BottomRight).y + 1
216        );
217
218        assert_eq!(
220            result.anchor_point(AnchorPoint::BottomRight).x,
221            rect2.anchor_point(AnchorPoint::BottomRight).x
222        );
223    }
224
225    #[test]
226    fn test_top_to_bottom_empty() {
227        let rect1 = Rectangle::new(Point::new(0, 0), Size::zero());
228        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
229
230        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::TopToBottom);
231        assert_eq!(result.size(), rect1.size());
233
234        assert_eq!(
236            result.top_left.y,
237            rect2.anchor_point(AnchorPoint::BottomRight).y
238        );
239
240        assert_eq!(
242            result.anchor_point(AnchorPoint::BottomRight).x,
243            rect1.anchor_point(AnchorPoint::BottomRight).x
244        );
245
246        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::TopToBottom);
248        assert_eq!(result.size(), rect2.size());
250
251        assert_eq!(
253            result.top_left.y,
254            rect1.anchor_point(AnchorPoint::BottomRight).y + 1
255        );
256
257        assert_eq!(
259            result.anchor_point(AnchorPoint::BottomRight).x,
260            rect2.anchor_point(AnchorPoint::BottomRight).x
261        );
262    }
263
264    #[test]
265    fn test_bottom_to_top() {
266        let rect1 = Rectangle::with_corners(Point::new(0, 0), Point::new(10, 10));
267        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
268
269        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::BottomToTop);
270        assert_eq!(result.size(), rect1.size());
272
273        assert_eq!(
275            result.anchor_point(AnchorPoint::BottomRight).y,
276            rect2.top_left.y - 1
277        );
278
279        assert_eq!(
281            result.anchor_point(AnchorPoint::BottomRight).x,
282            rect1.anchor_point(AnchorPoint::BottomRight).x
283        );
284
285        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::BottomToTop);
287        assert_eq!(result.size(), rect2.size());
289
290        assert_eq!(
292            result.anchor_point(AnchorPoint::BottomRight).y,
293            rect1.top_left.y - 1
294        );
295
296        assert_eq!(
298            result.anchor_point(AnchorPoint::BottomRight).x,
299            rect2.anchor_point(AnchorPoint::BottomRight).x
300        );
301    }
302
303    #[test]
304    fn test_bottom_to_top_empty() {
305        let rect1 = Rectangle::new(Point::new(0, 0), Size::zero());
306        let rect2 = Rectangle::with_corners(Point::new(30, 20), Point::new(40, 50));
307
308        let result = rect1.align_to(&rect2, horizontal::NoAlignment, vertical::BottomToTop);
309        assert_eq!(result.size(), rect1.size());
311
312        assert_eq!(
314            result.anchor_point(AnchorPoint::BottomRight).y,
315            rect2.top_left.y
316        );
317
318        assert_eq!(
320            result.anchor_point(AnchorPoint::BottomRight).x,
321            rect1.anchor_point(AnchorPoint::BottomRight).x
322        );
323
324        let result = rect2.align_to(&rect1, horizontal::NoAlignment, vertical::BottomToTop);
326        assert_eq!(result.size(), rect2.size());
328
329        assert_eq!(
331            result.anchor_point(AnchorPoint::BottomRight).y,
332            rect1.top_left.y - 1
333        );
334
335        assert_eq!(
337            result.anchor_point(AnchorPoint::BottomRight).x,
338            rect2.anchor_point(AnchorPoint::BottomRight).x
339        );
340    }
341}