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