1use crate::geometry::{Insets, Rect};
13use crate::Color;
14
15#[derive(Clone, Copy, Debug, Default, PartialEq)]
20pub struct Padding(pub Insets);
21
22impl Padding {
23 pub const ZERO: Padding = Padding(Insets::ZERO);
25
26 pub const fn new(top: f32, right: f32, bottom: f32, left: f32) -> Self {
28 Self(Insets::new(top, right, bottom, left))
29 }
30
31 pub const fn all(v: f32) -> Self {
33 Self(Insets::all(v))
34 }
35
36 pub const fn symmetric(vertical: f32, horizontal: f32) -> Self {
38 Self(Insets::symmetric(vertical, horizontal))
39 }
40
41 pub const fn insets(self) -> Insets {
43 self.0
44 }
45
46 pub fn shrink(self, rect: Rect) -> Rect {
48 rect.deflate(self.0)
49 }
50}
51
52impl From<Insets> for Padding {
53 fn from(insets: Insets) -> Self {
54 Self(insets)
55 }
56}
57
58impl From<Padding> for Insets {
59 fn from(padding: Padding) -> Self {
60 padding.0
61 }
62}
63
64#[derive(Clone, Copy, Debug, Default, PartialEq)]
68pub struct Margin(pub Insets);
69
70impl Margin {
71 pub const ZERO: Margin = Margin(Insets::ZERO);
73
74 pub const fn new(top: f32, right: f32, bottom: f32, left: f32) -> Self {
76 Self(Insets::new(top, right, bottom, left))
77 }
78
79 pub const fn all(v: f32) -> Self {
81 Self(Insets::all(v))
82 }
83
84 pub const fn symmetric(vertical: f32, horizontal: f32) -> Self {
86 Self(Insets::symmetric(vertical, horizontal))
87 }
88
89 pub const fn insets(self) -> Insets {
91 self.0
92 }
93
94 pub fn grow(self, rect: Rect) -> Rect {
96 rect.inflate(self.0)
97 }
98}
99
100impl From<Insets> for Margin {
101 fn from(insets: Insets) -> Self {
102 Self(insets)
103 }
104}
105
106impl From<Margin> for Insets {
107 fn from(margin: Margin) -> Self {
108 margin.0
109 }
110}
111
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
114pub enum BorderStyle {
115 #[default]
117 Solid,
118 Dashed,
120 Dotted,
122 Double,
124 None,
126}
127
128#[derive(Clone, Copy, Debug, PartialEq)]
130pub struct Border {
131 pub insets: Insets,
133 pub color: Color,
135 pub style: BorderStyle,
137}
138
139impl Border {
140 pub fn solid(width: f32, color: Color) -> Self {
142 Self {
143 insets: Insets::all(width),
144 color,
145 style: BorderStyle::Solid,
146 }
147 }
148
149 pub fn new(insets: Insets, color: Color, style: BorderStyle) -> Self {
151 Self {
152 insets,
153 color,
154 style,
155 }
156 }
157
158 pub fn with_style(mut self, style: BorderStyle) -> Self {
160 self.style = style;
161 self
162 }
163
164 pub fn with_color(mut self, color: Color) -> Self {
166 self.color = color;
167 self
168 }
169
170 pub fn is_none(&self) -> bool {
172 self.style == BorderStyle::None
173 || (self.insets.top <= 0.0
174 && self.insets.right <= 0.0
175 && self.insets.bottom <= 0.0
176 && self.insets.left <= 0.0)
177 }
178
179 pub fn content_rect(&self, rect: Rect) -> Rect {
181 rect.deflate(self.insets)
182 }
183}
184
185impl Default for Border {
186 fn default() -> Self {
188 Self {
189 insets: Insets::ZERO,
190 color: Color(0, 0, 0, 0),
191 style: BorderStyle::None,
192 }
193 }
194}
195
196#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
201#[non_exhaustive]
202pub enum CursorShape {
203 #[default]
205 Default,
206 Pointer,
208 Text,
210 Crosshair,
212 Move,
214 NotAllowed,
216 Wait,
218 Progress,
220 Grab,
222 Grabbing,
224 ResizeEw,
226 ResizeNs,
228 ResizeNesw,
230 ResizeNwse,
232 None,
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn padding_shrink_matches_deflate() {
242 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
243 let p = Padding::all(10.0);
244 assert_eq!(p.shrink(r), r.deflate(Insets::all(10.0)));
245 assert_eq!(p.insets(), Insets::all(10.0));
246 }
247
248 #[test]
249 fn margin_grow_matches_inflate() {
250 let r = Rect::new(10.0, 10.0, 50.0, 50.0);
251 let m = Margin::symmetric(4.0, 8.0);
252 assert_eq!(m.grow(r), r.inflate(Insets::symmetric(4.0, 8.0)));
253 }
254
255 #[test]
256 fn padding_margin_conversions() {
257 let i = Insets::new(1.0, 2.0, 3.0, 4.0);
258 let p: Padding = i.into();
259 let back: Insets = p.into();
260 assert_eq!(back, i);
261 let m: Margin = i.into();
262 assert_eq!(Insets::from(m), i);
263 }
264
265 #[test]
266 fn border_solid_and_content_rect() {
267 let b = Border::solid(2.0, Color(255, 0, 0, 255));
268 assert_eq!(b.style, BorderStyle::Solid);
269 assert_eq!(b.insets, Insets::all(2.0));
270 let content = b.content_rect(Rect::new(0.0, 0.0, 20.0, 20.0));
271 assert_eq!(content, Rect::new(2.0, 2.0, 16.0, 16.0));
272 assert!(!b.is_none());
273 }
274
275 #[test]
276 fn border_default_is_none() {
277 let b = Border::default();
278 assert!(b.is_none());
279 assert_eq!(b.style, BorderStyle::None);
280 let styled = Border::solid(3.0, Color(0, 0, 0, 255)).with_style(BorderStyle::None);
282 assert!(styled.is_none());
283 }
284
285 #[test]
286 fn cursor_shape_default() {
287 assert_eq!(CursorShape::default(), CursorShape::Default);
288 }
289}