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 _: &mut Self::LayoutState,
251 _: &mut Self::PaintState,
252 cx: &mut EventContext,
253 ) -> bool {
254 self.child.dispatch_event(event, cx)
255 }
256
257 fn debug(
258 &self,
259 bounds: RectF,
260 _: &Self::LayoutState,
261 _: &Self::PaintState,
262 cx: &crate::DebugContext,
263 ) -> serde_json::Value {
264 json!({
265 "type": "Container",
266 "bounds": bounds.to_json(),
267 "details": self.style.to_json(),
268 "child": self.child.debug(cx),
269 })
270 }
271}
272
273impl ToJson for ContainerStyle {
274 fn to_json(&self) -> serde_json::Value {
275 json!({
276 "margin": self.margin.to_json(),
277 "padding": self.padding.to_json(),
278 "background_color": self.background_color.to_json(),
279 "border": self.border.to_json(),
280 "corner_radius": self.corner_radius,
281 "shadow": self.shadow.to_json(),
282 })
283 }
284}
285
286#[derive(Clone, Copy, Debug, Default)]
287pub struct Margin {
288 pub top: f32,
289 pub left: f32,
290 pub bottom: f32,
291 pub right: f32,
292}
293
294impl ToJson for Margin {
295 fn to_json(&self) -> serde_json::Value {
296 let mut value = json!({});
297 if self.top > 0. {
298 value["top"] = json!(self.top);
299 }
300 if self.right > 0. {
301 value["right"] = json!(self.right);
302 }
303 if self.bottom > 0. {
304 value["bottom"] = json!(self.bottom);
305 }
306 if self.left > 0. {
307 value["left"] = json!(self.left);
308 }
309 value
310 }
311}
312
313#[derive(Clone, Copy, Debug, Default)]
314pub struct Padding {
315 pub top: f32,
316 pub left: f32,
317 pub bottom: f32,
318 pub right: f32,
319}
320
321impl<'de> Deserialize<'de> for Padding {
322 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
323 where
324 D: serde::Deserializer<'de>,
325 {
326 let spacing = Spacing::deserialize(deserializer)?;
327 Ok(match spacing {
328 Spacing::Uniform(size) => Padding {
329 top: size,
330 left: size,
331 bottom: size,
332 right: size,
333 },
334 Spacing::Specific {
335 top,
336 left,
337 bottom,
338 right,
339 } => Padding {
340 top,
341 left,
342 bottom,
343 right,
344 },
345 })
346 }
347}
348
349impl<'de> Deserialize<'de> for Margin {
350 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
351 where
352 D: serde::Deserializer<'de>,
353 {
354 let spacing = Spacing::deserialize(deserializer)?;
355 Ok(match spacing {
356 Spacing::Uniform(size) => Margin {
357 top: size,
358 left: size,
359 bottom: size,
360 right: size,
361 },
362 Spacing::Specific {
363 top,
364 left,
365 bottom,
366 right,
367 } => Margin {
368 top,
369 left,
370 bottom,
371 right,
372 },
373 })
374 }
375}
376#[derive(Deserialize)]
377#[serde(untagged)]
378enum Spacing {
379 Uniform(f32),
380 Specific {
381 #[serde(default)]
382 top: f32,
383 #[serde(default)]
384 left: f32,
385 #[serde(default)]
386 bottom: f32,
387 #[serde(default)]
388 right: f32,
389 },
390}
391
392impl Padding {
393 pub fn uniform(padding: f32) -> Self {
394 Self {
395 top: padding,
396 left: padding,
397 bottom: padding,
398 right: padding,
399 }
400 }
401}
402
403impl ToJson for Padding {
404 fn to_json(&self) -> serde_json::Value {
405 let mut value = json!({});
406 if self.top > 0. {
407 value["top"] = json!(self.top);
408 }
409 if self.right > 0. {
410 value["right"] = json!(self.right);
411 }
412 if self.bottom > 0. {
413 value["bottom"] = json!(self.bottom);
414 }
415 if self.left > 0. {
416 value["left"] = json!(self.left);
417 }
418 value
419 }
420}
421
422#[derive(Clone, Copy, Debug, Default, Deserialize)]
423pub struct Shadow {
424 #[serde(default, deserialize_with = "deserialize_vec2f")]
425 offset: Vector2F,
426 #[serde(default)]
427 blur: f32,
428 #[serde(default)]
429 color: Color,
430}
431
432impl ToJson for Shadow {
433 fn to_json(&self) -> serde_json::Value {
434 json!({
435 "offset": self.offset.to_json(),
436 "blur": self.blur,
437 "color": self.color.to_json()
438 })
439 }
440}