container.rs

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