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, 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_style(quad_bounds, style);
217        }
218
219        let child_origin =
220            quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
221
222        if self.style.border.overlay {
223            cx.scene.push_quad(Quad {
224                bounds: quad_bounds,
225                background: self.style.background_color,
226                border: Default::default(),
227                corner_radius: self.style.corner_radius,
228            });
229
230            self.child.paint(child_origin, visible_bounds, cx);
231
232            cx.scene.push_layer(None);
233            cx.scene.push_quad(Quad {
234                bounds: quad_bounds,
235                background: Default::default(),
236                border: self.style.border,
237                corner_radius: self.style.corner_radius,
238            });
239            cx.scene.pop_layer();
240        } else {
241            cx.scene.push_quad(Quad {
242                bounds: quad_bounds,
243                background: self.style.background_color,
244                border: self.style.border,
245                corner_radius: self.style.corner_radius,
246            });
247
248            let child_origin = child_origin
249                + vec2f(
250                    self.style.border.left_width(),
251                    self.style.border.top_width(),
252                );
253            self.child.paint(child_origin, visible_bounds, cx);
254        }
255    }
256
257    fn dispatch_event(
258        &mut self,
259        event: &Event,
260        _: RectF,
261        _: RectF,
262        _: &mut Self::LayoutState,
263        _: &mut Self::PaintState,
264        cx: &mut EventContext,
265    ) -> bool {
266        self.child.dispatch_event(event, cx)
267    }
268
269    fn debug(
270        &self,
271        bounds: RectF,
272        _: &Self::LayoutState,
273        _: &Self::PaintState,
274        cx: &crate::DebugContext,
275    ) -> serde_json::Value {
276        json!({
277            "type": "Container",
278            "bounds": bounds.to_json(),
279            "details": self.style.to_json(),
280            "child": self.child.debug(cx),
281        })
282    }
283}
284
285impl ToJson for ContainerStyle {
286    fn to_json(&self) -> serde_json::Value {
287        json!({
288            "margin": self.margin.to_json(),
289            "padding": self.padding.to_json(),
290            "background_color": self.background_color.to_json(),
291            "border": self.border.to_json(),
292            "corner_radius": self.corner_radius,
293            "shadow": self.shadow.to_json(),
294        })
295    }
296}
297
298#[derive(Clone, Copy, Debug, Default)]
299pub struct Margin {
300    pub top: f32,
301    pub left: f32,
302    pub bottom: f32,
303    pub right: f32,
304}
305
306impl ToJson for Margin {
307    fn to_json(&self) -> serde_json::Value {
308        let mut value = json!({});
309        if self.top > 0. {
310            value["top"] = json!(self.top);
311        }
312        if self.right > 0. {
313            value["right"] = json!(self.right);
314        }
315        if self.bottom > 0. {
316            value["bottom"] = json!(self.bottom);
317        }
318        if self.left > 0. {
319            value["left"] = json!(self.left);
320        }
321        value
322    }
323}
324
325#[derive(Clone, Copy, Debug, Default)]
326pub struct Padding {
327    pub top: f32,
328    pub left: f32,
329    pub bottom: f32,
330    pub right: f32,
331}
332
333impl<'de> Deserialize<'de> for Padding {
334    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
335    where
336        D: serde::Deserializer<'de>,
337    {
338        let spacing = Spacing::deserialize(deserializer)?;
339        Ok(match spacing {
340            Spacing::Uniform(size) => Padding {
341                top: size,
342                left: size,
343                bottom: size,
344                right: size,
345            },
346            Spacing::Specific {
347                top,
348                left,
349                bottom,
350                right,
351            } => Padding {
352                top,
353                left,
354                bottom,
355                right,
356            },
357        })
358    }
359}
360
361impl<'de> Deserialize<'de> for Margin {
362    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
363    where
364        D: serde::Deserializer<'de>,
365    {
366        let spacing = Spacing::deserialize(deserializer)?;
367        Ok(match spacing {
368            Spacing::Uniform(size) => Margin {
369                top: size,
370                left: size,
371                bottom: size,
372                right: size,
373            },
374            Spacing::Specific {
375                top,
376                left,
377                bottom,
378                right,
379            } => Margin {
380                top,
381                left,
382                bottom,
383                right,
384            },
385        })
386    }
387}
388#[derive(Deserialize)]
389#[serde(untagged)]
390enum Spacing {
391    Uniform(f32),
392    Specific {
393        #[serde(default)]
394        top: f32,
395        #[serde(default)]
396        left: f32,
397        #[serde(default)]
398        bottom: f32,
399        #[serde(default)]
400        right: f32,
401    },
402}
403
404impl Padding {
405    pub fn uniform(padding: f32) -> Self {
406        Self {
407            top: padding,
408            left: padding,
409            bottom: padding,
410            right: padding,
411        }
412    }
413}
414
415impl ToJson for Padding {
416    fn to_json(&self) -> serde_json::Value {
417        let mut value = json!({});
418        if self.top > 0. {
419            value["top"] = json!(self.top);
420        }
421        if self.right > 0. {
422            value["right"] = json!(self.right);
423        }
424        if self.bottom > 0. {
425            value["bottom"] = json!(self.bottom);
426        }
427        if self.left > 0. {
428            value["left"] = json!(self.left);
429        }
430        value
431    }
432}
433
434#[derive(Clone, Copy, Debug, Default, Deserialize)]
435pub struct Shadow {
436    #[serde(default, deserialize_with = "deserialize_vec2f")]
437    offset: Vector2F,
438    #[serde(default)]
439    blur: f32,
440    #[serde(default)]
441    color: Color,
442}
443
444impl ToJson for Shadow {
445    fn to_json(&self) -> serde_json::Value {
446        json!({
447            "offset": self.offset.to_json(),
448            "blur": self.blur,
449            "color": self.color.to_json()
450        })
451    }
452}