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