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