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