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    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
 14    SizeConstraint,
 15};
 16
 17#[derive(Clone, Debug, Default, Deserialize)]
 18pub struct ContainerStyle {
 19    #[serde(default)]
 20    pub margin: Margin,
 21    #[serde(default)]
 22    pub padding: Padding,
 23    #[serde(rename = "background")]
 24    pub background_color: Option<Color>,
 25    #[serde(default)]
 26    pub border: Border,
 27    #[serde(default)]
 28    pub corner_radius: f32,
 29    #[serde(default)]
 30    pub shadow: Option<Shadow>,
 31}
 32
 33pub struct Container {
 34    child: ElementBox,
 35    style: ContainerStyle,
 36}
 37
 38impl Container {
 39    pub fn new(child: ElementBox) -> Self {
 40        Self {
 41            child,
 42            style: Default::default(),
 43        }
 44    }
 45
 46    pub fn with_style(mut self, style: &ContainerStyle) -> Self {
 47        self.style = style.clone();
 48        self
 49    }
 50
 51    pub fn with_margin_top(mut self, margin: f32) -> Self {
 52        self.style.margin.top = margin;
 53        self
 54    }
 55
 56    pub fn with_margin_left(mut self, margin: f32) -> Self {
 57        self.style.margin.left = margin;
 58        self
 59    }
 60
 61    pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
 62        self.style.padding.left = padding;
 63        self.style.padding.right = padding;
 64        self
 65    }
 66
 67    pub fn with_vertical_padding(mut self, padding: f32) -> Self {
 68        self.style.padding.top = padding;
 69        self.style.padding.bottom = padding;
 70        self
 71    }
 72
 73    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
 74        self.style.padding = Padding {
 75            top: padding,
 76            left: padding,
 77            bottom: padding,
 78            right: padding,
 79        };
 80        self
 81    }
 82
 83    pub fn with_padding_right(mut self, padding: f32) -> Self {
 84        self.style.padding.right = padding;
 85        self
 86    }
 87
 88    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
 89        self.style.padding.bottom = padding;
 90        self
 91    }
 92
 93    pub fn with_background_color(mut self, color: Color) -> Self {
 94        self.style.background_color = Some(color);
 95        self
 96    }
 97
 98    pub fn with_border(mut self, border: Border) -> Self {
 99        self.style.border = border;
100        self
101    }
102
103    pub fn with_corner_radius(mut self, radius: f32) -> Self {
104        self.style.corner_radius = radius;
105        self
106    }
107
108    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
109        self.style.shadow = Some(Shadow {
110            offset,
111            blur,
112            color,
113        });
114        self
115    }
116
117    fn margin_size(&self) -> Vector2F {
118        vec2f(
119            self.style.margin.left + self.style.margin.right,
120            self.style.margin.top + self.style.margin.bottom,
121        )
122    }
123
124    fn padding_size(&self) -> Vector2F {
125        vec2f(
126            self.style.padding.left + self.style.padding.right,
127            self.style.padding.top + self.style.padding.bottom,
128        )
129    }
130
131    fn border_size(&self) -> Vector2F {
132        let mut x = 0.0;
133        if self.style.border.left {
134            x += self.style.border.width;
135        }
136        if self.style.border.right {
137            x += self.style.border.width;
138        }
139
140        let mut y = 0.0;
141        if self.style.border.top {
142            y += self.style.border.width;
143        }
144        if self.style.border.bottom {
145            y += self.style.border.width;
146        }
147
148        vec2f(x, y)
149    }
150}
151
152impl Element for Container {
153    type LayoutState = ();
154    type PaintState = ();
155
156    fn layout(
157        &mut self,
158        constraint: SizeConstraint,
159        cx: &mut LayoutContext,
160    ) -> (Vector2F, Self::LayoutState) {
161        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
162        let child_constraint = SizeConstraint {
163            min: (constraint.min - size_buffer).max(Vector2F::zero()),
164            max: (constraint.max - size_buffer).max(Vector2F::zero()),
165        };
166        let child_size = self.child.layout(child_constraint, cx);
167        (child_size + size_buffer, ())
168    }
169
170    fn after_layout(
171        &mut self,
172        _: Vector2F,
173        _: &mut Self::LayoutState,
174        cx: &mut AfterLayoutContext,
175    ) {
176        self.child.after_layout(cx);
177    }
178
179    fn paint(
180        &mut self,
181        bounds: RectF,
182        _: &mut Self::LayoutState,
183        cx: &mut PaintContext,
184    ) -> Self::PaintState {
185        let quad_bounds = RectF::from_points(
186            bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
187            bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
188        );
189
190        if let Some(shadow) = self.style.shadow.as_ref() {
191            cx.scene.push_shadow(scene::Shadow {
192                bounds: quad_bounds + shadow.offset,
193                corner_radius: self.style.corner_radius,
194                sigma: shadow.blur,
195                color: shadow.color,
196            });
197        }
198        cx.scene.push_quad(Quad {
199            bounds: quad_bounds,
200            background: self.style.background_color,
201            border: self.style.border,
202            corner_radius: self.style.corner_radius,
203        });
204
205        let child_origin = quad_bounds.origin()
206            + vec2f(self.style.padding.left, self.style.padding.top)
207            + vec2f(
208                self.style.border.left_width(),
209                self.style.border.top_width(),
210            );
211        self.child.paint(child_origin, cx);
212    }
213
214    fn dispatch_event(
215        &mut self,
216        event: &Event,
217        _: RectF,
218        _: &mut Self::LayoutState,
219        _: &mut Self::PaintState,
220        cx: &mut EventContext,
221    ) -> bool {
222        self.child.dispatch_event(event, cx)
223    }
224
225    fn debug(
226        &self,
227        bounds: RectF,
228        _: &Self::LayoutState,
229        _: &Self::PaintState,
230        cx: &crate::DebugContext,
231    ) -> serde_json::Value {
232        json!({
233            "type": "Container",
234            "bounds": bounds.to_json(),
235            "details": self.style.to_json(),
236            "child": self.child.debug(cx),
237        })
238    }
239}
240
241impl ToJson for ContainerStyle {
242    fn to_json(&self) -> serde_json::Value {
243        json!({
244            "margin": self.margin.to_json(),
245            "padding": self.padding.to_json(),
246            "background_color": self.background_color.to_json(),
247            "border": self.border.to_json(),
248            "corner_radius": self.corner_radius,
249            "shadow": self.shadow.to_json(),
250        })
251    }
252}
253
254#[derive(Clone, Debug, Default, Deserialize)]
255pub struct Margin {
256    #[serde(default)]
257    top: f32,
258    #[serde(default)]
259    left: f32,
260    #[serde(default)]
261    bottom: f32,
262    #[serde(default)]
263    right: f32,
264}
265
266impl ToJson for Margin {
267    fn to_json(&self) -> serde_json::Value {
268        let mut value = json!({});
269        if self.top > 0. {
270            value["top"] = json!(self.top);
271        }
272        if self.right > 0. {
273            value["right"] = json!(self.right);
274        }
275        if self.bottom > 0. {
276            value["bottom"] = json!(self.bottom);
277        }
278        if self.left > 0. {
279            value["left"] = json!(self.left);
280        }
281        value
282    }
283}
284
285#[derive(Clone, Debug, Default, Deserialize)]
286pub struct Padding {
287    #[serde(default)]
288    top: f32,
289    #[serde(default)]
290    left: f32,
291    #[serde(default)]
292    bottom: f32,
293    #[serde(default)]
294    right: f32,
295}
296
297impl ToJson for Padding {
298    fn to_json(&self) -> serde_json::Value {
299        let mut value = json!({});
300        if self.top > 0. {
301            value["top"] = json!(self.top);
302        }
303        if self.right > 0. {
304            value["right"] = json!(self.right);
305        }
306        if self.bottom > 0. {
307            value["bottom"] = json!(self.bottom);
308        }
309        if self.left > 0. {
310            value["left"] = json!(self.left);
311        }
312        value
313    }
314}
315
316#[derive(Clone, Debug, Default, Deserialize)]
317pub struct Shadow {
318    #[serde(default, deserialize_with = "deserialize_vec2f")]
319    offset: Vector2F,
320    #[serde(default)]
321    blur: f32,
322    #[serde(default)]
323    color: Color,
324}
325
326impl ToJson for Shadow {
327    fn to_json(&self) -> serde_json::Value {
328        json!({
329            "offset": self.offset.to_json(),
330            "blur": self.blur,
331            "color": self.color.to_json()
332        })
333    }
334}