epaint/
margin.rs

1use emath::{Rect, Vec2, 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::MarginF32`] 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
101impl From<i8> for Margin {
102    #[inline]
103    fn from(v: i8) -> Self {
104        Self::same(v)
105    }
106}
107
108impl From<f32> for Margin {
109    #[inline]
110    fn from(v: f32) -> Self {
111        Self::same(v.round() as _)
112    }
113}
114
115impl From<Vec2> for Margin {
116    #[inline]
117    fn from(v: Vec2) -> Self {
118        Self::symmetric(v.x.round() as _, v.y.round() as _)
119    }
120}
121
122/// `Margin + Margin`
123impl std::ops::Add for Margin {
124    type Output = Self;
125
126    #[inline]
127    fn add(self, other: Self) -> Self {
128        Self {
129            left: self.left.saturating_add(other.left),
130            right: self.right.saturating_add(other.right),
131            top: self.top.saturating_add(other.top),
132            bottom: self.bottom.saturating_add(other.bottom),
133        }
134    }
135}
136
137/// `Margin + i8`
138impl std::ops::Add<i8> for Margin {
139    type Output = Self;
140
141    #[inline]
142    fn add(self, v: i8) -> Self {
143        Self {
144            left: self.left.saturating_add(v),
145            right: self.right.saturating_add(v),
146            top: self.top.saturating_add(v),
147            bottom: self.bottom.saturating_add(v),
148        }
149    }
150}
151
152/// `Margin += i8`
153impl std::ops::AddAssign<i8> for Margin {
154    #[inline]
155    fn add_assign(&mut self, v: i8) {
156        *self = *self + v;
157    }
158}
159
160/// `Margin * f32`
161impl std::ops::Mul<f32> for Margin {
162    type Output = Self;
163
164    #[inline]
165    fn mul(self, v: f32) -> Self {
166        Self {
167            left: (self.leftf() * v).round() as _,
168            right: (self.rightf() * v).round() as _,
169            top: (self.topf() * v).round() as _,
170            bottom: (self.bottomf() * v).round() as _,
171        }
172    }
173}
174
175/// `Margin *= f32`
176impl std::ops::MulAssign<f32> for Margin {
177    #[inline]
178    fn mul_assign(&mut self, v: f32) {
179        *self = *self * v;
180    }
181}
182
183/// `Margin / f32`
184impl std::ops::Div<f32> for Margin {
185    type Output = Self;
186
187    #[inline]
188    fn div(self, v: f32) -> Self {
189        #![allow(clippy::suspicious_arithmetic_impl)]
190        self * v.recip()
191    }
192}
193
194/// `Margin /= f32`
195impl std::ops::DivAssign<f32> for Margin {
196    #[inline]
197    fn div_assign(&mut self, v: f32) {
198        *self = *self / v;
199    }
200}
201
202/// `Margin - Margin`
203impl std::ops::Sub for Margin {
204    type Output = Self;
205
206    #[inline]
207    fn sub(self, other: Self) -> Self {
208        Self {
209            left: self.left.saturating_sub(other.left),
210            right: self.right.saturating_sub(other.right),
211            top: self.top.saturating_sub(other.top),
212            bottom: self.bottom.saturating_sub(other.bottom),
213        }
214    }
215}
216
217/// `Margin - i8`
218impl std::ops::Sub<i8> for Margin {
219    type Output = Self;
220
221    #[inline]
222    fn sub(self, v: i8) -> Self {
223        Self {
224            left: self.left.saturating_sub(v),
225            right: self.right.saturating_sub(v),
226            top: self.top.saturating_sub(v),
227            bottom: self.bottom.saturating_sub(v),
228        }
229    }
230}
231
232/// `Margin -= i8`
233impl std::ops::SubAssign<i8> for Margin {
234    #[inline]
235    fn sub_assign(&mut self, v: i8) {
236        *self = *self - v;
237    }
238}
239
240/// `Rect + Margin`
241impl std::ops::Add<Margin> for Rect {
242    type Output = Self;
243
244    #[inline]
245    fn add(self, margin: Margin) -> Self {
246        Self::from_min_max(
247            self.min - margin.left_top(),
248            self.max + margin.right_bottom(),
249        )
250    }
251}
252
253/// `Rect += Margin`
254impl std::ops::AddAssign<Margin> for Rect {
255    #[inline]
256    fn add_assign(&mut self, margin: Margin) {
257        *self = *self + margin;
258    }
259}
260
261/// `Rect - Margin`
262impl std::ops::Sub<Margin> for Rect {
263    type Output = Self;
264
265    #[inline]
266    fn sub(self, margin: Margin) -> Self {
267        Self::from_min_max(
268            self.min + margin.left_top(),
269            self.max - margin.right_bottom(),
270        )
271    }
272}
273
274/// `Rect -= Margin`
275impl std::ops::SubAssign<Margin> for Rect {
276    #[inline]
277    fn sub_assign(&mut self, margin: Margin) {
278        *self = *self - margin;
279    }
280}