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