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