Make CSS Grid feature a default feature (#297)
1//! Geometric primitives useful for layout
2
3use crate::style::{Dimension, FlexDirection};
4use core::ops::Add;
5
6#[cfg(feature = "grid")]
7use crate::axis::AbstractAxis;
8
9/// An axis-aligned UI rectangle
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12#[cfg_attr(feature = "serde", serde(default))]
13pub struct Rect<T> {
14 /// This can represent either the x-coordinate of the starting edge,
15 /// or the amount of padding on the starting side.
16 ///
17 /// The starting edge is the left edge when working with LTR text,
18 /// and the right edge when working with RTL text.
19 pub left: T,
20 /// This can represent either the x-coordinate of the ending edge,
21 /// or the amount of padding on the ending side.
22 ///
23 /// The ending edge is the right edge when working with LTR text,
24 /// and the left edge when working with RTL text.
25 pub right: T,
26 /// This can represent either the y-coordinate of the top edge,
27 /// or the amount of padding on the top side.
28 pub top: T,
29 /// This can represent either the y-coordinate of the bottom edge,
30 /// or the amount of padding on the bottom side.
31 pub bottom: T,
32}
33
34impl<T: Default> Default for Rect<T> {
35 fn default() -> Self {
36 Rect {
37 left: Default::default(),
38 right: Default::default(),
39 top: Default::default(),
40 bottom: Default::default(),
41 }
42 }
43}
44
45impl<T> Rect<T> {
46 /// Applies the function `f` to all four sides of the rect
47 ///
48 /// When applied to the left and right sides, the width is used
49 /// as the second parameter of `f`.
50 /// When applied to the top or bottom sides, the height is used instead.
51 pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
52 where
53 F: Fn(T, U) -> R,
54 U: Copy,
55 {
56 Rect {
57 left: f(self.left, size.width),
58 right: f(self.right, size.width),
59 top: f(self.top, size.height),
60 bottom: f(self.bottom, size.height),
61 }
62 }
63
64 /// Applies the function `f` to the left, right, top, and bottom properties
65 ///
66 /// This is used to transform a `Rect<T>` into a `Rect<R>`.
67 pub fn map<R, F>(self, f: F) -> Rect<R>
68 where
69 F: Fn(T) -> R,
70 {
71 Rect { top: f(self.top), bottom: f(self.bottom), left: f(self.left), right: f(self.right) }
72 }
73
74 /// Returns a Line<T> representing the left and right properties of the Rect
75 pub fn horizontal_components(self) -> Line<T> {
76 Line { start: self.left, end: self.right }
77 }
78
79 /// Returns a Line<T> containing the top and bottom properties of the Rect
80 pub fn vertical_components(self) -> Line<T> {
81 Line { start: self.top, end: self.bottom }
82 }
83}
84
85impl<T, U> Rect<T>
86where
87 T: Add<Output = U> + Copy + Clone,
88{
89 /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
90 ///
91 /// This is typically used when computing total padding.
92 ///
93 /// **NOTE:** this is *not* the width of the rectangle.
94 #[inline(always)]
95 pub(crate) fn horizontal_axis_sum(&self) -> U {
96 self.left + self.right
97 }
98
99 /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
100 ///
101 /// This is typically used when computing total padding.
102 ///
103 /// **NOTE:** this is *not* the height of the rectangle.
104 #[inline(always)]
105 pub(crate) fn vertical_axis_sum(&self) -> U {
106 self.top + self.bottom
107 }
108
109 /// Both horizontal_axis_sum and vertical_axis_sum as a Size<T>
110 ///
111 /// **NOTE:** this is *not* the width/height of the rectangle.
112 #[inline(always)]
113 #[allow(dead_code)] // Fixes spurious clippy warning: this function is used!
114 pub(crate) fn sum_axes(&self) -> Size<U> {
115 Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() }
116 }
117
118 /// The sum of the two fields of the [`Rect`] representing the main axis.
119 ///
120 /// This is typically used when computing total padding.
121 ///
122 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
123 /// Otherwise, this is [`Rect::vertical`].
124 pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U {
125 if direction.is_row() {
126 self.horizontal_axis_sum()
127 } else {
128 self.vertical_axis_sum()
129 }
130 }
131
132 /// The sum of the two fields of the [`Rect`] representing the cross axis.
133 ///
134 /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
135 /// Otherwise, this is [`Rect::horizontal`].
136 pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U {
137 if direction.is_row() {
138 self.vertical_axis_sum()
139 } else {
140 self.horizontal_axis_sum()
141 }
142 }
143}
144
145impl<T> Rect<T>
146where
147 T: Copy + Clone,
148{
149 /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
150 pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
151 if direction.is_row() {
152 self.left
153 } else {
154 self.top
155 }
156 }
157
158 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
159 pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
160 if direction.is_row() {
161 self.right
162 } else {
163 self.bottom
164 }
165 }
166
167 /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
168 pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
169 if direction.is_row() {
170 self.top
171 } else {
172 self.left
173 }
174 }
175
176 /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
177 pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
178 if direction.is_row() {
179 self.bottom
180 } else {
181 self.right
182 }
183 }
184}
185
186impl Rect<f32> {
187 /// Creates a new Rect with `0.0` as all parameters
188 pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
189
190 /// Creates a new Rect
191 #[must_use]
192 pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
193 Self { left: start, right: end, top, bottom }
194 }
195}
196
197/// An abstract "line". Represents any type that has a start and an end
198#[derive(Debug, Copy, Clone, PartialEq, Eq)]
199#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
200#[cfg_attr(feature = "serde", serde(default))]
201pub struct Line<T> {
202 /// The start position of a line
203 pub start: T,
204 /// The end position of a line
205 pub end: T,
206}
207
208impl<T> Line<T> {
209 /// Applies the function `f` to both the width and height
210 ///
211 /// This is used to transform a `Line<T>` into a `Line<R>`.
212 pub fn map<R, F>(self, f: F) -> Line<R>
213 where
214 F: Fn(T) -> R,
215 {
216 Line { start: f(self.start), end: f(self.end) }
217 }
218}
219
220impl<T: Add + Copy> Line<T> {
221 /// Adds the start and end values together and returns the result
222 pub fn sum(&self) -> <T as Add>::Output {
223 self.start + self.end
224 }
225}
226
227/// The width and height of a [`Rect`]
228#[derive(Debug, Copy, Clone, PartialEq, Eq)]
229#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
230#[cfg_attr(feature = "serde", serde(default))]
231pub struct Size<T> {
232 /// The x extent of the rectangle
233 pub width: T,
234 /// The y extent of the rectangle
235 pub height: T,
236}
237
238impl<T: Default> Default for Size<T> {
239 fn default() -> Self {
240 Size { width: Default::default(), height: Default::default() }
241 }
242}
243
244// Generic Add impl for Size<T> + Size<U> where T + U has an Add impl
245impl<U, T: Add<U>> Add<Size<U>> for Size<T> {
246 type Output = Size<<T as Add<U>>::Output>;
247
248 fn add(self, rhs: Size<U>) -> Self::Output {
249 Size { width: self.width + rhs.width, height: self.height + rhs.height }
250 }
251}
252
253impl<T> Size<T> {
254 /// Applies the function `f` to both the width and height
255 ///
256 /// This is used to transform a `Size<T>` into a `Size<R>`.
257 pub fn map<R, F>(self, f: F) -> Size<R>
258 where
259 F: Fn(T) -> R,
260 {
261 Size { width: f(self.width), height: f(self.height) }
262 }
263
264 /// Applies the function `f` to the width
265 pub fn map_width<F>(self, f: F) -> Size<T>
266 where
267 F: Fn(T) -> T,
268 {
269 Size { width: f(self.width), height: self.height }
270 }
271
272 /// Applies the function `f` to the height
273 pub fn map_height<F>(self, f: F) -> Size<T>
274 where
275 F: Fn(T) -> T,
276 {
277 Size { width: self.width, height: f(self.height) }
278 }
279
280 /// Applies the function `f` to both the width and height
281 /// of this value and another passed value
282 pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
283 where
284 Func: Fn(T, Other) -> Ret,
285 {
286 Size { width: f(self.width, other.width), height: f(self.height, other.height) }
287 }
288
289 /// Sets the extent of the main layout axis
290 ///
291 /// Whether this is the width or height depends on the `direction` provided
292 pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
293 if direction.is_row() {
294 self.width = value
295 } else {
296 self.height = value
297 }
298 }
299
300 /// Sets the extent of the cross layout axis
301 ///
302 /// Whether this is the width or height depends on the `direction` provided
303 pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
304 if direction.is_row() {
305 self.height = value
306 } else {
307 self.width = value
308 }
309 }
310
311 /// Gets the extent of the main layout axis
312 ///
313 /// Whether this is the width or height depends on the `direction` provided
314 pub(crate) fn main(self, direction: FlexDirection) -> T {
315 if direction.is_row() {
316 self.width
317 } else {
318 self.height
319 }
320 }
321
322 /// Gets the extent of the cross layout axis
323 ///
324 /// Whether this is the width or height depends on the `direction` provided
325 pub(crate) fn cross(self, direction: FlexDirection) -> T {
326 if direction.is_row() {
327 self.height
328 } else {
329 self.width
330 }
331 }
332
333 /// Gets the extent of the specified layout axis
334 /// Whether this is the width or height depends on the `GridAxis` provided
335 #[cfg(feature = "grid")]
336 pub(crate) fn get(self, axis: AbstractAxis) -> T {
337 match axis {
338 AbstractAxis::Inline => self.width,
339 AbstractAxis::Block => self.height,
340 }
341 }
342
343 /// Sets the extent of the specified layout axis
344 /// Whether this is the width or height depends on the `GridAxis` provided
345 #[cfg(feature = "grid")]
346 pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) {
347 match axis {
348 AbstractAxis::Inline => self.width = value,
349 AbstractAxis::Block => self.height = value,
350 }
351 }
352}
353
354impl Size<f32> {
355 /// A [`Size`] with zero width and height
356 pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
357}
358
359impl Size<Option<f32>> {
360 /// A [`Size`] with `None` width and height
361 pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
362
363 /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
364 #[must_use]
365 pub const fn new(width: f32, height: f32) -> Self {
366 Size { width: Some(width), height: Some(height) }
367 }
368}
369
370impl<T> Size<Option<T>> {
371 /// Performs Option::unwrap_or on each component separately
372 pub fn unwrap_or(self, alt: Size<T>) -> Size<T> {
373 Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
374 }
375
376 /// Performs Option::or on each component separately
377 pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> {
378 Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
379 }
380}
381
382impl Size<Dimension> {
383 /// Generates a [`Size<Dimension>`] using [`Dimension::Points`] values
384 #[must_use]
385 pub const fn from_points(width: f32, height: f32) -> Self {
386 Size { width: Dimension::Points(width), height: Dimension::Points(height) }
387 }
388
389 /// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values
390 #[must_use]
391 pub const fn from_percent(width: f32, height: f32) -> Self {
392 Size { width: Dimension::Percent(width), height: Dimension::Percent(height) }
393 }
394}
395
396/// A 2-dimensional coordinate.
397///
398/// When used in association with a [`Rect`], represents the bottom-left corner.
399#[derive(Debug, Copy, Clone, PartialEq, Eq)]
400pub struct Point<T> {
401 /// The x-coordinate
402 pub x: T,
403 /// The y-coordinate
404 pub y: T,
405}
406
407impl Point<f32> {
408 /// A [`Point`] with values (0,0), representing the origin
409 pub const ZERO: Point<f32> = Self { x: 0.0, y: 0.0 };
410}
411
412/// Generic struct which holds a "min" value and a "max" value
413#[derive(Debug, Copy, Clone, PartialEq, Eq)]
414pub struct MinMax<Min, Max> {
415 /// The value representing the minimum
416 pub min: Min,
417 /// The value representing the maximum
418 pub max: Max,
419}