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