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_uniform_padding(mut self, padding: f32) -> Self {
 40        self.padding = Padding {
 41            top: padding,
 42            left: padding,
 43            bottom: padding,
 44            right: padding,
 45        };
 46        self
 47    }
 48
 49    pub fn with_padding_right(mut self, padding: f32) -> Self {
 50        self.padding.right = padding;
 51        self
 52    }
 53
 54    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
 55        self.padding.bottom = padding;
 56        self
 57    }
 58
 59    pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
 60        self.background_color = Some(color.into());
 61        self
 62    }
 63
 64    pub fn with_border(mut self, border: Border) -> Self {
 65        self.border = border;
 66        self
 67    }
 68
 69    pub fn with_corner_radius(mut self, radius: f32) -> Self {
 70        self.corner_radius = radius;
 71        self
 72    }
 73
 74    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
 75        self.shadow = Some(Shadow {
 76            offset,
 77            blur,
 78            color: color.into(),
 79        });
 80        self
 81    }
 82
 83    fn margin_size(&self) -> Vector2F {
 84        vec2f(
 85            self.margin.left + self.margin.right,
 86            self.margin.top + self.margin.bottom,
 87        )
 88    }
 89
 90    fn padding_size(&self) -> Vector2F {
 91        vec2f(
 92            self.padding.left + self.padding.right,
 93            self.padding.top + self.padding.bottom,
 94        )
 95    }
 96
 97    fn border_size(&self) -> Vector2F {
 98        let mut x = 0.0;
 99        if self.border.left {
100            x += self.border.width;
101        }
102        if self.border.right {
103            x += self.border.width;
104        }
105
106        let mut y = 0.0;
107        if self.border.top {
108            y += self.border.width;
109        }
110        if self.border.bottom {
111            y += self.border.width;
112        }
113
114        vec2f(x, y)
115    }
116}
117
118impl Element for Container {
119    type LayoutState = ();
120    type PaintState = ();
121
122    fn layout(
123        &mut self,
124        constraint: SizeConstraint,
125        ctx: &mut LayoutContext,
126    ) -> (Vector2F, Self::LayoutState) {
127        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
128        let child_constraint = SizeConstraint {
129            min: (constraint.min - size_buffer).max(Vector2F::zero()),
130            max: (constraint.max - size_buffer).max(Vector2F::zero()),
131        };
132        let child_size = self.child.layout(child_constraint, ctx);
133        (child_size + size_buffer, ())
134    }
135
136    fn after_layout(
137        &mut self,
138        _: Vector2F,
139        _: &mut Self::LayoutState,
140        ctx: &mut AfterLayoutContext,
141    ) {
142        self.child.after_layout(ctx);
143    }
144
145    fn paint(
146        &mut self,
147        bounds: RectF,
148        _: &mut Self::LayoutState,
149        ctx: &mut PaintContext,
150    ) -> Self::PaintState {
151        let quad_bounds = RectF::from_points(
152            bounds.origin() + vec2f(self.margin.left, self.margin.top),
153            bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom),
154        );
155
156        if let Some(shadow) = self.shadow.as_ref() {
157            ctx.scene.push_shadow(scene::Shadow {
158                bounds: quad_bounds + shadow.offset,
159                corner_radius: self.corner_radius,
160                sigma: shadow.blur,
161                color: shadow.color,
162            });
163        }
164        ctx.scene.push_quad(Quad {
165            bounds: quad_bounds,
166            background: self.background_color,
167            border: self.border,
168            corner_radius: self.corner_radius,
169        });
170
171        let child_origin = quad_bounds.origin()
172            + vec2f(self.padding.left, self.padding.top)
173            + vec2f(self.border.left_width(), self.border.top_width());
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 Shadow {
207    offset: Vector2F,
208    blur: f32,
209    color: ColorU,
210}