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