1mod align;
2mod canvas;
3mod clipped;
4mod component;
5mod constrained_box;
6mod container;
7mod empty;
8mod expanded;
9mod flex;
10mod hook;
11mod image;
12mod keystroke_label;
13mod label;
14mod list;
15mod mouse_event_handler;
16mod overlay;
17mod resizable;
18mod stack;
19mod svg;
20mod text;
21mod tooltip;
22mod uniform_list;
23
24pub use self::{
25 align::*, canvas::*, component::*, constrained_box::*, container::*, empty::*, flex::*,
26 hook::*, image::*, keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*,
27 resizable::*, stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
28};
29pub use crate::window::ChildView;
30
31use self::{clipped::Clipped, expanded::Expanded};
32use crate::{
33 geometry::{
34 rect::RectF,
35 vector::{vec2f, Vector2F},
36 },
37 json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
38 WeakViewHandle, WindowContext,
39};
40use anyhow::{anyhow, Result};
41use collections::HashMap;
42use core::panic;
43use json::ToJson;
44use smallvec::SmallVec;
45use std::{any::Any, borrow::Cow, mem, ops::Range};
46
47pub trait Element<V: View>: 'static {
48 type LayoutState;
49 type PaintState;
50
51 fn layout(
52 &mut self,
53 constraint: SizeConstraint,
54 view: &mut V,
55 cx: &mut LayoutContext<V>,
56 ) -> (Vector2F, Self::LayoutState);
57
58 fn paint(
59 &mut self,
60 scene: &mut SceneBuilder,
61 bounds: RectF,
62 visible_bounds: RectF,
63 layout: &mut Self::LayoutState,
64 view: &mut V,
65 cx: &mut PaintContext<V>,
66 ) -> Self::PaintState;
67
68 fn rect_for_text_range(
69 &self,
70 range_utf16: Range<usize>,
71 bounds: RectF,
72 visible_bounds: RectF,
73 layout: &Self::LayoutState,
74 paint: &Self::PaintState,
75 view: &V,
76 cx: &ViewContext<V>,
77 ) -> Option<RectF>;
78
79 fn metadata(&self) -> Option<&dyn Any> {
80 None
81 }
82
83 fn debug(
84 &self,
85 bounds: RectF,
86 layout: &Self::LayoutState,
87 paint: &Self::PaintState,
88 view: &V,
89 cx: &ViewContext<V>,
90 ) -> serde_json::Value;
91
92 fn into_any(self) -> AnyElement<V>
93 where
94 Self: 'static + Sized,
95 {
96 AnyElement {
97 state: Box::new(ElementState::Init { element: self }),
98 name: None,
99 }
100 }
101
102 fn into_any_named(self, name: impl Into<Cow<'static, str>>) -> AnyElement<V>
103 where
104 Self: 'static + Sized,
105 {
106 AnyElement {
107 state: Box::new(ElementState::Init { element: self }),
108 name: Some(name.into()),
109 }
110 }
111
112 fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
113 where
114 Self: 'static + Sized,
115 {
116 RootElement {
117 element: self.into_any(),
118 view: cx.handle().downgrade(),
119 }
120 }
121
122 fn constrained(self) -> ConstrainedBox<V>
123 where
124 Self: 'static + Sized,
125 {
126 ConstrainedBox::new(self.into_any())
127 }
128
129 fn aligned(self) -> Align<V>
130 where
131 Self: 'static + Sized,
132 {
133 Align::new(self.into_any())
134 }
135
136 fn clipped(self) -> Clipped<V>
137 where
138 Self: 'static + Sized,
139 {
140 Clipped::new(self.into_any())
141 }
142
143 fn contained(self) -> Container<V>
144 where
145 Self: 'static + Sized,
146 {
147 Container::new(self.into_any())
148 }
149
150 fn expanded(self) -> Expanded<V>
151 where
152 Self: 'static + Sized,
153 {
154 Expanded::new(self.into_any())
155 }
156
157 fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
158 where
159 Self: 'static + Sized,
160 {
161 FlexItem::new(self.into_any()).flex(flex, expanded)
162 }
163
164 fn flex_float(self) -> FlexItem<V>
165 where
166 Self: 'static + Sized,
167 {
168 FlexItem::new(self.into_any()).float()
169 }
170
171 fn with_tooltip<Tag: 'static>(
172 self,
173 id: usize,
174 text: impl Into<Cow<'static, str>>,
175 action: Option<Box<dyn Action>>,
176 style: TooltipStyle,
177 cx: &mut ViewContext<V>,
178 ) -> Tooltip<V>
179 where
180 Self: 'static + Sized,
181 {
182 Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
183 }
184
185 fn resizable(
186 self,
187 side: HandleSide,
188 size: f32,
189 on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
190 ) -> Resizable<V>
191 where
192 Self: 'static + Sized,
193 {
194 Resizable::new(self.into_any(), side, size, on_resize)
195 }
196
197 fn mouse<Tag: 'static>(self, region_id: usize) -> MouseEventHandler<V>
198 where
199 Self: Sized,
200 {
201 MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
202 }
203}
204
205pub trait RenderElement {
206 fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
207}
208
209trait AnyElementState<V: View> {
210 fn layout(
211 &mut self,
212 constraint: SizeConstraint,
213 view: &mut V,
214 cx: &mut LayoutContext<V>,
215 ) -> Vector2F;
216
217 fn paint(
218 &mut self,
219 scene: &mut SceneBuilder,
220 origin: Vector2F,
221 visible_bounds: RectF,
222 view: &mut V,
223 cx: &mut ViewContext<V>,
224 );
225
226 fn rect_for_text_range(
227 &self,
228 range_utf16: Range<usize>,
229 view: &V,
230 cx: &ViewContext<V>,
231 ) -> Option<RectF>;
232
233 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
234
235 fn size(&self) -> Vector2F;
236
237 fn metadata(&self) -> Option<&dyn Any>;
238}
239
240enum ElementState<V: View, E: Element<V>> {
241 Empty,
242 Init {
243 element: E,
244 },
245 PostLayout {
246 element: E,
247 constraint: SizeConstraint,
248 size: Vector2F,
249 layout: E::LayoutState,
250 },
251 PostPaint {
252 element: E,
253 constraint: SizeConstraint,
254 bounds: RectF,
255 visible_bounds: RectF,
256 layout: E::LayoutState,
257 paint: E::PaintState,
258 },
259}
260
261impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
262 fn layout(
263 &mut self,
264 constraint: SizeConstraint,
265 view: &mut V,
266 cx: &mut LayoutContext<V>,
267 ) -> Vector2F {
268 let result;
269 *self = match mem::take(self) {
270 ElementState::Empty => unreachable!(),
271 ElementState::Init { mut element }
272 | ElementState::PostLayout { mut element, .. }
273 | ElementState::PostPaint { mut element, .. } => {
274 let (size, layout) = element.layout(constraint, view, cx);
275 debug_assert!(size.x().is_finite());
276 debug_assert!(size.y().is_finite());
277
278 result = size;
279 ElementState::PostLayout {
280 element,
281 constraint,
282 size,
283 layout,
284 }
285 }
286 };
287 result
288 }
289
290 fn paint(
291 &mut self,
292 scene: &mut SceneBuilder,
293 origin: Vector2F,
294 visible_bounds: RectF,
295 view: &mut V,
296 cx: &mut ViewContext<V>,
297 ) {
298 *self = match mem::take(self) {
299 ElementState::PostLayout {
300 mut element,
301 constraint,
302 size,
303 mut layout,
304 } => {
305 let bounds = RectF::new(origin, size);
306 let paint = element.paint(
307 scene,
308 bounds,
309 visible_bounds,
310 &mut layout,
311 view,
312 &mut PaintContext::new(cx),
313 );
314 ElementState::PostPaint {
315 element,
316 constraint,
317 bounds,
318 visible_bounds,
319 layout,
320 paint,
321 }
322 }
323 ElementState::PostPaint {
324 mut element,
325 constraint,
326 bounds,
327 mut layout,
328 ..
329 } => {
330 let bounds = RectF::new(origin, bounds.size());
331 let paint = element.paint(
332 scene,
333 bounds,
334 visible_bounds,
335 &mut layout,
336 view,
337 &mut PaintContext::new(cx),
338 );
339 ElementState::PostPaint {
340 element,
341 constraint,
342 bounds,
343 visible_bounds,
344 layout,
345 paint,
346 }
347 }
348 ElementState::Empty => panic!("invalid element lifecycle state"),
349 ElementState::Init { .. } => {
350 panic!("invalid element lifecycle state, paint called before layout")
351 }
352 }
353 }
354
355 fn rect_for_text_range(
356 &self,
357 range_utf16: Range<usize>,
358 view: &V,
359 cx: &ViewContext<V>,
360 ) -> Option<RectF> {
361 if let ElementState::PostPaint {
362 element,
363 bounds,
364 visible_bounds,
365 layout,
366 paint,
367 ..
368 } = self
369 {
370 element.rect_for_text_range(
371 range_utf16,
372 *bounds,
373 *visible_bounds,
374 layout,
375 paint,
376 view,
377 cx,
378 )
379 } else {
380 None
381 }
382 }
383
384 fn size(&self) -> Vector2F {
385 match self {
386 ElementState::Empty | ElementState::Init { .. } => {
387 panic!("invalid element lifecycle state")
388 }
389 ElementState::PostLayout { size, .. } => *size,
390 ElementState::PostPaint { bounds, .. } => bounds.size(),
391 }
392 }
393
394 fn metadata(&self) -> Option<&dyn Any> {
395 match self {
396 ElementState::Empty => unreachable!(),
397 ElementState::Init { element }
398 | ElementState::PostLayout { element, .. }
399 | ElementState::PostPaint { element, .. } => element.metadata(),
400 }
401 }
402
403 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
404 match self {
405 ElementState::PostPaint {
406 element,
407 constraint,
408 bounds,
409 visible_bounds,
410 layout,
411 paint,
412 } => {
413 let mut value = element.debug(*bounds, layout, paint, view, cx);
414 if let json::Value::Object(map) = &mut value {
415 let mut new_map: crate::json::Map<String, serde_json::Value> =
416 Default::default();
417 if let Some(typ) = map.remove("type") {
418 new_map.insert("type".into(), typ);
419 }
420 new_map.insert("constraint".into(), constraint.to_json());
421 new_map.insert("bounds".into(), bounds.to_json());
422 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
423 new_map.append(map);
424 json::Value::Object(new_map)
425 } else {
426 value
427 }
428 }
429
430 _ => panic!("invalid element lifecycle state"),
431 }
432 }
433}
434
435impl<V: View, E: Element<V>> Default for ElementState<V, E> {
436 fn default() -> Self {
437 Self::Empty
438 }
439}
440
441pub struct AnyElement<V: View> {
442 state: Box<dyn AnyElementState<V>>,
443 name: Option<Cow<'static, str>>,
444}
445
446impl<V: View> AnyElement<V> {
447 pub fn name(&self) -> Option<&str> {
448 self.name.as_deref()
449 }
450
451 pub fn metadata<T: 'static>(&self) -> Option<&T> {
452 self.state
453 .metadata()
454 .and_then(|data| data.downcast_ref::<T>())
455 }
456
457 pub fn layout(
458 &mut self,
459 constraint: SizeConstraint,
460 view: &mut V,
461 cx: &mut LayoutContext<V>,
462 ) -> Vector2F {
463 self.state.layout(constraint, view, cx)
464 }
465
466 pub fn paint(
467 &mut self,
468 scene: &mut SceneBuilder,
469 origin: Vector2F,
470 visible_bounds: RectF,
471 view: &mut V,
472 cx: &mut ViewContext<V>,
473 ) {
474 self.state.paint(scene, origin, visible_bounds, view, cx);
475 }
476
477 pub fn rect_for_text_range(
478 &self,
479 range_utf16: Range<usize>,
480 view: &V,
481 cx: &ViewContext<V>,
482 ) -> Option<RectF> {
483 self.state.rect_for_text_range(range_utf16, view, cx)
484 }
485
486 pub fn size(&self) -> Vector2F {
487 self.state.size()
488 }
489
490 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
491 let mut value = self.state.debug(view, cx);
492
493 if let Some(name) = &self.name {
494 if let json::Value::Object(map) = &mut value {
495 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
496 new_map.insert("name".into(), json::Value::String(name.to_string()));
497 new_map.append(map);
498 return json::Value::Object(new_map);
499 }
500 }
501
502 value
503 }
504
505 pub fn with_metadata<T, F, R>(&self, f: F) -> R
506 where
507 T: 'static,
508 F: FnOnce(Option<&T>) -> R,
509 {
510 f(self.state.metadata().and_then(|m| m.downcast_ref()))
511 }
512}
513
514impl<V: View> Element<V> for AnyElement<V> {
515 type LayoutState = ();
516 type PaintState = ();
517
518 fn layout(
519 &mut self,
520 constraint: SizeConstraint,
521 view: &mut V,
522 cx: &mut LayoutContext<V>,
523 ) -> (Vector2F, Self::LayoutState) {
524 let size = self.layout(constraint, view, cx);
525 (size, ())
526 }
527
528 fn paint(
529 &mut self,
530 scene: &mut SceneBuilder,
531 bounds: RectF,
532 visible_bounds: RectF,
533 _: &mut Self::LayoutState,
534 view: &mut V,
535 cx: &mut PaintContext<V>,
536 ) -> Self::PaintState {
537 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
538 }
539
540 fn rect_for_text_range(
541 &self,
542 range_utf16: Range<usize>,
543 _: RectF,
544 _: RectF,
545 _: &Self::LayoutState,
546 _: &Self::PaintState,
547 view: &V,
548 cx: &ViewContext<V>,
549 ) -> Option<RectF> {
550 self.rect_for_text_range(range_utf16, view, cx)
551 }
552
553 fn debug(
554 &self,
555 _: RectF,
556 _: &Self::LayoutState,
557 _: &Self::PaintState,
558 view: &V,
559 cx: &ViewContext<V>,
560 ) -> serde_json::Value {
561 self.debug(view, cx)
562 }
563
564 fn into_any(self) -> AnyElement<V>
565 where
566 Self: Sized,
567 {
568 self
569 }
570}
571
572pub struct RootElement<V: View> {
573 element: AnyElement<V>,
574 view: WeakViewHandle<V>,
575}
576
577impl<V: View> RootElement<V> {
578 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
579 Self { element, view }
580 }
581}
582
583pub trait AnyRootElement {
584 fn layout(
585 &mut self,
586 constraint: SizeConstraint,
587 new_parents: &mut HashMap<usize, usize>,
588 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
589 refreshing: bool,
590 cx: &mut WindowContext,
591 ) -> Result<Vector2F>;
592 fn paint(
593 &mut self,
594 scene: &mut SceneBuilder,
595 origin: Vector2F,
596 visible_bounds: RectF,
597 cx: &mut WindowContext,
598 ) -> Result<()>;
599 fn rect_for_text_range(
600 &self,
601 range_utf16: Range<usize>,
602 cx: &WindowContext,
603 ) -> Result<Option<RectF>>;
604 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
605 fn name(&self) -> Option<&str>;
606}
607
608impl<V: View> AnyRootElement for RootElement<V> {
609 fn layout(
610 &mut self,
611 constraint: SizeConstraint,
612 new_parents: &mut HashMap<usize, usize>,
613 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
614 refreshing: bool,
615 cx: &mut WindowContext,
616 ) -> Result<Vector2F> {
617 let view = self
618 .view
619 .upgrade(cx)
620 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
621 view.update(cx, |view, cx| {
622 let mut cx = LayoutContext::new(
623 cx,
624 new_parents,
625 views_to_notify_if_ancestors_change,
626 refreshing,
627 );
628 Ok(self.element.layout(constraint, view, &mut cx))
629 })
630 }
631
632 fn paint(
633 &mut self,
634 scene: &mut SceneBuilder,
635 origin: Vector2F,
636 visible_bounds: RectF,
637 cx: &mut WindowContext,
638 ) -> Result<()> {
639 let view = self
640 .view
641 .upgrade(cx)
642 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
643
644 view.update(cx, |view, cx| {
645 self.element.paint(scene, origin, visible_bounds, view, cx);
646 Ok(())
647 })
648 }
649
650 fn rect_for_text_range(
651 &self,
652 range_utf16: Range<usize>,
653 cx: &WindowContext,
654 ) -> Result<Option<RectF>> {
655 let view = self.view.upgrade(cx).ok_or_else(|| {
656 anyhow!("rect_for_text_range called on a root element for a dropped view")
657 })?;
658 let view = view.read(cx);
659 let view_context = ViewContext::immutable(cx, self.view.id());
660 Ok(self
661 .element
662 .rect_for_text_range(range_utf16, view, &view_context))
663 }
664
665 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
666 let view = self
667 .view
668 .upgrade(cx)
669 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
670 let view = view.read(cx);
671 let view_context = ViewContext::immutable(cx, self.view.id());
672 Ok(serde_json::json!({
673 "view_id": self.view.id(),
674 "view_name": V::ui_name(),
675 "view": view.debug_json(cx),
676 "element": self.element.debug(view, &view_context)
677 }))
678 }
679
680 fn name(&self) -> Option<&str> {
681 self.element.name()
682 }
683}
684
685pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
686 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
687 self.extend(children.into_iter().map(|child| child.into_any()));
688 }
689
690 fn add_child<D: Element<V>>(&mut self, child: D) {
691 self.extend(Some(child.into_any()));
692 }
693
694 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
695 self.extend(children.into_iter().map(|child| child.into_any()));
696 self
697 }
698
699 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
700 self.extend(Some(child.into_any()));
701 self
702 }
703}
704
705impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
706
707pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
708 if max_size.x().is_infinite() && max_size.y().is_infinite() {
709 size
710 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
711 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
712 } else {
713 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
714 }
715}