1use crate::{
2 color::Color,
3 geometry::{
4 deserialize_vec2f,
5 rect::RectF,
6 vector::{vec2f, Vector2F},
7 },
8 json::ToJson,
9 platform::CursorStyle,
10 scene::{self, Border, Quad},
11 Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
12};
13use serde::Deserialize;
14use serde_json::json;
15
16#[derive(Clone, Copy, Debug, Default, Deserialize)]
17pub struct ContainerStyle {
18 #[serde(default)]
19 pub margin: Margin,
20 #[serde(default)]
21 pub padding: Padding,
22 #[serde(rename = "background")]
23 pub background_color: Option<Color>,
24 #[serde(default)]
25 pub border: Border,
26 #[serde(default)]
27 pub corner_radius: f32,
28 #[serde(default)]
29 pub shadow: Option<Shadow>,
30 #[serde(default)]
31 pub cursor: Option<CursorStyle>,
32}
33
34pub struct Container {
35 child: ElementBox,
36 style: ContainerStyle,
37}
38
39impl Container {
40 pub fn new(child: ElementBox) -> Self {
41 Self {
42 child,
43 style: Default::default(),
44 }
45 }
46
47 pub fn with_style(mut self, style: ContainerStyle) -> Self {
48 self.style = style;
49 self
50 }
51
52 pub fn with_margin_top(mut self, margin: f32) -> Self {
53 self.style.margin.top = margin;
54 self
55 }
56
57 pub fn with_margin_bottom(mut self, margin: f32) -> Self {
58 self.style.margin.bottom = margin;
59 self
60 }
61
62 pub fn with_margin_left(mut self, margin: f32) -> Self {
63 self.style.margin.left = margin;
64 self
65 }
66
67 pub fn with_margin_right(mut self, margin: f32) -> Self {
68 self.style.margin.right = margin;
69 self
70 }
71
72 pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
73 self.style.padding.left = padding;
74 self.style.padding.right = padding;
75 self
76 }
77
78 pub fn with_vertical_padding(mut self, padding: f32) -> Self {
79 self.style.padding.top = padding;
80 self.style.padding.bottom = padding;
81 self
82 }
83
84 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
85 self.style.padding = Padding {
86 top: padding,
87 left: padding,
88 bottom: padding,
89 right: padding,
90 };
91 self
92 }
93
94 pub fn with_padding_left(mut self, padding: f32) -> Self {
95 self.style.padding.left = padding;
96 self
97 }
98
99 pub fn with_padding_right(mut self, padding: f32) -> Self {
100 self.style.padding.right = padding;
101 self
102 }
103
104 pub fn with_padding_bottom(mut self, padding: f32) -> Self {
105 self.style.padding.bottom = padding;
106 self
107 }
108
109 pub fn with_background_color(mut self, color: Color) -> Self {
110 self.style.background_color = Some(color);
111 self
112 }
113
114 pub fn with_border(mut self, border: Border) -> Self {
115 self.style.border = border;
116 self
117 }
118
119 pub fn with_corner_radius(mut self, radius: f32) -> Self {
120 self.style.corner_radius = radius;
121 self
122 }
123
124 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
125 self.style.shadow = Some(Shadow {
126 offset,
127 blur,
128 color,
129 });
130 self
131 }
132
133 pub fn with_cursor(mut self, style: CursorStyle) -> Self {
134 self.style.cursor = Some(style);
135 self
136 }
137
138 fn margin_size(&self) -> Vector2F {
139 vec2f(
140 self.style.margin.left + self.style.margin.right,
141 self.style.margin.top + self.style.margin.bottom,
142 )
143 }
144
145 fn padding_size(&self) -> Vector2F {
146 vec2f(
147 self.style.padding.left + self.style.padding.right,
148 self.style.padding.top + self.style.padding.bottom,
149 )
150 }
151
152 fn border_size(&self) -> Vector2F {
153 let mut x = 0.0;
154 if self.style.border.left {
155 x += self.style.border.width;
156 }
157 if self.style.border.right {
158 x += self.style.border.width;
159 }
160
161 let mut y = 0.0;
162 if self.style.border.top {
163 y += self.style.border.width;
164 }
165 if self.style.border.bottom {
166 y += self.style.border.width;
167 }
168
169 vec2f(x, y)
170 }
171}
172
173impl Element for Container {
174 type LayoutState = ();
175 type PaintState = ();
176
177 fn layout(
178 &mut self,
179 constraint: SizeConstraint,
180 cx: &mut LayoutContext,
181 ) -> (Vector2F, Self::LayoutState) {
182 let mut size_buffer = self.margin_size() + self.padding_size();
183 if !self.style.border.overlay {
184 size_buffer += self.border_size();
185 }
186 let child_constraint = SizeConstraint {
187 min: (constraint.min - size_buffer).max(Vector2F::zero()),
188 max: (constraint.max - size_buffer).max(Vector2F::zero()),
189 };
190 let child_size = self.child.layout(child_constraint, cx);
191 (child_size + size_buffer, ())
192 }
193
194 fn paint(
195 &mut self,
196 bounds: RectF,
197 visible_bounds: RectF,
198 _: &mut Self::LayoutState,
199 cx: &mut PaintContext,
200 ) -> Self::PaintState {
201 let quad_bounds = RectF::from_points(
202 bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
203 bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
204 );
205
206 if let Some(shadow) = self.style.shadow.as_ref() {
207 cx.scene.push_shadow(scene::Shadow {
208 bounds: quad_bounds + shadow.offset,
209 corner_radius: self.style.corner_radius,
210 sigma: shadow.blur,
211 color: shadow.color,
212 });
213 }
214
215 if let Some(style) = self.style.cursor {
216 cx.scene.push_cursor_style(quad_bounds, style);
217 }
218
219 let child_origin =
220 quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
221
222 if self.style.border.overlay {
223 cx.scene.push_quad(Quad {
224 bounds: quad_bounds,
225 background: self.style.background_color,
226 border: Default::default(),
227 corner_radius: self.style.corner_radius,
228 });
229
230 self.child.paint(child_origin, visible_bounds, cx);
231
232 cx.scene.push_layer(None);
233 cx.scene.push_quad(Quad {
234 bounds: quad_bounds,
235 background: Default::default(),
236 border: self.style.border,
237 corner_radius: self.style.corner_radius,
238 });
239 cx.scene.pop_layer();
240 } else {
241 cx.scene.push_quad(Quad {
242 bounds: quad_bounds,
243 background: self.style.background_color,
244 border: self.style.border,
245 corner_radius: self.style.corner_radius,
246 });
247
248 let child_origin = child_origin
249 + vec2f(
250 self.style.border.left_width(),
251 self.style.border.top_width(),
252 );
253 self.child.paint(child_origin, visible_bounds, cx);
254 }
255 }
256
257 fn dispatch_event(
258 &mut self,
259 event: &Event,
260 _: RectF,
261 _: RectF,
262 _: &mut Self::LayoutState,
263 _: &mut Self::PaintState,
264 cx: &mut EventContext,
265 ) -> bool {
266 self.child.dispatch_event(event, cx)
267 }
268
269 fn debug(
270 &self,
271 bounds: RectF,
272 _: &Self::LayoutState,
273 _: &Self::PaintState,
274 cx: &crate::DebugContext,
275 ) -> serde_json::Value {
276 json!({
277 "type": "Container",
278 "bounds": bounds.to_json(),
279 "details": self.style.to_json(),
280 "child": self.child.debug(cx),
281 })
282 }
283}
284
285impl ToJson for ContainerStyle {
286 fn to_json(&self) -> serde_json::Value {
287 json!({
288 "margin": self.margin.to_json(),
289 "padding": self.padding.to_json(),
290 "background_color": self.background_color.to_json(),
291 "border": self.border.to_json(),
292 "corner_radius": self.corner_radius,
293 "shadow": self.shadow.to_json(),
294 })
295 }
296}
297
298#[derive(Clone, Copy, Debug, Default)]
299pub struct Margin {
300 pub top: f32,
301 pub left: f32,
302 pub bottom: f32,
303 pub right: f32,
304}
305
306impl ToJson for Margin {
307 fn to_json(&self) -> serde_json::Value {
308 let mut value = json!({});
309 if self.top > 0. {
310 value["top"] = json!(self.top);
311 }
312 if self.right > 0. {
313 value["right"] = json!(self.right);
314 }
315 if self.bottom > 0. {
316 value["bottom"] = json!(self.bottom);
317 }
318 if self.left > 0. {
319 value["left"] = json!(self.left);
320 }
321 value
322 }
323}
324
325#[derive(Clone, Copy, Debug, Default)]
326pub struct Padding {
327 pub top: f32,
328 pub left: f32,
329 pub bottom: f32,
330 pub right: f32,
331}
332
333impl<'de> Deserialize<'de> for Padding {
334 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
335 where
336 D: serde::Deserializer<'de>,
337 {
338 let spacing = Spacing::deserialize(deserializer)?;
339 Ok(match spacing {
340 Spacing::Uniform(size) => Padding {
341 top: size,
342 left: size,
343 bottom: size,
344 right: size,
345 },
346 Spacing::Specific {
347 top,
348 left,
349 bottom,
350 right,
351 } => Padding {
352 top,
353 left,
354 bottom,
355 right,
356 },
357 })
358 }
359}
360
361impl<'de> Deserialize<'de> for Margin {
362 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
363 where
364 D: serde::Deserializer<'de>,
365 {
366 let spacing = Spacing::deserialize(deserializer)?;
367 Ok(match spacing {
368 Spacing::Uniform(size) => Margin {
369 top: size,
370 left: size,
371 bottom: size,
372 right: size,
373 },
374 Spacing::Specific {
375 top,
376 left,
377 bottom,
378 right,
379 } => Margin {
380 top,
381 left,
382 bottom,
383 right,
384 },
385 })
386 }
387}
388#[derive(Deserialize)]
389#[serde(untagged)]
390enum Spacing {
391 Uniform(f32),
392 Specific {
393 #[serde(default)]
394 top: f32,
395 #[serde(default)]
396 left: f32,
397 #[serde(default)]
398 bottom: f32,
399 #[serde(default)]
400 right: f32,
401 },
402}
403
404impl Padding {
405 pub fn uniform(padding: f32) -> Self {
406 Self {
407 top: padding,
408 left: padding,
409 bottom: padding,
410 right: padding,
411 }
412 }
413}
414
415impl ToJson for Padding {
416 fn to_json(&self) -> serde_json::Value {
417 let mut value = json!({});
418 if self.top > 0. {
419 value["top"] = json!(self.top);
420 }
421 if self.right > 0. {
422 value["right"] = json!(self.right);
423 }
424 if self.bottom > 0. {
425 value["bottom"] = json!(self.bottom);
426 }
427 if self.left > 0. {
428 value["left"] = json!(self.left);
429 }
430 value
431 }
432}
433
434#[derive(Clone, Copy, Debug, Default, Deserialize)]
435pub struct Shadow {
436 #[serde(default, deserialize_with = "deserialize_vec2f")]
437 offset: Vector2F,
438 #[serde(default)]
439 blur: f32,
440 #[serde(default)]
441 color: Color,
442}
443
444impl ToJson for Shadow {
445 fn to_json(&self) -> serde_json::Value {
446 json!({
447 "offset": self.offset.to_json(),
448 "blur": self.blur,
449 "color": self.color.to_json()
450 })
451 }
452}