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