container.rs

  1use crate::{
  2    color::ColorU,
  3    geometry::vector::{vec2f, Vector2F},
  4    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
  5    PaintContext, SizeConstraint,
  6};
  7
  8pub struct Container {
  9    margin: Margin,
 10    padding: Padding,
 11    overdraw: Overdraw,
 12    background_color: Option<ColorU>,
 13    border: Border,
 14    corner_radius: f32,
 15    shadow: Option<Shadow>,
 16    child: Box<dyn Element>,
 17    size: Option<Vector2F>,
 18    origin: Option<Vector2F>,
 19}
 20
 21impl Container {
 22    pub fn new(child: Box<dyn Element>) -> Self {
 23        Self {
 24            margin: Margin::default(),
 25            padding: Padding::default(),
 26            overdraw: Overdraw::default(),
 27            background_color: None,
 28            border: Border::default(),
 29            corner_radius: 0.0,
 30            shadow: None,
 31            child,
 32            size: None,
 33            origin: None,
 34        }
 35    }
 36
 37    pub fn with_margin_top(mut self, margin: f32) -> Self {
 38        self.margin.top = margin;
 39        self
 40    }
 41
 42    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
 43        self.padding = Padding {
 44            top: padding,
 45            left: padding,
 46            bottom: padding,
 47            right: padding,
 48        };
 49        self
 50    }
 51
 52    pub fn with_padding_right(mut self, padding: f32) -> Self {
 53        self.padding.right = padding;
 54        self
 55    }
 56
 57    pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
 58        self.background_color = Some(color.into());
 59        self
 60    }
 61
 62    pub fn with_border(mut self, border: Border) -> Self {
 63        self.border = border;
 64        self
 65    }
 66
 67    pub fn with_overdraw_bottom(mut self, overdraw: f32) -> Self {
 68        self.overdraw.bottom = overdraw;
 69        self
 70    }
 71
 72    pub fn with_corner_radius(mut self, radius: f32) -> Self {
 73        self.corner_radius = radius;
 74        self
 75    }
 76
 77    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
 78        self.shadow = Some(Shadow {
 79            offset,
 80            blur,
 81            color: color.into(),
 82        });
 83        self
 84    }
 85
 86    fn margin_size(&self) -> Vector2F {
 87        vec2f(
 88            self.margin.left + self.margin.right,
 89            self.margin.top + self.margin.bottom,
 90        )
 91    }
 92
 93    fn padding_size(&self) -> Vector2F {
 94        vec2f(
 95            self.padding.left + self.padding.right,
 96            self.padding.top + self.padding.bottom,
 97        )
 98    }
 99
