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