view.rs

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