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