zng_layout/unit/
side_offsets.rs

1use std::{fmt, ops};
2
3use zng_unit::DipSideOffsets;
4use zng_var::{animation::Transitionable, impl_from_and_into_var};
5
6use crate::unit::{LengthCompositeParser, ParseCompositeError};
7
8use super::{Factor, FactorPercent, FactorSideOffsets, Layout1d, LayoutMask, Length, PxSideOffsets, impl_length_comp_conversions};
9
10/// 2D size offsets in [`Length`] units.
11///
12/// This unit defines spacing around all four sides of a box, a widget margin can be defined using a value of this type.
13#[derive(Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
14pub struct SideOffsets {
15    /// Spacing above, in length units.
16    pub top: Length,
17    /// Spacing to the right, in length units.
18    pub right: Length,
19    /// Spacing bellow, in length units.
20    pub bottom: Length,
21    /// Spacing to the left ,in length units.
22    pub left: Length,
23}
24impl fmt::Debug for SideOffsets {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        if f.alternate() {
27            f.debug_struct("SideOffsets")
28                .field("top", &self.top)
29                .field("right", &self.right)
30                .field("bottom", &self.bottom)
31                .field("left", &self.left)
32                .finish()
33        } else if self.all_eq() {
34            write!(f, "{:.p$?}", self.top, p = f.precision().unwrap_or(0))
35        } else if self.dimensions_eq() {
36            write!(f, "({:.p$?}, {:.p$?})", self.top, self.left, p = f.precision().unwrap_or(0))
37        } else {
38            write!(
39                f,
40                "({:.p$?}, {:.p$?}, {:.p$?}, {:.p$?})",
41                self.top,
42                self.right,
43                self.bottom,
44                self.left,
45                p = f.precision().unwrap_or(0)
46            )
47        }
48    }
49}
50impl std::str::FromStr for SideOffsets {
51    type Err = ParseCompositeError;
52
53    fn from_str(s: &str) -> Result<Self, Self::Err> {
54        let mut parser = LengthCompositeParser::new(s)?;
55        let a = parser.next()?;
56        if parser.has_ended() {
57            return Ok(Self::new_all(a));
58        }
59        let b = parser.next()?;
60        if parser.has_ended() {
61            return Ok(Self::new_vh(a, b));
62        }
63        let c = parser.next()?;
64        let d = parser.expect_last()?;
65        Ok(Self::new(a, b, c, d))
66    }
67}
68impl SideOffsets {
69    /// New top, right, bottom left offsets. From any [`Length`] type.
70    pub fn new<T: Into<Length>, R: Into<Length>, B: Into<Length>, L: Into<Length>>(top: T, right: R, bottom: B, left: L) -> Self {
71        SideOffsets {
72            top: top.into(),
73            right: right.into(),
74            bottom: bottom.into(),
75            left: left.into(),
76        }
77    }
78
79    /// Top-bottom and left-right equal. From any [`Length`] type.
80    pub fn new_vh<TB: Into<Length>, LR: Into<Length>>(top_bottom: TB, left_right: LR) -> Self {
81        let top_bottom = top_bottom.into();
82        let left_right = left_right.into();
83        SideOffsets {
84            top: top_bottom.clone(),
85            bottom: top_bottom,
86            left: left_right.clone(),
87            right: left_right,
88        }
89    }
90
91    /// All sides equal. From any [`Length`] type.
92    pub fn new_all<T: Into<Length>>(all_sides: T) -> Self {
93        let all_sides = all_sides.into();
94        SideOffsets {
95            top: all_sides.clone(),
96            right: all_sides.clone(),
97            bottom: all_sides.clone(),
98            left: all_sides,
99        }
100    }
101
102    /// All sides [zero](Length::zero).
103    pub fn zero() -> Self {
104        Self::new_all(Length::zero())
105    }
106
107    /// If all sides are equal.
108    pub fn all_eq(&self) -> bool {
109        self.top == self.bottom && self.top == self.left && self.top == self.right
110    }
111
112    /// If top and bottom are equal; and left and right are equal.
113    pub fn dimensions_eq(&self) -> bool {
114        self.top == self.bottom && self.left == self.right
115    }
116}
117impl super::Layout2d for SideOffsets {
118    type Px = PxSideOffsets;
119
120    fn layout_dft(&self, default: Self::Px) -> Self::Px {
121        PxSideOffsets::new(
122            self.top.layout_dft_y(default.top),
123            self.right.layout_dft_x(default.right),
124            self.bottom.layout_dft_y(default.bottom),
125            self.left.layout_dft_x(default.left),
126        )
127    }
128
129    fn affect_mask(&self) -> LayoutMask {
130        self.top.affect_mask() | self.right.affect_mask() | self.bottom.affect_mask() | self.left.affect_mask()
131    }
132}
133impl_from_and_into_var! {
134    /// All sides equal.
135    fn from(all: Length) -> SideOffsets {
136        SideOffsets::new_all(all)
137    }
138
139    /// All sides equal relative length.
140    fn from(percent: FactorPercent) -> SideOffsets {
141        SideOffsets::new_all(percent)
142    }
143    /// All sides equal relative length.
144    fn from(norm: Factor) -> SideOffsets {
145        SideOffsets::new_all(norm)
146    }
147
148    /// All sides equal exact length.
149    fn from(f: f32) -> SideOffsets {
150        SideOffsets::new_all(f)
151    }
152    /// All sides equal exact length.
153    fn from(i: i32) -> SideOffsets {
154        SideOffsets::new_all(i)
155    }
156
157    /// From exact lengths.
158    fn from(offsets: PxSideOffsets) -> SideOffsets {
159        SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
160    }
161
162    // From exact lengths.
163    fn from(offsets: DipSideOffsets) -> SideOffsets {
164        SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
165    }
166}
167
168impl_length_comp_conversions! {
169    /// (top-bottom, left-right)
170    fn from(top_bottom: TB, left_right: LR) -> SideOffsets {
171        SideOffsets::new_vh(top_bottom, left_right)
172    }
173
174    /// (top, right, bottom, left)
175    fn from(top: T, right: R, bottom: B, left: L) -> SideOffsets {
176        SideOffsets::new(top, right, bottom, left)
177    }
178}
179
180impl ops::Add for SideOffsets {
181    type Output = Self;
182
183    fn add(mut self, rhs: Self) -> Self {
184        self += rhs;
185        self
186    }
187}
188impl ops::AddAssign for SideOffsets {
189    fn add_assign(&mut self, rhs: Self) {
190        self.top += rhs.top;
191        self.right += rhs.right;
192        self.bottom += rhs.bottom;
193        self.left += rhs.left;
194    }
195}
196impl ops::Sub for SideOffsets {
197    type Output = Self;
198
199    fn sub(mut self, rhs: Self) -> Self {
200        self -= rhs;
201        self
202    }
203}
204impl ops::SubAssign for SideOffsets {
205    fn sub_assign(&mut self, rhs: Self) {
206        self.top -= rhs.top;
207        self.right -= rhs.right;
208        self.bottom -= rhs.bottom;
209        self.left -= rhs.left;
210    }
211}
212impl<S: Into<FactorSideOffsets>> ops::Mul<S> for SideOffsets {
213    type Output = Self;
214
215    fn mul(mut self, rhs: S) -> Self {
216        self *= rhs;
217        self
218    }
219}
220impl<S: Into<FactorSideOffsets>> ops::Mul<S> for &SideOffsets {
221    type Output = SideOffsets;
222
223    fn mul(self, rhs: S) -> Self::Output {
224        self.clone() * rhs
225    }
226}
227impl<S: Into<FactorSideOffsets>> ops::MulAssign<S> for SideOffsets {
228    fn mul_assign(&mut self, rhs: S) {
229        let rhs = rhs.into();
230        self.top *= rhs.top;
231        self.right *= rhs.right;
232        self.bottom *= rhs.bottom;
233        self.left *= rhs.left;
234    }
235}
236impl<S: Into<FactorSideOffsets>> ops::Div<S> for SideOffsets {
237    type Output = Self;
238
239    fn div(mut self, rhs: S) -> Self {
240        self /= rhs;
241        self
242    }
243}
244impl<S: Into<FactorSideOffsets>> ops::Div<S> for &SideOffsets {
245    type Output = SideOffsets;
246
247    fn div(self, rhs: S) -> Self::Output {
248        self.clone() / rhs
249    }
250}
251impl<S: Into<FactorSideOffsets>> ops::DivAssign<S> for SideOffsets {
252    fn div_assign(&mut self, rhs: S) {
253        let rhs = rhs.into();
254        self.top /= rhs.top;
255        self.right /= rhs.right;
256        self.bottom /= rhs.bottom;
257        self.left /= rhs.left;
258    }
259}