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