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, Deserialize)]
245pub struct Margin {
246    #[serde(default)]
247    top: f32,
248    #[serde(default)]
249    left: f32,
250    #[serde(default)]
251    bottom: f32,
252    #[serde(default)]
253    right: f32,
254}
255
256impl ToJson for Margin {
257    fn to_json(&self) -> serde_json::Value {
258        let mut value = json!({});
259        if self.top > 0. {
260            value["top"] = json!(self.top);
261        }
262        if self.right > 0. {
263            value["right"] = json!(self.right);
264        }
265        if self.bottom > 0. {
266            value["bottom"] = json!(self.bottom);
267        }
268        if self.left > 0. {
269            value["left"] = json!(self.left);
270        }
271        value
272    }
273}
274
275#[derive(Clone, Debug, Default, Deserialize)]
276pub struct Padding {
277    #[serde(default)]
278    top: f32,
279    #[serde(default)]
280    left: f32,
281    #[serde(default)]
282    bottom: f32,
283    #[serde(default)]
284    right: f32,
285}
286
287impl ToJson for Padding {
288    fn to_json(&self) -> serde_json::Value {
289        let mut value = json!({});
290        if self.top > 0. {
291            value["top"] = json!(self.top);
292        }
293        if self.right > 0. {
294            value["right"] = json!(self.right);
295        }
296        if self.bottom > 0. {
297            value["bottom"] = json!(self.bottom);
298        }
299        if self.left > 0. {
300            value["left"] = json!(self.left);
301        }
302        value
303    }
304}
305
306#[derive(Clone, Debug, Default, Deserialize)]
307pub struct Shadow {
308    #[serde(default, deserialize_with = "deserialize_vec2f")]
309    offset: Vector2F,
310    #[serde(default)]
311    blur: f32,
312    #[serde(default)]
313    color: Color,
314}
315
316impl ToJson for Shadow {
317    fn to_json(&self) -> serde_json::Value {
318        json!({
319            "offset": self.offset.to_json(),
320            "blur": self.blur,
321            "color": self.color.to_json()
322        })
323    }
324}