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