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