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