1use crate::{
2 color::ColorU,
3 geometry::vector::{vec2f, Vector2F},
4 AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
5 PaintContext, SizeConstraint,
6};
7
8pub struct Container {
9 margin: Margin,
10 padding: Padding,
11 overdraw: Overdraw,
12 background_color: Option<ColorU>,
13 border: Border,
14 corner_radius: f32,
15 shadow: Option<Shadow>,
16 child: Box<dyn Element>,
17 size: Option<Vector2F>,
18 origin: Option<Vector2F>,
19}
20
21impl Container {
22 pub fn new(child: Box<dyn Element>) -> Self {
23 Self {
24 margin: Margin::default(),
25 padding: Padding::default(),
26 overdraw: Overdraw::default(),
27 background_color: None,
28 border: Border::default(),
29 corner_radius: 0.0,
30 shadow: None,
31 child,
32 size: None,
33 origin: None,
34 }
35 }
36
37 pub fn with_margin_top(mut self, margin: f32) -> Self {
38 self.margin.top = margin;
39 self
40 }
41
42 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
43 self.padding = Padding {
44 top: padding,
45 left: padding,
46 bottom: padding,
47 right: padding,
48 };
49 self
50 }
51
52 pub fn with_padding_right(mut self, padding: f32) -> Self {
53 self.padding.right = padding;
54 self
55 }
56
57 pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
58 self.background_color = Some(color.into());
59 self
60 }
61
62 pub fn with_border(mut self, border: Border) -> Self {
63 self.border = border;
64 self
65 }
66
67 pub fn with_overdraw_bottom(mut self, overdraw: f32) -> Self {
68 self.overdraw.bottom = overdraw;
69 self
70 }
71
72 pub fn with_corner_radius(mut self, radius: f32) -> Self {
73 self.corner_radius = radius;
74 self
75 }
76
77 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
78 self.shadow = Some(Shadow {
79 offset,
80 blur,
81 color: color.into(),
82 });
83 self
84 }
85
86 fn margin_size(&self) -> Vector2F {
87 vec2f(
88 self.margin.left + self.margin.right,
89 self.margin.top + self.margin.bottom,
90 )
91 }
92
93 fn padding_size(&self) -> Vector2F {
94 vec2f(
95 self.padding.left + self.padding.right,
96 self.padding.top + self.padding.bottom,
97 )
98 }
99
100 fn border_size(&self) -> Vector2F {
101 let mut x = 0.0;
102 if self.border.left {
103 x += self.border.width;
104 }
105 if self.border.right {
106 x += self.border.width;
107 }
108
109 let mut y = 0.0;
110 if self.border.top {
111 y += self.border.width;
112 }
113 if self.border.bottom {
114 y += self.border.width;
115 }
116
117 vec2f(x, y)
118 }
119}
120
121impl Element for Container {
122 fn layout(
123 &mut self,
124 constraint: SizeConstraint,
125 ctx: &mut LayoutContext,
126 app: &AppContext,
127 ) -> Vector2F {
128 let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
129 let child_constraint = SizeConstraint {
130 min: (constraint.min - size_buffer).max(Vector2F::zero()),
131 max: (constraint.max - size_buffer).max(Vector2F::zero()),
132 };
133 let child_size = self.child.layout(child_constraint, ctx, app);
134 let size = child_size + size_buffer;
135 self.size = Some(size);
136 size
137 }
138
139 fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
140 self.child.after_layout(ctx, app);
141 }
142
143 fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
144 // self.origin = Some(origin);
145
146 // let canvas = &mut ctx.canvas;
147 // let size = self.size.unwrap() - self.margin_size()
148 // + vec2f(self.overdraw.right, self.overdraw.bottom);
149 // let origin = origin + vec2f(self.margin.left, self.margin.top)
150 // - vec2f(self.overdraw.left, self.overdraw.top);
151 // let rect = RectF::new(origin, size);
152
153 // let mut path = Path2D::new();
154 // if self.corner_radius > 0.0 {
155 // path.move_to(rect.upper_right() - vec2f(self.corner_radius, 0.0));
156 // path.arc_to(
157 // rect.upper_right(),
158 // rect.upper_right() + vec2f(0.0, self.corner_radius),
159 // self.corner_radius,
160 // );
161 // path.line_to(rect.lower_right() - vec2f(0.0, self.corner_radius));
162 // path.arc_to(
163 // rect.lower_right(),
164 // rect.lower_right() - vec2f(self.corner_radius, 0.0),
165 // self.corner_radius,
166 // );
167 // path.line_to(rect.lower_left() + vec2f(self.corner_radius, 0.0));
168 // path.arc_to(
169 // rect.lower_left(),
170 // rect.lower_left() - vec2f(0.0, self.corner_radius),
171 // self.corner_radius,
172 // );
173 // path.line_to(origin + vec2f(0.0, self.corner_radius));
174 // path.arc_to(
175 // origin,
176 // origin + vec2f(self.corner_radius, 0.0),
177 // self.corner_radius,
178 // );
179 // path.close_path();
180 // } else {
181 // path.rect(rect);
182 // }
183
184 // canvas.save();
185 // if let Some(shadow) = self.shadow.as_ref() {
186 // canvas.set_shadow_offset(shadow.offset);
187 // canvas.set_shadow_blur(shadow.blur);
188 // canvas.set_shadow_color(shadow.color);
189 // }
190
191 // if let Some(background_color) = self.background_color {
192 // canvas.set_fill_style(FillStyle::Color(background_color));
193 // canvas.fill_path(path.clone(), FillRule::Winding);
194 // }
195
196 // canvas.set_line_width(self.border.width);
197 // canvas.set_stroke_style(FillStyle::Color(self.border.color));
198
199 // let border_rect = rect.contract(self.border.width / 2.0);
200
201 // // For now, we ignore the corner radius unless we draw a border on all sides.
202 // // This could be improved.
203 // if self.border.all_sides() {
204 // let mut path = Path2D::new();
205 // path.rect(border_rect);
206 // canvas.stroke_path(path);
207 // } else {
208 // canvas.set_line_cap(LineCap::Square);
209
210 // if self.border.top {
211 // let mut path = Path2D::new();
212 // path.move_to(border_rect.origin());
213 // path.line_to(border_rect.upper_right());
214 // canvas.stroke_path(path);
215 // }
216
217 // if self.border.left {
218 // let mut path = Path2D::new();
219 // path.move_to(border_rect.origin());
220 // path.line_to(border_rect.lower_left());
221 // canvas.stroke_path(path);
222 // }
223
224 // if self.border.bottom {
225 // let mut path = Path2D::new();
226 // path.move_to(border_rect.lower_left());
227 // path.line_to(border_rect.lower_right());
228 // canvas.stroke_path(path);
229 // }
230
231 // if self.border.right {
232 // let mut path = Path2D::new();
233 // path.move_to(border_rect.upper_right());
234 // path.line_to(border_rect.lower_right());
235 // canvas.stroke_path(path);
236 // }
237 // }
238 // canvas.restore();
239
240 // let mut child_origin = origin + vec2f(self.padding.left, self.padding.top);
241 // if self.border.left {
242 // child_origin.set_x(child_origin.x() + self.border.width);
243 // }
244 // if self.border.top {
245 // child_origin.set_y(child_origin.y() + self.border.width);
246 // }
247 // self.child.paint(child_origin, ctx, app);
248 }
249
250 fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
251 self.child.dispatch_event(event, ctx, app)
252 }
253
254 fn size(&self) -> Option<Vector2F> {
255 self.size
256 }
257}
258
259#[derive(Default)]
260pub struct Margin {
261 top: f32,
262 left: f32,
263 bottom: f32,
264 right: f32,
265}
266
267#[derive(Default)]
268pub struct Padding {
269 top: f32,
270 left: f32,
271 bottom: f32,
272 right: f32,
273}
274
275#[derive(Default)]
276pub struct Overdraw {
277 top: f32,
278 left: f32,
279 bottom: f32,
280 right: f32,
281}
282
283#[derive(Default)]
284pub struct Border {
285 width: f32,
286 color: ColorU,
287 pub top: bool,
288 pub left: bool,
289 pub bottom: bool,
290 pub right: bool,
291}
292
293impl Border {
294 pub fn new(width: f32, color: impl Into<ColorU>) -> Self {
295 Self {
296 width,
297 color: color.into(),
298 top: false,
299 left: false,
300 bottom: false,
301 right: false,
302 }
303 }
304
305 pub fn all(width: f32, color: impl Into<ColorU>) -> Self {
306 Self {
307 width,
308 color: color.into(),
309 top: true,
310 left: true,
311 bottom: true,
312 right: true,
313 }
314 }
315
316 pub fn top(width: f32, color: impl Into<ColorU>) -> Self {
317 let mut border = Self::new(width, color);
318 border.top = true;
319 border
320 }
321
322 pub fn left(width: f32, color: impl Into<ColorU>) -> Self {
323 let mut border = Self::new(width, color);
324 border.left = true;
325 border
326 }
327
328 pub fn bottom(width: f32, color: impl Into<ColorU>) -> Self {
329 let mut border = Self::new(width, color);
330 border.bottom = true;
331 border
332 }
333
334 pub fn right(width: f32, color: impl Into<ColorU>) -> Self {
335 let mut border = Self::new(width, color);
336 border.right = true;
337 border
338 }
339
340 pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
341 self.top = top;
342 self.left = left;
343 self.bottom = bottom;
344 self.right = right;
345 self
346 }
347
348 fn all_sides(&self) -> bool {
349 self.top && self.left && self.bottom && self.right
350 }
351}
352
353#[derive(Default)]
354pub struct Shadow {
355 offset: Vector2F,
356 blur: f32,
357 color: ColorU,
358}