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