1use std::ops::Range;
2
3use crate::{
4 color::Color,
5 geometry::{
6 deserialize_vec2f,
7 rect::RectF,
8 vector::{vec2f, Vector2F},
9 },
10 json::ToJson,
11 platform::CursorStyle,
12 presenter::MeasurementContext,
13 scene::{self, Border, CursorRegion, Quad},
14 Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
15};
16use serde::Deserialize;
17use serde_json::json;
18
19#[derive(Clone, Copy, Debug, Default, Deserialize)]
20pub struct ContainerStyle {
21 #[serde(default)]
22 pub margin: Margin,
23 #[serde(default)]
24 pub padding: Padding,
25 #[serde(rename = "background")]
26 pub background_color: Option<Color>,
27 #[serde(default)]
28 pub border: Border,
29 #[serde(default)]
30 pub corner_radius: f32,
31 #[serde(default)]
32 pub shadow: Option<Shadow>,
33 #[serde(default)]
34 pub cursor: Option<CursorStyle>,
35}
36
37pub struct Container {
38 child: ElementBox,
39 style: ContainerStyle,
40}
41
42impl Container {
43 pub fn new(child: ElementBox) -> Self {
44 Self {
45 child,
46 style: Default::default(),
47 }
48 }
49
50 pub fn with_style(mut self, style: ContainerStyle) -> Self {
51 self.style = style;
52 self
53 }
54
55 pub fn with_margin_top(mut self, margin: f32) -> Self {
56 self.style.margin.top = margin;
57 self
58 }
59
60 pub fn with_margin_bottom(mut self, margin: f32) -> Self {
61 self.style.margin.bottom = margin;
62 self
63 }
64
65 pub fn with_margin_left(mut self, margin: f32) -> Self {
66 self.style.margin.left = margin;
67 self
68 }
69
70 pub fn with_margin_right(mut self, margin: f32) -> Self {
71 self.style.margin.right = margin;
72 self
73 }
74
75 pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
76 self.style.padding.left = padding;
77 self.style.padding.right = padding;
78 self
79 }
80
81 pub fn with_vertical_padding(mut self, padding: f32) -> Self {
82 self.style.padding.top = padding;
83 self.style.padding.bottom = padding;
84 self
85 }
86
87 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
88 self.style.padding = Padding {
89 top: padding,
90 left: padding,
91 bottom: padding,
92 right: padding,
93 };
94 self
95 }
96
97 pub fn with_padding_left(mut self, padding: f32) -> Self {
98 self.style.padding.left = padding;
99 self
100 }
101
102 pub fn with_padding_right(mut self, padding: f32) -> Self {
103 self.style.padding.right = padding;
104 self
105 }
106
107 pub fn with_padding_bottom(mut self, padding: f32) -> Self {
108 self.style.padding.bottom = padding;
109 self
110 }
111
112 pub fn with_background_color(mut self, color: Color) -> Self {
113 self.style.background_color = Some(color);
114 self
115 }
116
117 pub fn with_border(mut self, border: Border) -> Self {
118 self.style.border = border;
119 self
120 }
121
122 pub fn with_corner_radius(mut self, radius: f32) -> Self {
123 self.style.corner_radius = radius;
124 self
125 }
126
127 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
128 self.style.shadow = Some(Shadow {
129 offset,
130 blur,
131 color,
132 });
133 self
134 }
135
136 pub fn with_cursor(mut self, style: CursorStyle) -> Self {
137 self.style.cursor = Some(style);
138 self
139 }
140
141 fn margin_size(&self) -> Vector2F {
142 vec2f(
143 self.style.margin.left + self.style.margin.right,
144 self.style.margin.top + self.style.margin.bottom,
145 )
146 }
147
148 fn padding_size(&self) -> Vector2F {
149 vec2f(
150 self.style.padding.left + self.style.padding.right,
151 self.style.padding.top + self.style.padding.bottom,
152 )
153 }
154
155 fn border_size(&self) -> Vector2F {
156 let mut x = 0.0;
157 if self.style.border.left {
158 x += self.style.border.width;
159 }
160 if self.style.border.right {
161 x += self.style.border.width;
162 }
163
164 let mut y = 0.0;
165 if self.style.border.top {
166 y += self.style.border.width;
167 }
168 if self.style.border.bottom {
169 y += self.style.border.width;
170 }
171
172 vec2f(x, y)
173 }
174}
175
176impl Element for Container {
177 type LayoutState = ();
178 type PaintState = ();
179
180 fn layout(
181 &mut self,
182 constraint: SizeConstraint,
183 cx: &mut LayoutContext,
184 ) -> (Vector2F, Self::LayoutState) {
185 let mut size_buffer = self.margin_size() + self.padding_size();
186 if !self.style.border.overlay {
187 size_buffer += self.border_size();
188 }
189 let child_constraint = SizeConstraint {
190 min: (constraint.min - size_buffer).max(Vector2F::zero()),
191 max: (constraint.max - size_buffer).max(Vector2F::zero()),
192 };
193 let child_size = self.child.layout(child_constraint, cx);
194 (child_size + size_buffer, ())
195 }
196
197 fn paint(
198 &mut self,
199 bounds: RectF,
200 visible_bounds: RectF,
201 _: &mut Self::LayoutState,
202 cx: &mut PaintContext,
203 ) -> Self::PaintState {
204 let quad_bounds = RectF::from_points(
205 bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
206 bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
207 );
208
209 if let Some(shadow) = self.style.shadow.as_ref() {
210 cx.scene.push_shadow(scene::Shadow {
211 bounds: quad_bounds + shadow.offset,
212 corner_radius: self.style.corner_radius,
213 sigma: shadow.blur,
214 color: shadow.color,
215 });
216 }
217
218 if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
219 if let Some(style) = self.style.cursor {
220 cx.scene.push_cursor_region(CursorRegion {
221 bounds: hit_bounds,
222 style,
223 });
224 }
225 }
226
227 let child_origin =
228 quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
229
230 if self.style.border.overlay {
231 cx.scene.push_quad(Quad {
232 bounds: quad_bounds,
233 background: self.style.background_color,
234 border: Default::default(),
235 corner_radius: self.style.corner_radius,
236 });
237
238 self.child.paint(child_origin, visible_bounds, cx);
239
240 cx.scene.push_layer(None);
241 cx.scene.push_quad(Quad {
242 bounds: quad_bounds,
243 background: Default::default(),
244 border: self.style.border,
245 corner_radius: self.style.corner_radius,
246 });
247 cx.scene.pop_layer();
248 } else {
249 cx.scene.push_quad(Quad {
250 bounds: quad_bounds,
251 background: self.style.background_color,
252 border: self.style.border,
253 corner_radius: self.style.corner_radius,
254 });
255
256 let child_origin = child_origin
257 + vec2f(
258 self.style.border.left_width(),
259 self.style.border.top_width(),
260 );
261 self.child.paint(child_origin, visible_bounds, cx);
262 }
263 }
264
265 fn dispatch_event(
266 &mut self,
267 event: &Event,
268 _: RectF,
269 _: RectF,
270 _: &mut Self::LayoutState,
271 _: &mut Self::PaintState,
272 cx: &mut EventContext,
273 ) -> bool {
274 self.child.dispatch_event(event, cx)
275 }
276
277 fn rect_for_text_range(
278 &self,
279 range_utf16: Range<usize>,
280 _: RectF,
281 _: RectF,
282 _: &Self::LayoutState,
283 _: &Self::PaintState,
284 cx: &MeasurementContext,
285 ) -> Option<RectF> {
286 self.child.rect_for_text_range(range_utf16, cx)
287 }
288
289 fn debug(
290 &self,
291 bounds: RectF,
292 _: &Self::LayoutState,
293 _: &Self::PaintState,
294 cx: &crate::DebugContext,
295 ) -> serde_json::Value {
296 json!({
297 "type": "Container",
298 "bounds": bounds.to_json(),
299 "details": self.style.to_json(),
300 "child": self.child.debug(cx),
301 })
302 }
303}
304
305impl ToJson for ContainerStyle {
306 fn to_json(&self) -> serde_json::Value {
307 json!({
308 "margin": self.margin.to_json(),
309 "padding": self.padding.to_json(),
310 "background_color": self.background_color.to_json(),
311 "border": self.border.to_json(),
312 "corner_radius": self.corner_radius,
313 "shadow": self.shadow.to_json(),
314 })
315 }
316}
317
318#[derive(Clone, Copy, Debug, Default)]
319pub struct Margin {
320 pub top: f32,
321 pub left: f32,
322 pub bottom: f32,
323 pub right: f32,
324}
325
326impl ToJson for Margin {
327 fn to_json(&self) -> serde_json::Value {
328 let mut value = json!({});
329 if self.top > 0. {
330 value["top"] = json!(self.top);
331 }
332 if self.right > 0. {
333 value["right"] = json!(self.right);
334 }
335 if self.bottom > 0. {
336 value["bottom"] = json!(self.bottom);
337 }
338 if self.left > 0. {
339 value["left"] = json!(self.left);
340 }
341 value
342 }
343}
344
345#[derive(Clone, Copy, Debug, Default)]
346pub struct Padding {
347 pub top: f32,
348 pub left: f32,
349 pub bottom: f32,
350 pub right: f32,
351}
352
353impl<'de> Deserialize<'de> for Padding {
354 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355 where
356 D: serde::Deserializer<'de>,
357 {
358 let spacing = Spacing::deserialize(deserializer)?;
359 Ok(match spacing {
360 Spacing::Uniform(size) => Padding {
361 top: size,
362 left: size,
363 bottom: size,
364 right: size,
365 },
366 Spacing::Specific {
367 top,
368 left,
369 bottom,
370 right,
371 } => Padding {
372 top,
373 left,
374 bottom,
375 right,
376 },
377 })
378 }
379}
380
381impl<'de> Deserialize<'de> for Margin {
382 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
383 where
384 D: serde::Deserializer<'de>,
385 {
386 let spacing = Spacing::deserialize(deserializer)?;
387 Ok(match spacing {
388 Spacing::Uniform(size) => Margin {
389 top: size,
390 left: size,
391 bottom: size,
392 right: size,
393 },
394 Spacing::Specific {
395 top,
396 left,
397 bottom,
398 right,
399 } => Margin {
400 top,
401 left,
402 bottom,
403 right,
404 },
405 })
406 }
407}
408#[derive(Deserialize)]
409#[serde(untagged)]
410enum Spacing {
411 Uniform(f32),
412 Specific {
413 #[serde(default)]
414 top: f32,
415 #[serde(default)]
416 left: f32,
417 #[serde(default)]
418 bottom: f32,
419 #[serde(default)]
420 right: f32,
421 },
422}
423
424impl Padding {
425 pub fn uniform(padding: f32) -> Self {
426 Self {
427 top: padding,
428 left: padding,
429 bottom: padding,
430 right: padding,
431 }
432 }
433}
434
435impl ToJson for Padding {
436 fn to_json(&self) -> serde_json::Value {
437 let mut value = json!({});
438 if self.top > 0. {
439 value["top"] = json!(self.top);
440 }
441 if self.right > 0. {
442 value["right"] = json!(self.right);
443 }
444 if self.bottom > 0. {
445 value["bottom"] = json!(self.bottom);
446 }
447 if self.left > 0. {
448 value["left"] = json!(self.left);
449 }
450 value
451 }
452}
453
454#[derive(Clone, Copy, Debug, Default, Deserialize)]
455pub struct Shadow {
456 #[serde(default, deserialize_with = "deserialize_vec2f")]
457 offset: Vector2F,
458 #[serde(default)]
459 blur: f32,
460 #[serde(default)]
461 color: Color,
462}
463
464impl ToJson for Shadow {
465 fn to_json(&self) -> serde_json::Value {
466 json!({
467 "offset": self.offset.to_json(),
468 "blur": self.blur,
469 "color": self.color.to_json()
470 })
471 }
472}