container.rs

  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, 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 ViewContext<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        bounds: RectF,
391        visible_bounds: RectF,
392        _: &mut Self::LayoutState,
393        view: &mut V,
394        cx: &mut ViewContext<V>,
395    ) -> Self::PaintState {
396        let quad_bounds = RectF::from_points(
397            bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
398            bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
399        );
400
401        if let Some(shadow) = self.style.shadow.as_ref() {
402            cx.scene().push_shadow(scene::Shadow {
403                bounds: quad_bounds + shadow.offset,
404                corner_radii: self.style.corner_radii,
405                sigma: shadow.blur,
406                color: shadow.color,
407            });
408        }
409
410        if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
411            if let Some(style) = self.style.cursor {
412                cx.scene().push_cursor_region(CursorRegion {
413                    bounds: hit_bounds,
414                    style,
415                });
416            }
417        }
418
419        let child_origin =
420            quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
421
422        if self.style.border.overlay {
423            cx.scene().push_quad(Quad {
424                bounds: quad_bounds,
425                background: self.style.background_color,
426                border: Default::default(),
427                corner_radii: self.style.corner_radii.into(),
428            });
429
430            self.child.paint(child_origin, visible_bounds, view, cx);
431
432            cx.scene().push_layer(None);
433            cx.scene().push_quad(Quad {
434                bounds: quad_bounds,
435                background: self.style.overlay_color,
436                border: self.style.border.into(),
437                corner_radii: self.style.corner_radii.into(),
438            });
439            cx.scene().pop_layer();
440        } else {
441            cx.scene().push_quad(Quad {
442                bounds: quad_bounds,
443                background: self.style.background_color,
444                border: self.style.border.into(),
445                corner_radii: self.style.corner_radii.into(),
446            });
447
448            let child_origin = child_origin
449                + vec2f(
450                    self.style.border.left_width(),
451                    self.style.border.top_width(),
452                );
453            self.child.paint(child_origin, visible_bounds, view, cx);
454
455            if self.style.overlay_color.is_some() {
456                cx.scene().push_layer(None);
457                cx.scene().push_quad(Quad {
458                    bounds: quad_bounds,
459                    background: self.style.overlay_color,
460                    border: Default::default(),
461                    corner_radii: self.style.corner_radii.into(),
462                });
463                cx.scene().pop_layer();
464            }
465        }
466    }
467
468    fn rect_for_text_range(
469        &self,
470        range_utf16: Range<usize>,
471        _: RectF,
472        _: RectF,
473        _: &Self::LayoutState,
474        _: &Self::PaintState,
475        view: &V,
476        cx: &ViewContext<V>,
477    ) -> Option<RectF> {
478        self.child.rect_for_text_range(range_utf16, view, cx)
479    }
480
481    fn debug(
482        &self,
483        bounds: RectF,
484        _: &Self::LayoutState,
485        _: &Self::PaintState,
486        view: &V,
487        cx: &ViewContext<V>,
488    ) -> serde_json::Value {
489        json!({
490            "type": "Container",
491            "bounds": bounds.to_json(),
492            "details": self.style.to_json(),
493            "child": self.child.debug(view, cx),
494        })
495    }
496}
497
498impl ToJson for ContainerStyle {
499    fn to_json(&self) -> serde_json::Value {
500        json!({
501            "margin": self.margin.to_json(),
502            "padding": self.padding.to_json(),
503            "background_color": self.background_color.to_json(),
504            "border": self.border.to_json(),
505            "corner_radius": self.corner_radii,
506            "shadow": self.shadow.to_json(),
507        })
508    }
509}
510
511#[derive(Clone, Copy, Debug, Default, JsonSchema)]
512pub struct Margin {
513    pub top: f32,
514    pub bottom: f32,
515    pub left: f32,
516    pub right: f32,
517}
518
519impl ToJson for Margin {
520    fn to_json(&self) -> serde_json::Value {
521        let mut value = json!({});
522        if self.top > 0. {
523            value["top"] = json!(self.top);
524        }
525        if self.right > 0. {
526            value["right"] = json!(self.right);
527        }
528        if self.bottom > 0. {
529            value["bottom"] = json!(self.bottom);
530        }
531        if self.left > 0. {
532            value["left"] = json!(self.left);
533        }
534        value
535    }
536}
537
538#[derive(Clone, Copy, Debug, Default, JsonSchema)]
539pub struct Padding {
540    pub top: f32,
541    pub left: f32,
542    pub bottom: f32,
543    pub right: f32,
544}
545
546impl Padding {
547    pub fn horizontal(padding: f32) -> Self {
548        Self {
549            left: padding,
550            right: padding,
551            ..Default::default()
552        }
553    }
554
555    pub fn vertical(padding: f32) -> Self {
556        Self {
557            top: padding,
558            bottom: padding,
559            ..Default::default()
560        }
561    }
562}
563
564impl<'de> Deserialize<'de> for Padding {
565    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
566    where
567        D: serde::Deserializer<'de>,
568    {
569        let spacing = Spacing::deserialize(deserializer)?;
570        Ok(match spacing {
571            Spacing::Uniform(size) => Padding {
572                top: size,
573                left: size,
574                bottom: size,
575                right: size,
576            },
577            Spacing::Specific {
578                top,
579                left,
580                bottom,
581                right,
582            } => Padding {
583                top,
584                left,
585                bottom,
586                right,
587            },
588        })
589    }
590}
591
592impl<'de> Deserialize<'de> for Margin {
593    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
594    where
595        D: serde::Deserializer<'de>,
596    {
597        let spacing = Spacing::deserialize(deserializer)?;
598        Ok(match spacing {
599            Spacing::Uniform(size) => Margin {
600                top: size,
601                left: size,
602                bottom: size,
603                right: size,
604            },
605            Spacing::Specific {
606                top,
607                left,
608                bottom,
609                right,
610            } => Margin {
611                top,
612                left,
613                bottom,
614                right,
615            },
616        })
617    }
618}
619#[derive(Deserialize)]
620#[serde(untagged)]
621enum Spacing {
622    Uniform(f32),
623    Specific {
624        #[serde(default)]
625        top: f32,
626        #[serde(default)]
627        left: f32,
628        #[serde(default)]
629        bottom: f32,
630        #[serde(default)]
631        right: f32,
632    },
633}
634
635impl Padding {
636    pub fn uniform(padding: f32) -> Self {
637        Self {
638            top: padding,
639            left: padding,
640            bottom: padding,
641            right: padding,
642        }
643    }
644}
645
646impl ToJson for Padding {
647    fn to_json(&self) -> serde_json::Value {
648        let mut value = json!({});
649        if self.top > 0. {
650            value["top"] = json!(self.top);
651        }
652        if self.right > 0. {
653            value["right"] = json!(self.right);
654        }
655        if self.bottom > 0. {
656            value["bottom"] = json!(self.bottom);
657        }
658        if self.left > 0. {
659            value["left"] = json!(self.left);
660        }
661        value
662    }
663}
664
665#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
666pub struct Shadow {
667    #[serde(default, deserialize_with = "deserialize_vec2f")]
668    #[schemars(with = "Vec::<f32>")]
669    offset: Vector2F,
670    #[serde(default)]
671    blur: f32,
672    #[serde(default)]
673    color: Color,
674}
675
676impl ToJson for Shadow {
677    fn to_json(&self) -> serde_json::Value {
678        json!({
679            "offset": self.offset.to_json(),
680            "blur": self.blur,
681            "color": self.color.to_json()
682        })
683    }
684}