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