container.rs

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