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