container.rs

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