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