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, 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}