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