container.rs

  1use pathfinder_geometry::rect::RectF;
  2
  3use crate::{
  4    color::ColorU,
  5    geometry::vector::{vec2f, Vector2F},
  6    scene::{self, Border, Quad},
  7    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
  8    SizeConstraint,
  9};
 10
 11pub struct Container {
 12    margin: Margin,
 13    padding: Padding,
 14    overdraw: Overdraw,
 15    background_color: Option<ColorU>,
 16    border: Border,
 17    corner_radius: f32,
 18    shadow: Option<Shadow>,
 19    child: ElementBox,
 20}
 21
 22impl Container {
 23    pub fn new(child: ElementBox) -> Self {
 24        Self {
 25            margin: Margin::default(),
 26            padding: Padding::default(),
 27            overdraw: Overdraw::default(),
 28            background_color: None,
 29            border: Border::default(),
 30            corner_radius: 0.0,
 31            shadow: None,
 32            child,
 33        }
 34    }
 35
 36    pub fn with_margin_top(mut self, margin: f32) -> Self {
 37        self.margin.top = margin;
 38        self
 39    }
 40
 41    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
 42        self.padding = Padding {
 43            top: padding,
 44            left: padding,
 45            bottom: padding,
 46            right: padding,
 47        };
 48        self
 49    }
 50
 51    pub fn with_padding_right(mut self, padding: f32) -> Self {
 52        self.padding.right = padding;
 53        self
 54    }
 55
 56    pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
 57        self.background_color = Some(color.into());
 58        self
 59    }
 60
 61    pub fn with_border(mut self, border: Border) -> Self {
 62        self.border = border;
 63        self
 64    }
 65
 66    pub fn with_overdraw_bottom(mut self, overdraw: f32) -> Self {
 67        self.overdraw.bottom = overdraw;
 68        self
 69    }
 70
 71    pub fn with_corner_radius(mut self, radius: f32) -> Self {
 72        self.corner_radius = radius;
 73        self
 74    }
 75
 76    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
 77        self.shadow = Some(Shadow {
 78            offset,
 79            blur,
 80            color: color.into(),
 81        });
 82        self
 83    }
 84
 85    fn margin_size(&self) -> Vector2F {
 86        vec2f(
 87            self.margin.left + self.margin.right,
 88            self.margin.top + self.margin.bottom,
 89        )
 90    }
 91
 92    fn padding_size(&self) -> Vector2F {
 93        vec2f(
 94            self.padding.left + self.padding.right,
 95            self.padding.top + self.padding.bottom,
 96        )
 97    }
 98
 99    fn border_size(&self) -> Vector2F {
100        let mut x = 0.0;
101        if self.border.left {
102            x += self.border.width;
103        }
104        if self.border.right {
105            x += self.border.width;
106        }
107
108        let mut y = 0.0;
109        if self.border.top {
110            y += self.border.width;
111        }
112        if self.border.bottom {
113            y += self.border.width;
114        }
115
116        vec2f(x, y)
117    }
118}
119
120impl Element for Container {
121    type LayoutState = ();
122    type PaintState = ();
123
124    fn layout(
125        &mut self,
126        constraint: SizeConstraint,
127        ctx: &mut LayoutContext,
128    ) -> (Vector2F, Self::LayoutState) {
129        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
130        let child_constraint = SizeConstraint {
131            min: (constraint.min - size_buffer).max(Vector2F::zero()),
132            max: (constraint.max - size_buffer).max(Vector2F::zero()),
133        };
134        let child_size = self.child.layout(child_constraint, ctx);
135        (child_size + size_buffer, ())
136    }
137
138    fn after_layout(
139        &mut self,
140        _: Vector2F,
141        _: &mut Self::LayoutState,
142        ctx: &mut AfterLayoutContext,
143    ) {
144        self.child.after_layout(ctx);
145    }
146
147    fn paint(
148        &mut self,
149        bounds: RectF,
150        _: &mut Self::LayoutState,
151        ctx: &mut PaintContext,
152    ) -> Self::PaintState {
153        if let Some(shadow) = self.shadow.as_ref() {
154            ctx.scene.push_shadow(scene::Shadow {
155                bounds: bounds + shadow.offset,
156                corner_radius: self.corner_radius,
157                sigma: shadow.blur,
158                color: shadow.color,
159            });
160        }
161        ctx.scene.push_quad(Quad {
162            bounds,
163            background: self.background_color,
164            border: self.border,
165            corner_radius: self.corner_radius,
166        });
167        self.child.paint(bounds.origin(), ctx);
168    }
169
170    fn dispatch_event(
171        &mut self,
172        event: &Event,
173        _: RectF,
174        _: &mut Self::LayoutState,
175        _: &mut Self::PaintState,
176        ctx: &mut EventContext,
177    ) -> bool {
178        self.child.dispatch_event(event, ctx)
179    }
180}
181
182#[derive(Default)]
183pub struct Margin {
184    top: f32,
185    left: f32,
186    bottom: f32,
187    right: f32,
188}
189
190#[derive(Default)]
191pub struct Padding {
192    top: f32,
193    left: f32,
194    bottom: f32,
195    right: f32,
196}
197
198#[derive(Default)]
199pub struct Overdraw {
200    top: f32,
201    left: f32,
202    bottom: f32,
203    right: f32,
204}
205
206#[derive(Default)]
207pub struct Shadow {
208    offset: Vector2F,
209    blur: f32,
210    color: ColorU,
211}