• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Geometric primitives useful for layout
2 
3 use crate::util::sys::f32_max;
4 use crate::{style::Dimension, util::sys::f32_min};
5 use core::ops::{Add, Sub};
6 
7 #[cfg(feature = "flexbox")]
8 use crate::style::FlexDirection;
9 
10 /// The simple absolute horizontal and vertical axis
11 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
12 pub enum AbsoluteAxis {
13     /// The horizontal axis
14     Horizontal,
15     /// The vertical axis
16     Vertical,
17 }
18 
19 impl AbsoluteAxis {
20     /// Returns the other variant of the enum
21     #[inline]
other_axis(&self) -> Self22     pub const fn other_axis(&self) -> Self {
23         match *self {
24             AbsoluteAxis::Horizontal => AbsoluteAxis::Vertical,
25             AbsoluteAxis::Vertical => AbsoluteAxis::Horizontal,
26         }
27     }
28 }
29 
30 impl<T> Size<T> {
31     #[inline(always)]
32     /// Get either the width or height depending on the AbsoluteAxis passed in
get_abs(self, axis: AbsoluteAxis) -> T33     pub fn get_abs(self, axis: AbsoluteAxis) -> T {
34         match axis {
35             AbsoluteAxis::Horizontal => self.width,
36             AbsoluteAxis::Vertical => self.height,
37         }
38     }
39 }
40 
41 impl<T: Add> Rect<T> {
42     #[inline(always)]
43     /// Get either the width or height depending on the AbsoluteAxis passed in
grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output44     pub fn grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output {
45         match axis {
46             AbsoluteAxis::Horizontal => self.left + self.right,
47             AbsoluteAxis::Vertical => self.top + self.bottom,
48         }
49     }
50 }
51 
52 /// The CSS abstract axis
53 /// <https://www.w3.org/TR/css-writing-modes-3/#abstract-axes>
54 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
55 pub enum AbstractAxis {
56     /// The axis in the inline dimension, i.e. the horizontal axis in horizontal writing modes and the vertical axis in vertical writing modes.
57     Inline,
58     /// The axis in the block dimension, i.e. the vertical axis in horizontal writing modes and the horizontal axis in vertical writing modes.
59     Block,
60 }
61 
62 impl AbstractAxis {
63     /// Returns the other variant of the enum
64     #[inline]
other(&self) -> AbstractAxis65     pub fn other(&self) -> AbstractAxis {
66         match *self {
67             AbstractAxis::Inline => AbstractAxis::Block,
68             AbstractAxis::Block => AbstractAxis::Inline,
69         }
70     }
71 
72     /// Convert an `AbstractAxis` into an `AbsoluteAxis` naively assuming that the Inline axis is Horizontal
73     /// This is currently always true, but will change if Taffy ever implements the `writing_mode` property
74     #[inline]
as_abs_naive(&self) -> AbsoluteAxis75     pub fn as_abs_naive(&self) -> AbsoluteAxis {
76         match self {
77             AbstractAxis::Inline => AbsoluteAxis::Horizontal,
78             AbstractAxis::Block => AbsoluteAxis::Vertical,
79         }
80     }
81 }
82 
83 /// Container that holds an item in each absolute axis without specifying
84 /// what kind of item it is.
85 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
86 pub(crate) struct InBothAbsAxis<T> {
87     /// The item in the horizontal axis
88     pub horizontal: T,
89     /// The item in the vertical axis
90     pub vertical: T,
91 }
92 
93 impl<T: Copy> InBothAbsAxis<T> {
94     #[cfg(feature = "grid")]
95     /// Get the contained item based on the AbsoluteAxis passed
get(&self, axis: AbsoluteAxis) -> T96     pub fn get(&self, axis: AbsoluteAxis) -> T {
97         match axis {
98             AbsoluteAxis::Horizontal => self.horizontal,
99             AbsoluteAxis::Vertical => self.vertical,
100         }
101     }
102 }
103 
104 /// An axis-aligned UI rectangle
105 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
106 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
107 pub struct Rect<T> {
108     /// This can represent either the x-coordinate of the starting edge,
109     /// or the amount of padding on the starting side.
110     ///
111     /// The starting edge is the left edge when working with LTR text,
112     /// and the right edge when working with RTL text.
113     pub left: T,
114     /// This can represent either the x-coordinate of the ending edge,
115     /// or the amount of padding on the ending side.
116     ///
117     /// The ending edge is the right edge when working with LTR text,
118     /// and the left edge when working with RTL text.
119     pub right: T,
120     /// This can represent either the y-coordinate of the top edge,
121     /// or the amount of padding on the top side.
122     pub top: T,
123     /// This can represent either the y-coordinate of the bottom edge,
124     /// or the amount of padding on the bottom side.
125     pub bottom: T,
126 }
127 
128 impl<U, T: Add<U>> Add<Rect<U>> for Rect<T> {
129     type Output = Rect<T::Output>;
130 
add(self, rhs: Rect<U>) -> Self::Output131     fn add(self, rhs: Rect<U>) -> Self::Output {
132         Rect {
133             left: self.left + rhs.left,
134             right: self.right + rhs.right,
135             top: self.top + rhs.top,
136             bottom: self.bottom + rhs.bottom,
137         }
138     }
139 }
140 
141 impl<T> Rect<T> {
142     /// Applies the function `f` to all four sides of the rect
143     ///
144     /// When applied to the left and right sides, the width is used
145     /// as the second parameter of `f`.
146     /// When applied to the top or bottom sides, the height is used instead.
147     #[cfg(any(feature = "flexbox", feature = "block_layout"))]
zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R> where F: Fn(T, U) -> R, U: Copy,148     pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
149     where
150         F: Fn(T, U) -> R,
151         U: Copy,
152     {
153         Rect {
154             left: f(self.left, size.width),
155             right: f(self.right, size.width),
156             top: f(self.top, size.height),
157             bottom: f(self.bottom, size.height),
158         }
159     }
160 
161     /// Applies the function `f` to the left, right, top, and bottom properties
162     ///
163     /// This is used to transform a `Rect<T>` into a `Rect<R>`.
map<R, F>(self, f: F) -> Rect<R> where F: Fn(T) -> R,164     pub fn map<R, F>(self, f: F) -> Rect<R>
165     where
166         F: Fn(T) -> R,
167     {
168         Rect { left: f(self.left), right: f(self.right), top: f(self.top), bottom: f(self.bottom) }
169     }
170 
171     /// Returns a `Line<T>` representing the left and right properties of the Rect
horizontal_components(self) -> Line<T>172     pub fn horizontal_components(self) -> Line<T> {
173         Line { start: self.left, end: self.right }
174     }
175 
176     /// Returns a `Line<T>` containing the top and bottom properties of the Rect
vertical_components(self) -> Line<T>177     pub fn vertical_components(self) -> Line<T> {
178         Line { start: self.top, end: self.bottom }
179     }
180 }
181 
182 impl<T, U> Rect<T>
183 where
184     T: Add<Output = U> + Copy + Clone,
185 {
186     /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
187     ///
188     /// This is typically used when computing total padding.
189     ///
190     /// **NOTE:** this is *not* the width of the rectangle.
191     #[inline(always)]
horizontal_axis_sum(&self) -> U192     pub(crate) fn horizontal_axis_sum(&self) -> U {
193         self.left + self.right
194     }
195 
196     /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
197     ///
198     /// This is typically used when computing total padding.
199     ///
200     /// **NOTE:** this is *not* the height of the rectangle.
201     #[inline(always)]
vertical_axis_sum(&self) -> U202     pub(crate) fn vertical_axis_sum(&self) -> U {
203         self.top + self.bottom
204     }
205 
206     /// Both horizontal_axis_sum and vertical_axis_sum as a Size<T>
207     ///
208     /// **NOTE:** this is *not* the width/height of the rectangle.
209     #[inline(always)]
210     #[allow(dead_code)] // Fixes spurious clippy warning: this function is used!
sum_axes(&self) -> Size<U>211     pub(crate) fn sum_axes(&self) -> Size<U> {
212         Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() }
213     }
214 
215     /// The sum of the two fields of the [`Rect`] representing the main axis.
216     ///
217     /// This is typically used when computing total padding.
218     ///
219     /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
220     /// Otherwise, this is [`Rect::vertical`].
221     #[cfg(feature = "flexbox")]
main_axis_sum(&self, direction: FlexDirection) -> U222     pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U {
223         if direction.is_row() {
224             self.horizontal_axis_sum()
225         } else {
226             self.vertical_axis_sum()
227         }
228     }
229 
230     /// The sum of the two fields of the [`Rect`] representing the cross axis.
231     ///
232     /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
233     /// Otherwise, this is [`Rect::horizontal`].
234     #[cfg(feature = "flexbox")]
cross_axis_sum(&self, direction: FlexDirection) -> U235     pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U {
236         if direction.is_row() {
237             self.vertical_axis_sum()
238         } else {
239             self.horizontal_axis_sum()
240         }
241     }
242 }
243 
244 impl<T> Rect<T>
245 where
246     T: Copy + Clone,
247 {
248     /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
249     #[cfg(feature = "flexbox")]
main_start(&self, direction: FlexDirection) -> T250     pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
251         if direction.is_row() {
252             self.left
253         } else {
254             self.top
255         }
256     }
257 
258     /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
259     #[cfg(feature = "flexbox")]
main_end(&self, direction: FlexDirection) -> T260     pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
261         if direction.is_row() {
262             self.right
263         } else {
264             self.bottom
265         }
266     }
267 
268     /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
269     #[cfg(feature = "flexbox")]
cross_start(&self, direction: FlexDirection) -> T270     pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
271         if direction.is_row() {
272             self.top
273         } else {
274             self.left
275         }
276     }
277 
278     /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
279     #[cfg(feature = "flexbox")]
cross_end(&self, direction: FlexDirection) -> T280     pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
281         if direction.is_row() {
282             self.bottom
283         } else {
284             self.right
285         }
286     }
287 }
288 
289 impl Rect<f32> {
290     /// Creates a new Rect with `0.0` as all parameters
291     pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
292 
293     /// Creates a new Rect
294     #[must_use]
new(start: f32, end: f32, top: f32, bottom: f32) -> Self295     pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
296         Self { left: start, right: end, top, bottom }
297     }
298 }
299 
300 /// An abstract "line". Represents any type that has a start and an end
301 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
302 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
303 #[cfg_attr(feature = "serde", serde(default))]
304 pub struct Line<T> {
305     /// The start position of a line
306     pub start: T,
307     /// The end position of a line
308     pub end: T,
309 }
310 
311 impl<T> Line<T> {
312     /// Applies the function `f` to both the width and height
313     ///
314     /// This is used to transform a `Line<T>` into a `Line<R>`.
map<R, F>(self, f: F) -> Line<R> where F: Fn(T) -> R,315     pub fn map<R, F>(self, f: F) -> Line<R>
316     where
317         F: Fn(T) -> R,
318     {
319         Line { start: f(self.start), end: f(self.end) }
320     }
321 }
322 
323 impl Line<bool> {
324     /// A `Line<bool>` with both start and end set to `true`
325     pub const TRUE: Self = Line { start: true, end: true };
326     /// A `Line<bool>` with both start and end set to `false`
327     pub const FALSE: Self = Line { start: false, end: false };
328 }
329 
330 impl<T: Add + Copy> Line<T> {
331     /// Adds the start and end values together and returns the result
sum(&self) -> <T as Add>::Output332     pub fn sum(&self) -> <T as Add>::Output {
333         self.start + self.end
334     }
335 }
336 
337 /// The width and height of a [`Rect`]
338 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
339 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
340 pub struct Size<T> {
341     /// The x extent of the rectangle
342     pub width: T,
343     /// The y extent of the rectangle
344     pub height: T,
345 }
346 
347 // Generic Add impl for Size<T> + Size<U> where T + U has an Add impl
348 impl<U, T: Add<U>> Add<Size<U>> for Size<T> {
349     type Output = Size<<T as Add<U>>::Output>;
350 
add(self, rhs: Size<U>) -> Self::Output351     fn add(self, rhs: Size<U>) -> Self::Output {
352         Size { width: self.width + rhs.width, height: self.height + rhs.height }
353     }
354 }
355 
356 // Generic Sub impl for Size<T> + Size<U> where T + U has an Sub impl
357 impl<U, T: Sub<U>> Sub<Size<U>> for Size<T> {
358     type Output = Size<<T as Sub<U>>::Output>;
359 
sub(self, rhs: Size<U>) -> Self::Output360     fn sub(self, rhs: Size<U>) -> Self::Output {
361         Size { width: self.width - rhs.width, height: self.height - rhs.height }
362     }
363 }
364 
365 // Note: we allow dead_code here as we want to provide a complete API of helpers that is symmetrical in all axes,
366 // but sometimes we only currently have a use for the helper in a single axis
367 #[allow(dead_code)]
368 impl<T> Size<T> {
369     /// Applies the function `f` to both the width and height
370     ///
371     /// This is used to transform a `Size<T>` into a `Size<R>`.
map<R, F>(self, f: F) -> Size<R> where F: Fn(T) -> R,372     pub fn map<R, F>(self, f: F) -> Size<R>
373     where
374         F: Fn(T) -> R,
375     {
376         Size { width: f(self.width), height: f(self.height) }
377     }
378 
379     /// Applies the function `f` to the width
map_width<F>(self, f: F) -> Size<T> where F: Fn(T) -> T,380     pub fn map_width<F>(self, f: F) -> Size<T>
381     where
382         F: Fn(T) -> T,
383     {
384         Size { width: f(self.width), height: self.height }
385     }
386 
387     /// Applies the function `f` to the height
map_height<F>(self, f: F) -> Size<T> where F: Fn(T) -> T,388     pub fn map_height<F>(self, f: F) -> Size<T>
389     where
390         F: Fn(T) -> T,
391     {
392         Size { width: self.width, height: f(self.height) }
393     }
394 
395     /// Applies the function `f` to both the width and height
396     /// of this value and another passed value
zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret> where Func: Fn(T, Other) -> Ret,397     pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
398     where
399         Func: Fn(T, Other) -> Ret,
400     {
401         Size { width: f(self.width, other.width), height: f(self.height, other.height) }
402     }
403 
404     /// Sets the extent of the main layout axis
405     ///
406     /// Whether this is the width or height depends on the `direction` provided
407     #[cfg(feature = "flexbox")]
set_main(&mut self, direction: FlexDirection, value: T)408     pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
409         if direction.is_row() {
410             self.width = value
411         } else {
412             self.height = value
413         }
414     }
415 
416     /// Sets the extent of the cross layout axis
417     ///
418     /// Whether this is the width or height depends on the `direction` provided
419     #[cfg(feature = "flexbox")]
set_cross(&mut self, direction: FlexDirection, value: T)420     pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
421         if direction.is_row() {
422             self.height = value
423         } else {
424             self.width = value
425         }
426     }
427 
428     /// Creates a new value of type Self with the main axis set to value provided
429     ///
430     /// Whether this is the width or height depends on the `direction` provided
431     #[cfg(feature = "flexbox")]
with_main(self, direction: FlexDirection, value: T) -> Self432     pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self {
433         let mut new = self;
434         if direction.is_row() {
435             new.width = value
436         } else {
437             new.height = value
438         }
439         new
440     }
441 
442     /// Creates a new value of type Self with the cross axis set to value provided
443     ///
444     /// Whether this is the width or height depends on the `direction` provided
445     #[cfg(feature = "flexbox")]
with_cross(self, direction: FlexDirection, value: T) -> Self446     pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self {
447         let mut new = self;
448         if direction.is_row() {
449             new.height = value
450         } else {
451             new.width = value
452         }
453         new
454     }
455 
456     /// Creates a new value of type Self with the main axis modified by the callback provided
457     ///
458     /// Whether this is the width or height depends on the `direction` provided
459     #[cfg(feature = "flexbox")]
map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self460     pub(crate) fn map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
461         let mut new = self;
462         if direction.is_row() {
463             new.width = mapper(new.width);
464         } else {
465             new.height = mapper(new.height);
466         }
467         new
468     }
469 
470     /// Creates a new value of type Self with the cross axis modified by the callback provided
471     ///
472     /// Whether this is the width or height depends on the `direction` provided
473     #[cfg(feature = "flexbox")]
map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self474     pub(crate) fn map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
475         let mut new = self;
476         if direction.is_row() {
477             new.height = mapper(new.height);
478         } else {
479             new.width = mapper(new.width);
480         }
481         new
482     }
483 
484     /// Gets the extent of the main layout axis
485     ///
486     /// Whether this is the width or height depends on the `direction` provided
487     #[cfg(feature = "flexbox")]
main(self, direction: FlexDirection) -> T488     pub(crate) fn main(self, direction: FlexDirection) -> T {
489         if direction.is_row() {
490             self.width
491         } else {
492             self.height
493         }
494     }
495 
496     /// Gets the extent of the cross layout axis
497     ///
498     /// Whether this is the width or height depends on the `direction` provided
499     #[cfg(feature = "flexbox")]
cross(self, direction: FlexDirection) -> T500     pub(crate) fn cross(self, direction: FlexDirection) -> T {
501         if direction.is_row() {
502             self.height
503         } else {
504             self.width
505         }
506     }
507 
508     /// Gets the extent of the specified layout axis
509     /// Whether this is the width or height depends on the `GridAxis` provided
510     #[cfg(feature = "grid")]
get(self, axis: AbstractAxis) -> T511     pub(crate) fn get(self, axis: AbstractAxis) -> T {
512         match axis {
513             AbstractAxis::Inline => self.width,
514             AbstractAxis::Block => self.height,
515         }
516     }
517 
518     /// Sets the extent of the specified layout axis
519     /// Whether this is the width or height depends on the `GridAxis` provided
520     #[cfg(feature = "grid")]
set(&mut self, axis: AbstractAxis, value: T)521     pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) {
522         match axis {
523             AbstractAxis::Inline => self.width = value,
524             AbstractAxis::Block => self.height = value,
525         }
526     }
527 }
528 
529 impl Size<f32> {
530     /// A [`Size`] with zero width and height
531     pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
532 
533     /// Applies f32_max to each component separately
534     #[inline(always)]
f32_max(self, rhs: Size<f32>) -> Size<f32>535     pub fn f32_max(self, rhs: Size<f32>) -> Size<f32> {
536         Size { width: f32_max(self.width, rhs.width), height: f32_max(self.height, rhs.height) }
537     }
538 
539     /// Applies f32_min to each component separately
540     #[inline(always)]
f32_min(self, rhs: Size<f32>) -> Size<f32>541     pub fn f32_min(self, rhs: Size<f32>) -> Size<f32> {
542         Size { width: f32_min(self.width, rhs.width), height: f32_min(self.height, rhs.height) }
543     }
544 
545     /// Return true if both width and height are greater than 0 else false
546     #[inline(always)]
has_non_zero_area(self) -> bool547     pub fn has_non_zero_area(self) -> bool {
548         self.width > 0.0 && self.height > 0.0
549     }
550 }
551 
552 impl Size<Option<f32>> {
553     /// A [`Size`] with `None` width and height
554     pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
555 
556     /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
557     #[must_use]
new(width: f32, height: f32) -> Self558     pub const fn new(width: f32, height: f32) -> Self {
559         Size { width: Some(width), height: Some(height) }
560     }
561 
562     /// Creates a new [`Size<Option<f32>>`] with either the width or height set based on the provided `direction`
563     #[cfg(feature = "flexbox")]
from_cross(direction: FlexDirection, value: Option<f32>) -> Self564     pub fn from_cross(direction: FlexDirection, value: Option<f32>) -> Self {
565         let mut new = Self::NONE;
566         if direction.is_row() {
567             new.height = value
568         } else {
569             new.width = value
570         }
571         new
572     }
573 
574     /// Applies aspect_ratio (if one is supplied) to the Size:
575     ///   - If width is `Some` but height is `None`, then height is computed from width and aspect_ratio
576     ///   - If height is `Some` but width is `None`, then width is computed from height and aspect_ratio
577     ///
578     /// If aspect_ratio is `None` then this function simply returns self.
maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>>579     pub fn maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>> {
580         match aspect_ratio {
581             Some(ratio) => match (self.width, self.height) {
582                 (Some(width), None) => Size { width: Some(width), height: Some(width / ratio) },
583                 (None, Some(height)) => Size { width: Some(height * ratio), height: Some(height) },
584                 _ => self,
585             },
586             None => self,
587         }
588     }
589 }
590 
591 impl<T> Size<Option<T>> {
592     /// Performs Option::unwrap_or on each component separately
unwrap_or(self, alt: Size<T>) -> Size<T>593     pub fn unwrap_or(self, alt: Size<T>) -> Size<T> {
594         Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
595     }
596 
597     /// Performs Option::or on each component separately
or(self, alt: Size<Option<T>>) -> Size<Option<T>>598     pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> {
599         Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
600     }
601 
602     /// Return true if both components are Some, else false.
603     #[inline(always)]
both_axis_defined(&self) -> bool604     pub fn both_axis_defined(&self) -> bool {
605         self.width.is_some() && self.height.is_some()
606     }
607 }
608 
609 impl Size<Dimension> {
610     /// Generates a [`Size<Dimension>`] using [`Dimension::Length`] values
611     #[must_use]
from_lengths(width: f32, height: f32) -> Self612     pub const fn from_lengths(width: f32, height: f32) -> Self {
613         Size { width: Dimension::Length(width), height: Dimension::Length(height) }
614     }
615 
616     /// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values
617     #[must_use]
from_percent(width: f32, height: f32) -> Self618     pub const fn from_percent(width: f32, height: f32) -> Self {
619         Size { width: Dimension::Percent(width), height: Dimension::Percent(height) }
620     }
621 }
622 
623 /// A 2-dimensional coordinate.
624 ///
625 /// When used in association with a [`Rect`], represents the top-left corner.
626 #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
627 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
628 pub struct Point<T> {
629     /// The x-coordinate
630     pub x: T,
631     /// The y-coordinate
632     pub y: T,
633 }
634 
635 impl Point<f32> {
636     /// A [`Point`] with values (0,0), representing the origin
637     pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
638 }
639 
640 impl Point<Option<f32>> {
641     /// A [`Point`] with values (None, None)
642     pub const NONE: Self = Self { x: None, y: None };
643 }
644 
645 // Generic Add impl for Point<T> + Point<U> where T + U has an Add impl
646 impl<U, T: Add<U>> Add<Point<U>> for Point<T> {
647     type Output = Point<<T as Add<U>>::Output>;
648 
add(self, rhs: Point<U>) -> Self::Output649     fn add(self, rhs: Point<U>) -> Self::Output {
650         Point { x: self.x + rhs.x, y: self.y + rhs.y }
651     }
652 }
653 
654 impl<T> Point<T> {
655     /// Applies the function `f` to both the x and y
656     ///
657     /// This is used to transform a `Point<T>` into a `Point<R>`.
map<R, F>(self, f: F) -> Point<R> where F: Fn(T) -> R,658     pub fn map<R, F>(self, f: F) -> Point<R>
659     where
660         F: Fn(T) -> R,
661     {
662         Point { x: f(self.x), y: f(self.y) }
663     }
664 
665     /// Gets the extent of the specified layout axis
666     /// Whether this is the width or height depends on the `GridAxis` provided
667     #[cfg(feature = "grid")]
get(self, axis: AbstractAxis) -> T668     pub fn get(self, axis: AbstractAxis) -> T {
669         match axis {
670             AbstractAxis::Inline => self.x,
671             AbstractAxis::Block => self.y,
672         }
673     }
674 
675     /// Swap x and y components
transpose(self) -> Point<T>676     pub fn transpose(self) -> Point<T> {
677         Point { x: self.y, y: self.x }
678     }
679 
680     /// Sets the extent of the specified layout axis
681     /// Whether this is the width or height depends on the `GridAxis` provided
682     #[cfg(feature = "grid")]
set(&mut self, axis: AbstractAxis, value: T)683     pub fn set(&mut self, axis: AbstractAxis, value: T) {
684         match axis {
685             AbstractAxis::Inline => self.x = value,
686             AbstractAxis::Block => self.y = value,
687         }
688     }
689 
690     /// Gets the component in the main layout axis
691     ///
692     /// Whether this is the x or y depends on the `direction` provided
693     #[cfg(feature = "flexbox")]
main(self, direction: FlexDirection) -> T694     pub(crate) fn main(self, direction: FlexDirection) -> T {
695         if direction.is_row() {
696             self.x
697         } else {
698             self.y
699         }
700     }
701 
702     /// Gets the component in the cross layout axis
703     ///
704     /// Whether this is the x or y depends on the `direction` provided
705     #[cfg(feature = "flexbox")]
cross(self, direction: FlexDirection) -> T706     pub(crate) fn cross(self, direction: FlexDirection) -> T {
707         if direction.is_row() {
708             self.y
709         } else {
710             self.x
711         }
712     }
713 }
714 
715 impl<T> From<Point<T>> for Size<T> {
from(value: Point<T>) -> Self716     fn from(value: Point<T>) -> Self {
717         Size { width: value.x, height: value.y }
718     }
719 }
720 
721 /// Generic struct which holds a "min" value and a "max" value
722 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
723 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
724 pub struct MinMax<Min, Max> {
725     /// The value representing the minimum
726     pub min: Min,
727     /// The value representing the maximum
728     pub max: Max,
729 }
730