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 size_buffer = self.margin_size() + self.padding_size() + self.border_size();
171        let child_constraint = SizeConstraint {
172            min: (constraint.min - size_buffer).max(Vector2F::zero()),
173            max: (constraint.max - size_buffer).max(Vector2F::zero()),
174        };
175        let child_size = self.child.layout(child_constraint, cx);
176        (child_size + size_buffer, ())
177    }
178
179    fn paint(
180        &mut self,
181        bounds: RectF,
182        visible_bounds: RectF,
183        _: &mut Self::LayoutState,
184        cx: &mut PaintContext,
185    ) -> Self::PaintState {
186        let quad_bounds = RectF::from_points(
187            bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
188            bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
189        );
190
191        if let Some(shadow) = self.style.shadow.as_ref() {
192            cx.scene.push_shadow(scene::Shadow {
193                bounds: quad_bounds + shadow.offset,
194                corner_radius: self.style.corner_radius,
195                sigma: shadow.blur,
196                color: shadow.color,
197            });
198        }
199        cx.scene.push_quad(Quad {
200            bounds: quad_bounds,
201            background: self.style.background_color,
202            border: self.style.border,
203            corner_radius: self.style.corner_radius,
204        });
205
206        let child_origin = quad_bounds.origin()
207            + vec2f(self.style.padding.left, self.style.padding.top)
208            + vec2f(
209                self.style.border.left_width(),
210                self.style.border.top_width(),
211            );
212        self.child.paint(child_origin, visible_bounds, cx);
213    }
214
215    fn dispatch_event(
216        &mut self,
217        event: &Event,
218        _: RectF,
219        _: &mut Self::LayoutState,
220        _: &mut Self::PaintState,
221        cx: &mut EventContext,
222    ) -> bool {
223        self.child.dispatch_event(event, cx)
224    }
225
226    fn debug(
227        &self,
228        bounds: RectF,
229        _: &Self::LayoutState,
230        _: &Self::PaintState,
231        cx: &crate::DebugContext,
232    ) -> serde_json::Value {
233        json!({
234            "type": "Container",
235            "bounds": bounds.to_json(),
236            "details": self.style.to_json(),
237            "child": self.child.debug(cx),
238        })
239    }
240}
241
242impl ToJson for ContainerStyle {
243    fn to_json(&self) -> serde_json::Value {
244        json!({
245            "margin": self.margin.to_json(),
246            "padding": self.padding.to_json(),
247            "background_color": self.background_color.to_json(),
248            "border": self.border.to_json(),
249            "corner_radius": self.corner_radius,
250            "shadow": self.shadow.to_json(),
251        })
252    }
253}
254
255#[derive(Clone, Copy, Debug, Default)]
256pub struct Margin {
257    pub top: f32,
258    pub left: f32,
259    pub bottom: f32,
260    pub right: f32,
261}
262
263impl ToJson for Margin {
264    fn to_json(&self) -> serde_json::Value {
265        let mut value = json!({});
266        if self.top > 0. {
267            value["top"] = json!(self.top);
268        }
269        if self.right > 0. {
270            value["right"] = json!(self.right);
271        }
272        if self.bottom > 0. {
273            value["bottom"] = json!(self.bottom);
274        }
275        if self.left > 0. {
276            value["left"] = json!(self.left);
277        }
278        value
279    }
280}
281
282#[derive(Clone, Copy, Debug, Default)]
283pub struct Padding {
284    pub top: f32,
285    pub left: f32,
286    pub bottom: f32,
287    pub right: f32,
288}
289
290impl<'de> Deserialize<'de> for Padding {
291    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292    where
293        D: serde::Deserializer<'de>,
294    {
295        let spacing = Spacing::deserialize(deserializer)?;
296        Ok(match spacing {
297            Spacing::Uniform(size) => Padding {
298                top: size,
299                left: size,
300                bottom: size,
301                right: size,
302            },
303            Spacing::Specific {
304                top,
305                left,
306                bottom,
307                right,
308            } => Padding {
309                top,
310                left,
311                bottom,
312                right,
313            },
314        })
315    }
316}
317
318impl<'de> Deserialize<'de> for Margin {
319    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
320    where
321        D: serde::Deserializer<'de>,
322    {
323        let spacing = Spacing::deserialize(deserializer)?;
324        Ok(match spacing {
325            Spacing::Uniform(size) => Margin {
326                top: size,
327                left: size,
328                bottom: size,
329                right: size,
330            },
331            Spacing::Specific {
332                top,
333                left,
334                bottom,
335                right,
336            } => Margin {
337                top,
338                left,
339                bottom,
340                right,
341            },
342        })
343    }
344}
345#[derive(Deserialize)]
346#[serde(untagged)]
347enum Spacing {
348    Uniform(f32),
349    Specific {
350        #[serde(default)]
351        top: f32,
352        #[serde(default)]
353        left: f32,
354        #[serde(default)]
355        bottom: f32,
356        #[serde(default)]
357        right: f32,
358    },
359}
360
361impl Padding {
362    pub fn uniform(padding: f32) -> Self {
363        Self {
364            top: padding,
365            left: padding,
366            bottom: padding,
367            right: padding,
368        }
369    }
370}
371
372impl ToJson for Padding {
373    fn to_json(&self) -> serde_json::Value {
374        let mut value = json!({});
375        if self.top > 0. {
376            value["top"] = json!(self.top);
377        }
378        if self.right > 0. {
379            value["right"] = json!(self.right);
380        }
381        if self.bottom > 0. {
382            value["bottom"] = json!(self.bottom);
383        }
384        if self.left > 0. {
385            value["left"] = json!(self.left);
386        }
387        value
388    }
389}
390
391#[derive(Clone, Copy, Debug, Default, Deserialize)]
392pub struct Shadow {
393    #[serde(default, deserialize_with = "deserialize_vec2f")]
394    offset: Vector2F,
395    #[serde(default)]
396    blur: f32,
397    #[serde(default)]
398    color: Color,
399}
400
401impl ToJson for Shadow {
402    fn to_json(&self) -> serde_json::Value {
403        json!({
404            "offset": self.offset.to_json(),
405            "blur": self.blur,
406            "color": self.color.to_json()
407        })
408    }
409}