zng_layout/unit/
rect.rs

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