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