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_padding_bottom(mut self, padding: f32) -> Self {
 57        self.padding.bottom = padding;
 58        self
 59    }
 60
 61    pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
 62        self.background_color = Some(color.into());
 63        self
 64    }
 65
 66    pub fn with_border(mut self, border: Border) -> Self {
 67        self.border = border;
 68        self
 69    }
 70
 71    pub fn with_overdraw_bottom(mut self, overdraw: f32) -> Self {
 72        self.overdraw.bottom = overdraw;
 73        self
 74    }
 75
 76    pub fn with_corner_radius(mut self, radius: f32) -> Self {
 77        self.corner_radius = radius;
 78        self
 79    }
 80
 81    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
 82        self.shadow = Some(Shadow {
 83            offset,
 84            blur,
 85            color: color.into(),
 86        });
 87        self
 88    }
 89
 90    fn margin_size(&self) -> Vector2F {
 91        vec2f(
 92            self.margin.left + self.margin.right,
 93            self.margin.top + self.margin.bottom,
 94        )
 95    }
 96
 97    fn padding_size(&self) -> Vector2F {
 98        vec2f(
 99            self.padding.left + self.padding.right,
100            self.padding.top + self.padding.bottom,
101        )
102    }
103
104    fn border_size(&self) -> Vector2F {
105        let mut x = 0.0;
106        if self.border.left {
107            x += self.border.width;
108        }
109        if self.border.right {
110            x += self.border.width;
111        }
112
113        let mut y = 0.0;
114        if self.border.top {
115            y += self.border.width;
116        }
117        if self.border.bottom {
118            y += self.border.width;
119        }
120
121        vec2f(x, y)
122    }
123}
124
125impl Element for Container {
126    type LayoutState = ();
127    type PaintState = ();
128
129    fn layout(
130        &mut self,
131        constraint: SizeConstraint,
132        ctx: &mut LayoutContext,
133    ) -> (Vector2F, Self::LayoutState) {
134        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
135        let child_constraint = SizeConstraint {
136            min: (constraint.min - size_buffer).max(Vector2F::zero()),
137            max: (constraint.max - size_buffer).max(Vector2F::zero()),
138        };
139        let child_size = self.child.layout(child_constraint, ctx);
140        (child_size + size_buffer, ())
141    }
142
143    fn after_layout(
144        &mut self,
145        _: Vector2F,
146        _: &mut Self::LayoutState,
147        ctx: &mut AfterLayoutContext,
148    ) {
149        self.child.after_layout(ctx);
150    }
151
152    fn paint(
153        &mut self,
154        bounds: RectF,
155        _: &mut Self::LayoutState,
156        ctx: &mut PaintContext,
157    ) -> Self::PaintState {
158        let quad_bounds = RectF::from_points(
159            bounds.origin() + vec2f(self.margin.left, self.margin.top),
160            bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom),
161        );
162
163        if let Some(shadow) = self.shadow.as_ref() {
164            ctx.scene.push_shadow(scene::Shadow {
165                bounds: quad_bounds + shadow.offset,
166                corner_radius: self.corner_radius,
167                sigma: shadow.blur,
168                color: shadow.color,
169            });
170        }
171        ctx.scene.push_quad(Quad {
172            bounds: quad_bounds,
173            background: self.background_color,
174            border: self.border,
175            corner_radius: self.corner_radius,
176        });
177
178        let child_origin = quad_bounds.origin() + vec2f(self.padding.left, self.padding.top);
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 Overdraw {
212    top: f32,
213    left: f32,
214    bottom: f32,
215    right: f32,
216}
217
218#[derive(Default)]
219pub struct Shadow {
220    offset: Vector2F,
221    blur: f32,
222    color: ColorU,
223}