1use crate::{
2 AnyElement, AnyEntity, AnyWeakEntity, App, AvailableSpace, Bounds, ContentMask, Context,
3 EdgeFade, Element, ElementId, Empty, Entity, EntityId, GlobalElementId, InspectorElementId,
4 IntoElement, LayoutId, PaintIndex, Pixels, Point, PrepaintStateIndex, Render, RenderOnce, Size,
5 Style, StyleRefinement, TextStyle, WeakEntity, Window,
6};
7use anyhow::Result;
8use collections::FxHashSet;
9use refineable::Refineable;
10use std::{
11 any::{TypeId, type_name},
12 fmt,
13 ops::Range,
14};
15
16#[derive(Clone, Debug)]
22pub struct AnyView {
23 entity: AnyEntity,
24 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
25 view_type_name: &'static str,
26}
27
28impl<V: Render> From<Entity<V>> for AnyView {
29 fn from(value: Entity<V>) -> Self {
30 AnyView {
31 entity: value.into_any(),
32 render: any_view::render::<V>,
33 view_type_name: type_name::<V>(),
34 }
35 }
36}
37
38impl AnyView {
39 pub fn cached(self, style: StyleRefinement) -> ViewElement<AnyView> {
44 self.into_element().cached(style)
45 }
46
47 pub fn cached_movable(self, style: StyleRefinement) -> ViewElement<AnyView> {
50 self.into_element().cached_movable(style)
51 }
52
53 pub fn downgrade(&self) -> AnyWeakView {
55 AnyWeakView {
56 entity: self.entity.downgrade(),
57 render: self.render,
58 view_type_name: self.view_type_name,
59 }
60 }
61
62 pub fn downcast<T: 'static>(self) -> Result<Entity<T>, Self> {
65 match self.entity.downcast() {
66 Ok(entity) => Ok(entity),
67 Err(entity) => Err(Self {
68 entity,
69 render: self.render,
70 view_type_name: self.view_type_name,
71 }),
72 }
73 }
74
75 pub fn entity_type(&self) -> TypeId {
77 self.entity.entity_type
78 }
79
80 pub fn entity_id(&self) -> EntityId {
82 self.entity.entity_id()
83 }
84}
85
86impl PartialEq for AnyView {
87 fn eq(&self, other: &Self) -> bool {
88 self.entity == other.entity
89 }
90}
91
92impl Eq for AnyView {}
93
94impl View for AnyView {
98 fn entity_id(&self) -> Option<EntityId> {
99 Some(self.entity.entity_id())
100 }
101
102 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
103 (self.render)(&self, window, cx)
104 }
105}
106
107impl<V: 'static + Render> IntoElement for Entity<V> {
108 type Element = ViewElement<Entity<V>>;
109
110 fn into_element(self) -> Self::Element {
111 ViewElement::with_type_name(self, type_name::<V>())
112 }
113
114 #[inline(never)]
115 fn into_any_element(self) -> AnyElement {
116 self.into_element().into_any()
117 }
118}
119
120impl IntoElement for AnyView {
121 type Element = ViewElement<AnyView>;
122
123 fn into_element(self) -> Self::Element {
124 let view_type_name = self.view_type_name;
125 ViewElement::with_type_name(self, view_type_name)
126 }
127}
128
129pub struct AnyWeakView {
131 entity: AnyWeakEntity,
132 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
133 view_type_name: &'static str,
134}
135
136impl AnyWeakView {
137 pub fn upgrade(&self) -> Option<AnyView> {
139 let entity = self.entity.upgrade()?;
140 Some(AnyView {
141 entity,
142 render: self.render,
143 view_type_name: self.view_type_name,
144 })
145 }
146}
147
148impl<V: 'static + Render> From<WeakEntity<V>> for AnyWeakView {
149 fn from(view: WeakEntity<V>) -> Self {
150 AnyWeakView {
151 entity: view.into(),
152 render: any_view::render::<V>,
153 view_type_name: type_name::<V>(),
154 }
155 }
156}
157
158impl PartialEq for AnyWeakView {
159 fn eq(&self, other: &Self) -> bool {
160 self.entity == other.entity
161 }
162}
163
164impl std::fmt::Debug for AnyWeakView {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.debug_struct("AnyWeakView")
167 .field("entity_id", &self.entity.entity_id)
168 .finish_non_exhaustive()
169 }
170}
171
172mod any_view {
173 use crate::{AnyElement, AnyView, App, IntoElement, Render, Window};
174
175 pub(crate) fn render<V: 'static + Render>(
176 view: &AnyView,
177 window: &mut Window,
178 cx: &mut App,
179 ) -> AnyElement {
180 let view = view.clone().downcast::<V>().unwrap();
181 #[cfg(debug_assertions)]
184 window
185 .a11y
186 .view_type_names
187 .insert(view.entity_id(), std::any::type_name::<V>());
188 view.update(cx, |view, cx| view.render(window, cx).into_any_element())
189 }
190}
191
192pub trait View: 'static + Sized {
204 fn entity_id(&self) -> Option<EntityId>;
213
214 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
216}
217
218impl<T: RenderOnce> View for T {
220 fn entity_id(&self) -> Option<EntityId> {
221 None
222 }
223
224 #[inline]
225 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
226 RenderOnce::render(self, window, cx)
227 }
228}
229
230impl<T: Render> View for Entity<T> {
232 fn entity_id(&self) -> Option<EntityId> {
233 Some(Entity::entity_id(self))
234 }
235
236 #[inline]
237 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
238 self.update(cx, |this, cx| {
239 Render::render(this, window, cx).into_any_element()
240 })
241 }
242}
243
244impl<T: Render> Entity<T> {
245 #[track_caller]
253 pub fn cached(self, style: StyleRefinement) -> ViewElement<Entity<T>> {
254 ViewElement::with_type_name(self, type_name::<T>()).cached(style)
255 }
256
257 pub fn cached_movable(self, style: StyleRefinement) -> ViewElement<Entity<T>> {
260 ViewElement::with_type_name(self, type_name::<T>()).cached_movable(style)
261 }
262}
263
264#[doc(hidden)]
267pub struct ViewElement<V: View> {
268 view: Option<V>,
269 entity_id: Option<EntityId>,
270 cached_style: Option<StyleRefinement>,
271 movable: bool,
272 #[cfg(feature = "profiler")]
273 view_type_name: &'static str,
274 #[cfg(debug_assertions)]
275 source: &'static core::panic::Location<'static>,
276}
277
278impl<V: View> ViewElement<V> {
279 #[track_caller]
281 pub fn new(view: V) -> Self {
282 Self::with_type_name(view, type_name::<V>())
283 }
284
285 #[track_caller]
288 #[cfg_attr(not(feature = "profiler"), expect(unused_variables))]
289 pub(crate) fn with_type_name(view: V, view_type_name: &'static str) -> Self {
290 let entity_id = view.entity_id();
291 ViewElement {
292 entity_id,
293 cached_style: None,
294 movable: false,
295 #[cfg(feature = "profiler")]
296 view_type_name,
297 view: Some(view),
298 #[cfg(debug_assertions)]
299 source: core::panic::Location::caller(),
300 }
301 }
302
303 pub(crate) fn cached(mut self, style: StyleRefinement) -> Self {
313 self.cached_style = Some(style);
314 self
315 }
316
317 fn cached_movable(mut self, style: StyleRefinement) -> Self {
318 self.cached_style = Some(style);
319 self.movable = true;
320 self
321 }
322}
323
324impl<V: View> IntoElement for ViewElement<V> {
325 type Element = Self;
326
327 fn into_element(self) -> Self::Element {
328 self
329 }
330}
331
332pub(crate) struct ViewElementState {
333 prepaint_range: Range<PrepaintStateIndex>,
334 paint_range: Range<PaintIndex>,
335 cache_key: ViewElementCacheKey,
336 accessed_entities: FxHashSet<EntityId>,
337 translation: Point<Pixels>,
338}
339
340impl ViewElementState {
341 pub(crate) fn rebase_prepaint(
342 &mut self,
343 previous: &PrepaintStateIndex,
344 next: &PrepaintStateIndex,
345 ) {
346 self.prepaint_range.start.rebase(previous, next);
347 self.prepaint_range.end.rebase(previous, next);
348 }
349
350 pub(crate) fn rebase_paint(
351 &mut self,
352 previous: &PaintIndex,
353 next: &PaintIndex,
354 offset: Point<Pixels>,
355 ) {
356 self.paint_range.start.rebase(previous, next);
357 self.paint_range.end.rebase(previous, next);
358 self.cache_key.bounds.origin += offset;
359 self.cache_key.content_mask.bounds.origin += offset;
360 self.translation = Point::default();
361 }
362}
363
364struct ViewElementCacheKey {
365 bounds: Bounds<Pixels>,
366 content_mask: ContentMask<Pixels>,
367 text_style: TextStyle,
368 movable: bool,
369 scale_factor: f32,
370 opacity: f32,
371 rem_size: Pixels,
372 edge_fade: Option<EdgeFade>,
373}
374
375impl<V: View> Element for ViewElement<V> {
376 type RequestLayoutState = Option<AnyElement>;
377 type PrepaintState = Option<AnyElement>;
378
379 fn id(&self) -> Option<ElementId> {
380 self.entity_id.map(ElementId::View)
381 }
382
383 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
384 #[cfg(debug_assertions)]
385 return Some(self.source);
386
387 #[cfg(not(debug_assertions))]
388 return None;
389 }
390
391 fn request_layout(
392 &mut self,
393 _id: Option<&GlobalElementId>,
394 _inspector_id: Option<&InspectorElementId>,
395 window: &mut Window,
396 cx: &mut App,
397 ) -> (LayoutId, Self::RequestLayoutState) {
398 if let Some(entity_id) = self.entity_id {
399 #[cfg(feature = "profiler")]
401 let start = window.window_profiler.begin_view();
402 let view = &mut self.view;
403 let layout = request_layout_view(
404 entity_id,
405 self.cached_style.as_ref(),
406 window,
407 cx,
408 &mut |window, cx| view.take().unwrap().render(window, cx).into_any_element(),
409 );
410 #[cfg(feature = "profiler")]
411 window.window_profiler.end_view(
412 start,
413 self.view_type_name,
414 crate::profiler::ViewPhase::RequestLayout,
415 );
416 layout
417 } else {
418 request_layout_component(type_name::<V>(), window, cx, &mut |window, cx| {
420 self.view
421 .take()
422 .unwrap()
423 .render(window, cx)
424 .into_any_element()
425 })
426 }
427 }
428
429 fn prepaint(
430 &mut self,
431 global_id: Option<&GlobalElementId>,
432 _inspector_id: Option<&InspectorElementId>,
433 bounds: Bounds<Pixels>,
434 element: &mut Self::RequestLayoutState,
435 window: &mut Window,
436 cx: &mut App,
437 ) -> Option<AnyElement> {
438 if let Some(entity_id) = self.entity_id {
439 #[cfg(feature = "profiler")]
441 let start = window.window_profiler.begin_view();
442 let prepainted = prepaint_view(
443 entity_id,
444 global_id,
445 bounds,
446 self.movable,
447 element,
448 window,
449 cx,
450 &mut |window, cx| {
451 self.view
452 .take()
453 .unwrap()
454 .render(window, cx)
455 .into_any_element()
456 },
457 );
458 #[cfg(feature = "profiler")]
459 window.window_profiler.end_view(
460 start,
461 self.view_type_name,
462 crate::profiler::ViewPhase::Prepaint,
463 );
464 prepainted
465 } else {
466 prepaint_component(type_name::<V>(), element, window, cx)
468 }
469 }
470
471 fn paint(
472 &mut self,
473 global_id: Option<&GlobalElementId>,
474 _inspector_id: Option<&InspectorElementId>,
475 _bounds: Bounds<Pixels>,
476 _request_layout: &mut Self::RequestLayoutState,
477 element: &mut Self::PrepaintState,
478 window: &mut Window,
479 cx: &mut App,
480 ) {
481 if let Some(entity_id) = self.entity_id {
482 #[cfg(feature = "profiler")]
484 let start = window.window_profiler.begin_view();
485 paint_view(
486 entity_id,
487 self.cached_style.is_some(),
488 global_id,
489 element,
490 window,
491 cx,
492 );
493 #[cfg(feature = "profiler")]
494 window.window_profiler.end_view(
495 start,
496 self.view_type_name,
497 crate::profiler::ViewPhase::Paint,
498 );
499 } else {
500 paint_component(std::any::type_name::<V>(), element, window, cx);
502 }
503 }
504}
505
506pub struct EmptyView;
508
509impl Render for EmptyView {
510 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
511 Empty
512 }
513}
514
515#[inline(never)]
516fn request_layout_view(
517 entity_id: EntityId,
518 cached_style: Option<&StyleRefinement>,
519 window: &mut Window,
520 cx: &mut App,
521 render: &mut dyn FnMut(&mut Window, &mut App) -> AnyElement,
522) -> (LayoutId, Option<AnyElement>) {
523 window.with_rendered_view(entity_id, |window| {
524 let caching_disabled = window.is_inspector_picking(cx);
525 match cached_style {
526 Some(style) if !caching_disabled => {
527 let mut root_style = Style::default();
528 root_style.refine(style);
529 let layout_id = window.request_layout(root_style, None, cx);
530 (layout_id, None)
531 }
532 _ => {
533 let mut element = render(window, cx);
534 let layout_id = element.request_layout(window, cx);
535 (layout_id, Some(element))
536 }
537 }
538 })
539}
540
541#[inline(never)]
542fn request_layout_component(
543 name: &'static str,
544 window: &mut Window,
545 cx: &mut App,
546 render: &mut dyn FnMut(&mut Window, &mut App) -> AnyElement,
547) -> (LayoutId, Option<AnyElement>) {
548 window.with_id(ElementId::from(name), |window| {
549 let mut element = render(window, cx);
550 let layout_id = element.request_layout(window, cx);
551 (layout_id, Some(element))
552 })
553}
554
555#[inline(never)]
556fn prepaint_view(
557 entity_id: EntityId,
558 global_id: Option<&GlobalElementId>,
559 bounds: Bounds<Pixels>,
560 movable: bool,
561 element: &mut Option<AnyElement>,
562 window: &mut Window,
563 cx: &mut App,
564 render: &mut dyn FnMut(&mut Window, &mut App) -> AnyElement,
565) -> Option<AnyElement> {
566 window.set_view_id(entity_id);
567 window.with_rendered_view(entity_id, |window| {
568 if let Some(mut element) = element.take() {
569 element.prepaint(window, cx);
570 return Some(element);
571 }
572
573 window.with_element_state::<ViewElementState, _>(
574 global_id.unwrap(),
575 |element_state, window| {
576 let content_mask = window.content_mask();
577 let text_style = window.text_style();
578
579 if let Some(mut element_state) = element_state
580 && element_state.cache_key.movable == movable
581 && element_state.cache_key.scale_factor == window.scale_factor()
582 && element_state.cache_key.opacity == window.element_opacity()
583 && element_state.cache_key.rem_size == window.rem_size()
584 && element_state.cache_key.edge_fade == window.edge_fade
585 && (element_state.cache_key.bounds == bounds
586 && element_state.cache_key.content_mask == content_mask
587 || movable
588 && window.edge_fade.is_none()
589 && window.can_move_cached_view(
590 &element_state.prepaint_range,
591 &element_state.paint_range,
592 element_state.cache_key.bounds,
593 element_state.cache_key.content_mask,
594 bounds,
595 content_mask,
596 ))
597 && element_state.cache_key.text_style == text_style
598 && !window.dirty_views.contains(&entity_id)
599 && !window.refreshing
600 {
601 element_state.translation =
602 bounds.origin - element_state.cache_key.bounds.origin;
603 element_state.cache_key.bounds = bounds;
604 element_state.cache_key.content_mask = content_mask;
605 let prepaint_start = window.prepaint_index();
606 window.reuse_prepaint(element_state.prepaint_range.clone());
607 cx.entities
608 .extend_accessed(&element_state.accessed_entities);
609 let prepaint_end = window.prepaint_index();
610 element_state.prepaint_range = prepaint_start..prepaint_end;
611
612 return (None, element_state);
613 }
614
615 let prepaint_start = window.prepaint_index();
616 let (element, accessed_entities) = cx.detect_accessed_entities(|cx| {
617 let mut element = render(window, cx);
618 element.layout_as_root(Size::<AvailableSpace>::from(bounds.size), window, cx);
619 window.with_content_mask(movable.then(|| ContentMask::new(bounds)), |window| {
620 element.prepaint_at(bounds.origin, window, cx);
621 });
622 element
623 });
624
625 let prepaint_end = window.prepaint_index();
626
627 (
628 Some(element),
629 ViewElementState {
630 translation: Point::default(),
631 accessed_entities,
632 prepaint_range: prepaint_start..prepaint_end,
633 paint_range: PaintIndex::default()..PaintIndex::default(),
634 cache_key: ViewElementCacheKey {
635 bounds,
636 content_mask,
637 text_style,
638 movable,
639 scale_factor: window.scale_factor(),
640 opacity: window.element_opacity(),
641 rem_size: window.rem_size(),
642 edge_fade: window.edge_fade,
643 },
644 },
645 )
646 },
647 )
648 })
649}
650
651#[inline(never)]
652fn prepaint_component(
653 name: &'static str,
654 element: &mut Option<AnyElement>,
655 window: &mut Window,
656 cx: &mut App,
657) -> Option<AnyElement> {
658 window.with_id(ElementId::from(name), |window| {
659 element.as_mut().unwrap().prepaint(window, cx);
660 });
661 Some(element.take().unwrap())
662}
663
664#[inline(never)]
665fn paint_view(
666 entity_id: EntityId,
667 cached: bool,
668 global_id: Option<&GlobalElementId>,
669 element: &mut Option<AnyElement>,
670 window: &mut Window,
671 cx: &mut App,
672) {
673 window.with_rendered_view(entity_id, |window| {
674 let caching_disabled = window.is_inspector_picking(cx);
675 if cached && !caching_disabled {
676 window.with_element_state::<ViewElementState, _>(
677 global_id.unwrap(),
678 |element_state, window| {
679 let mut element_state = element_state.unwrap();
680
681 let paint_start = window.paint_index();
682
683 if let Some(element) = element {
684 window.with_content_mask(
685 element_state
686 .cache_key
687 .movable
688 .then(|| ContentMask::new(element_state.cache_key.bounds)),
689 |window| element.paint(window, cx),
690 );
691 } else {
692 window.reuse_paint_translated(
693 element_state.paint_range.clone(),
694 element_state.translation,
695 );
696 }
697
698 let paint_end = window.paint_index();
699 element_state.paint_range = paint_start..paint_end;
700
701 ((), element_state)
702 },
703 )
704 } else {
705 element.as_mut().unwrap().paint(window, cx);
706 }
707 });
708}
709
710#[inline(never)]
711fn paint_component(
712 name: &'static str,
713 element: &mut Option<AnyElement>,
714 window: &mut Window,
715 cx: &mut App,
716) {
717 window.with_id(ElementId::Name(name.into()), |window| {
718 element.as_mut().unwrap().paint(window, cx);
719 });
720}