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