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