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 scene::{self, CornerRadii, CursorRegion, Quad},
13 AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
14};
15use schemars::JsonSchema;
16use serde::Deserialize;
17use serde_json::json;
18
19#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
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(rename = "overlay")]
28 pub overlay_color: Option<Color>,
29 #[serde(default)]
30 pub border: Border,
31 #[serde(default)]
32 #[serde(alias = "corner_radius")]
33 pub corner_radii: CornerRadii,
34 #[serde(default)]
35 pub shadow: Option<Shadow>,
36 #[serde(default)]
37 pub cursor: Option<CursorStyle>,
38}
39
40impl ContainerStyle {
41 pub fn fill(color: Color) -> Self {
42 Self {
43 background_color: Some(color),
44 ..Default::default()
45 }
46 }
47
48 pub fn additional_length(&self) -> f32 {
49 self.padding.left
50 + self.padding.right
51 + self.border.width * 2.
52 + self.margin.left
53 + self.margin.right
54 }
55}
56
57pub struct Container<V> {
58 child: AnyElement<V>,
59 style: ContainerStyle,
60}
61
62impl<V> Container<V> {
63 pub fn new(child: AnyElement<V>) -> Self {
64 Self {
65 child,
66 style: Default::default(),
67 }
68 }
69
70 pub fn with_style(mut self, style: ContainerStyle) -> Self {
71 self.style = style;
72 self
73 }
74
75 pub fn with_margin_top(mut self, margin: f32) -> Self {
76 self.style.margin.top = margin;
77 self
78 }
79
80 pub fn with_margin_bottom(mut self, margin: f32) -> Self {
81 self.style.margin.bottom = margin;
82 self
83 }
84
85 pub fn with_margin_left(mut self, margin: f32) -> Self {
86 self.style.margin.left = margin;
87 self
88 }
89
90 pub fn with_margin_right(mut self, margin: f32) -> Self {
91 self.style.margin.right = margin;
92 self
93 }
94
95 pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
96 self.style.padding.left = padding;
97 self.style.padding.right = padding;
98 self
99 }
100
101 pub fn with_vertical_padding(mut self, padding: f32) -> Self {
102 self.style.padding.top = padding;
103 self.style.padding.bottom = padding;
104 self
105 }
106
107 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
108 self.style.padding = Padding {
109 top: padding,
110 left: padding,
111 bottom: padding,
112 right: padding,
113 };
114 self
115 }
116
117 pub fn with_padding_left(mut self, padding: f32) -> Self {
118 self.style.padding.left = padding;
119 self
120 }
121
122 pub fn with_padding_right(mut self, padding: f32) -> Self {
123 self.style.padding.right = padding;
124 self
125 }
126
127 pub fn with_padding_top(mut self, padding: f32) -> Self {
128 self.style.padding.top = padding;
129 self
130 }
131
132 pub fn with_padding_bottom(mut self, padding: f32) -> Self {
133 self.style.padding.bottom = padding;
134 self
135 }
136
137 pub fn with_background_color(mut self, color: Color) -> Self {
138 self.style.background_color = Some(color);
139 self
140 }
141
142 pub fn with_overlay_color(mut self, color: Color) -> Self {
143 self.style.overlay_color = Some(color);
144 self
145 }
146
147 pub fn with_border(mut self, border: Border) -> Self {
148 self.style.border = border;
149 self
150 }
151
152 pub fn with_corner_radius(mut self, radius: f32) -> Self {
153 self.style.corner_radii.top_left = radius;
154 self.style.corner_radii.top_right = radius;
155 self.style.corner_radii.bottom_right = radius;
156 self.style.corner_radii.bottom_left = radius;
157 self
158 }
159
160 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
161 self.style.shadow = Some(Shadow {
162 offset,
163 blur,
164 color,
165 });
166 self
167 }
168
169 pub fn with_cursor(mut self, style: CursorStyle) -> Self {
170 self.style.cursor = Some(style);
171 self
172 }
173
174 fn margin_size(&self) -> Vector2F {
175 vec2f(
176 self.style.margin.left + self.style.margin.right,
177 self.style.margin.top + self.style.margin.bottom,
178 )
179 }
180
181 fn padding_size(&self) -> Vector2F {
182 vec2f(
183 self.style.padding.left + self.style.padding.right,
184 self.style.padding.top + self.style.padding.bottom,
185 )
186 }
187
188 fn border_size(&self) -> Vector2F {
189 let mut x = 0.0;
190 if self.style.border.left {
191 x += self.style.border.width;
192 }
193 if self.style.border.right {
194 x += self.style.border.width;
195 }
196
197 let mut y = 0.0;
198 if self.style.border.top {
199 y += self.style.border.width;
200 }
201 if self.style.border.bottom {
202 y += self.style.border.width;
203 }
204
205 vec2f(x, y)
206 }
207}
208
209#[derive(Copy, Clone, Debug, Default, JsonSchema)]
210pub struct Border {
211 pub color: Color,
212 pub width: f32,
213 pub overlay: bool,
214 pub top: bool,
215 pub bottom: bool,
216 pub left: bool,
217 pub right: bool,
218}
219
220impl Into<scene::Border> for Border {
221 fn into(self) -> scene::Border {
222 scene::Border {
223 color: self.color,
224 left: if self.left { self.width } else { 0.0 },
225 right: if self.right { self.width } else { 0.0 },
226 top: if self.top { self.width } else { 0.0 },
227 bottom: if self.bottom { self.width } else { 0.0 },
228 }
229 }
230}
231
232impl Border {
233 pub fn new(width: f32, color: Color) -> Self {
234 Self {
235 width,
236 color,
237 overlay: false,
238 top: false,
239 left: false,
240 bottom: false,
241 right: false,
242 }
243 }
244
245 pub fn all(width: f32, color: Color) -> Self {
246 Self {
247 width,
248 color,
249 overlay: false,
250 top: true,
251 left: true,
252 bottom: true,
253 right: true,
254 }
255 }
256
257 pub fn top(width: f32, color: Color) -> Self {
258 let mut border = Self::new(width, color);
259 border.top = true;
260 border
261 }
262
263 pub fn left(width: f32, color: Color) -> Self {
264 let mut border = Self::new(width, color);
265 border.left = true;
266 border
267 }
268
269 pub fn bottom(width: f32, color: Color) -> Self {
270 let mut border = Self::new(width, color);
271 border.bottom = true;
272 border
273 }
274
275 pub fn right(width: f32, color: Color) -> Self {
276 let mut border = Self::new(width, color);
277 border.right = true;
278 border
279 }
280
281 pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
282 self.top = top;
283 self.left = left;
284 self.bottom = bottom;
285 self.right = right;
286 self
287 }
288
289 pub fn top_width(&self) -> f32 {
290 if self.top {
291 self.width
292 } else {
293 0.0
294 }
295 }
296
297 pub fn left_width(&self) -> f32 {
298 if self.left {
299 self.width
300 } else {
301 0.0
302 }
303 }
304}
305
306impl<'de> Deserialize<'de> for Border {
307 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308 where
309 D: serde::Deserializer<'de>,
310 {
311 #[derive(Deserialize)]
312 struct BorderData {
313 pub width: f32,
314 pub color: Color,
315 #[serde(default)]
316 pub overlay: bool,
317 #[serde(default)]
318 pub top: bool,
319 #[serde(default)]
320 pub right: bool,
321 #[serde(default)]
322 pub bottom: bool,
323 #[serde(default)]
324 pub left: bool,
325 }
326
327 let data = BorderData::deserialize(deserializer)?;
328 let mut border = Border {
329 width: data.width,
330 color: data.color,
331 overlay: data.overlay,
332 top: data.top,
333 bottom: data.bottom,
334 left: data.left,
335 right: data.right,
336 };
337 if !border.top && !border.bottom && !border.left && !border.right {
338 border.top = true;
339 border.bottom = true;
340 border.left = true;
341 border.right = true;
342 }
343 Ok(border)
344 }
345}
346
347impl ToJson for Border {
348 fn to_json(&self) -> serde_json::Value {
349 let mut value = json!({});
350 if self.top {
351 value["top"] = json!(self.width);
352 }
353 if self.right {
354 value["right"] = json!(self.width);
355 }
356 if self.bottom {
357 value["bottom"] = json!(self.width);
358 }
359 if self.left {
360 value["left"] = json!(self.width);
361 }
362 value
363 }
364}
365
366impl<V: 'static> Element<V> for Container<V> {
367 type LayoutState = ();
368 type PaintState = ();
369
370 fn layout(
371 &mut self,
372 constraint: SizeConstraint,
373 view: &mut V,
374 cx: &mut LayoutContext<V>,
375 ) -> (Vector2F, Self::LayoutState) {
376 let mut size_buffer = self.margin_size() + self.padding_size();
377 if !self.style.border.overlay {
378 size_buffer += self.border_size();
379 }
380 let child_constraint = SizeConstraint {
381 min: (constraint.min - size_buffer).max(Vector2F::zero()),
382 max: (constraint.max - size_buffer).max(Vector2F::zero()),
383 };
384 let child_size = self.child.layout(child_constraint, view, cx);
385 (child_size + size_buffer, ())
386 }
387
388 fn paint(
389 &mut self,
390 scene: &mut SceneBuilder,
391 bounds: RectF,
392 visible_bounds: RectF,
393 _: &mut Self::LayoutState,
394 view: &mut V,
395 cx: &mut PaintContext<V>,
396 ) -> Self::PaintState {
397 let quad_bounds = RectF::from_points(
398 bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
399 bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
400 );
401
402 if let Some(shadow) = self.style.shadow.as_ref() {
403 scene.push_shadow(scene::Shadow {
404 bounds: quad_bounds + shadow.offset,
405 corner_radii: self.style.corner_radii,
406 sigma: shadow.blur,
407 color: shadow.color,
408 });
409 }
410
411 if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
412 if let Some(style) = self.style.cursor {
413 scene.push_cursor_region(CursorRegion {
414 bounds: hit_bounds,
415 style,
416 });
417 }
418 }
419
420 let child_origin =
421 quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
422
423 if self.style.border.overlay {
424 scene.push_quad(Quad {
425 bounds: quad_bounds,
426 background: self.style.background_color,
427 border: Default::default(),
428 corner_radii: self.style.corner_radii.into(),
429 });
430
431 self.child
432 .paint(scene, child_origin, visible_bounds, view, cx);
433
434 scene.push_layer(None);
435 scene.push_quad(Quad {
436 bounds: quad_bounds,
437 background: self.style.overlay_color,
438 border: self.style.border.into(),
439 corner_radii: self.style.corner_radii.into(),
440 });
441 scene.pop_layer();
442 } else {
443 scene.push_quad(Quad {
444 bounds: quad_bounds,
445 background: self.style.background_color,
446 border: self.style.border.into(),
447 corner_radii: self.style.corner_radii.into(),
448 });
449
450 let child_origin = child_origin
451 + vec2f(
452 self.style.border.left_width(),
453 self.style.border.top_width(),
454 );
455 self.child
456 .paint(scene, child_origin, visible_bounds, view, cx);
457
458 if self.style.overlay_color.is_some() {
459 scene.push_layer(None);
460 scene.push_quad(Quad {
461 bounds: quad_bounds,
462 background: self.style.overlay_color,
463 border: Default::default(),
464 corner_radii: self.style.corner_radii.into(),
465 });
466 scene.pop_layer();
467 }
468 }
469 }
470
471 fn rect_for_text_range(
472 &self,
473 range_utf16: Range<usize>,
474 _: RectF,
475 _: RectF,
476 _: &Self::LayoutState,
477 _: &Self::PaintState,
478 view: &V,
479 cx: &ViewContext<V>,
480 ) -> Option<RectF> {
481 self.child.rect_for_text_range(range_utf16, view, cx)
482 }
483
484 fn debug(
485 &self,
486 bounds: RectF,
487 _: &Self::LayoutState,
488 _: &Self::PaintState,
489 view: &V,
490 cx: &ViewContext<V>,
491 ) -> serde_json::Value {
492 json!({
493 "type": "Container",
494 "bounds": bounds.to_json(),
495 "details": self.style.to_json(),
496 "child": self.child.debug(view, cx),
497 })
498 }
499}
500
501impl ToJson for ContainerStyle {
502 fn to_json(&self) -> serde_json::Value {
503 json!({
504 "margin": self.margin.to_json(),
505 "padding": self.padding.to_json(),
506 "background_color": self.background_color.to_json(),
507 "border": self.border.to_json(),
508 "corner_radius": self.corner_radii,
509 "shadow": self.shadow.to_json(),
510 })
511 }
512}
513
514#[derive(Clone, Copy, Debug, Default, JsonSchema)]
515pub struct Margin {
516 pub top: f32,
517 pub bottom: f32,
518 pub left: f32,
519 pub right: f32,
520}
521
522impl ToJson for Margin {
523 fn to_json(&self) -> serde_json::Value {
524 let mut value = json!({});
525 if self.top > 0. {
526 value["top"] = json!(self.top);
527 }
528 if self.right > 0. {
529 value["right"] = json!(self.right);
530 }
531 if self.bottom > 0. {
532 value["bottom"] = json!(self.bottom);
533 }
534 if self.left > 0. {
535 value["left"] = json!(self.left);
536 }
537 value
538 }
539}
540
541#[derive(Clone, Copy, Debug, Default, JsonSchema)]
542pub struct Padding {
543 pub top: f32,
544 pub left: f32,
545 pub bottom: f32,
546 pub right: f32,
547}
548
549impl Padding {
550 pub fn horizontal(padding: f32) -> Self {
551 Self {
552 left: padding,
553 right: padding,
554 ..Default::default()
555 }
556 }
557
558 pub fn vertical(padding: f32) -> Self {
559 Self {
560 top: padding,
561 bottom: padding,
562 ..Default::default()
563 }
564 }
565}
566
567impl<'de> Deserialize<'de> for Padding {
568 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
569 where
570 D: serde::Deserializer<'de>,
571 {
572 let spacing = Spacing::deserialize(deserializer)?;
573 Ok(match spacing {
574 Spacing::Uniform(size) => Padding {
575 top: size,
576 left: size,
577 bottom: size,
578 right: size,
579 },
580 Spacing::Specific {
581 top,
582 left,
583 bottom,
584 right,
585 } => Padding {
586 top,
587 left,
588 bottom,
589 right,
590 },
591 })
592 }
593}
594
595impl<'de> Deserialize<'de> for Margin {
596 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
597 where
598 D: serde::Deserializer<'de>,
599 {
600 let spacing = Spacing::deserialize(deserializer)?;
601 Ok(match spacing {
602 Spacing::Uniform(size) => Margin {
603 top: size,
604 left: size,
605 bottom: size,
606 right: size,
607 },
608 Spacing::Specific {
609 top,
610 left,
611 bottom,
612 right,
613 } => Margin {
614 top,
615 left,
616 bottom,
617 right,
618 },
619 })
620 }
621}
622#[derive(Deserialize)]
623#[serde(untagged)]
624enum Spacing {
625 Uniform(f32),
626 Specific {
627 #[serde(default)]
628 top: f32,
629 #[serde(default)]
630 left: f32,
631 #[serde(default)]
632 bottom: f32,
633 #[serde(default)]
634 right: f32,
635 },
636}
637
638impl Padding {
639 pub fn uniform(padding: f32) -> Self {
640 Self {
641 top: padding,
642 left: padding,
643 bottom: padding,
644 right: padding,
645 }
646 }
647}
648
649impl ToJson for Padding {
650 fn to_json(&self) -> serde_json::Value {
651 let mut value = json!({});
652 if self.top > 0. {
653 value["top"] = json!(self.top);
654 }
655 if self.right > 0. {
656 value["right"] = json!(self.right);
657 }
658 if self.bottom > 0. {
659 value["bottom"] = json!(self.bottom);
660 }
661 if self.left > 0. {
662 value["left"] = json!(self.left);
663 }
664 value
665 }
666}
667
668#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
669pub struct Shadow {
670 #[serde(default, deserialize_with = "deserialize_vec2f")]
671 #[schemars(with = "Vec::<f32>")]
672 offset: Vector2F,
673 #[serde(default)]
674 blur: f32,
675 #[serde(default)]
676 color: Color,
677}
678
679impl ToJson for Shadow {
680 fn to_json(&self) -> serde_json::Value {
681 json!({
682 "offset": self.offset.to_json(),
683 "blur": self.blur,
684 "color": self.color.to_json()
685 })
686 }
687}