epaint/
margin.rs

1use emath::{vec2, Rect, Vec2};
2
3/// A value for all four sides of a rectangle,
4/// often used to express padding or spacing.
5///
6/// Can be added and subtracted to/from [`Rect`]s.
7///
8/// Negative margins are possible, but may produce weird behavior.
9/// Use with care.
10///
11/// All values are stored as [`i8`] to keep the size of [`Margin`] small.
12/// If you want floats, use [`crate::Marginf`] instead.
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
15pub struct Margin {
16    pub left: i8,
17    pub right: i8,
18    pub top: i8,
19    pub bottom: i8,
20}
21
22impl Margin {
23    pub const ZERO: Self = Self {
24        left: 0,
25        right: 0,
26        top: 0,
27        bottom: 0,
28    };
29
30    /// The same margin on every side.
31    #[doc(alias = "symmetric")]
32    #[inline]
33    pub const fn same(margin: i8) -> Self {
34        Self {
35            left: margin,
36            right: margin,
37            top: margin,
38            bottom: margin,
39        }
40    }
41
42    /// Margins with the same size on opposing sides
43    #[inline]
44    pub const fn symmetric(x: i8, y: i8) -> Self {
45        Self {
46            left: x,
47            right: x,
48            top: y,
49            bottom: y,
50        }
51    }
52
53    /// Left margin, as `f32`
54    #[inline]
55    pub const fn leftf(self) -> f32 {
56        self.left as _
57    }
58
59    /// Right margin, as `f32`
60    #[inline]
61    pub const fn rightf(self) -> f32 {
62        self.right as _
63    }
64
65    /// Top margin, as `f32`
66    #[inline]
67    pub const fn topf(self) -> f32 {
68        self.top as _
69    }
70
71    /// Bottom margin, as `f32`
72    #[inline]
73    pub const fn bottomf(self) -> f32 {
74        self.bottom as _
75    }
76
77    /// Total margins on both sides
78    #[inline]
79    pub fn sum(self) -> Vec2 {
80        vec2(self.leftf() + self.rightf(), self.topf() + self.bottomf())
81    }
82
83    #[inline]
84    pub const fn left_top(self) -> Vec2 {
85        vec2(self.leftf(), self.topf())
86    }
87
88    #[inline]
89    pub const fn right_bottom(self) -> Vec2 {
90        vec2(self.rightf(), self.bottomf())
91    }
92
93    /// Are the margin on every side the same?
94    #[doc(alias = "symmetric")]
95    #[inline]
96    pub const fn is_same(self) -> bool {
97        self.left == self.right && self.left == self.top && self.left == self.bottom
98    }
99
100    #[deprecated = "Use `rect + margin` instead"]
101    #[inline]
102    pub fn expand_rect(self, rect: Rect) -> Rect {
103        Rect::from_min_max(rect.min - self.left_top(), rect.max + self.right_bottom())
104    }
105
106    #[deprecated = "Use `rect - margin` instead"]
107    #[inline]
108    pub fn shrink_rect(self, rect: Rect) -> Rect {
109        Rect::from_min_max(rect.min + self.left_top(), rect.max - self.right_bottom())
110    }
111}
112
113impl From<i8> for Margin {
114    #[inline]
115    fn from(v: i8) -> Self {
116        Self::same(v)
117    }
118}
119
120impl From<f32> for Margin {
121    #[inline]
122    fn from(v: f32) -> Self {
123        Self::same(v.round() as _)
124    }
125}
126
127impl From<Vec2> for Margin {
128    #[inline]
129    fn from(v: Vec2) -> Self {
130        Self::symmetric(v.x.round() as _, v.y.round() as _)
131    }
132}
133
134/// `Margin + Margin`
135impl std::ops::Add for Margin {
136    type Output = Self;
137
138    #[inline]
139    fn add(self, other: Self) -> Self {
140        Self {
141            left: self.left.saturating_add(other.left),
142            right: self.right.saturating_add(other.right),
143            top: self.top.saturating_add(other.top),
144            bottom: self.bottom.saturating_add(other.bottom),
145        }
146    }
147}
148
149/// `Margin + i8`
150impl std::ops::Add<i8> for Margin {
151    type Output = Self;
152
153    #[inline]
154    fn add(self, v: i8) -> Self {
155        Self {
156            left: self.left.saturating_add(v),
157            right: self.right.saturating_add(v),
158            top: self.top.saturating_add(v),
159            bottom: self.bottom.saturating_add(v),
160        }
161    }
162}
163
164/// `Margin += i8`
165impl std::ops::AddAssign<i8> for Margin {
166    #[inline]
167    fn add_assign(&mut self, v: i8) {
168        *self = *self + v;
169    }
170}
171
172/// `Margin * f32`
173impl std::ops::Mul<f32> for Margin {
174    type Output = Self;
175
176    #[inline]
177    fn mul(self, v: f32) -> Self {
178        Self {
179            left: (self.leftf() * v).round() as _,
180            right: (self.rightf() * v).round() as _,
181            top: (self.topf() * v).round() as _,
182            bottom: (self.bottomf() * v).round() as _,
183        }
184    }
185}
186
187/// `Margin *= f32`
188impl std::ops::MulAssign<f32> for Margin {
189    #[inline]
190    fn mul_assign(&mut self, v: f32) {
191        *self = *self * v;
192    }
193}
194
195/// `Margin / f32`
196impl std::ops::Div<f32> for Margin {
197    type Output = Self;
198
199    #[inline]
200    fn div(self, v: f32) -> Self {
201        #![allow(clippy::suspicious_arithmetic_impl)]
202        self * v.recip()
203    }
204}
205
206/// `Margin /= f32`
207impl std::ops::DivAssign<f32> for Margin {
208    #[inline]
209    fn div_assign(&mut self, v: f32) {
210        *self = *self / v;
211    }
212}
213
214/// `Margin - Margin`
215impl std::ops::Sub for Margin {
216    type Output = Self;
217
218    #[inline]
219    fn sub(self, other: Self) -> Self {
220        Self {
221            left: self.left.saturating_sub(other.left),
222            right: self.right.saturating_sub(other.right),
223            top: self.top.saturating_sub(other.top),
224            bottom: self.bottom.saturating_sub(other.bottom),
225        }
226    }
227}
228
229/// `Margin - i8`
230impl std::ops::Sub<i8> for Margin {
231    type Output = Self;
232
233    #[inline]
234    fn sub(self, v: i8) -> Self {
235        Self {
236            left: self.left.saturating_sub(v),
237            right: self.right.saturating_sub(v),
238            top: self.top.saturating_sub(v),
239            bottom: self.bottom.saturating_sub(v),
240        }
241    }
242}
243
244/// `Margin -= i8`
245impl std::ops::SubAssign<i8> for Margin {
246    #[inline]
247    fn sub_assign(&mut self, v: i8) {
248        *self = *self - v;
249    }
250}
251
252/// `Rect + Margin`
253impl std::ops::Add<Margin> for Rect {
254    type Output = Self;
255
256    #[inline]
257    fn add(self, margin: Margin) -> Self {
258        Self::from_min_max(
259            self.min - margin.left_top(),
260            self.max + margin.right_bottom(),
261        )
262    }
263}
264
265/// `Rect += Margin`
266impl std::ops::AddAssign<Margin> for Rect {
267    #[inline]
268    fn add_assign(&mut self, margin: Margin) {
269        *self = *self + margin;
270    }
271}
272
273/// `Rect - Margin`
274impl std::ops::Sub<Margin> for Rect {
275    type Output = Self;
276
277    #[inline]
278    fn sub(self, margin: Margin) -> Self {
279        Self::from_min_max(
280            self.min + margin.left_top(),
281            self.max - margin.right_bottom(),
282        )
283    }
284}
285
286/// `Rect -= Margin`
287impl std::ops::SubAssign<Margin> for Rect {
288    #[inline]
289    fn sub_assign(&mut self, margin: Margin) {
290        *self = *self - margin;
291    }
292}