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