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#[derive(Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
14pub struct SideOffsets {
15 pub top: Length,
17 pub right: Length,
19 pub bottom: Length,
21 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 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 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 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 pub fn zero() -> Self {
104 Self::new_all(Length::zero())
105 }
106
107 pub fn all_eq(&self) -> bool {
109 self.top == self.bottom && self.top == self.left && self.top == self.right
110 }
111
112 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 fn from(all: Length) -> SideOffsets {
136 SideOffsets::new_all(all)
137 }
138
139 fn from(percent: FactorPercent) -> SideOffsets {
141 SideOffsets::new_all(percent)
142 }
143 fn from(norm: Factor) -> SideOffsets {
145 SideOffsets::new_all(norm)
146 }
147
148 fn from(f: f32) -> SideOffsets {
150 SideOffsets::new_all(f)
151 }
152 fn from(i: i32) -> SideOffsets {
154 SideOffsets::new_all(i)
155 }
156
157 fn from(offsets: PxSideOffsets) -> SideOffsets {
159 SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
160 }
161
162 fn from(offsets: DipSideOffsets) -> SideOffsets {
164 SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
165 }
166}
167
168impl_length_comp_conversions! {
169 fn from(top_bottom: TB, left_right: LR) -> SideOffsets {
171 SideOffsets::new_vh(top_bottom, left_right)
172 }
173
174 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}