container.rs

  1use std::ops::Range;
  2
  3use crate::{
  4    color::Color,
  5    geometry::{
  6        deserialize_vec2f,
  7        rect::RectF,
  8        vector::{vec2f, Vector2F},
  9    },
 10    json::ToJson,
 11    platform::CursorStyle,
 12    presenter::MeasurementContext,
 13    scene::{self, Border, CursorRegion, Quad},
 14    Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 15};
 16use serde::Deserialize;
 17use serde_json::json;
 18
 19#[derive(Clone, Copy, Debug, Default, Deserialize)]
 20pub struct ContainerStyle {
 21    #[serde(default)]
 22    pub margin: Margin,
 23    #[serde(default)]
 24    pub padding: Padding,
 25    #[serde(rename = "background")]
 26    pub background_color: Option<Color>,
 27    #[serde(default)]
 28    pub border: Border,
 29    #[serde(default)]
 30    pub corner_radius: f32,
 31    #[serde(default)]
 32    pub shadow: Option<Shadow>,
 33    #[serde(default)]
 34    pub cursor: Option<CursorStyle>,
 35}
 36
 37pub struct Container {
 38    child: ElementBox,
 39    style: ContainerStyle,
 40}
 41
 42impl Container {
 43    pub fn new(child: ElementBox) -> Self {
 44        Self {
 45            child,
 46            style: Default::default(),
 47        }
 48    }
 49
 50    pub fn with_style(mut self, style: ContainerStyle) -> Self {
 51        self.style = style;
 52        self
 53    }
 54
 55    pub fn with_margin_top(mut self, margin: f32) -> Self {
 56        self.style.margin.top = margin;
 57        self
 58    }
 59
 60    pub fn with_margin_bottom(mut self, margin: f32) -> Self {
 61        self.style.margin.bottom = margin;
 62        self
 63    }
 64
 65    pub fn with_margin_left(mut self, margin: f32) -> Self {
 66        self.style.margin.left = margin;
 67        self
 68    }
 69
 70    pub fn with_margin_right(mut self, margin: f32) -> Self {
 71        self.style.margin.right = margin;
 72        self
 73    }
 74
 75    pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
 76        self.style.padding.left = padding;
 77        self.style.padding.right = padding;
 78        self
 79    }
 80
 81    pub fn with_vertical_padding(mut self, padding: f32) -> Self {
 82        self.style.padding.top = padding;
 83        self.style.padding.bottom = padding;
 84        self
 85    }
 86
 87    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
 88        self.style.padding = Padding {
 89            top: padding,
 90            left: padding,
 91            bottom: padding,
 92            right: padding,
 93        };
 94        self
 95    }
 96
 97    pub fn with_padding_left(mut self, padding: f32) -> Self {
 98        self.style.padding.left = padding;
 99        self
100    }
101
102    pub fn with_padding_right(mut self, padding: f32) -> Self {
103        self.style.padding.right = padding;
104        self
105    }
106
107    pub fn with_padding_top(mut self, padding: f32) -> Self {
108        self.style.padding.top = padding;
109        self
110    }
111
112    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
113        self.style.padding.bottom = padding;
114        self
115    }
116
117    pub fn with_background_color(mut self, color: Color) -> Self {
118        self.style.background_color = Some(color);
119        self
120    }
121
122    pub fn with_border(mut self, border: Border) -> Self {
123        self.style.border = border;
124        self
125    }
126
127    pub fn with_corner_radius(mut self, radius: f32) -> Self {
128        self.style.corner_radius = radius;
129        self
130    }
131
132    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
133        self.style.shadow = Some(Shadow {
134            offset,
135            blur,
136            color,
137        });
138        self
139    }
140
141    pub fn with_cursor(mut self, style: CursorStyle) -> Self {
142        self.style.cursor = Some(style);
143        self
144    }
145
146    fn margin_size(&self) -> Vector2F {
147        vec2f(
148            self.style.margin.left + self.style.margin.right,
149            self.style.margin.top + self.style.margin.bottom,
150        )
151    }
152
153    fn padding_size(&self) -> Vector2F {
154        vec2f(
155            self.style.padding.left + self.style.padding.right,
156            self.style.padding.top + self.style.padding.bottom,
157        )
158    }
159
160    fn border_size(&self) -> Vector2F {
161        let mut x = 0.0;
162        if self.style.border.left {
163            x += self.style.border.width;
164        }
165        if self.style.border.right {
166            x += self.style.border.width;
167        }
168
169        let mut y = 0.0;
170        if self.style.border.top {
171            y += self.style.border.width;
172        }
173        if self.style.border.bottom {
174            y += self.style.border.width;
175        }
176
177        vec2f(x, y)
178    }
179}
180
181impl Element for Container {
182    type LayoutState = ();
183    type PaintState = ();
184
185    fn layout(
186        &mut self,
187        constraint: SizeConstraint,
188        cx: &mut LayoutContext,
189    ) -> (Vector2F, Self::LayoutState) {
190        let mut size_buffer = self.margin_size() + self.padding_size();
191        if !self.style.border.overlay {
192            size_buffer += self.border_size();
193        }
194        let child_constraint = SizeConstraint {
195            min: (constraint.min - size_buffer).max(Vector2F::zero()),
196            max: (constraint.max - size_buffer).max(Vector2F::zero()),
197        };
198        let child_size = self.child.layout(child_constraint, cx);
199        (child_size + size_buffer, ())
200    }
201
202    fn paint(
203        &mut self,
204        bounds: RectF,
205        visible_bounds: RectF,
206        _: &mut Self::LayoutState,
207        cx: &mut PaintContext,
208    ) -> Self::PaintState {
209        let quad_bounds = RectF::from_points(
210            bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
211            bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
212        );
213
214        if let Some(shadow) = self.style.shadow.as_ref() {
215            cx.scene.push_shadow(scene::Shadow {
216                bounds: quad_bounds + shadow.offset,
217                corner_radius: self.style.corner_radius,
218                sigma: shadow.blur,
219                color: shadow.color,
220            });
221        }
222
223        if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
224            if let Some(style) = self.style.cursor {
225                cx.scene.push_cursor_region(CursorRegion {
226                    bounds: hit_bounds,
227                    style,
228                });
229            }
230        }
231
232        let child_origin =
233            quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
234
235        if self.style.border.overlay {
236            cx.scene.push_quad(Quad {
237                bounds: quad_bounds,
238                background: self.style.background_color,
239                border: Default::default(),
240                corner_radius: self.style.corner_radius,
241            });
242
243            self.child.paint(child_origin, visible_bounds, cx);
244
245            cx.scene.push_layer(None);
246            cx.scene.push_quad(Quad {
247                bounds: quad_bounds,
248                background: Default::default(),
249                border: self.style.border,
250                corner_radius: self.style.corner_radius,
251            });
252            cx.scene.pop_layer();
253        } else {
254            cx.scene.push_quad(Quad {
255                bounds: quad_bounds,
256                background: self.style.background_color,
257                border: self.style.border,
258                corner_radius: self.style.corner_radius,
259            });
260
261            let child_origin = child_origin
262                + vec2f(
263                    self.style.border.left_width(),
264                    self.style.border.top_width(),
265                );
266            self.child.paint(child_origin, visible_bounds, cx);
267        }
268    }
269
270    fn dispatch_event(
271        &mut self,
272        event: &Event,
273        _: RectF,
274        _: RectF,
275        _: &mut Self::LayoutState,
276        _: &mut Self::PaintState,
277        cx: &mut EventContext,
278    ) -> bool {
279        self.child.dispatch_event(event, cx)
280    }
281
282    fn rect_for_text_range(
283        &self,
284        range_utf16: Range<usize>,
285        _: RectF,
286        _: RectF,
287        _: &Self::LayoutState,
288        _: &Self::PaintState,
289        cx: &MeasurementContext,
290    ) -> Option<RectF> {
291        self.child.rect_for_text_range(range_utf16, cx)
292    }
293
294    fn debug(
295        &self,
296        bounds: RectF,
297        _: &Self::LayoutState,
298        _: &Self::PaintState,
299        cx: &crate::DebugContext,
300    ) -> serde_json::Value {
301        json!({
302            "type": "Container",
303            "bounds": bounds.to_json(),
304            "details": self.style.to_json(),
305            "child": self.child.debug(cx),
306        })
307    }
308}
309
310impl ToJson for ContainerStyle {
311    fn to_json(&self) -> serde_json::Value {
312        json!({
313            "margin": self.margin.to_json(),
314            "padding": self.padding.to_json(),
315            "background_color": self.background_color.to_json(),
316            "border": self.border.to_json(),
317            "corner_radius": self.corner_radius,
318            "shadow": self.shadow.to_json(),
319        })
320    }
321}
322
323#[derive(Clone, Copy, Debug, Default)]
324pub struct Margin {
325    pub top: f32,
326    pub left: f32,
327    pub bottom: f32,
328    pub right: f32,
329}
330
331impl ToJson for Margin {
332    fn to_json(&self) -> serde_json::Value {
333        let mut value = json!({});
334        if self.top > 0. {
335            value["top"] = json!(self.top);
336        }
337        if self.right > 0. {
338            value["right"] = json!(self.right);
339        }
340        if self.bottom > 0. {
341            value["bottom"] = json!(self.bottom);
342        }
343        if self.left > 0. {
344            value["left"] = json!(self.left);
345        }
346        value
347    }
348}
349
350#[derive(Clone, Copy, Debug, Default)]
351pub struct Padding {
352    pub top: f32,
353    pub left: f32,
354    pub bottom: f32,
355    pub right: f32,
356}
357
358impl<'de> Deserialize<'de> for Padding {
359    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
360    where
361        D: serde::Deserializer<'de>,
362    {
363        let spacing = Spacing::deserialize(deserializer)?;
364        Ok(match spacing {
365            Spacing::Uniform(size) => Padding {
366                top: size,
367                left: size,
368                bottom: size,
369                right: size,
370            },
371            Spacing::Specific {
372                top,
373                left,
374                bottom,
375                right,
376            } => Padding {
377                top,
378                left,
379                bottom,
380                right,
381            },
382        })
383    }
384}
385
386impl<'de> Deserialize<'de> for Margin {
387    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
388    where
389        D: serde::Deserializer<'de>,
390    {
391        let spacing = Spacing::deserialize(deserializer)?;
392        Ok(match spacing {
393            Spacing::Uniform(size) => Margin {
394                top: size,
395                left: size,
396                bottom: size,
397                right: size,
398            },
399            Spacing::Specific {
400                top,
401                left,
402                bottom,
403                right,
404            } => Margin {
405                top,
406                left,
407                bottom,
408                right,
409            },
410        })
411    }
412}
413#[derive(Deserialize)]
414#[serde(untagged)]
415enum Spacing {
416    Uniform(f32),
417    Specific {
418        #[serde(default)]
419        top: f32,
420        #[serde(default)]
421        left: f32,
422        #[serde(default)]
423        bottom: f32,
424        #[serde(default)]
425        right: f32,
426    },
427}
428
429impl Padding {
430    pub fn uniform(padding: f32) -> Self {
431        Self {
432            top: padding,
433            left: padding,
434            bottom: padding,
435            right: padding,
436        }
437    }
438}
439
440impl ToJson for Padding {
441    fn to_json(&self) -> serde_json::Value {
442        let mut value = json!({});
443        if self.top > 0. {
444            value["top"] = json!(self.top);
445        }
446        if self.right > 0. {
447            value["right"] = json!(self.right);
448        }
449        if self.bottom > 0. {
450            value["bottom"] = json!(self.bottom);
451        }
452        if self.left > 0. {
453            value["left"] = json!(self.left);
454        }
455        value
456    }
457}
458
459#[derive(Clone, Copy, Debug, Default, Deserialize)]
460pub struct Shadow {
461    #[serde(default, deserialize_with = "deserialize_vec2f")]
462    offset: Vector2F,
463    #[serde(default)]
464    blur: f32,
465    #[serde(default)]
466    color: Color,
467}
468
469impl ToJson for Shadow {
470    fn to_json(&self) -> serde_json::Value {
471        json!({
472            "offset": self.offset.to_json(),
473            "blur": self.blur,
474            "color": self.color.to_json()
475        })
476    }
477}