1use crate::{
2 AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Dependencies, Element,
3 ElementId, Entity, EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
4 Modifiers, PaintIndex, Pixels, Point, PrepaintStateIndex, Render, RenderOnce,
5 ResolvedDirection, Style, StyleRefinement, TextStyle, UnicodeBidi, WeakEntity,
6 scene::ReplaySummary,
7 taffy::LayoutIsland,
8 window::{InputReads, a11y::A11yRecording},
9};
10use crate::{Empty, Window};
11use anyhow::Result;
12use refineable::Refineable;
13use std::{
14 any::{TypeId, type_name},
15 fmt,
16 ops::Range,
17};
18
19#[derive(Clone, Debug)]
54pub struct AnyView {
55 entity: AnyEntity,
56 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
57 view_type_name: &'static str,
58}
59
60impl<V: Render> From<Entity<V>> for AnyView {
61 fn from(value: Entity<V>) -> Self {
62 AnyView {
63 entity: value.into_any(),
64 render: any_view::render::<V>,
65 view_type_name: type_name::<V>(),
66 }
67 }
68}
69
70impl AnyView {
71 pub fn cached(self, style: StyleRefinement) -> ViewElement<AnyView> {
83 self.into_element().cached(style)
84 }
85
86 pub fn downgrade(&self) -> AnyWeakView {
91 AnyWeakView {
92 entity: self.entity.downgrade(),
93 render: self.render,
94 view_type_name: self.view_type_name,
95 }
96 }
97
98 pub fn downcast<T: 'static>(self) -> Result<Entity<T>, Self> {
102 match self.entity.downcast() {
103 Ok(entity) => Ok(entity),
104 Err(entity) => Err(Self {
105 entity,
106 render: self.render,
107 view_type_name: self.view_type_name,
108 }),
109 }
110 }
111
112 pub fn entity_type(&self) -> TypeId {
114 self.entity.entity_type
115 }
116
117 pub fn entity_id(&self) -> EntityId {
119 self.entity.entity_id()
120 }
121}
122
123impl PartialEq for AnyView {
124 fn eq(&self, other: &Self) -> bool {
125 self.entity == other.entity
126 }
127}
128
129impl Eq for AnyView {}
130
131impl View for AnyView {
135 fn entity_id(&self) -> Option<EntityId> {
136 Some(self.entity.entity_id())
137 }
138
139 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
140 (self.render)(&self, window, cx)
141 }
142}
143
144impl<V: 'static + Render> IntoElement for Entity<V> {
145 type Element = ViewElement<Entity<V>>;
146
147 fn into_element(self) -> Self::Element {
148 ViewElement::with_type_name(self, type_name::<V>())
149 }
150
151 #[inline(never)]
152 fn into_any_element(self) -> AnyElement {
153 self.into_element().into_any()
154 }
155}
156
157impl IntoElement for AnyView {
158 type Element = ViewElement<AnyView>;
159
160 fn into_element(self) -> Self::Element {
161 let view_type_name = self.view_type_name;
162 ViewElement::with_type_name(self, view_type_name)
163 }
164}
165
166pub struct AnyWeakView {
171 entity: AnyWeakEntity,
172 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
173 view_type_name: &'static str,
174}
175
176impl AnyWeakView {
177 pub fn upgrade(&self) -> Option<AnyView> {
179 let entity = self.entity.upgrade()?;
180 Some(AnyView {
181 entity,
182 render: self.render,
183 view_type_name: self.view_type_name,
184 })
185 }
186}
187
188impl<V: 'static + Render> From<WeakEntity<V>> for AnyWeakView {
189 fn from(view: WeakEntity<V>) -> Self {
190 AnyWeakView {
191 entity: view.into(),
192 render: any_view::render::<V>,
193 view_type_name: type_name::<V>(),
194 }
195 }
196}
197
198impl PartialEq for AnyWeakView {
199 fn eq(&self, other: &Self) -> bool {
200 self.entity == other.entity
201 }
202}
203
204impl std::fmt::Debug for AnyWeakView {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 f.debug_struct("AnyWeakView")
207 .field("entity_id", &self.entity.entity_id)
208 .finish_non_exhaustive()
209 }
210}
211
212mod any_view {
213 use crate::{AnyElement, AnyView, App, IntoElement, Render, Window};
214
215 pub(crate) fn render<V: 'static + Render>(
216 view: &AnyView,
217 window: &mut Window,
218 cx: &mut App,
219 ) -> AnyElement {
220 let view = view.clone().downcast::<V>().unwrap();
221 #[cfg(debug_assertions)]
224 window
225 .a11y
226 .view_type_names
227 .insert(view.entity_id(), std::any::type_name::<V>());
228 view.update(cx, |view, cx| {
229 #[cfg(all(
230 feature = "hot-patching",
231 debug_assertions,
232 not(target_family = "wasm")
233 ))]
234 let element = subsecond::HotFn::current(V::render).call((view, window, cx));
235 #[cfg(not(all(
236 feature = "hot-patching",
237 debug_assertions,
238 not(target_family = "wasm")
239 )))]
240 let element = view.render(window, cx);
241 element.into_any_element()
242 })
243 }
244}
245
246pub trait View: 'static + Sized {
258 fn entity_id(&self) -> Option<EntityId>;
265
266 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
268}
269
270impl<T: RenderOnce> View for T {
272 fn entity_id(&self) -> Option<EntityId> {
273 None
274 }
275
276 #[inline]
277 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
278 #[cfg(all(
279 feature = "hot-patching",
280 debug_assertions,
281 not(target_family = "wasm")
282 ))]
283 let element = subsecond::HotFn::current(<T as RenderOnce>::render).call((self, window, cx));
284 #[cfg(not(all(
285 feature = "hot-patching",
286 debug_assertions,
287 not(target_family = "wasm")
288 )))]
289 let element = RenderOnce::render(self, window, cx);
290 element
291 }
292}
293
294impl<T: Render> View for Entity<T> {
296 fn entity_id(&self) -> Option<EntityId> {
297 Some(Entity::entity_id(self))
298 }
299
300 #[inline]
301 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
302 self.update(cx, |this, cx| {
303 #[cfg(all(
304 feature = "hot-patching",
305 debug_assertions,
306 not(target_family = "wasm")
307 ))]
308 let element = subsecond::HotFn::current(T::render).call((this, window, cx));
309 #[cfg(not(all(
310 feature = "hot-patching",
311 debug_assertions,
312 not(target_family = "wasm")
313 )))]
314 let element = Render::render(this, window, cx);
315 element.into_any_element()
316 })
317 }
318}
319
320impl<T: Render> Entity<T> {
321 #[track_caller]
329 pub fn cached(self, style: StyleRefinement) -> ViewElement<Entity<T>> {
330 ViewElement::with_type_name(self, type_name::<T>()).cached(style)
331 }
332}
333
334#[doc(hidden)]
340pub struct ViewElement<V: View> {
341 view: Option<V>,
342 entity_id: Option<EntityId>,
343 retention: Retention,
344 #[cfg(feature = "profiler")]
345 view_type_name: &'static str,
346 #[cfg(debug_assertions)]
347 source: &'static core::panic::Location<'static>,
348}
349
350enum Retention {
351 None,
353 Natural,
355 Fixed(StyleRefinement),
357}
358
359impl<V: View> ViewElement<V> {
360 #[track_caller]
366 pub fn new(view: V) -> Self {
367 let mut element = Self::with_type_name(view, type_name::<V>());
368 element.retention = Retention::None;
369 element
370 }
371
372 #[track_caller]
375 #[cfg_attr(not(feature = "profiler"), expect(unused_variables))]
376 pub(crate) fn with_type_name(view: V, view_type_name: &'static str) -> Self {
377 let entity_id = view.entity_id();
378 ViewElement {
379 entity_id,
380 retention: if entity_id.is_some() {
381 Retention::Natural
382 } else {
383 Retention::None
384 },
385 #[cfg(feature = "profiler")]
386 view_type_name,
387 view: Some(view),
388 #[cfg(debug_assertions)]
389 source: core::panic::Location::caller(),
390 }
391 }
392
393 pub(crate) fn cached(mut self, style: StyleRefinement) -> Self {
399 debug_assert!(self.entity_id.is_some(), "only entity views can be cached");
400 self.retention = Retention::Fixed(style);
401 self
402 }
403
404 fn render(&mut self, window: &mut Window, cx: &mut App) -> AnyElement {
405 render_view(self.view.take().unwrap(), window, cx)
406 }
407}
408
409impl<V: View> IntoElement for ViewElement<V> {
410 type Element = Self;
411
412 fn into_element(self) -> Self::Element {
413 self
414 }
415}
416
417pub(crate) struct ViewState {
420 island: LayoutIsland,
421 layout: Option<LayoutKey>,
422 dependencies: Dependencies,
423 input: Option<InputSnapshot>,
425 output: Option<ViewOutput>,
426 render_states: Vec<(GlobalElementId, TypeId)>,
429}
430
431#[derive(PartialEq)]
433struct LayoutKey {
434 text_style: TextStyle,
435 rem_size: Pixels,
436 scale_factor: f32,
437}
438
439#[derive(PartialEq)]
441struct OutputKey {
442 bounds: Bounds<Pixels>,
443 content_mask: ContentMask<Pixels>,
444 direction: ResolvedDirection,
445 unicode_bidi: UnicodeBidi,
446}
447
448pub(crate) struct InputSnapshot {
450 reads: InputReads,
451 mouse_position: Point<Pixels>,
452 modifiers: Modifiers,
453 modality: crate::window::InputModality,
454}
455
456impl InputSnapshot {
457 fn new(reads: InputReads, window: &Window) -> Self {
458 Self {
459 reads,
460 mouse_position: window.mouse_position,
461 modifiers: window.modifiers,
462 modality: window.last_input_modality,
463 }
464 }
465
466 fn still_valid(&self, window: &Window) -> bool {
467 (!self.reads.contains(InputReads::POINTER) || self.mouse_position == window.mouse_position)
468 && (!self.reads.contains(InputReads::MODIFIERS) || self.modifiers == window.modifiers)
469 && (!self.reads.contains(InputReads::MODALITY)
470 || self.modality == window.last_input_modality)
471 }
472}
473
474struct ViewOutput {
475 key: OutputKey,
476 prepaint: Range<PrepaintStateIndex>,
477 paint: Range<PaintIndex>,
478 painted: bool,
480 frame: u64,
482 a11y: Option<A11yRecording>,
484 replay: Option<ReplaySummary>,
486}
487
488impl ViewOutput {
489 fn complete(&self, window: &Window) -> bool {
491 self.a11y.is_some() || !window.a11y.is_active()
492 }
493}
494
495impl ViewState {
496 pub(crate) fn rebase_prepaint(
499 &mut self,
500 source: &Range<PrepaintStateIndex>,
501 destination: &PrepaintStateIndex,
502 frame: u64,
503 ) {
504 if let Some(output) = &mut self.output {
505 if output.painted
506 && output.frame + 1 == frame
507 && PrepaintStateIndex::within(&output.prepaint, source)
508 {
509 output.prepaint = output.prepaint.start.rebased(source, destination)
510 ..output.prepaint.end.rebased(source, destination);
511 output.frame = frame;
512 output.painted = false;
513 } else {
514 self.output = None;
515 }
516 }
517 }
518
519 pub(crate) fn rebase_paint(
521 &mut self,
522 source: &Range<PaintIndex>,
523 destination: &PaintIndex,
524 frame: u64,
525 ) {
526 if let Some(output) = &mut self.output {
527 if output.frame == frame && PaintIndex::within(&output.paint, source) {
528 output.paint = output.paint.start.rebased(source, destination)
529 ..output.paint.end.rebased(source, destination);
530 output.painted = true;
531 } else {
532 self.output = None;
533 }
534 }
535 }
536
537 fn reusable(&self, entity_id: EntityId, layout: &LayoutKey, window: &Window, cx: &App) -> bool {
538 self.layout.as_ref() == Some(layout)
539 && !window.refreshing
540 && !window.dirty_views.contains(&entity_id)
541 && !cx.dependencies_changed(&self.dependencies, &window.dirty_views)
542 && self
543 .input
544 .as_ref()
545 .is_none_or(|input| input.still_valid(window))
546 && !window.is_inspector_picking(cx)
547 && self
548 .output
549 .as_ref()
550 .is_none_or(|output| output.complete(window))
551 }
552}
553
554#[inline]
555fn render_view<V: View>(view: V, window: &mut Window, cx: &mut App) -> AnyElement {
556 view.render(window, cx).into_any_element()
557}
558
559impl<V: View> Element for ViewElement<V> {
560 type RequestLayoutState = Option<AnyElement>;
561 type PrepaintState = Option<AnyElement>;
562
563 fn id(&self) -> Option<ElementId> {
564 self.entity_id.map(ElementId::View)
565 }
566
567 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
568 #[cfg(debug_assertions)]
569 return Some(self.source);
570
571 #[cfg(not(debug_assertions))]
572 return None;
573 }
574
575 fn request_layout(
576 &mut self,
577 global_id: Option<&GlobalElementId>,
578 _inspector_id: Option<&InspectorElementId>,
579 window: &mut Window,
580 cx: &mut App,
581 ) -> (LayoutId, Option<AnyElement>) {
582 #[cfg(feature = "profiler")]
583 let start = window.window_profiler.begin_view();
584 let result = match (self.entity_id, &self.retention) {
585 (None, _) => window.with_id(ElementId::Name(type_name::<V>().into()), |window| {
586 let mut element = self.render(window, cx);
587 (element.request_layout(window, cx), Some(element))
588 }),
589 (Some(entity_id), Retention::None) => window.with_rendered_view(entity_id, |window| {
590 let mut element = self.render(window, cx);
591 (element.request_layout(window, cx), Some(element))
592 }),
593 (Some(entity_id), _) => window.with_rendered_view(entity_id, |window| {
594 window.with_element_state::<ViewState, _>(global_id.unwrap(), |state, window| {
595 let mut state = state.unwrap_or_else(|| ViewState {
596 island: window.create_layout_island(),
597 layout: None,
598 dependencies: Dependencies::default(),
599 input: None,
600 output: None,
601 render_states: Vec::new(),
602 });
603 let layout = LayoutKey {
604 text_style: window.text_style(),
605 rem_size: window.rem_size(),
606 scale_factor: window.scale_factor(),
607 };
608 let element = if state.reusable(entity_id, &layout, window, cx) {
609 cx.replay_dependencies(&state.dependencies);
610 window.keep_element_states(&state.render_states);
611 None
612 } else {
613 state.layout = Some(layout);
614 state.output = None;
615 Some(self.render_into_island(&mut state, window, cx))
616 };
617 let island = state.island.id();
618 let layout_id = match &self.retention {
619 Retention::Fixed(style) => {
620 let mut root_style = Style::default();
621 root_style.refine(style);
622 let layout_id = window.request_layout(root_style, None, cx);
623 window.anchor_layout_island(island, layout_id);
624 let direction = window.layout_island_direction_contribution(island);
627 window.set_layout_auto_direction_hint(layout_id, direction);
628 layout_id
629 }
630 _ => window.request_layout_island(island),
631 };
632 ((layout_id, element), state)
633 })
634 }),
635 };
636 #[cfg(feature = "profiler")]
637 window.window_profiler.end_view(
638 start,
639 self.view_type_name,
640 crate::profiler::ViewPhase::RequestLayout,
641 );
642 result
643 }
644
645 fn prepaint(
646 &mut self,
647 global_id: Option<&GlobalElementId>,
648 _inspector_id: Option<&InspectorElementId>,
649 bounds: Bounds<Pixels>,
650 element: &mut Option<AnyElement>,
651 window: &mut Window,
652 cx: &mut App,
653 ) -> Option<AnyElement> {
654 #[cfg(feature = "profiler")]
655 let start = window.window_profiler.begin_view();
656 let result = match (self.entity_id, &self.retention) {
657 (None, _) => window.with_id(ElementId::Name(type_name::<V>().into()), |window| {
658 let mut element = element.take().unwrap();
659 element.prepaint(window, cx);
660 Some(element)
661 }),
662 (Some(entity_id), Retention::None) => {
663 window.set_view_id(entity_id);
664 window.with_rendered_view(entity_id, |window| {
665 let mut element = element.take().unwrap();
666 element.prepaint(window, cx);
667 Some(element)
668 })
669 }
670 (Some(entity_id), retention) => {
671 let fixed = matches!(retention, Retention::Fixed(_));
672 window.set_view_id(entity_id);
673 window.with_rendered_view(entity_id, |window| {
674 window.with_element_state::<ViewState, _>(
675 global_id.unwrap(),
676 |state, window| {
677 let mut state = state.unwrap();
678 let key = OutputKey {
679 bounds,
680 content_mask: window.content_mask(),
681 direction: window.resolved_direction(),
682 unicode_bidi: window.resolved_unicode_bidi(),
683 };
684 let mut element = element.take();
685 if element.is_none()
686 && let Some(output) = state.output.as_mut()
687 && output.painted
688 && output.frame + 1 == window.frame_number
689 && output.key == key
690 && output.complete(window)
691 {
692 let start = window.prepaint_index();
693 window.reuse_prepaint(output.prepaint.clone());
694 if let Some(a11y) =
695 output.a11y.as_ref().filter(|_| window.a11y.is_active())
696 {
697 window.a11y.replay(a11y);
698 }
699 output.prepaint = start..window.prepaint_index();
700 output.frame = window.frame_number;
701 output.painted = false;
702 return (None, state);
703 }
704
705 let rendered_now = element.is_none();
708 let mut element = match element.take() {
709 Some(element) => element,
710 None => self.render_into_island(&mut state, window, cx),
711 };
712 if fixed || rendered_now {
713 window.layout_island(state.island.id(), bounds.size, cx);
714 }
715 let start = window.prepaint_index();
716 let a11y = window.a11y.is_active().then(|| window.a11y.mark());
717 let (_, dependencies) = record(&mut state, window, cx, |window, cx| {
718 element.prepaint(window, cx)
719 });
720 state.dependencies.extend(dependencies);
721 state.output = Some(ViewOutput {
722 key,
723 prepaint: start..window.prepaint_index(),
724 paint: PaintIndex::default()..PaintIndex::default(),
725 painted: false,
726 frame: window.frame_number,
727 a11y: a11y.map(|mark| window.a11y.record(mark)),
728 replay: None,
729 });
730 (Some(element), state)
731 },
732 )
733 })
734 }
735 };
736 #[cfg(feature = "profiler")]
737 window.window_profiler.end_view(
738 start,
739 self.view_type_name,
740 crate::profiler::ViewPhase::Prepaint,
741 );
742 result
743 }
744
745 fn paint(
746 &mut self,
747 global_id: Option<&GlobalElementId>,
748 _inspector_id: Option<&InspectorElementId>,
749 _bounds: Bounds<Pixels>,
750 _request_layout: &mut Option<AnyElement>,
751 element: &mut Option<AnyElement>,
752 window: &mut Window,
753 cx: &mut App,
754 ) {
755 #[cfg(feature = "profiler")]
756 let start = window.window_profiler.begin_view();
757 match (self.entity_id, &self.retention) {
758 (None, _) => window.with_id(ElementId::Name(type_name::<V>().into()), |window| {
759 element.as_mut().unwrap().paint(window, cx);
760 }),
761 (Some(entity_id), Retention::None) => window.with_rendered_view(entity_id, |window| {
762 element.as_mut().unwrap().paint(window, cx);
763 }),
764 (Some(entity_id), _) => window.with_rendered_view(entity_id, |window| {
765 window.with_element_state::<ViewState, _>(global_id.unwrap(), |state, window| {
766 let mut state = state.unwrap();
767 let start = window.paint_index();
768 match element {
769 Some(element) => {
770 let ((), dependencies) =
771 record(&mut state, window, cx, |window, cx| {
772 element.paint(window, cx)
773 });
774 state.dependencies.extend(dependencies);
775 }
776 None => {
777 let output = state.output.as_mut().unwrap();
778 window.reuse_paint(output.paint.clone(), &mut output.replay);
779 }
780 }
781 let output = state.output.as_mut().unwrap();
782 output.paint = start..window.paint_index();
783 output.painted = true;
784 ((), state)
785 })
786 }),
787 }
788 #[cfg(feature = "profiler")]
789 window.window_profiler.end_view(
790 start,
791 self.view_type_name,
792 crate::profiler::ViewPhase::Paint,
793 );
794 }
795}
796
797impl<V: View> ViewElement<V> {
798 fn render_into_island(
800 &mut self,
801 state: &mut ViewState,
802 window: &mut Window,
803 cx: &mut App,
804 ) -> AnyElement {
805 let island = state.island.id();
806 state.input = None;
807 state.dependencies = Dependencies::default();
808 let states_start = window.element_states_mark();
809 let view = self.view.take().unwrap();
810 let (element, dependencies) = record(state, window, cx, |window, cx| {
811 window.with_layout_island(island, |window| {
812 let mut element = render_view(view, window, cx);
813 let root = element.request_layout(window, cx);
814 (element, root)
815 })
816 });
817 let (element, root) = element;
818 window.set_layout_island_root(island, root);
819 state.dependencies = dependencies;
820 state.render_states = window.element_states_since(states_start);
821 element
822 }
823}
824
825fn record<R>(
827 state: &mut ViewState,
828 window: &mut Window,
829 cx: &mut App,
830 f: impl FnOnce(&mut Window, &mut App) -> R,
831) -> (R, Dependencies) {
832 let outer_reads = window.input_reads.replace(InputReads::empty());
833 let result = cx.record_dependencies(|cx| f(window, cx));
834 let reads = window.input_reads.get();
835 window.input_reads.set(outer_reads | reads);
836 if !reads.is_empty() {
837 let previous = state
838 .input
839 .take()
840 .map_or(InputReads::empty(), |input| input.reads);
841 state.input = Some(InputSnapshot::new(previous | reads, window));
842 }
843 result
844}
845
846pub struct EmptyView;
851
852impl Render for EmptyView {
853 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
854 Empty
855 }
856}