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