100    fn border_size(&self) -> Vector2F {
101        let mut x = 0.0;
102        if self.border.left {
103            x += self.border.width;
104        }
105        if self.border.right {
106            x += self.border.width;
107        }
108
109        let mut y = 0.0;
110        if self.border.top {
111            y += self.border.width;
112        }
113        if self.border.bottom {
114            y += self.border.width;
115        }
116
117        vec2f(x, y)
118    }
119}
120
121impl Element for Container {
122    fn layout(
123        &mut self,
124        constraint: SizeConstraint,
125        ctx: &mut LayoutContext,
126        app: &AppContext,
127    ) -> Vector2F {
128        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
129        let child_constraint = SizeConstraint {
130            min: (constraint.min - size_buffer).max(Vector2F::zero()),
131            max: (constraint.max - size_buffer).max(Vector2F::zero()),
132        };
133        let child_size = self.child.layout(child_constraint, ctx, app);
134        let size = child_size + size_buffer;
135        self.size = Some(size);
136        size
137    }
138
139    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
140        self.child.after_layout(ctx, app);
141    }
142
143    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
144        // self.origin = Some(origin);
145
146        // let canvas = &mut ctx.canvas;
147        // let size = self.size.unwrap() - self.margin_size()
148        //     + vec2f(self.overdraw.right, self.overdraw.bottom);
149        // let origin = origin + vec2f(self.margin.left, self.margin.top)
150        //     - vec2f(self.overdraw.left, self.overdraw.top);
151        // let rect = RectF::new(origin, size);
152
153        // let mut path = Path2D::new();
154        // if self.corner_radius > 0.0 {
155        //     path.move_to(rect.upper_right() - vec2f(self.corner_radius, 0.0));
156        //     path.arc_to(
157        //         rect.upper_right(),
158        //         rect.upper_right() + vec2f(0.0, self.corner_radius),
159        //         self.corner_radius,
160        //     );
161        //     path.line_to(rect.lower_right() - vec2f(0.0, self.corner_radius));
162        //     path.arc_to(
163        //         rect.lower_right(),
164        //         rect.lower_right() - vec2f(self.corner_radius, 0.0),
165        //         self.corner_radius,
166        //     );
167        //     path.line_to(rect.lower_left() + vec2f(self.corner_radius, 0.0));
168        //     path.arc_to(
169        //         rect.lower_left(),
170        //         rect.lower_left() - vec2f(0.0, self.corner_radius),
171        //         self.corner_radius,
172        //     );
173        //     path.line_to(origin + vec2f(0.0, self.corner_radius));
174        //     path.arc_to(
175        //         origin,
176        //         origin + vec2f(self.corner_radius, 0.0),
177        //         self.corner_radius,
178        //     );
179        //     path.close_path();
180        // } else {
181        //     path.rect(rect);
182        // }
183
184        // canvas.save();
185        // if let Some(shadow) = self.shadow.as_ref() {
186        //     canvas.set_shadow_offset(shadow.offset);
187        //     canvas.set_shadow_blur(shadow.blur);
188        //     canvas.set_shadow_color(shadow.color);
189        // }
190
191        // if let Some(background_color) = self.background_color {
192        //     canvas.set_fill_style(FillStyle::Color(background_color));
193        //     canvas.fill_path(path.clone(), FillRule::Winding);
194        // }
195
196        // canvas.set_line_width(self.border.width);
197        // canvas.set_stroke_style(FillStyle::Color(self.border.color));
198
199        // let border_rect = rect.contract(self.border.width / 2.0);
200
201        // // For now, we ignore the corner radius unless we draw a border on all sides.
202        // // This could be improved.
203        // if self.border.all_sides() {
204        //     let mut path = Path2D::new();
205        //     path.rect(border_rect);
206        //     canvas.stroke_path(path);
207        // } else {
208        //     canvas.set_line_cap(LineCap::Square);
209
210        //     if self.border.top {
211        //         let mut path = Path2D::new();
212        //         path.move_to(border_rect.origin());
213        //         path.line_to(border_rect.upper_right());
214        //         canvas.stroke_path(path);
215        //     }
216
217        //     if self.border.left {
218        //         let mut path = Path2D::new();
219        //         path.move_to(border_rect.origin());
220        //         path.line_to(border_rect.lower_left());
221        //         canvas.stroke_path(path);
222        //     }
223
224        //     if self.border.bottom {
225        //         let mut path = Path2D::new();
226        //         path.move_to(border_rect.lower_left());
227        //         path.line_to(border_rect.lower_right());
228        //         canvas.stroke_path(path);
229        //     }
230
231        //     if self.border.right {
232        //         let mut path = Path2D::new();
233        //         path.move_to(border_rect.upper_right());
234        //         path.line_to(border_rect.lower_right());
235        //         canvas.stroke_path(path);
236        //     }
237        // }
238        // canvas.restore();
239
240        // let mut child_origin = origin + vec2f(self.padding.left, self.padding.top);
241        // if self.border.left {
242        //     child_origin.set_x(child_origin.x() + self.border.width);
243        // }
244        // if self.border.top {
245        //     child_origin.set_y(child_origin.y() + self.border.width);
246        // }
247        // self.child.paint(child_origin, ctx, app);
248    }
249
250    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
251        self.child.dispatch_event(event, ctx, app)
252    }
253
254    fn size(&self) -> Option<Vector2F> {
255        self.size
256    }
257}
258
259#[derive(Default)]
260pub struct Margin {
261    top: f32,
262    left: f32,
263    bottom: f32,
264    right: f32,
265}
266
267#[derive(Default)]
268pub struct Padding {
269    top: f32,
270    left: f32,
271    bottom: f32,
272    right: f32,
273}
274
275#[derive(Default)]
276pub struct Overdraw {
277    top: f32,
278    left: f32,
279    bottom: f32,
280    right: f32,
281}
282
283#[derive(Default)]
284pub struct Border {
285    width: f32,
286    color: ColorU,
287    pub top: bool,
288    pub left: bool,
289    pub bottom: bool,
290    pub right: bool,
291}
292
293impl Border {
294    pub fn new(width: f32, color: impl Into<ColorU>) -> Self {
295        Self {
296            width,
297            color: color.into(),
298            top: false,
299            left: false,
300            bottom: false,
301            right: false,
302        }
303    }
304
305    pub fn all(width: f32, color: impl Into<ColorU>) -> Self {
306        Self {
307            width,
308            color: color.into(),
309            top: true,
310            left: true,
311            bottom: true,
312            right: true,
313        }
314    }
315
316    pub fn top(width: f32, color: impl Into<ColorU>) -> Self {
317        let mut border = Self::new(width, color);
318        border.top = true;
319        border
320    }
321
322    pub fn left(width: f32, color: impl Into<ColorU>) -> Self {
323        let mut border = Self::new(width, color);
324        border.left = true;
325        border
326    }
327
328    pub fn bottom(width: f32, color: impl Into<ColorU>) -> Self {
329        let mut border = Self::new(width, color);
330        border.bottom = true;
331        border
332    }
333
334    pub fn right(width: f32, color: impl Into<ColorU>) -> Self {
335        let mut border = Self::new(width, color);
336        border.right = true;
337        border
338    }
339
340    pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
341        self.top = top;
342        self.left = left;
343        self.bottom = bottom;
344        self.right = right;
345        self
346    }
347
348    fn all_sides(&self) -> bool {
349        self.top && self.left && self.bottom && self.right
350    }
351}
352
353#[derive(Default)]
354pub struct Shadow {
355    offset: Vector2F,
356    blur: f32,
357    color: ColorU,
358}