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