1use crate::{
2 AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, ContentMask, Context, Element, ElementId,
3 Entity, EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, PaintIndex,
4 Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, WeakEntity,
5};
6use crate::{Empty, Window};
7use anyhow::Result;
8use collections::FxHashSet;
9use refineable::Refineable;
10use std::mem;
11use std::rc::Rc;
12use std::{any::TypeId, fmt, ops::Range};
13
14struct AnyViewState {
15 prepaint_range: Range<PrepaintStateIndex>,
16 paint_range: Range<PaintIndex>,
17 cache_key: ViewCacheKey,
18 accessed_entities: FxHashSet<EntityId>,
19}
20
21#[derive(Default)]
22struct ViewCacheKey {
23 bounds: Bounds<Pixels>,
24 content_mask: ContentMask<Pixels>,
25 text_style: TextStyle,
26}
27
28impl<V: Render> Element for Entity<V> {
29 type RequestLayoutState = AnyElement;
30 type PrepaintState = ();
31
32 fn id(&self) -> Option<ElementId> {
33 Some(ElementId::View(self.entity_id()))
34 }
35
36 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
37 None
38 }
39
40 fn request_layout(
41 &mut self,
42 _id: Option<&GlobalElementId>,
43 _inspector_id: Option<&InspectorElementId>,
44 window: &mut Window,
45 cx: &mut App,
46 ) -> (LayoutId, Self::RequestLayoutState) {
47 let mut element = self.update(cx, |view, cx| view.render(window, cx).into_any_element());
48 let layout_id = window.with_rendered_view(self.entity_id(), |window| {
49 element.request_layout(window, cx)
50 });
51 (layout_id, element)
52 }
53
54 fn prepaint(
55 &mut self,
56 _id: Option<&GlobalElementId>,
57 _inspector_id: Option<&InspectorElementId>,
58 _: Bounds<Pixels>,
59 element: &mut Self::RequestLayoutState,
60 window: &mut Window,
61 cx: &mut App,
62 ) {
63 window.set_view_id(self.entity_id());
64 window.with_rendered_view(self.entity_id(), |window| element.prepaint(window, cx));
65 }
66
67 fn paint(
68 &mut self,
69 _id: Option<&GlobalElementId>,
70 _inspector_id: Option<&InspectorElementId>,
71 _: Bounds<Pixels>,
72 element: &mut Self::RequestLayoutState,
73 _: &mut Self::PrepaintState,
74 window: &mut Window,
75 cx: &mut App,
76 ) {
77 window.with_rendered_view(self.entity_id(), |window| element.paint(window, cx));
78 }
79}
80
81/// A dynamically-typed handle to a view, which can be downcast to a [Entity] for a specific type.
82#[derive(Clone, Debug)]
83pub struct AnyView {
84 entity: AnyEntity,
85 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
86 cached_style: Option<Rc<StyleRefinement>>,
87}
88
89impl<V: Render> From<Entity<V>> for AnyView {
90 fn from(value: Entity<V>) -> Self {
91 AnyView {
92 entity: value.into_any(),
93 render: any_view::render::<V>,
94 cached_style: None,
95 }
96 }
97}
98
99impl AnyView {
100 /// Indicate that this view should be cached when using it as an element.
101 /// 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.
102 /// The one exception is when [Window::refresh] is called, in which case caching is ignored.
103 pub fn cached(mut self, style: StyleRefinement) -> Self {
104 self.cached_style = Some(style.into());
105 self
106 }
107
108 /// Convert this to a weak handle.
109 pub fn downgrade(&self) -> AnyWeakView {
110 AnyWeakView {
111 entity: self.entity.downgrade(),
112 render: self.render,
113 }
114 }
115
116 /// Convert this to a [Entity] of a specific type.
117 /// If this handle does not contain a view of the specified type, returns itself in an `Err` variant.
118 pub fn downcast<T: 'static>(self) -> Result<Entity<T>, Self> {
119 match self.entity.downcast() {
120 Ok(entity) => Ok(entity),
121 Err(entity) => Err(Self {
122 entity,
123 render: self.render,
124 cached_style: self.cached_style,
125 }),
126 }
127 }
128
129 /// Gets the [TypeId] of the underlying view.
130 pub const fn entity_type(&self) -> TypeId {
131 self.entity.entity_type
132 }
133
134 /// Gets the entity id of this handle.
135 pub const fn entity_id(&self) -> EntityId {
136 self.entity.entity_id()
137 }
138}
139
140impl PartialEq for AnyView {
141 fn eq(&self, other: &Self) -> bool {
142 self.entity == other.entity
143 }
144}
145
146impl Eq for AnyView {}
147
148impl Element for AnyView {
149 type RequestLayoutState = Option<AnyElement>;
150 type PrepaintState = Option<AnyElement>;
151
152 fn id(&self) -> Option<ElementId> {
153 Some(ElementId::View(self.entity_id()))
154 }
155
156 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
157 None
158 }
159
160 fn request_layout(
161 &mut self,
162 _id: Option<&GlobalElementId>,
163 _inspector_id: Option<&InspectorElementId>,
164 window: &mut Window,
165 cx: &mut App,
166 ) -> (LayoutId, Self::RequestLayoutState) {
167 window.with_rendered_view(self.entity_id(), |window| {
168 // Disable caching when inspecting so that mouse_hit_test has all hitboxes.
169 let caching_disabled = window.is_inspector_picking(cx);
170 match self.cached_style.as_ref() {
171 Some(style) if !caching_disabled => {
172 let mut root_style = Style::default();
173 root_style.refine(style);
174 let layout_id = window.request_layout(root_style, None, cx);
175 (layout_id, None)
176 }
177 _ => {
178 let mut element = (self.render)(self, window, cx);
179 let layout_id = element.request_layout(window, cx);
180 (layout_id, Some(element))
181 }
182 }
183 })
184 }
185
186 fn prepaint(
187 &mut self,
188 global_id: Option<&GlobalElementId>,
189 _inspector_id: Option<&InspectorElementId>,
190 bounds: Bounds<Pixels>,
191 element: &mut Self::RequestLayoutState,
192 window: &mut Window,
193 cx: &mut App,
194 ) -> Option<AnyElement> {
195 window.set_view_id(self.entity_id());
196 window.with_rendered_view(self.entity_id(), |window| {
197 if let Some(mut element) = element.take() {
198 element.prepaint(window, cx);
199 return Some(element);
200 }
201
202 window.with_element_state::<AnyViewState, _>(
203 global_id.unwrap(),
204 |element_state, window| {
205 let content_mask = window.content_mask();
206 let text_style = window.text_style();
207
208 if let Some(mut element_state) = element_state
209 && element_state.cache_key.bounds == bounds
210 && element_state.cache_key.content_mask == content_mask
211 && element_state.cache_key.text_style == text_style
212 && !window.dirty_views.contains(&self.entity_id())
213 && !window.refreshing
214 {
215 let prepaint_start = window.prepaint_index();
216 window.reuse_prepaint(element_state.prepaint_range.clone());
217 cx.entities
218 .extend_accessed(&element_state.accessed_entities);
219 let prepaint_end = window.prepaint_index();
220 element_state.prepaint_range = prepaint_start..prepaint_end;
221
222 return (None, element_state);
223 }
224
225 let refreshing = mem::replace(&mut window.refreshing, true);
226 let prepaint_start = window.prepaint_index();
227 let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
228 let mut element = (self.render)(self, window, cx);
229 element.layout_as_root(bounds.size.into(), window, cx);
230 element.prepaint_at(bounds.origin, window, cx);
231 element
232 });
233
234 let prepaint_end = window.prepaint_index();
235 window.refreshing = refreshing;
236
237 (
238 Some(element),
239 AnyViewState {
240 accessed_entities,
241 prepaint_range: prepaint_start..prepaint_end,
242 paint_range: PaintIndex::default()..PaintIndex::default(),
243 cache_key: ViewCacheKey {
244 bounds,
245 content_mask,
246 text_style,
247 },
248 },
249 )
250 },
251 )
252 })
253 }
254
255 fn paint(
256 &mut self,
257 global_id: Option<&GlobalElementId>,
258 _inspector_id: Option<&InspectorElementId>,
259 _bounds: Bounds<Pixels>,
260 _: &mut Self::RequestLayoutState,
261 element: &mut Self::PrepaintState,
262 window: &mut Window,
263 cx: &mut App,
264 ) {
265 window.with_rendered_view(self.entity_id(), |window| {
266 let caching_disabled = window.is_inspector_picking(cx);
267 if self.cached_style.is_some() && !caching_disabled {
268 window.with_element_state::<AnyViewState, _>(
269 global_id.unwrap(),
270 |element_state, window| {
271 let mut element_state = element_state.unwrap();
272
273 let paint_start = window.paint_index();
274
275 if let Some(element) = element {
276 let refreshing = mem::replace(&mut window.refreshing, true);
277 element.paint(window, cx);
278 window.refreshing = refreshing;
279 } else {
280 window.reuse_paint(element_state.paint_range.clone());
281 }
282
283 let paint_end = window.paint_index();
284 element_state.paint_range = paint_start..paint_end;
285
286 ((), element_state)
287 },
288 )
289 } else {
290 element.as_mut().unwrap().paint(window, cx);
291 }
292 });
293 }
294}
295
296impl<V: 'static + Render> IntoElement for Entity<V> {
297 type Element = Entity<V>;
298
299 fn into_element(self) -> Self::Element {
300 self
301 }
302}
303
304impl IntoElement for AnyView {
305 type Element = Self;
306
307 fn into_element(self) -> Self::Element {
308 self
309 }
310}
311
312/// A weak, dynamically-typed view handle that does not prevent the view from being released.
313pub struct AnyWeakView {
314 entity: AnyWeakEntity,
315 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
316}
317
318impl AnyWeakView {
319 /// Convert to a strongly-typed handle if the referenced view has not yet been released.
320 pub fn upgrade(&self) -> Option<AnyView> {
321 let entity = self.entity.upgrade()?;
322 Some(AnyView {
323 entity,
324 render: self.render,
325 cached_style: None,
326 })
327 }
328}
329
330impl<V: 'static + Render> From<WeakEntity<V>> for AnyWeakView {
331 fn from(view: WeakEntity<V>) -> Self {
332 AnyWeakView {
333 entity: view.into(),
334 render: any_view::render::<V>,
335 }
336 }
337}
338
339impl PartialEq for AnyWeakView {
340 fn eq(&self, other: &Self) -> bool {
341 self.entity == other.entity
342 }
343}
344
345impl std::fmt::Debug for AnyWeakView {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 f.debug_struct("AnyWeakView")
348 .field("entity_id", &self.entity.entity_id)
349 .finish_non_exhaustive()
350 }
351}
352
353mod any_view {
354 use crate::{AnyElement, AnyView, App, IntoElement, Render, Window};
355
356 pub(crate) fn render<V: 'static + Render>(
357 view: &AnyView,
358 window: &mut Window,
359 cx: &mut App,
360 ) -> AnyElement {
361 let view = view.clone().downcast::<V>().unwrap();
362 view.update(cx, |view, cx| view.render(window, cx).into_any_element())
363 }
364}
365
366/// A view that renders nothing
367pub struct EmptyView;
368
369impl Render for EmptyView {
370 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
371 Empty
372 }
373}