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