embedded_layout/layout/linear/
orientation.rs

1use crate::{
2    align::{horizontal, vertical, Alignment, HorizontalAlignment, VerticalAlignment},
3    layout::linear::{
4        secondary_alignment::SecondaryAlignment,
5        spacing::{ElementSpacing, Tight},
6    },
7    View,
8};
9use embedded_graphics::{
10    prelude::{Point, Size},
11    primitives::Rectangle,
12};
13
14/// Helper trait that describes a linear layout orientation.
15pub trait Orientation: Copy + Clone {
16    /// Secondary alignment that will be applied to all the views
17    type Secondary: SecondaryAlignment + Alignment;
18
19    /// Destructure `Size` into `(primary_size, secondary_size)`
20    fn destructure_size(size: Size) -> (u32, u32);
21
22    /// Create a `Size` from primary and secondary size values
23    fn create_size(primary: u32, secondary: u32) -> Size;
24
25    /// Computes translation for the next view.
26    fn compute_offset(
27        &self,
28        bounds: Rectangle,
29        size: Size,
30        previous: Rectangle,
31        n: usize,
32        count: usize,
33    ) -> Point;
34
35    /// Place view
36    #[inline]
37    fn place(
38        &self,
39        view: &mut dyn View,
40        size: Size,
41        previous: Rectangle,
42        n: usize,
43        count: usize,
44    ) -> Rectangle {
45        let offset = self.compute_offset(view.bounds(), size, previous, n, count);
46        view.translate_impl(offset);
47        view.bounds()
48    }
49}
50
51/// Horizontal layout direction
52#[derive(Copy, Clone)]
53pub struct Horizontal<Secondary, Spacing = Tight>
54where
55    Secondary: SecondaryAlignment + VerticalAlignment,
56    Spacing: ElementSpacing,
57{
58    pub(crate) secondary: Secondary,
59    pub(crate) spacing: Spacing,
60}
61
62impl<Secondary, Spacing> Horizontal<Secondary, Spacing>
63where
64    Secondary: SecondaryAlignment + VerticalAlignment,
65    Spacing: ElementSpacing,
66{
67    /// Change secondary alignment
68    #[inline]
69    pub fn with_secondary_alignment<Sec: SecondaryAlignment + VerticalAlignment>(
70        self,
71        secondary: Sec,
72    ) -> Horizontal<Sec, Spacing> {
73        Horizontal {
74            secondary,
75            spacing: self.spacing,
76        }
77    }
78
79    /// Change element spacing
80    #[inline]
81    pub fn with_spacing<ElSpacing: ElementSpacing>(
82        self,
83        spacing: ElSpacing,
84    ) -> Horizontal<Secondary, ElSpacing> {
85        Horizontal {
86            secondary: self.secondary,
87            spacing,
88        }
89    }
90}
91
92impl Default for Horizontal<vertical::Bottom, Tight> {
93    #[inline]
94    fn default() -> Self {
95        Self {
96            secondary: vertical::Bottom,
97            spacing: Tight,
98        }
99    }
100}
101
102impl<Secondary, Spacing> Orientation for Horizontal<Secondary, Spacing>
103where
104    Secondary: SecondaryAlignment + VerticalAlignment,
105    Spacing: ElementSpacing,
106{
107    type Secondary = Secondary;
108
109    #[inline]
110    fn destructure_size(size: Size) -> (u32, u32) {
111        (size.width, size.height)
112    }
113
114    #[inline]
115    fn create_size(primary: u32, secondary: u32) -> Size {
116        Size::new(primary, secondary)
117    }
118
119    #[inline]
120    fn compute_offset(
121        &self,
122        bounds: Rectangle,
123        size: Size,
124        previous: Rectangle,
125        n: usize,
126        count: usize,
127    ) -> Point {
128        let (primary_size, _) = Self::destructure_size(size);
129
130        if n == 0 {
131            Point::new(
132                self.spacing
133                    .align(horizontal::Left, bounds, previous, n, count, primary_size),
134                Secondary::First::default().align(bounds, previous),
135            )
136        } else {
137            Point::new(
138                self.spacing.align(
139                    horizontal::LeftToRight,
140                    bounds,
141                    previous,
142                    n,
143                    count,
144                    primary_size,
145                ),
146                Secondary::default().align(bounds, previous),
147            )
148        }
149    }
150}
151
152/// Vertical layout direction
153#[derive(Copy, Clone)]
154pub struct Vertical<Secondary, Spacing = Tight>
155where
156    Secondary: SecondaryAlignment + HorizontalAlignment,
157    Spacing: ElementSpacing,
158{
159    pub(crate) secondary: Secondary,
160    pub(crate) spacing: Spacing,
161}
162
163impl Default for Vertical<horizontal::Left, Tight> {
164    #[inline]
165    fn default() -> Self {
166        Self {
167            secondary: horizontal::Left,
168            spacing: Tight,
169        }
170    }
171}
172
173impl<Secondary, Spacing> Vertical<Secondary, Spacing>
174where
175    Secondary: SecondaryAlignment + HorizontalAlignment,
176    Spacing: ElementSpacing,
177{
178    /// Change secondary alignment
179    #[inline]
180    pub fn with_secondary_alignment<Sec: SecondaryAlignment + HorizontalAlignment>(
181        self,
182        secondary: Sec,
183    ) -> Vertical<Sec, Spacing> {
184        Vertical {
185            secondary,
186            spacing: self.spacing,
187        }
188    }
189
190    /// Change element spacing
191    #[inline]
192    pub fn with_spacing<ElSpacing: ElementSpacing>(
193        self,
194        spacing: ElSpacing,
195    ) -> Vertical<Secondary, ElSpacing> {
196        Vertical {
197            secondary: self.secondary,
198            spacing,
199        }
200    }
201}
202
203impl<Secondary, Spacing> Orientation for Vertical<Secondary, Spacing>
204where
205    Secondary: SecondaryAlignment + HorizontalAlignment,
206    Spacing: ElementSpacing,
207{
208    type Secondary = Secondary;
209
210    #[inline]
211    fn destructure_size(size: Size) -> (u32, u32) {
212        (size.height, size.width)
213    }
214
215    #[inline]
216    fn create_size(primary: u32, secondary: u32) -> Size {
217        Size::new(secondary, primary)
218    }
219
220    #[inline]
221    fn compute_offset(
222        &self,
223        bounds: Rectangle,
224        size: Size,
225        previous: Rectangle,
226        n: usize,
227        count: usize,
228    ) -> Point {
229        let (primary_size, _) = Self::destructure_size(size);
230
231        if n == 0 {
232            Point::new(
233                Secondary::First::default().align(bounds, previous),
234                self.spacing
235                    .align(vertical::Top, bounds, previous, n, count, primary_size),
236            )
237        } else {
238            Point::new(
239                Secondary::default().align(bounds, previous),
240                self.spacing.align(
241                    vertical::TopToBottom,
242                    bounds,
243                    previous,
244                    n,
245                    count,
246                    primary_size,
247                ),
248            )
249        }
250    }
251}