simple_layout/
align.rs

1use std::marker::PhantomData;
2use std::num::Saturating;
3
4use embedded_graphics::{
5    draw_target::DrawTarget, geometry::Point, pixelcolor::PixelColor, prelude::Size,
6    primitives::Rectangle,
7};
8
9use crate::layoutable::Layoutable;
10use crate::{ComponentSize, ValueRange};
11
12///
13/// Arrange a layoutable into the center of its available space
14///
15/// # Arguments
16///
17/// * `l`: element to place
18///
19/// returns: impl Layoutable<C>+Sized
20///
21/// # Examples
22///
23/// ```
24/// use embedded_graphics::mono_font::iso_8859_1::FONT_6X12;
25/// use embedded_graphics::mono_font::MonoTextStyle;
26/// use embedded_graphics::pixelcolor::BinaryColor;
27/// use simple_layout::prelude::{center, owned_text};
28/// let temperature=20.3;
29/// let element = center(owned_text(format!("{temperature:.1}°C"), MonoTextStyle::new(&FONT_6X12, BinaryColor::On)));
30/// ```
31pub fn center<L: Layoutable<C>, C: PixelColor>(l: L) -> impl Layoutable<C> {
32    AlignLayout::<_, _, CenteredAlignment, CenteredAlignment>::new(l)
33}
34
35///
36/// Arrange a layoutable into the left middle of its available space
37///
38pub fn west<L: Layoutable<C>, C: PixelColor>(l: L) -> impl Layoutable<C> {
39    AlignLayout::<_, _, StartAlignment, CenteredAlignment>::new(l)
40}
41///
42/// Arrange a layoutable into the right middle of its available space
43///
44pub fn east<L: Layoutable<C>, C: PixelColor>(l: L) -> impl Layoutable<C> {
45    AlignLayout::<_, _, EndAlignment, CenteredAlignment>::new(l)
46}
47///
48/// Arrange a layoutable into the top center of its available space
49///
50pub fn north<L: Layoutable<C>, C: PixelColor>(l: L) -> impl Layoutable<C> {
51    AlignLayout::<_, _, CenteredAlignment, StartAlignment>::new(l)
52}
53///
54/// Arrange a layoutable into the bottom center of its available space
55///
56pub fn south<L: Layoutable<C>, C: PixelColor>(l: L) -> impl Layoutable<C> {
57    AlignLayout::<_, _, CenteredAlignment, EndAlignment>::new(l)
58}
59
60trait Alignment {
61    fn place(
62        available_range: Saturating<u32>,
63        target_range: ValueRange<Saturating<u32>>,
64    ) -> (Saturating<i32>, Saturating<u32>);
65}
66
67struct AlignLayout<L: Layoutable<C>, C: PixelColor, HA: Alignment, VA: Alignment> {
68    layoutable: L,
69    p1: PhantomData<C>,
70    p2: PhantomData<VA>,
71    p3: PhantomData<HA>,
72}
73
74impl<L: Layoutable<C>, C: PixelColor, HA: Alignment, VA: Alignment> AlignLayout<L, C, HA, VA> {
75    fn new(layoutable: L) -> Self {
76        Self {
77            layoutable,
78            p1: PhantomData,
79            p2: PhantomData,
80            p3: PhantomData,
81        }
82    }
83    fn place(component_size: ComponentSize, available_area: Rectangle) -> Rectangle {
84        let Size {
85            width: available_width,
86            height: available_height,
87        } = available_area.size;
88        let ComponentSize { width, height } = component_size;
89        let origin = available_area.top_left;
90        let (Saturating(x), Saturating(width)) = HA::place(Saturating(available_width), width);
91        let (Saturating(y), Saturating(height)) = VA::place(Saturating(available_height), height);
92        Rectangle {
93            top_left: origin + Point { x, y },
94            size: Size { width, height },
95        }
96    }
97}
98
99impl<L: Layoutable<C>, C: PixelColor, HA: Alignment, VA: Alignment> Layoutable<C>
100    for AlignLayout<L, C, HA, VA>
101{
102    #[inline]
103    fn size(&self) -> ComponentSize {
104        self.layoutable.size()
105    }
106
107    #[inline]
108    fn draw_placed<DrawError>(
109        &self,
110        target: &mut impl DrawTarget<Color = C, Error = DrawError>,
111        position: Rectangle,
112    ) -> Result<(), DrawError> {
113        self.layoutable
114            .draw_placed(target, Self::place(self.layoutable.size(), position))
115    }
116}
117
118pub struct CenteredAlignment;
119
120impl Alignment for CenteredAlignment {
121    #[inline]
122    fn place(
123        available_range: Saturating<u32>,
124        target_range: ValueRange<Saturating<u32>>,
125    ) -> (Saturating<i32>, Saturating<u32>) {
126        if target_range.max_value < available_range {
127            (
128                Saturating((available_range - target_range.max_value).0 as i32 / 2),
129                target_range.max_value,
130            )
131        } else if target_range.min_value > available_range {
132            (
133                (Saturating(available_range.0 as i32)
134                    - Saturating(target_range.min_value.0 as i32))
135                    / Saturating(2),
136                target_range.min_value,
137            )
138        } else {
139            (Saturating(0), available_range)
140        }
141    }
142}
143
144pub struct StartAlignment;
145
146impl Alignment for StartAlignment {
147    #[inline]
148    fn place(
149        available_range: Saturating<u32>,
150        target_range: ValueRange<Saturating<u32>>,
151    ) -> (Saturating<i32>, Saturating<u32>) {
152        if target_range.max_value < available_range {
153            (Saturating(0), target_range.max_value)
154        } else if target_range.min_value > available_range {
155            (Saturating(0), target_range.min_value)
156        } else {
157            (Saturating(0), available_range)
158        }
159    }
160}
161
162pub struct EndAlignment;
163
164impl Alignment for EndAlignment {
165    #[inline]
166    fn place(
167        available_range: Saturating<u32>,
168        target_range: ValueRange<Saturating<u32>>,
169    ) -> (Saturating<i32>, Saturating<u32>) {
170        if target_range.max_value < available_range {
171            (
172                Saturating(available_range.0 as i32) - Saturating(target_range.max_value.0 as i32),
173                target_range.max_value,
174            )
175        } else if target_range.min_value > available_range {
176            (
177                Saturating(available_range.0 as i32) - Saturating(target_range.min_value.0 as i32),
178                target_range.min_value,
179            )
180        } else {
181            (Saturating(0), available_range)
182        }
183    }
184}