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        let quad_bounds = RectF::from_points(
154            bounds.origin() + vec2f(self.margin.left, self.margin.top),
155            bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom),
156        );
157
158        if let Some(shadow) = self.shadow.as_ref() {
159            ctx.scene.push_shadow(scene::Shadow {
160                bounds: quad_bounds + shadow.offset,
161                corner_radius: self.corner_radius,
162                sigma: shadow.blur,
163                color: shadow.color,
164            });
165        }
166        ctx.scene.push_quad(Quad {
167            bounds: quad_bounds,
168            background: self.background_color,
169            border: self.border,
170            corner_radius: self.corner_radius,
171        });
172
173        let child_origin = quad_bounds.origin() + vec2f(self.padding.left, self.padding.top);
174        self.child.paint(child_origin, ctx);
175    }
176
177    fn dispatch_event(
178        &mut self,
179        event: &Event,
180        _: RectF,
181        _: &mut Self::LayoutState,
182        _: &mut Self::PaintState,
183        ctx: &mut EventContext,
184    ) -> bool {
185        self.child.dispatch_event(event, ctx)
186    }
187}
188
189#[derive(Default)]
190pub struct Margin {
191    top: f32,
192    left: f32,
193    bottom: f32,
194    right: f32,
195}
196
197#[derive(Default)]
198pub struct Padding {
199    top: f32,
200    left: f32,
201    bottom: f32,
202    right: f32,
203}
204
205#[derive(Default)]
206pub struct Overdraw {
207    top: f32,
208    left: f32,
209    bottom: f32,
210    right: f32,
211}
212
213#[derive(Default)]
214pub struct Shadow {
215    offset: Vector2F,
216    blur: f32,
217    color: ColorU,
218}