Make library more usable in a `const` context (#203)
1//! Geometric primitives useful for layout
2
3use crate::style::{Dimension, FlexDirection};
4use core::ops::Add;
5
6/// An axis-aligned UI rectangle
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9#[cfg_attr(feature = "serde", serde(default))]
10pub struct Rect<T> {
11 /// This can represent either the x-coordinate of the starting edge,
12 /// or the amount of padding on the starting side.
13 ///
14 /// The starting edge is the left edge when working with LTR text,
15 /// and the right edge when working with RTL text.
16 pub start: T,
17 /// This can represent either the x-coordinate of the ending edge,
18 /// or the amount of padding on the ending side.
19 ///
20 /// The ending edge is the right edge when working with LTR text,
21 /// and the left edge when working with RTL text.
22 pub end: T,
23 /// This can represent either the y-coordinate of the top edge,
24 /// or the amount of padding on the top side.
25 pub top: T,
26 /// This can represent either the y-coordinate of the bottom edge,
27 /// or the amount of padding on the bottom side.
28 pub bottom: T,
29}
30
31impl<T> Rect<T> {
32 /// Applies the function `f` to all four sides of the rect
33 ///
34 /// When applied to the left and right sides, the width is used
35 /// as the second parameter of `f`.
36 /// When applied to the top or bottom sides, the height is used instead.
37 pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
38 where
39 F: Fn(T, U) -> R,
40 U: Copy,
41 {
42 Rect {
43 start: f(self.start, size.width),
44 end: f(self.end, size.width),
45 top: f(self.top, size.height),
46 bottom: f(self.bottom, size.height),
47 }
48 }
49}
50
51impl<T> Rect<T>
52where
53 T: Add<Output = T> + Copy + Clone,
54{
55 /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
56 ///
57 /// This is typically used when computing total padding.
58 ///
59 /// **NOTE:** this is *not* the width of the rectangle.
60 pub(crate) fn horizontal_axis_sum(&self) -> T {
61 self.start + self.end
62 }
63
64 /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
65 ///
66 /// This is typically used when computing total padding.
67 ///
68 /// **NOTE:** this is *not* the height of the rectangle.
69 pub(crate) fn vertical_axis_sum(&self) -> T {
70 self.top + self.bottom
71 }
72
73 /// The sum of the two fields of the [`Rect`] representing the main axis.
74 ///
75 /// This is typically used when computing total padding.
76 ///
77 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
78 /// Otherwise, this is [`Rect::vertical`].
79 pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> T {
80 if direction.is_row() {
81 self.horizontal_axis_sum()
82 } else {
83 self.vertical_axis_sum()
84 }
85 }
86
87 /// The sum of the two fields of the [`Rect`] representing the cross axis.
88 ///
89 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
90 /// Otherwise, this is [`Rect::horizontal`].
91 pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> T {
92 if direction.is_row() {
93 self.vertical_axis_sum()
94 } else {
95 self.horizontal_axis_sum()
96 }
97 }
98}
99
100impl<T> Rect<T>
101where
102 T: Copy + Clone,
103{
104 /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
105 pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
106 if direction.is_row() {
107 self.start
108 } else {
109 self.top
110 }
111 }
112
113 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
114 pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
115 if direction.is_row() {
116 self.end
117 } else {
118 self.bottom
119 }
120 }
121
122 /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
123 pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
124 if direction.is_row() {
125 self.top
126 } else {
127 self.start
128 }
129 }
130
131 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
132 pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
133 if direction.is_row() {
134 self.bottom
135 } else {
136 self.end
137 }
138 }
139}
140
141impl Rect<f32> {
142 /// Creates a new Rect with `0.0` as all parameters
143 pub const ZERO: Rect<f32> = Self { start: 0.0, end: 0.0, top: 0.0, bottom: 0.0 };
144
145 /// Creates a new Rect
146 #[must_use]
147 pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
148 Self { start, end, top, bottom }
149 }
150}
151
152/// The width and height of a [`Rect`]
153#[derive(Debug, Copy, Clone, PartialEq, Eq)]
154#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155#[cfg_attr(feature = "serde", serde(default))]
156pub struct Size<T> {
157 /// The x extent of the rectangle
158 pub width: T,
159 /// The y extent of the rectangle
160 pub height: T,
161}
162
163impl<T> Size<T> {
164 /// Applies the function `f` to both the width and height
165 ///
166 /// This is used to transform a `Rect<T>` into a `Rect<R>`.
167 pub fn map<R, F>(self, f: F) -> Size<R>
168 where
169 F: Fn(T) -> R,
170 {
171 Size { width: f(self.width), height: f(self.height) }
172 }
173
174 /// Sets the extent of the main layout axis
175 ///
176 /// Whether this is the width or height depends on the `direction` provided
177 pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
178 if direction.is_row() {
179 self.width = value
180 } else {
181 self.height = value
182 }
183 }
184
185 /// Sets the extent of the cross layout axis
186 ///
187 /// Whether this is the width or height depends on the `direction` provided
188 pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
189 if direction.is_row() {
190 self.height = value
191 } else {
192 self.width = value
193 }
194 }
195
196 /// Gets the extent of the main layout axis
197 ///
198 /// Whether this is the width or height depends on the `direction` provided
199 pub(crate) fn main(self, direction: FlexDirection) -> T {
200 if direction.is_row() {
201 self.width
202 } else {
203 self.height
204 }
205 }
206
207 /// Gets the extent of the cross layout axis
208 ///
209 /// Whether this is the width or height depends on the `direction` provided
210 pub(crate) fn cross(self, direction: FlexDirection) -> T {
211 if direction.is_row() {
212 self.height
213 } else {
214 self.width
215 }
216 }
217}
218
219impl Size<f32> {
220 /// A [`Size`] with zero width and height
221 pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
222}
223
224impl Size<Option<f32>> {
225 /// A [`Size`] with `None` width and height
226 pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
227
228 /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
229 #[must_use]
230 pub const fn new(width: f32, height: f32) -> Self {
231 Size { width: Some(width), height: Some(height) }
232 }
233}
234
235impl Size<Dimension> {
236 /// Generates a [`Size<Dimension>`] using [`Dimension::Points`] values
237 #[must_use]
238 pub const fn from_points(width: f32, height: f32) -> Self {
239 Size { width: Dimension::Points(width), height: Dimension::Points(height) }
240 }
241
242 /// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values
243 #[must_use]
244 pub const fn from_percent(width: f32, height: f32) -> Self {
245 Size { width: Dimension::Percent(width), height: Dimension::Percent(height) }
246 }
247
248 /// Generates a [`Size<Dimension>`] using [`Dimension::Auto`] in both width and height
249 pub const AUTO: Size<Dimension> = Self { width: Dimension::Auto, height: Dimension::Auto };
250
251 /// Generates a [`Size<Dimension>`] using [`Dimension::Undefined`] in both width and height
252 pub const UNDEFINED: Size<Dimension> = Self { width: Dimension::Undefined, height: Dimension::Undefined };
253}
254
255/// A 2-dimensional coordinate.
256///
257/// When used in association with a [`Rect`], represents the bottom-left corner.
258#[derive(Debug, Copy, Clone, PartialEq, Eq)]
259pub struct Point<T> {
260 /// The x-coordinate
261 pub x: T,
262 /// The y-coordinate
263 pub y: T,
264}
265
266impl Point<f32> {
267 /// A [`Point`] with values (0,0), representing the origin
268 pub const ZERO: Point<f32> = Self { x: 0.0, y: 0.0 };
269}