embedded_layout/layout/linear/spacing.rs
1//! Element spacing
2//!
3//! `ElementSpacing` can be used to change the distance of objects along the layout orientation.
4//! The default spacing is [`Tight`], which means objects are placed right next to each other,
5//! without any space between them.
6//!
7//! Change the default spacing by calling [`LinearLayout::with_spacing`]
8//!
9//! [`LinearLayout::with_spacing`]: crate::layout::linear::LinearLayout::with_spacing
10
11use crate::align::Alignment;
12use embedded_graphics::primitives::Rectangle;
13
14/// `ElementSpacing` base trait
15pub trait ElementSpacing: Copy + Clone {
16 /// Align `view` to `reference` using the element spacing rules
17 fn align(
18 &self,
19 alignment: impl Alignment,
20 view: Rectangle,
21 reference: Rectangle,
22 n: usize,
23 objects: usize,
24 total_size: u32,
25 ) -> i32;
26}
27
28/// Lay out objects tightly, leaving no space between them
29///
30/// # Example:
31/// ```rust
32/// use embedded_layout::{
33/// layout::linear::{spacing::Tight, LinearLayout},
34/// prelude::*,
35/// };
36/// use embedded_graphics::{prelude::*, primitives::Line};
37///
38/// let _ = LinearLayout::horizontal(
39/// Views::new(&mut [
40/// Line::new(Point::zero(), Point::new(0, 5)),
41/// Line::new(Point::zero(), Point::new(0, 5)),
42/// Line::new(Point::zero(), Point::new(0, 5)),
43/// ])
44/// )
45/// .with_spacing(Tight);
46/// ```
47#[derive(Copy, Clone)]
48pub struct Tight;
49impl ElementSpacing for Tight {
50 #[inline]
51 fn align(
52 &self,
53 alignment: impl Alignment,
54 view: Rectangle,
55 reference: Rectangle,
56 _n: usize,
57 _objects: usize,
58 _total_size: u32,
59 ) -> i32 {
60 alignment.align_with_offset(view, reference, 0)
61 }
62}
63
64/// Lay out objects with fixed margin between them
65///
66/// The margin can be negative, in which case the elements will be placed over one another.
67///
68/// # Example:
69/// ```
70/// use embedded_layout::{
71/// layout::linear::{spacing::FixedMargin, LinearLayout},
72/// prelude::*,
73/// };
74/// use embedded_graphics::{prelude::*, primitives::Line};
75///
76/// // Apply a 3px margin between objects
77/// let _ = LinearLayout::horizontal(
78/// Views::new(&mut [
79/// Line::new(Point::zero(), Point::new(0, 5)),
80/// Line::new(Point::zero(), Point::new(0, 5)),
81/// Line::new(Point::zero(), Point::new(0, 5)),
82/// ])
83/// )
84/// .with_spacing(FixedMargin(3));
85/// ```
86#[derive(Copy, Clone)]
87pub struct FixedMargin(pub i32);
88impl ElementSpacing for FixedMargin {
89 #[inline]
90 fn align(
91 &self,
92 alignment: impl Alignment,
93 view: Rectangle,
94 reference: Rectangle,
95 n: usize,
96 _objects: usize,
97 _total_size: u32,
98 ) -> i32 {
99 let offset = if n == 0 { 0 } else { self.0 };
100 alignment.align_with_offset(view, reference, offset)
101 }
102}
103
104/// Distribute views to fill a given space
105///
106/// Forces the layout to be as high or wide as set for this spacing
107///
108/// # Example:
109/// ```rust
110/// use embedded_layout::{
111/// layout::linear::{spacing::DistributeFill, LinearLayout},
112/// prelude::*,
113/// };
114/// use embedded_graphics::{prelude::*, primitives::Line};
115///
116/// // Distribute views in a 64px high space
117/// let _ = LinearLayout::vertical(
118/// Views::new(&mut [
119/// Line::new(Point::zero(), Point::new(0, 5)),
120/// Line::new(Point::zero(), Point::new(0, 5)),
121/// Line::new(Point::zero(), Point::new(0, 5)),
122/// ])
123/// )
124/// .with_spacing(DistributeFill(64));
125/// ```
126#[derive(Copy, Clone)]
127pub struct DistributeFill(pub u32);
128impl ElementSpacing for DistributeFill {
129 #[inline]
130 fn align(
131 &self,
132 alignment: impl Alignment,
133 view: Rectangle,
134 reference: Rectangle,
135 n: usize,
136 objects: usize,
137 total_size: u32,
138 ) -> i32 {
139 // bit of a mess, but calculate using i32 in case the views don't fit the space
140 let empty_space = self.0 as i32 - total_size as i32;
141
142 if objects <= 1 {
143 return alignment.align_with_offset(view, reference, empty_space / 2);
144 }
145
146 let base = empty_space / (objects - 1) as i32;
147 let remainder = empty_space % (objects - 1) as i32;
148
149 let offset = if n == 0 {
150 0
151 } else if n as i32 <= remainder {
152 base + 1
153 } else {
154 base
155 };
156 alignment.align_with_offset(view, reference, offset)
157 }
158}