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