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, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle,
37 WindowContext,
38};
39use anyhow::{anyhow, Result};
40use collections::HashMap;
41use core::panic;
42use json::ToJson;
43use smallvec::SmallVec;
44use std::{
45 any::Any,
46 borrow::Cow,
47 marker::PhantomData,
48 mem,
49 ops::{Deref, DerefMut, Range},
50};
51
52pub trait Element<V: View>: 'static {
53 type LayoutState;
54 type PaintState;
55
56 fn layout(
57 &mut self,
58 constraint: SizeConstraint,
59 view: &mut V,
60 cx: &mut LayoutContext<V>,
61 ) -> (Vector2F, Self::LayoutState);
62
63 fn paint(
64 &mut self,
65 scene: &mut SceneBuilder,
66 bounds: RectF,
67 visible_bounds: RectF,
68 layout: &mut Self::LayoutState,
69 view: &mut V,
70 cx: &mut ViewContext<V>,
71 ) -> Self::PaintState;
72
73 fn rect_for_text_range(
74 &self,
75 range_utf16: Range<usize>,
76 bounds: RectF,
77 visible_bounds: RectF,
78 layout: &Self::LayoutState,
79 paint: &Self::PaintState,
80 view: &V,
81 cx: &ViewContext<V>,
82 ) -> Option<RectF>;
83
84 fn metadata(&self) -> Option<&dyn Any> {
85 None
86 }
87
88 fn debug(
89 &self,
90 bounds: RectF,
91 layout: &Self::LayoutState,
92 paint: &Self::PaintState,
93 view: &V,
94 cx: &ViewContext<V>,
95 ) -> serde_json::Value;
96
97 fn into_any(self) -> AnyElement<V>
98 where
99 Self: 'static + Sized,
100 {
101 AnyElement {
102 state: Box::new(ElementState::Init { element: self }),
103 name: None,
104 }
105 }
106
107 fn into_any_named(self, name: impl Into<Cow<'static, str>>) -> AnyElement<V>
108 where
109 Self: 'static + Sized,
110 {
111 AnyElement {
112 state: Box::new(ElementState::Init { element: self }),
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_any(),
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_any())
132 }
133
134 fn aligned(self) -> Align<V>
135 where
136 Self: 'static + Sized,
137 {
138 Align::new(self.into_any())
139 }
140
141 fn clipped(self) -> Clipped<V>
142 where
143 Self: 'static + Sized,
144 {
145 Clipped::new(self.into_any())
146 }
147
148 fn contained(self) -> Container<V>
149 where
150 Self: 'static + Sized,
151 {
152 Container::new(self.into_any())
153 }
154
155 fn expanded(self) -> Expanded<V>
156 where
157 Self: 'static + Sized,
158 {
159 Expanded::new(self.into_any())
160 }
161
162 fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
163 where
164 Self: 'static + Sized,
165 {
166 FlexItem::new(self.into_any()).flex(flex, expanded)
167 }
168
169 fn flex_float(self) -> FlexItem<V>
170 where
171 Self: 'static + Sized,
172 {
173 FlexItem::new(self.into_any()).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_any(), 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_any(),
203 element_id,
204 side,
205 handle_size,
206 initial_size,
207 cx,
208 )
209 }
210}
211
212trait AnyElementState<V: View> {
213 fn layout(
214 &mut self,
215 constraint: SizeConstraint,
216 view: &mut V,
217 cx: &mut LayoutContext<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 ElementState<V: View, E: Element<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: Element<V>> AnyElementState<V> for ElementState<V, E> {
265 fn layout(
266 &mut self,
267 constraint: SizeConstraint,
268 view: &mut V,
269 cx: &mut LayoutContext<V>,
270 ) -> Vector2F {
271 let result;
272 *self = match mem::take(self) {
273 ElementState::Empty => unreachable!(),
274 ElementState::Init { mut element }
275 | ElementState::PostLayout { mut element, .. }
276 | ElementState::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 ElementState::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 ElementState::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 ElementState::PostPaint {
311 element,
312 constraint,
313 bounds,
314 visible_bounds,
315 layout,
316 paint,
317 }
318 }
319 ElementState::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 ElementState::PostPaint {
329 element,
330 constraint,
331 bounds,
332 visible_bounds,
333 layout,
334 paint,
335 }
336 }
337 ElementState::Empty => panic!("invalid element lifecycle state"),
338 ElementState::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 ElementState::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 ElementState::Empty | ElementState::Init { .. } => {
376 panic!("invalid element lifecycle state")
377 }
378 ElementState::PostLayout { size, .. } => *size,
379 ElementState::PostPaint { bounds, .. } => bounds.size(),
380 }
381 }
382
383 fn metadata(&self) -> Option<&dyn Any> {
384 match self {
385 ElementState::Empty => unreachable!(),
386 ElementState::Init { element }
387 | ElementState::PostLayout { element, .. }
388 | ElementState::PostPaint { element, .. } => element.metadata(),
389 }
390 }
391
392 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
393 match self {
394 ElementState::PostPaint {
395 element,
396 constraint,
397 bounds,
398 visible_bounds,
399 layout,
400 paint,
401 } => {
402 let mut value = element.debug(*bounds, layout, paint, view, cx);
403 if let json::Value::Object(map) = &mut value {
404 let mut new_map: crate::json::Map<String, serde_json::Value> =
405 Default::default();
406 if let Some(typ) = map.remove("type") {
407 new_map.insert("type".into(), typ);
408 }
409 new_map.insert("constraint".into(), constraint.to_json());
410 new_map.insert("bounds".into(), bounds.to_json());
411 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
412 new_map.append(map);
413 json::Value::Object(new_map)
414 } else {
415 value
416 }
417 }
418
419 _ => panic!("invalid element lifecycle state"),
420 }
421 }
422}
423
424impl<V: View, E: Element<V>> Default for ElementState<V, E> {
425 fn default() -> Self {
426 Self::Empty
427 }
428}
429
430pub struct AnyElement<V: View> {
431 state: Box<dyn AnyElementState<V>>,
432 name: Option<Cow<'static, str>>,
433}
434
435impl<V: View> AnyElement<V> {
436 pub fn name(&self) -> Option<&str> {
437 self.name.as_deref()
438 }
439
440 pub fn metadata<T: 'static>(&self) -> Option<&T> {
441 self.state
442 .metadata()
443 .and_then(|data| data.downcast_ref::<T>())
444 }
445
446 pub fn layout(
447 &mut self,
448 constraint: SizeConstraint,
449 view: &mut V,
450 cx: &mut LayoutContext<V>,
451 ) -> Vector2F {
452 self.state.layout(constraint, view, cx)
453 }
454
455 pub fn paint(
456 &mut self,
457 scene: &mut SceneBuilder,
458 origin: Vector2F,
459 visible_bounds: RectF,
460 view: &mut V,
461 cx: &mut ViewContext<V>,
462 ) {
463 self.state.paint(scene, origin, visible_bounds, view, cx);
464 }
465
466 pub fn rect_for_text_range(
467 &self,
468 range_utf16: Range<usize>,
469 view: &V,
470 cx: &ViewContext<V>,
471 ) -> Option<RectF> {
472 self.state.rect_for_text_range(range_utf16, view, cx)
473 }
474
475 pub fn size(&self) -> Vector2F {
476 self.state.size()
477 }
478
479 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
480 let mut value = self.state.debug(view, cx);
481
482 if let Some(name) = &self.name {
483 if let json::Value::Object(map) = &mut value {
484 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
485 new_map.insert("name".into(), json::Value::String(name.to_string()));
486 new_map.append(map);
487 return json::Value::Object(new_map);
488 }
489 }
490
491 value
492 }
493
494 pub fn with_metadata<T, F, R>(&self, f: F) -> R
495 where
496 T: 'static,
497 F: FnOnce(Option<&T>) -> R,
498 {
499 f(self.state.metadata().and_then(|m| m.downcast_ref()))
500 }
501}
502
503impl<V: View> Element<V> for AnyElement<V> {
504 type LayoutState = ();
505 type PaintState = ();
506
507 fn layout(
508 &mut self,
509 constraint: SizeConstraint,
510 view: &mut V,
511 cx: &mut LayoutContext<V>,
512 ) -> (Vector2F, Self::LayoutState) {
513 let size = self.layout(constraint, view, cx);
514 (size, ())
515 }
516
517 fn paint(
518 &mut self,
519 scene: &mut SceneBuilder,
520 bounds: RectF,
521 visible_bounds: RectF,
522 _: &mut Self::LayoutState,
523 view: &mut V,
524 cx: &mut ViewContext<V>,
525 ) -> Self::PaintState {
526 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
527 }
528
529 fn rect_for_text_range(
530 &self,
531 range_utf16: Range<usize>,
532 _: RectF,
533 _: RectF,
534 _: &Self::LayoutState,
535 _: &Self::PaintState,
536 view: &V,
537 cx: &ViewContext<V>,
538 ) -> Option<RectF> {
539 self.rect_for_text_range(range_utf16, view, cx)
540 }
541
542 fn debug(
543 &self,
544 _: RectF,
545 _: &Self::LayoutState,
546 _: &Self::PaintState,
547 view: &V,
548 cx: &ViewContext<V>,
549 ) -> serde_json::Value {
550 self.debug(view, cx)
551 }
552
553 fn into_any(self) -> AnyElement<V>
554 where
555 Self: Sized,
556 {
557 self
558 }
559}
560
561pub struct RootElement<V: View> {
562 element: AnyElement<V>,
563 view: WeakViewHandle<V>,
564}
565
566impl<V: View> RootElement<V> {
567 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
568 Self { element, view }
569 }
570}
571
572pub trait Component<V: View>: 'static {
573 fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
574}
575
576pub struct ComponentHost<V: View, C: Component<V>> {
577 component: C,
578 view_type: PhantomData<V>,
579}
580
581impl<V: View, C: Component<V>> ComponentHost<V, C> {
582 pub fn new(c: C) -> Self {
583 Self {
584 component: c,
585 view_type: PhantomData,
586 }
587 }
588}
589
590impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
591 type Target = C;
592
593 fn deref(&self) -> &Self::Target {
594 &self.component
595 }
596}
597
598impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
599 fn deref_mut(&mut self) -> &mut Self::Target {
600 &mut self.component
601 }
602}
603
604impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
605 type LayoutState = AnyElement<V>;
606 type PaintState = ();
607
608 fn layout(
609 &mut self,
610 constraint: SizeConstraint,
611 view: &mut V,
612 cx: &mut LayoutContext<V>,
613 ) -> (Vector2F, AnyElement<V>) {
614 let mut element = self.component.render(view, cx);
615 let size = element.layout(constraint, view, cx);
616 (size, element)
617 }
618
619 fn paint(
620 &mut self,
621 scene: &mut SceneBuilder,
622 bounds: RectF,
623 visible_bounds: RectF,
624 element: &mut AnyElement<V>,
625 view: &mut V,
626 cx: &mut ViewContext<V>,
627 ) {
628 element.paint(scene, bounds.origin(), visible_bounds, view, cx);
629 }
630
631 fn rect_for_text_range(
632 &self,
633 range_utf16: Range<usize>,
634 _: RectF,
635 _: RectF,
636 element: &AnyElement<V>,
637 _: &(),
638 view: &V,
639 cx: &ViewContext<V>,
640 ) -> Option<RectF> {
641 element.rect_for_text_range(range_utf16, view, cx)
642 }
643
644 fn debug(
645 &self,
646 _: RectF,
647 element: &AnyElement<V>,
648 _: &(),
649 view: &V,
650 cx: &ViewContext<V>,
651 ) -> serde_json::Value {
652 element.debug(view, cx)
653 }
654}
655
656pub trait AnyRootElement {
657 fn layout(
658 &mut self,
659 constraint: SizeConstraint,
660 new_parents: &mut HashMap<usize, usize>,
661 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
662 refreshing: bool,
663 cx: &mut WindowContext,
664 ) -> Result<Vector2F>;
665 fn paint(
666 &mut self,
667 scene: &mut SceneBuilder,
668 origin: Vector2F,
669 visible_bounds: RectF,
670 cx: &mut WindowContext,
671 ) -> Result<()>;
672 fn rect_for_text_range(
673 &self,
674 range_utf16: Range<usize>,
675 cx: &WindowContext,
676 ) -> Result<Option<RectF>>;
677 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
678 fn name(&self) -> Option<&str>;
679}
680
681impl<V: View> AnyRootElement for RootElement<V> {
682 fn layout(
683 &mut self,
684 constraint: SizeConstraint,
685 new_parents: &mut HashMap<usize, usize>,
686 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
687 refreshing: bool,
688 cx: &mut WindowContext,
689 ) -> Result<Vector2F> {
690 let view = self
691 .view
692 .upgrade(cx)
693 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
694 view.update(cx, |view, cx| {
695 let mut cx = LayoutContext::new(
696 cx,
697 new_parents,
698 views_to_notify_if_ancestors_change,
699 refreshing,
700 );
701 Ok(self.element.layout(constraint, view, &mut cx))
702 })
703 }
704
705 fn paint(
706 &mut self,
707 scene: &mut SceneBuilder,
708 origin: Vector2F,
709 visible_bounds: RectF,
710 cx: &mut WindowContext,
711 ) -> Result<()> {
712 let view = self
713 .view
714 .upgrade(cx)
715 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
716
717 view.update(cx, |view, cx| {
718 self.element.paint(scene, origin, visible_bounds, view, cx);
719 Ok(())
720 })
721 }
722
723 fn rect_for_text_range(
724 &self,
725 range_utf16: Range<usize>,
726 cx: &WindowContext,
727 ) -> Result<Option<RectF>> {
728 let view = self.view.upgrade(cx).ok_or_else(|| {
729 anyhow!("rect_for_text_range called on a root element for a dropped view")
730 })?;
731 let view = view.read(cx);
732 let view_context = ViewContext::immutable(cx, self.view.id());
733 Ok(self
734 .element
735 .rect_for_text_range(range_utf16, view, &view_context))
736 }
737
738 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
739 let view = self
740 .view
741 .upgrade(cx)
742 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
743 let view = view.read(cx);
744 let view_context = ViewContext::immutable(cx, self.view.id());
745 Ok(serde_json::json!({
746 "view_id": self.view.id(),
747 "view_name": V::ui_name(),
748 "view": view.debug_json(cx),
749 "element": self.element.debug(view, &view_context)
750 }))
751 }
752
753 fn name(&self) -> Option<&str> {
754 self.element.name()
755 }
756}
757
758pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
759 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
760 self.extend(children.into_iter().map(|child| child.into_any()));
761 }
762
763 fn add_child<D: Element<V>>(&mut self, child: D) {
764 self.extend(Some(child.into_any()));
765 }
766
767 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
768 self.extend(children.into_iter().map(|child| child.into_any()));
769 self
770 }
771
772 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
773 self.extend(Some(child.into_any()));
774 self
775 }
776}
777
778impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
779
780pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
781 if max_size.x().is_infinite() && max_size.y().is_infinite() {
782 size
783 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
784 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
785 } else {
786 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
787 }
788}