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