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 resizable(
191 self,
192 side: HandleSide,
193 size: f32,
194 on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
195 ) -> Resizable<V>
196 where
197 Self: 'static + Sized,
198 {
199 Resizable::new(self.into_any(), side, size, on_resize)
200 }
201}
202
203trait AnyElementState<V: View> {
204 fn layout(
205 &mut self,
206 constraint: SizeConstraint,
207 view: &mut V,
208 cx: &mut LayoutContext<V>,
209 ) -> Vector2F;
210
211 fn paint(
212 &mut self,
213 scene: &mut SceneBuilder,
214 origin: Vector2F,
215 visible_bounds: RectF,
216 view: &mut V,
217 cx: &mut ViewContext<V>,
218 );
219
220 fn rect_for_text_range(
221 &self,
222 range_utf16: Range<usize>,
223 view: &V,
224 cx: &ViewContext<V>,
225 ) -> Option<RectF>;
226
227 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
228
229 fn size(&self) -> Vector2F;
230
231 fn metadata(&self) -> Option<&dyn Any>;
232}
233
234enum ElementState<V: View, E: Element<V>> {
235 Empty,
236 Init {
237 element: E,
238 },
239 PostLayout {
240 element: E,
241 constraint: SizeConstraint,
242 size: Vector2F,
243 layout: E::LayoutState,
244 },
245 PostPaint {
246 element: E,
247 constraint: SizeConstraint,
248 bounds: RectF,
249 visible_bounds: RectF,
250 layout: E::LayoutState,
251 paint: E::PaintState,
252 },
253}
254
255impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
256 fn layout(
257 &mut self,
258 constraint: SizeConstraint,
259 view: &mut V,
260 cx: &mut LayoutContext<V>,
261 ) -> Vector2F {
262 let result;
263 *self = match mem::take(self) {
264 ElementState::Empty => unreachable!(),
265 ElementState::Init { mut element }
266 | ElementState::PostLayout { mut element, .. }
267 | ElementState::PostPaint { mut element, .. } => {
268 let (size, layout) = element.layout(constraint, view, cx);
269 debug_assert!(size.x().is_finite());
270 debug_assert!(size.y().is_finite());
271
272 result = size;
273 ElementState::PostLayout {
274 element,
275 constraint,
276 size,
277 layout,
278 }
279 }
280 };
281 result
282 }
283
284 fn paint(
285 &mut self,
286 scene: &mut SceneBuilder,
287 origin: Vector2F,
288 visible_bounds: RectF,
289 view: &mut V,
290 cx: &mut ViewContext<V>,
291 ) {
292 *self = match mem::take(self) {
293 ElementState::PostLayout {
294 mut element,
295 constraint,
296 size,
297 mut layout,
298 } => {
299 let bounds = RectF::new(origin, size);
300 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
301 ElementState::PostPaint {
302 element,
303 constraint,
304 bounds,
305 visible_bounds,
306 layout,
307 paint,
308 }
309 }
310 ElementState::PostPaint {
311 mut element,
312 constraint,
313 bounds,
314 mut layout,
315 ..
316 } => {
317 let bounds = RectF::new(origin, bounds.size());
318 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
319 ElementState::PostPaint {
320 element,
321 constraint,
322 bounds,
323 visible_bounds,
324 layout,
325 paint,
326 }
327 }
328 ElementState::Empty => panic!("invalid element lifecycle state"),
329 ElementState::Init { .. } => {
330 panic!("invalid element lifecycle state, paint called before layout")
331 }
332 }
333 }
334
335 fn rect_for_text_range(
336 &self,
337 range_utf16: Range<usize>,
338 view: &V,
339 cx: &ViewContext<V>,
340 ) -> Option<RectF> {
341 if let ElementState::PostPaint {
342 element,
343 bounds,
344 visible_bounds,
345 layout,
346 paint,
347 ..
348 } = self
349 {
350 element.rect_for_text_range(
351 range_utf16,
352 *bounds,
353 *visible_bounds,
354 layout,
355 paint,
356 view,
357 cx,
358 )
359 } else {
360 None
361 }
362 }
363
364 fn size(&self) -> Vector2F {
365 match self {
366 ElementState::Empty | ElementState::Init { .. } => {
367 panic!("invalid element lifecycle state")
368 }
369 ElementState::PostLayout { size, .. } => *size,
370 ElementState::PostPaint { bounds, .. } => bounds.size(),
371 }
372 }
373
374 fn metadata(&self) -> Option<&dyn Any> {
375 match self {
376 ElementState::Empty => unreachable!(),
377 ElementState::Init { element }
378 | ElementState::PostLayout { element, .. }
379 | ElementState::PostPaint { element, .. } => element.metadata(),
380 }
381 }
382
383 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
384 match self {
385 ElementState::PostPaint {
386 element,
387 constraint,
388 bounds,
389 visible_bounds,
390 layout,
391 paint,
392 } => {
393 let mut value = element.debug(*bounds, layout, paint, view, cx);
394 if let json::Value::Object(map) = &mut value {
395 let mut new_map: crate::json::Map<String, serde_json::Value> =
396 Default::default();
397 if let Some(typ) = map.remove("type") {
398 new_map.insert("type".into(), typ);
399 }
400 new_map.insert("constraint".into(), constraint.to_json());
401 new_map.insert("bounds".into(), bounds.to_json());
402 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
403 new_map.append(map);
404 json::Value::Object(new_map)
405 } else {
406 value
407 }
408 }
409
410 _ => panic!("invalid element lifecycle state"),
411 }
412 }
413}
414
415impl<V: View, E: Element<V>> Default for ElementState<V, E> {
416 fn default() -> Self {
417 Self::Empty
418 }
419}
420
421pub struct AnyElement<V: View> {
422 state: Box<dyn AnyElementState<V>>,
423 name: Option<Cow<'static, str>>,
424}
425
426impl<V: View> AnyElement<V> {
427 pub fn name(&self) -> Option<&str> {
428 self.name.as_deref()
429 }
430
431 pub fn metadata<T: 'static>(&self) -> Option<&T> {
432 self.state
433 .metadata()
434 .and_then(|data| data.downcast_ref::<T>())
435 }
436
437 pub fn layout(
438 &mut self,
439 constraint: SizeConstraint,
440 view: &mut V,
441 cx: &mut LayoutContext<V>,
442 ) -> Vector2F {
443 self.state.layout(constraint, view, cx)
444 }
445
446 pub fn paint(
447 &mut self,
448 scene: &mut SceneBuilder,
449 origin: Vector2F,
450 visible_bounds: RectF,
451 view: &mut V,
452 cx: &mut ViewContext<V>,
453 ) {
454 self.state.paint(scene, origin, visible_bounds, view, cx);
455 }
456
457 pub fn rect_for_text_range(
458 &self,
459 range_utf16: Range<usize>,
460 view: &V,
461 cx: &ViewContext<V>,
462 ) -> Option<RectF> {
463 self.state.rect_for_text_range(range_utf16, view, cx)
464 }
465
466 pub fn size(&self) -> Vector2F {
467 self.state.size()
468 }
469
470 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
471 let mut value = self.state.debug(view, cx);
472
473 if let Some(name) = &self.name {
474 if let json::Value::Object(map) = &mut value {
475 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
476 new_map.insert("name".into(), json::Value::String(name.to_string()));
477 new_map.append(map);
478 return json::Value::Object(new_map);
479 }
480 }
481
482 value
483 }
484
485 pub fn with_metadata<T, F, R>(&self, f: F) -> R
486 where
487 T: 'static,
488 F: FnOnce(Option<&T>) -> R,
489 {
490 f(self.state.metadata().and_then(|m| m.downcast_ref()))
491 }
492}
493
494impl<V: View> Element<V> for AnyElement<V> {
495 type LayoutState = ();
496 type PaintState = ();
497
498 fn layout(
499 &mut self,
500 constraint: SizeConstraint,
501 view: &mut V,
502 cx: &mut LayoutContext<V>,
503 ) -> (Vector2F, Self::LayoutState) {
504 let size = self.layout(constraint, view, cx);
505 (size, ())
506 }
507
508 fn paint(
509 &mut self,
510 scene: &mut SceneBuilder,
511 bounds: RectF,
512 visible_bounds: RectF,
513 _: &mut Self::LayoutState,
514 view: &mut V,
515 cx: &mut ViewContext<V>,
516 ) -> Self::PaintState {
517 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
518 }
519
520 fn rect_for_text_range(
521 &self,
522 range_utf16: Range<usize>,
523 _: RectF,
524 _: RectF,
525 _: &Self::LayoutState,
526 _: &Self::PaintState,
527 view: &V,
528 cx: &ViewContext<V>,
529 ) -> Option<RectF> {
530 self.rect_for_text_range(range_utf16, view, cx)
531 }
532
533 fn debug(
534 &self,
535 _: RectF,
536 _: &Self::LayoutState,
537 _: &Self::PaintState,
538 view: &V,
539 cx: &ViewContext<V>,
540 ) -> serde_json::Value {
541 self.debug(view, cx)
542 }
543
544 fn into_any(self) -> AnyElement<V>
545 where
546 Self: Sized,
547 {
548 self
549 }
550}
551
552pub struct RootElement<V: View> {
553 element: AnyElement<V>,
554 view: WeakViewHandle<V>,
555}
556
557impl<V: View> RootElement<V> {
558 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
559 Self { element, view }
560 }
561}
562
563pub trait Component<V: View>: 'static {
564 fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
565}
566
567pub struct ComponentHost<V: View, C: Component<V>> {
568 component: C,
569 view_type: PhantomData<V>,
570}
571
572impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
573 type Target = C;
574
575 fn deref(&self) -> &Self::Target {
576 &self.component
577 }
578}
579
580impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
581 fn deref_mut(&mut self) -> &mut Self::Target {
582 &mut self.component
583 }
584}
585
586impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
587 type LayoutState = AnyElement<V>;
588 type PaintState = ();
589
590 fn layout(
591 &mut self,
592 constraint: SizeConstraint,
593 view: &mut V,
594 cx: &mut LayoutContext<V>,
595 ) -> (Vector2F, AnyElement<V>) {
596 let mut element = self.component.render(view, cx);
597 let size = element.layout(constraint, view, cx);
598 (size, element)
599 }
600
601 fn paint(
602 &mut self,
603 scene: &mut SceneBuilder,
604 bounds: RectF,
605 visible_bounds: RectF,
606 element: &mut AnyElement<V>,
607 view: &mut V,
608 cx: &mut ViewContext<V>,
609 ) {
610 element.paint(scene, bounds.origin(), visible_bounds, view, cx);
611 }
612
613 fn rect_for_text_range(
614 &self,
615 range_utf16: Range<usize>,
616 _: RectF,
617 _: RectF,
618 element: &AnyElement<V>,
619 _: &(),
620 view: &V,
621 cx: &ViewContext<V>,
622 ) -> Option<RectF> {
623 element.rect_for_text_range(range_utf16, view, cx)
624 }
625
626 fn debug(
627 &self,
628 _: RectF,
629 element: &AnyElement<V>,
630 _: &(),
631 view: &V,
632 cx: &ViewContext<V>,
633 ) -> serde_json::Value {
634 element.debug(view, cx)
635 }
636}
637
638pub trait AnyRootElement {
639 fn layout(
640 &mut self,
641 constraint: SizeConstraint,
642 new_parents: &mut HashMap<usize, usize>,
643 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
644 refreshing: bool,
645 cx: &mut WindowContext,
646 ) -> 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(
665 &mut self,
666 constraint: SizeConstraint,
667 new_parents: &mut HashMap<usize, usize>,
668 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
669 refreshing: bool,
670 cx: &mut WindowContext,
671 ) -> Result<Vector2F> {
672 let view = self
673 .view
674 .upgrade(cx)
675 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
676 view.update(cx, |view, cx| {
677 let mut cx = LayoutContext::new(
678 cx,
679 new_parents,
680 views_to_notify_if_ancestors_change,
681 refreshing,
682 );
683 Ok(self.element.layout(constraint, view, &mut cx))
684 })
685 }
686
687 fn paint(
688 &mut self,
689 scene: &mut SceneBuilder,
690 origin: Vector2F,
691 visible_bounds: RectF,
692 cx: &mut WindowContext,
693 ) -> Result<()> {
694 let view = self
695 .view
696 .upgrade(cx)
697 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
698
699 view.update(cx, |view, cx| {
700 self.element.paint(scene, origin, visible_bounds, view, cx);
701 Ok(())
702 })
703 }
704
705 fn rect_for_text_range(
706 &self,
707 range_utf16: Range<usize>,
708 cx: &WindowContext,
709 ) -> Result<Option<RectF>> {
710 let view = self.view.upgrade(cx).ok_or_else(|| {
711 anyhow!("rect_for_text_range called on a root element for a dropped view")
712 })?;
713 let view = view.read(cx);
714 let view_context = ViewContext::immutable(cx, self.view.id());
715 Ok(self
716 .element
717 .rect_for_text_range(range_utf16, view, &view_context))
718 }
719
720 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
721 let view = self
722 .view
723 .upgrade(cx)
724 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
725 let view = view.read(cx);
726 let view_context = ViewContext::immutable(cx, self.view.id());
727 Ok(serde_json::json!({
728 "view_id": self.view.id(),
729 "view_name": V::ui_name(),
730 "view": view.debug_json(cx),
731 "element": self.element.debug(view, &view_context)
732 }))
733 }
734
735 fn name(&self) -> Option<&str> {
736 self.element.name()
737 }
738}
739
740pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
741 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
742 self.extend(children.into_iter().map(|child| child.into_any()));
743 }
744
745 fn add_child<D: Element<V>>(&mut self, child: D) {
746 self.extend(Some(child.into_any()));
747 }
748
749 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
750 self.extend(children.into_iter().map(|child| child.into_any()));
751 self
752 }
753
754 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
755 self.extend(Some(child.into_any()));
756 self
757 }
758}
759
760impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
761
762pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
763 if max_size.x().is_infinite() && max_size.y().is_infinite() {
764 size
765 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
766 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
767 } else {
768 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
769 }
770}