gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

⎇ 01babab32d8138f3c3fa05095fb6c65fca0d3967 src/geometry.rs Rust · 419 líneas · 12.9 KB Historia Quién tocó qué crudo

Make CSS Grid feature a default feature (#297) 01babab32 · Nico Burns ·

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}