zng_layout/unit/
rect.rs

1use std::{fmt, ops};
2
3use zng_var::{animation::Transitionable, impl_from_and_into_var};
4
5use super::{DipRect, Factor2d, LayoutMask, Length, Point, PxRect, Size, Vector, impl_length_comp_conversions};
6
7/// 2D rect in [`Length`] units.
8#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
9pub struct Rect {
10    /// Top-left origin of the rectangle in length units.
11    pub origin: Point,
12    /// Size of the rectangle in length units.
13    pub size: Size,
14}
15impl fmt::Debug for Rect {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        if f.alternate() {
18            f.debug_struct("Rect")
19                .field("origin", &self.origin)
20                .field("size", &self.size)
21                .finish()
22        } else {
23            write!(f, "{:?}.at{:?}", self.origin, self.size)
24        }
25    }
26}
27impl fmt::Display for Rect {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        if let Some(p) = f.precision() {
30            write!(f, "{:.p$} {:.p$}", self.origin, self.size, p = p)
31        } else {
32            write!(f, "{} {}", self.origin, self.size)
33        }
34    }
35}
36impl Rect {
37    /// New rectangle defined by an origin point (top-left) and a size, both in any type that converts to
38    /// [`Point`] and [`Size`].
39    ///
40    /// Also see [`RectFromTuplesBuilder`] for another way of initializing a rectangle value.
41    pub fn new<O: Into<Point>, S: Into<Size>>(origin: O, size: S) -> Self {
42        Rect {
43            origin: origin.into(),
44            size: size.into(),
45        }
46    }
47
48    /// New rectangle at [`Point::zero`]. The size is in any [`Length`] unit.
49    pub fn from_size<S: Into<Size>>(size: S) -> Self {
50        Self::new(Point::zero(), size)
51    }
52
53    /// New rectangle at [`Point::zero`] and [`Size::zero`].
54    pub fn zero() -> Self {
55        Self::new(Point::zero(), Size::zero())
56    }
57
58    /// Rect that fills the available space.
59    pub fn fill() -> Self {
60        Self::from_size(Size::fill())
61    }
62
63    /// Min x and y, this is the [`origin`].
64    ///
65    /// [`origin`]: Self::origin
66    pub fn min(&self) -> Point {
67        self.origin.clone()
68    }
69
70    /// Max x and y, this is the sum of [`origin`] and [`size`].
71    ///
72    /// [`origin`]: Self::origin
73    /// [`size`]: Self::size
74    pub fn max(&self) -> Point {
75        self.origin.clone() + self.size.clone()
76    }
77
78    /// Min x, this is the `origin.x`.
79    pub fn min_x(&self) -> Length {
80        self.origin.x.clone()
81    }
82    /// Min y, this is the `origin.y`.
83    pub fn min_y(&self) -> Length {
84        self.origin.y.clone()
85    }
86
87    /// Max x, this is the `origin.x + width`.
88    pub fn max_x(&self) -> Length {
89        self.origin.x.clone() + self.size.width.clone()
90    }
91    /// Max y, this is the `origin.y + height`.
92    pub fn max_y(&self) -> Length {
93        self.origin.y.clone() + self.size.height.clone()
94    }
95
96    /// Returns a rectangle of same size that adds the vector to the origin.
97    pub fn translate(&self, by: impl Into<Vector>) -> Self {
98        let mut r = self.clone();
99        r.origin += by.into();
100        r
101    }
102
103    /// Returns `true` if all values are [`Length::Default`].
104    pub fn is_default(&self) -> bool {
105        self.origin.is_default() && self.size.is_default()
106    }
107
108    /// Replaces [`Length::Default`] values with `overwrite` values.
109    pub fn replace_default(&mut self, overwrite: &Rect) {
110        self.origin.replace_default(&overwrite.origin);
111        self.size.replace_default(&overwrite.size);
112    }
113}
114impl super::Layout2d for Rect {
115    type Px = PxRect;
116
117    fn layout_dft(&self, default: Self::Px) -> Self::Px {
118        PxRect {
119            origin: self.origin.layout_dft(default.origin),
120            size: self.size.layout_dft(default.size),
121        }
122    }
123
124    fn affect_mask(&self) -> LayoutMask {
125        self.origin.affect_mask() | self.size.affect_mask()
126    }
127}
128impl_length_comp_conversions! {
129    fn from(x: X, y: Y, width: W, height: H) -> Rect {
130        Rect::new((x, y), (width, height))
131    }
132}
133impl_from_and_into_var! {
134    /// New in exact length.
135    fn from(rect: PxRect) -> Rect {
136        Rect::new(rect.origin, rect.size)
137    }
138
139    /// New in exact length.
140    fn from(rect: DipRect) -> Rect {
141        Rect::new(rect.origin, rect.size)
142    }
143
144    fn from(size: Size) -> Rect {
145        Rect::from_size(size)
146    }
147
148    /// New from origin and size.
149    fn from<O: Into<Point>, S: Into<Size>>((origin, size): (O, S)) -> Rect {
150        Rect::new(origin, size)
151    }
152}
153impl<S: Into<Factor2d>> ops::Mul<S> for Rect {
154    type Output = Self;
155
156    fn mul(mut self, rhs: S) -> Self {
157        self *= rhs;
158        self
159    }
160}
161impl<S: Into<Factor2d>> ops::Mul<S> for &Rect {
162    type Output = Rect;
163
164    fn mul(self, rhs: S) -> Self::Output {
165        self.clone() * rhs
166    }
167}
168impl<S: Into<Factor2d>> ops::MulAssign<S> for Rect {
169    fn mul_assign(&mut self, rhs: S) {
170        let rhs = rhs.into();
171        self.origin *= rhs;
172        self.size *= rhs;
173    }
174}
175impl<S: Into<Factor2d>> ops::Div<S> for Rect {
176    type Output = Self;
177
178    fn div(mut self, rhs: S) -> Self {
179        self /= rhs;
180        self
181    }
182}
183impl<S: Into<Factor2d>> ops::Div<S> for &Rect {
184    type Output = Rect;
185
186    fn div(self, rhs: S) -> Self::Output {
187        self.clone() / rhs
188    }
189}
190impl<S: Into<Factor2d>> ops::DivAssign<S> for Rect {
191    fn div_assign(&mut self, rhs: S) {
192        let rhs = rhs.into();
193        self.origin /= rhs;
194        self.size /= rhs;
195    }
196}
197impl ops::Add for Rect {
198    type Output = Self;
199
200    fn add(mut self, rhs: Self) -> Self {
201        self += rhs;
202        self
203    }
204}
205impl ops::AddAssign for Rect {
206    fn add_assign(&mut self, rhs: Self) {
207        self.origin += rhs.origin;
208        self.size += rhs.size;
209    }
210}
211impl ops::Sub for Rect {
212    type Output = Self;
213
214    fn sub(mut self, rhs: Self) -> Self {
215        self -= rhs;
216        self
217    }
218}
219impl ops::SubAssign for Rect {
220    fn sub_assign(&mut self, rhs: Self) {
221        self.origin -= rhs.origin;
222        self.size -= rhs.size;
223    }
224}
225
226/// Build a [`Rect`] using the syntax `(width, height).at(x, y)`.
227///
228/// # Examples
229///
230/// ```
231/// # use zng_layout::unit::*;
232/// let rect = (800, 600).at(10, 20);
233/// assert_eq!(Rect::new(Point::new(10, 20), Size::new(800, 600)), rect);
234/// ```
235pub trait RectFromTuplesBuilder {
236    /// New [`Rect`] from `self` as the size placed at the `x, y` origin.
237    fn at<X: Into<Length>, Y: Into<Length>>(self, x: X, y: Y) -> Rect;
238}
239impl<W: Into<Length>, H: Into<Length>> RectFromTuplesBuilder for (W, H) {
240    fn at<X: Into<Length>, Y: Into<Length>>(self, x: X, y: Y) -> Rect {
241        Rect::new((x, y), self)
242    }
243}