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