use crate::{ AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId, Entity, EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity, }; use crate::{Empty, Window}; use anyhow::Result; use collections::FxHashSet; use refineable::Refineable; use std::mem; use std::rc::Rc; use std::{any::TypeId, fmt, ops::Range}; struct AnyViewState { prepaint_range: Range, paint_range: Range, cache_key: ViewCacheKey, accessed_entities: FxHashSet, } #[derive(Default)] struct ViewCacheKey { bounds: Bounds, content_mask: ContentMask, text_style: TextStyle, } impl Element for Entity { type RequestLayoutState = AnyElement; type PrepaintState = (); fn id(&self) -> Option { Some(ElementId::View(self.entity_id())) } fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { None } fn request_layout( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App, ) -> (LayoutId, Self::RequestLayoutState) { let mut element = self.update(cx, |view, cx| view.render(window, cx).into_any_element()); let layout_id = window.with_rendered_view(self.entity_id(), |window| { element.request_layout(window, cx) }); (layout_id, element) } fn prepaint( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, _: Bounds, element: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App, ) { window.set_view_id(self.entity_id()); window.with_rendered_view(self.entity_id(), |window| element.prepaint(window, cx)); } fn paint( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, _: Bounds, element: &mut Self::RequestLayoutState, _: &mut Self::PrepaintState, window: &mut Window, cx: &mut App, ) { window.with_rendered_view(self.entity_id(), |window| element.paint(window, cx)); } } /// A dynamically-typed handle to a view, which can be downcast to a [Entity] for a specific type. #[derive(Clone, Debug)] pub struct AnyView { entity: AnyEntity, render: fn(&AnyView, &mut Window, &mut App) -> AnyElement, cached_style: Option>, } impl From> for AnyView { fn from(value: Entity) -> Self { AnyView { entity: value.into_any(), render: any_view::render::, cached_style: None, } } } impl AnyView { /// Indicate that this view should be cached when using it as an element. /// When using this method, the view's previous layout and paint will be recycled from the previous frame if [Context::notify] has not been called since it was rendered. /// The one exception is when [Window::refresh] is called, in which case caching is ignored. pub fn cached(mut self, style: StyleRefinement) -> Self { self.cached_style = Some(style.into()); self } /// Convert this to a weak handle. pub fn downgrade(&self) -> AnyWeakView { AnyWeakView { entity: self.entity.downgrade(), render: self.render, } } /// Convert this to a [Entity] of a specific type. /// If this handle does not contain a view of the specified type, returns itself in an `Err` variant. pub fn downcast(self) -> Result, Self> { match self.entity.downcast() { Ok(entity) => Ok(entity), Err(entity) => Err(Self { entity, render: self.render, cached_style: self.cached_style, }), } } /// Gets the [TypeId] of the underlying view. pub fn entity_type(&self) -> TypeId { self.entity.entity_type } /// Gets the entity id of this handle. pub fn entity_id(&self) -> EntityId { self.entity.entity_id() } } impl PartialEq for AnyView { fn eq(&self, other: &Self) -> bool { self.entity == other.entity } } impl Eq for AnyView {} impl Element for AnyView { type RequestLayoutState = Option; type PrepaintState = Option; fn id(&self) -> Option { Some(ElementId::View(self.entity_id())) } fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { None } fn request_layout( &mut self, _id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, window: &mut Window, cx: &mut App, ) -> (LayoutId, Self::RequestLayoutState) { window.with_rendered_view(self.entity_id(), |window| { // Disable caching when inspecting so that mouse_hit_test has all hitboxes. let caching_disabled = window.is_inspector_picking(cx); match self.cached_style.as_ref() { Some(style) if !caching_disabled => { let mut root_style = Style::default(); root_style.refine(style); let layout_id = window.request_layout(root_style, None, cx); (layout_id, None) } _ => { let mut element = (self.render)(self, window, cx); let layout_id = element.request_layout(window, cx); (layout_id, Some(element)) } } }) } fn prepaint( &mut self, global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, bounds: Bounds, element: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App, ) -> Option { window.set_view_id(self.entity_id()); window.with_rendered_view(self.entity_id(), |window| { if let Some(mut element) = element.take() { element.prepaint(window, cx); return Some(element); } window.with_element_state::( global_id.unwrap(), |element_state, window| { let content_mask = window.content_mask(); let text_style = window.text_style(); if let Some(mut element_state) = element_state { if element_state.cache_key.bounds == bounds && element_state.cache_key.content_mask == content_mask && element_state.cache_key.text_style == text_style && !window.dirty_views.contains(&self.entity_id()) && !window.refreshing { let prepaint_start = window.prepaint_index(); window.reuse_prepaint(element_state.prepaint_range.clone()); cx.entities .extend_accessed(&element_state.accessed_entities); let prepaint_end = window.prepaint_index(); element_state.prepaint_range = prepaint_start..prepaint_end; return (None, element_state); } } let refreshing = mem::replace(&mut window.refreshing, true); let prepaint_start = window.prepaint_index(); let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| { let mut element = (self.render)(self, window, cx); element.layout_as_root(bounds.size.into(), window, cx); element.prepaint_at(bounds.origin, window, cx); element }); let prepaint_end = window.prepaint_index(); window.refreshing = refreshing; ( Some(element), AnyViewState { accessed_entities, prepaint_range: prepaint_start..prepaint_end, paint_range: PaintIndex::default()..PaintIndex::default(), cache_key: ViewCacheKey { bounds, content_mask, text_style, }, }, ) }, ) }) } fn paint( &mut self, global_id: Option<&GlobalElementId>, _inspector_id: Option<&InspectorElementId>, _bounds: Bounds, _: &mut Self::RequestLayoutState, element: &mut Self::PrepaintState, window: &mut Window, cx: &mut App, ) { window.with_rendered_view(self.entity_id(), |window| { let caching_disabled = window.is_inspector_picking(cx); if self.cached_style.is_some() && !caching_disabled { window.with_element_state::( global_id.unwrap(), |element_state, window| { let mut element_state = element_state.unwrap(); let paint_start = window.paint_index(); if let Some(element) = element { let refreshing = mem::replace(&mut window.refreshing, true); element.paint(window, cx); window.refreshing = refreshing; } else { window.reuse_paint(element_state.paint_range.clone()); } let paint_end = window.paint_index(); element_state.paint_range = paint_start..paint_end; ((), element_state) }, ) } else { element.as_mut().unwrap().paint(window, cx); } }); } } impl IntoElement for Entity { type Element = Entity; fn into_element(self) -> Self::Element { self } } impl IntoElement for AnyView { type Element = Self; fn into_element(self) -> Self::Element { self } } /// A weak, dynamically-typed view handle that does not prevent the view from being released. pub struct AnyWeakView { entity: AnyWeakEntity, render: fn(&AnyView, &mut Window, &mut App) -> AnyElement, } impl AnyWeakView { /// Convert to a strongly-typed handle if the referenced view has not yet been released. pub fn upgrade(&self) -> Option { let entity = self.entity.upgrade()?; Some(AnyView { entity, render: self.render, cached_style: None, }) } } impl From> for AnyWeakView { fn from(view: WeakEntity) -> Self { AnyWeakView { entity: view.into(), render: any_view::render::, } } } impl PartialEq for AnyWeakView { fn eq(&self, other: &Self) -> bool { self.entity == other.entity } } impl std::fmt::Debug for AnyWeakView { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AnyWeakView") .field("entity_id", &self.entity.entity_id) .finish_non_exhaustive() } } mod any_view { use crate::{AnyElement, AnyView, App, IntoElement, Render, Window}; pub(crate) fn render( view: &AnyView, window: &mut Window, cx: &mut App, ) -> AnyElement { let view = view.clone().downcast::().unwrap(); view.update(cx, |view, cx| view.render(window, cx).into_any_element()) } } /// A view that renders nothing pub struct EmptyView; impl Render for EmptyView { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { Empty } }