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