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