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