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