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::{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 ViewContext<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(scene, bounds, visible_bounds, &mut layout, view, cx);
314 ElementState::PostPaint {
315 element,
316 constraint,
317 bounds,
318 visible_bounds,
319 layout,
320 paint,
321 }
322 }
323 ElementState::PostPaint {
324 mut element,
325 constraint,
326 bounds,
327 mut layout,
328 ..
329 } => {
330 let bounds = RectF::new(origin, bounds.size());
331 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
332 ElementState::PostPaint {
333 element,
334 constraint,
335 bounds,
336 visible_bounds,
337 layout,
338 paint,
339 }
340 }
341 ElementState::Empty => panic!("invalid element lifecycle state"),
342 ElementState::Init { .. } => {
343 panic!("invalid element lifecycle state, paint called before layout")
344 }
345 }
346 }
347
348 fn rect_for_text_range(
349 &self,
350 range_utf16: Range<usize>,
351 view: &V,
352 cx: &ViewContext<V>,
353 ) -> Option<RectF> {
354 if let ElementState::PostPaint {
355 element,
356 bounds,
357 visible_bounds,
358 layout,
359 paint,
360 ..
361 } = self
362 {
363 element.rect_for_text_range(
364 range_utf16,
365 *bounds,
366 *visible_bounds,
367 layout,
368 paint,
369 view,
370 cx,
371 )
372 } else {
373 None
374 }
375 }
376
377 fn size(&self) -> Vector2F {
378 match self {
379 ElementState::Empty | ElementState::Init { .. } => {
380 panic!("invalid element lifecycle state")
381 }
382 ElementState::PostLayout { size, .. } => *size,
383 ElementState::PostPaint { bounds, .. } => bounds.size(),
384 }
385 }
386
387 fn metadata(&self) -> Option<&dyn Any> {
388 match self {
389 ElementState::Empty => unreachable!(),
390 ElementState::Init { element }
391 | ElementState::PostLayout { element, .. }
392 | ElementState::PostPaint { element, .. } => element.metadata(),
393 }
394 }
395
396 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
397 match self {
398 ElementState::PostPaint {
399 element,
400 constraint,
401 bounds,
402 visible_bounds,
403 layout,
404 paint,
405 } => {
406 let mut value = element.debug(*bounds, layout, paint, view, cx);
407 if let json::Value::Object(map) = &mut value {
408 let mut new_map: crate::json::Map<String, serde_json::Value> =
409 Default::default();
410 if let Some(typ) = map.remove("type") {
411 new_map.insert("type".into(), typ);
412 }
413 new_map.insert("constraint".into(), constraint.to_json());
414 new_map.insert("bounds".into(), bounds.to_json());
415 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
416 new_map.append(map);
417 json::Value::Object(new_map)
418 } else {
419 value
420 }
421 }
422
423 _ => panic!("invalid element lifecycle state"),
424 }
425 }
426}
427
428impl<V: View, E: Element<V>> Default for ElementState<V, E> {
429 fn default() -> Self {
430 Self::Empty
431 }
432}
433
434pub struct AnyElement<V: View> {
435 state: Box<dyn AnyElementState<V>>,
436 name: Option<Cow<'static, str>>,
437}
438
439impl<V: View> AnyElement<V> {
440 pub fn name(&self) -> Option<&str> {
441 self.name.as_deref()
442 }
443
444 pub fn metadata<T: 'static>(&self) -> Option<&T> {
445 self.state
446 .metadata()
447 .and_then(|data| data.downcast_ref::<T>())
448 }
449
450 pub fn layout(
451 &mut self,
452 constraint: SizeConstraint,
453 view: &mut V,
454 cx: &mut LayoutContext<V>,
455 ) -> Vector2F {
456 self.state.layout(constraint, view, cx)
457 }
458
459 pub fn paint(
460 &mut self,
461 scene: &mut SceneBuilder,
462 origin: Vector2F,
463 visible_bounds: RectF,
464 view: &mut V,
465 cx: &mut ViewContext<V>,
466 ) {
467 self.state.paint(scene, origin, visible_bounds, view, cx);
468 }
469
470 pub fn rect_for_text_range(
471 &self,
472 range_utf16: Range<usize>,
473 view: &V,
474 cx: &ViewContext<V>,
475 ) -> Option<RectF> {
476 self.state.rect_for_text_range(range_utf16, view, cx)
477 }
478
479 pub fn size(&self) -> Vector2F {
480 self.state.size()
481 }
482
483 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
484 let mut value = self.state.debug(view, cx);
485
486 if let Some(name) = &self.name {
487 if let json::Value::Object(map) = &mut value {
488 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
489 new_map.insert("name".into(), json::Value::String(name.to_string()));
490 new_map.append(map);
491 return json::Value::Object(new_map);
492 }
493 }
494
495 value
496 }
497
498 pub fn with_metadata<T, F, R>(&self, f: F) -> R
499 where
500 T: 'static,
501 F: FnOnce(Option<&T>) -> R,
502 {
503 f(self.state.metadata().and_then(|m| m.downcast_ref()))
504 }
505}
506
507impl<V: View> Element<V> for AnyElement<V> {
508 type LayoutState = ();
509 type PaintState = ();
510
511 fn layout(
512 &mut self,
513 constraint: SizeConstraint,
514 view: &mut V,
515 cx: &mut LayoutContext<V>,
516 ) -> (Vector2F, Self::LayoutState) {
517 let size = self.layout(constraint, view, cx);
518 (size, ())
519 }
520
521 fn paint(
522 &mut self,
523 scene: &mut SceneBuilder,
524 bounds: RectF,
525 visible_bounds: RectF,
526 _: &mut Self::LayoutState,
527 view: &mut V,
528 cx: &mut ViewContext<V>,
529 ) -> Self::PaintState {
530 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
531 }
532
533 fn rect_for_text_range(
534 &self,
535 range_utf16: Range<usize>,
536 _: RectF,
537 _: RectF,
538 _: &Self::LayoutState,
539 _: &Self::PaintState,
540 view: &V,
541 cx: &ViewContext<V>,
542 ) -> Option<RectF> {
543 self.rect_for_text_range(range_utf16, view, cx)
544 }
545
546 fn debug(
547 &self,
548 _: RectF,
549 _: &Self::LayoutState,
550 _: &Self::PaintState,
551 view: &V,
552 cx: &ViewContext<V>,
553 ) -> serde_json::Value {
554 self.debug(view, cx)
555 }
556
557 fn into_any(self) -> AnyElement<V>
558 where
559 Self: Sized,
560 {
561 self
562 }
563}
564
565pub struct RootElement<V: View> {
566 element: AnyElement<V>,
567 view: WeakViewHandle<V>,
568}
569
570impl<V: View> RootElement<V> {
571 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
572 Self { element, view }
573 }
574}
575
576pub trait AnyRootElement {
577 fn layout(
578 &mut self,
579 constraint: SizeConstraint,
580 new_parents: &mut HashMap<usize, usize>,
581 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
582 refreshing: bool,
583 cx: &mut WindowContext,
584 ) -> Result<Vector2F>;
585 fn paint(
586 &mut self,
587 scene: &mut SceneBuilder,
588 origin: Vector2F,
589 visible_bounds: RectF,
590 cx: &mut WindowContext,
591 ) -> Result<()>;
592 fn rect_for_text_range(
593 &self,
594 range_utf16: Range<usize>,
595 cx: &WindowContext,
596 ) -> Result<Option<RectF>>;
597 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
598 fn name(&self) -> Option<&str>;
599}
600
601impl<V: View> AnyRootElement for RootElement<V> {
602 fn layout(
603 &mut self,
604 constraint: SizeConstraint,
605 new_parents: &mut HashMap<usize, usize>,
606 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
607 refreshing: bool,
608 cx: &mut WindowContext,
609 ) -> Result<Vector2F> {
610 let view = self
611 .view
612 .upgrade(cx)
613 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
614 view.update(cx, |view, cx| {
615 let mut cx = LayoutContext::new(
616 cx,
617 new_parents,
618 views_to_notify_if_ancestors_change,
619 refreshing,
620 );
621 Ok(self.element.layout(constraint, view, &mut cx))
622 })
623 }
624
625 fn paint(
626 &mut self,
627 scene: &mut SceneBuilder,
628 origin: Vector2F,
629 visible_bounds: RectF,
630 cx: &mut WindowContext,
631 ) -> Result<()> {
632 let view = self
633 .view
634 .upgrade(cx)
635 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
636
637 view.update(cx, |view, cx| {
638 self.element.paint(scene, origin, visible_bounds, view, cx);
639 Ok(())
640 })
641 }
642
643 fn rect_for_text_range(
644 &self,
645 range_utf16: Range<usize>,
646 cx: &WindowContext,
647 ) -> Result<Option<RectF>> {
648 let view = self.view.upgrade(cx).ok_or_else(|| {
649 anyhow!("rect_for_text_range called on a root element for a dropped view")
650 })?;
651 let view = view.read(cx);
652 let view_context = ViewContext::immutable(cx, self.view.id());
653 Ok(self
654 .element
655 .rect_for_text_range(range_utf16, view, &view_context))
656 }
657
658 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
659 let view = self
660 .view
661 .upgrade(cx)
662 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
663 let view = view.read(cx);
664 let view_context = ViewContext::immutable(cx, self.view.id());
665 Ok(serde_json::json!({
666 "view_id": self.view.id(),
667 "view_name": V::ui_name(),
668 "view": view.debug_json(cx),
669 "element": self.element.debug(view, &view_context)
670 }))
671 }
672
673 fn name(&self) -> Option<&str> {
674 self.element.name()
675 }
676}
677
678pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
679 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
680 self.extend(children.into_iter().map(|child| child.into_any()));
681 }
682
683 fn add_child<D: Element<V>>(&mut self, child: D) {
684 self.extend(Some(child.into_any()));
685 }
686
687 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
688 self.extend(children.into_iter().map(|child| child.into_any()));
689 self
690 }
691
692 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
693 self.extend(Some(child.into_any()));
694 self
695 }
696}
697
698impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
699
700pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
701 if max_size.x().is_infinite() && max_size.y().is_infinite() {
702 size
703 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
704 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
705 } else {
706 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
707 }
708}