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, "{:?}", self.top)
35 } else if self.dimensions_eq() {
36 write!(f, "({:?}, {:?})", self.top, self.left)
37 } else {
38 write!(f, "({:?}, {:?}, {:?}, {:?})", self.top, self.right, self.bottom, self.left)
39 }
40 }
41}
42impl std::str::FromStr for SideOffsets {
43 type Err = ParseCompositeError;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 let mut parser = LengthCompositeParser::new(s)?;
47 let a = parser.next()?;
48 if parser.has_ended() {
49 return Ok(Self::new_all(a));
50 }
51 let b = parser.next()?;
52 if parser.has_ended() {
53 return Ok(Self::new_vh(a, b));
54 }
55 let c = parser.next()?;
56 let d = parser.expect_last()?;
57 Ok(Self::new(a, b, c, d))
58 }
59}
60impl SideOffsets {
61 pub fn new<T: Into<Length>, R: Into<Length>, B: Into<Length>, L: Into<Length>>(top: T, right: R, bottom: B, left: L) -> Self {
63 SideOffsets {
64 top: top.into(),
65 right: right.into(),
66 bottom: bottom.into(),
67 left: left.into(),
68 }
69 }
70
71 pub fn new_vh<TB: Into<Length>, LR: Into<Length>>(top_bottom: TB, left_right: LR) -> Self {
73 let top_bottom = top_bottom.into();
74 let left_right = left_right.into();
75 SideOffsets {
76 top: top_bottom.clone(),
77 bottom: top_bottom,
78 left: left_right.clone(),
79 right: left_right,
80 }
81 }
82
83 pub fn new_all<T: Into<Length>>(all_sides: T) -> Self {
85 let all_sides = all_sides.into();
86 SideOffsets {
87 top: all_sides.clone(),
88 right: all_sides.clone(),
89 bottom: all_sides.clone(),
90 left: all_sides,
91 }
92 }
93
94 pub fn zero() -> Self {
96 Self::new_all(Length::zero())
97 }
98
99 pub fn all_eq(&self) -> bool {
101 self.top == self.bottom && self.top == self.left && self.top == self.right
102 }
103
104 pub fn dimensions_eq(&self) -> bool {
106 self.top == self.bottom && self.left == self.right
107 }
108}
109impl super::Layout2d for SideOffsets {
110 type Px = PxSideOffsets;
111
112 fn layout_dft(&self, default: Self::Px) -> Self::Px {
113 PxSideOffsets::new(
114 self.top.layout_dft_y(default.top),
115 self.right.layout_dft_x(default.right),
116 self.bottom.layout_dft_y(default.bottom),
117 self.left.layout_dft_x(default.left),
118 )
119 }
120
121 fn affect_mask(&self) -> LayoutMask {
122 self.top.affect_mask() | self.right.affect_mask() | self.bottom.affect_mask() | self.left.affect_mask()
123 }
124}
125impl_from_and_into_var! {
126 fn from(all: Length) -> SideOffsets {
128 SideOffsets::new_all(all)
129 }
130
131 fn from(percent: FactorPercent) -> SideOffsets {
133 SideOffsets::new_all(percent)
134 }
135 fn from(norm: Factor) -> SideOffsets {
137 SideOffsets::new_all(norm)
138 }
139
140 fn from(f: f32) -> SideOffsets {
142 SideOffsets::new_all(f)
143 }
144 fn from(i: i32) -> SideOffsets {
146 SideOffsets::new_all(i)
147 }
148
149 fn from(offsets: PxSideOffsets) -> SideOffsets {
151 SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
152 }
153
154 fn from(offsets: DipSideOffsets) -> SideOffsets {
156 SideOffsets::new(offsets.top, offsets.right, offsets.bottom, offsets.left)
157 }
158}
159
160impl_length_comp_conversions! {
161 fn from(top_bottom: TB, left_right: LR) -> SideOffsets {
163 SideOffsets::new_vh(top_bottom, left_right)
164 }
165
166 fn from(top: T, right: R, bottom: B, left: L) -> SideOffsets {
168 SideOffsets::new(top, right, bottom, left)
169 }
170}
171
172impl ops::Add for SideOffsets {
173 type Output = Self;
174
175 fn add(mut self, rhs: Self) -> Self {
176 self += rhs;
177 self
178 }
179}
180impl ops::AddAssign for SideOffsets {
181 fn add_assign(&mut self, rhs: Self) {
182 self.top += rhs.top;
183 self.right += rhs.right;
184 self.bottom += rhs.bottom;
185 self.left += rhs.left;
186 }
187}
188impl ops::Sub for SideOffsets {
189 type Output = Self;
190
191 fn sub(mut self, rhs: Self) -> Self {
192 self -= rhs;
193 self
194 }
195}
196impl ops::SubAssign for SideOffsets {
197 fn sub_assign(&mut self, rhs: Self) {
198 self.top -= rhs.top;
199 self.right -= rhs.right;
200 self.bottom -= rhs.bottom;
201 self.left -= rhs.left;
202 }
203}
204impl<S: Into<FactorSideOffsets>> ops::Mul<S> for SideOffsets {
205 type Output = Self;
206
207 fn mul(mut self, rhs: S) -> Self {
208 self *= rhs;
209 self
210 }
211}
212impl<S: Into<FactorSideOffsets>> ops::Mul<S> for &SideOffsets {
213 type Output = SideOffsets;
214
215 fn mul(self, rhs: S) -> Self::Output {
216 self.clone() * rhs
217 }
218}
219impl<S: Into<FactorSideOffsets>> ops::MulAssign<S> for SideOffsets {
220 fn mul_assign(&mut self, rhs: S) {
221 let rhs = rhs.into();
222 self.top *= rhs.top;
223 self.right *= rhs.right;
224 self.bottom *= rhs.bottom;
225 self.left *= rhs.left;
226 }
227}
228impl<S: Into<FactorSideOffsets>> ops::Div<S> for SideOffsets {
229 type Output = Self;
230
231 fn div(mut self, rhs: S) -> Self {
232 self /= rhs;
233 self
234 }
235}
236impl<S: Into<FactorSideOffsets>> ops::Div<S> for &SideOffsets {
237 type Output = SideOffsets;
238
239 fn div(self, rhs: S) -> Self::Output {
240 self.clone() / rhs
241 }
242}
243impl<S: Into<FactorSideOffsets>> ops::DivAssign<S> for SideOffsets {
244 fn div_assign(&mut self, rhs: S) {
245 let rhs = rhs.into();
246 self.top /= rhs.top;
247 self.right /= rhs.right;
248 self.bottom /= rhs.bottom;
249 self.left /= rhs.left;
250 }
251}