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