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