container.rs

  1use pathfinder_geometry::rect::RectF;
  2use serde_json::json;
  3
  4use crate::{
  5    color::ColorU,
  6    geometry::vector::{vec2f, Vector2F},
  7    json::ToJson,
  8    scene::{self, Border, Quad},
  9    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
 10    SizeConstraint,
 11};
 12
 13#[derive(Clone, Debug, Default)]
 14pub struct ContainerStyle {
 15    margin: Margin,
 16    padding: Padding,
 17    background_color: Option<ColorU>,
 18    border: Border,
 19    corner_radius: f32,
 20    shadow: Option<Shadow>,
 21}
 22
 23pub struct Container {
 24    child: ElementBox,
 25    style: ContainerStyle,
 26}
 27
 28impl Container {
 29    pub fn new(child: ElementBox) -> Self {
 30        Self {
 31            child,
 32            style: Default::default(),
 33        }
 34    }
 35
 36    pub fn with_style(mut self, style: &ContainerStyle) -> Self {
 37        self.style = style.clone();
 38        self
 39    }
 40
 41    pub fn with_margin_top(mut self, margin: f32) -> Self {
 42        self.style.margin.top = margin;
 43        self
 44    }
 45
 46    pub fn with_margin_left(mut self, margin: f32) -> Self {
 47        self.style.margin.left = margin;
 48        self
 49    }
 50
 51    pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
 52        self.style.padding.left = padding;
 53        self.style.padding.right = padding;
 54        self
 55    }
 56
 57    pub fn with_vertical_padding(mut self, padding: f32) -> Self {
 58        self.style.padding.top = padding;
 59        self.style.padding.bottom = padding;
 60        self
 61    }
 62
 63    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
 64        self.style.padding = Padding {
 65            top: padding,
 66            left: padding,
 67            bottom: padding,
 68            right: padding,
 69        };
 70        self
 71    }
 72
 73    pub fn with_padding_right(mut self, padding: f32) -> Self {
 74        self.style.padding.right = padding;
 75        self
 76    }
 77
 78    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
 79        self.style.padding.bottom = padding;
 80        self
 81    }
 82
 83    pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
 84        self.style.background_color = Some(color.into());
 85        self
 86    }
 87
 88    pub fn with_border(mut self, border: Border) -> Self {
 89        self.style.border = border;
 90        self
 91    }
 92
 93    pub fn with_corner_radius(mut self, radius: f32) -> Self {
 94        self.style.corner_radius = radius;
 95        self
 96    }
 97
 98    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
 99        self.style.shadow = Some(Shadow {
100            offset,
101            blur,
102            color: color.into(),
103        });
104        self
105    }
106
107    fn margin_size(&self) -> Vector2F {
108        vec2f(
109            self.style.margin.left + self.style.margin.right,
110            self.style.margin.top + self.style.margin.bottom,
111        )
112    }
113
114    fn padding_size(&self) -> Vector2F {
115        vec2f(
116            self.style.padding.left + self.style.padding.right,
117            self.style.padding.top + self.style.padding.bottom,
118        )
119    }
120
121    fn border_size(&self) -> Vector2F {
122        let mut x = 0.0;
123        if self.style.border.left {
124            x += self.style.border.width;
125        }
126        if self.style.border.right {
127            x += self.style.border.width;
128        }
129
130        let mut y = 0.0;
131        if self.style.border.top {
132            y += self.style.border.width;
133        }
134        if self.style.border.bottom {
135            y += self.style.border.width;
136        }
137
138        vec2f(x, y)
139    }
140}
141
142impl Element for Container {
143    type LayoutState = ();
144    type PaintState = ();
145
146    fn layout(
147        &mut self,
148        constraint: SizeConstraint,
149        cx: &mut LayoutContext,
150    ) -> (Vector2F, Self::LayoutState) {
151        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
152        let child_constraint = SizeConstraint {
153            min: (constraint.min - size_buffer).max(Vector2F::zero()),
154            max: (constraint.max - size_buffer).max(Vector2F::zero()),
155        };
156        let child_size = self.child.layout(child_constraint, cx);
157        (child_size + size_buffer, ())
158    }
159
160    fn after_layout(
161        &mut self,
162        _: Vector2F,
163        _: &mut Self::LayoutState,
164        cx: &mut AfterLayoutContext,
165    ) {
166        self.child.after_layout(cx);
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)]
245pub struct Margin {
246    top: f32,
247    left: f32,
248    bottom: f32,
249    right: f32,
250}
251
252impl ToJson for Margin {
253    fn to_json(&self) -> serde_json::Value {
254        let mut value = json!({});
255        if self.top > 0. {
256            value["top"] = json!(self.top);
257        }
258        if self.right > 0. {
259            value["right"] = json!(self.right);
260        }
261        if self.bottom > 0. {
262            value["bottom"] = json!(self.bottom);
263        }
264        if self.left > 0. {
265            value["left"] = json!(self.left);
266        }
267        value
268    }
269}
270
271#[derive(Clone, Debug, Default)]
272pub struct Padding {
273    top: f32,
274    left: f32,
275    bottom: f32,
276    right: f32,
277}
278
279impl ToJson for Padding {
280    fn to_json(&self) -> serde_json::Value {
281        let mut value = json!({});
282        if self.top > 0. {
283            value["top"] = json!(self.top);
284        }
285        if self.right > 0. {
286            value["right"] = json!(self.right);
287        }
288        if self.bottom > 0. {
289            value["bottom"] = json!(self.bottom);
290        }
291        if self.left > 0. {
292            value["left"] = json!(self.left);
293        }
294        value
295    }
296}
297
298#[derive(Clone, Debug, Default)]
299pub struct Shadow {
300    offset: Vector2F,
301    blur: f32,
302    color: ColorU,
303}
304
305impl ToJson for Shadow {
306    fn to_json(&self) -> serde_json::Value {
307        json!({
308            "offset": self.offset.to_json(),
309            "blur": self.blur,
310            "color": self.color.to_json()
311        })
312    }
313}