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