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