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 fn entity_type(&self) -> TypeId {
131 self.entity.entity_type
132 }
133
134 /// Gets the entity id of this handle.
135 pub 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 if 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
226 let refreshing = mem::replace(&mut window.refreshing, true);
227 let prepaint_start = window.prepaint_index();
228 let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| {
229 let mut element = (self.render)(self, window, cx);
230 element.layout_as_root(bounds.size.into(), window, cx);
231 element.prepaint_at(bounds.origin, window, cx);
232 element
233 });
234
235 let prepaint_end = window.prepaint_index();
236 window.refreshing = refreshing;
237
238 (
239 Some(element),
240 AnyViewState {
241 accessed_entities,
242 prepaint_range: prepaint_start..prepaint_end,
243 paint_range: PaintIndex::default()..PaintIndex::default(),
244 cache_key: ViewCacheKey {
245 bounds,
246 content_mask,
247 text_style,
248 },
249 },
250 )
251 },
252 )
253 })
254 }
255
256 fn paint(
257 &mut self,
258 global_id: Option<&GlobalElementId>,
259 _inspector_id: Option<&InspectorElementId>,
260 _bounds: Bounds<Pixels>,
261 _: &mut Self::RequestLayoutState,
262 element: &mut Self::PrepaintState,
263 window: &mut Window,
264 cx: &mut App,
265 ) {
266 window.with_rendered_view(self.entity_id(), |window| {
267 let caching_disabled = window.is_inspector_picking(cx);
268 if self.cached_style.is_some() && !caching_disabled {
269 window.with_element_state::<AnyViewState, _>(
270 global_id.unwrap(),
271 |element_state, window| {
272 let mut element_state = element_state.unwrap();
273
274 let paint_start = window.paint_index();
275
276 if let Some(element) = element {
277 let refreshing = mem::replace(&mut window.refreshing, true);
278 element.paint(window, cx);
279 window.refreshing = refreshing;
280 } else {
281 window.reuse_paint(element_state.paint_range.clone());
282 }
283
284 let paint_end = window.paint_index();
285 element_state.paint_range = paint_start..paint_end;
286
287 ((), element_state)
288 },
289 )
290 } else {
291 element.as_mut().unwrap().paint(window, cx);
292 }
293 });
294 }
295}
296
297impl<V: 'static + Render> IntoElement for Entity<V> {
298 type Element = Entity<V>;
299
300 fn into_element(self) -> Self::Element {
301 self
302 }
303}
304
305impl IntoElement for AnyView {
306 type Element = Self;
307
308 fn into_element(self) -> Self::Element {
309 self
310 }
311}
312
313/// A weak, dynamically-typed view handle that does not prevent the view from being released.
314pub struct AnyWeakView {
315 entity: AnyWeakEntity,
316 render: fn(&AnyView, &mut Window, &mut App) -> AnyElement,
317}
318
319impl AnyWeakView {
320 /// Convert to a strongly-typed handle if the referenced view has not yet been released.
321 pub fn upgrade(&self) -> Option<AnyView> {
322 let entity = self.entity.upgrade()?;
323 Some(AnyView {
324 entity,
325 render: self.render,
326 cached_style: None,
327 })
328 }
329}
330
331impl<V: 'static + Render> From<WeakEntity<V>> for AnyWeakView {
332 fn from(view: WeakEntity<V>) -> Self {
333 AnyWeakView {
334 entity: view.into(),
335 render: any_view::render::<V>,
336 }
337 }
338}
339
340impl PartialEq for AnyWeakView {
341 fn eq(&self, other: &Self) -> bool {
342 self.entity == other.entity
343 }
344}
345
346impl std::fmt::Debug for AnyWeakView {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 f.debug_struct("AnyWeakView")
349 .field("entity_id", &self.entity.entity_id)
350 .finish_non_exhaustive()
351 }
352}
353
354mod any_view {
355 use crate::{AnyElement, AnyView, App, IntoElement, Render, Window};
356
357 pub(crate) fn render<V: 'static + Render>(
358 view: &AnyView,
359 window: &mut Window,
360 cx: &mut App,
361 ) -> AnyElement {
362 let view = view.clone().downcast::<V>().unwrap();
363 view.update(cx, |view, cx| view.render(window, cx).into_any_element())
364 }
365}
366
367/// A view that renders nothing
368pub struct EmptyView;
369
370impl Render for EmptyView {
371 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
372 Empty
373 }
374}