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