view.rs

  1use crate::Empty;
  2use crate::{
  3    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
  4    ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, GlobalElementId, IntoElement,
  5    LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
  6    TextStyle, ViewContext, VisualContext, WeakModel, WindowContext,
  7};
  8use anyhow::{Context, Result};
  9use refineable::Refineable;
 10use std::{
 11    any::{type_name, TypeId},
 12    fmt,
 13    hash::{Hash, Hasher},
 14    ops::Range,
 15};
 16
 17/// A view is a piece of state that can be presented on screen by implementing the [Render] trait.
 18/// Views implement [Element] and can composed with other views, and every window is created with a root view.
 19pub struct View<V> {
 20    /// A view is just a [Model] whose type implements `Render`, and the model is accessible via this field.
 21    pub model: Model<V>,
 22}
 23
 24impl<V> Sealed for View<V> {}
 25
 26struct AnyViewState {
 27    prepaint_range: Range<PrepaintStateIndex>,
 28    paint_range: Range<PaintIndex>,
 29    cache_key: ViewCacheKey,
 30}
 31
 32#[derive(Default)]
 33struct ViewCacheKey {
 34    bounds: Bounds<Pixels>,
 35    content_mask: ContentMask<Pixels>,
 36    text_style: TextStyle,
 37}
 38
 39impl<V: 'static> Entity<V> for View<V> {
 40    type Weak = WeakView<V>;
 41
 42    fn entity_id(&self) -> EntityId {
 43        self.model.entity_id
 44    }
 45
 46    fn downgrade(&self) -> Self::Weak {
 47        WeakView {
 48            model: self.model.downgrade(),
 49        }
 50    }
 51
 52    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
 53    where
 54        Self: Sized,
 55    {
 56        let model = weak.model.upgrade()?;
 57        Some(View { model })
 58    }
 59}
 60
 61impl<V: 'static> View<V> {
 62    /// Convert this strong view reference into a weak view reference.
 63    pub fn downgrade(&self) -> WeakView<V> {
 64        Entity::downgrade(self)
 65    }
 66
 67    /// Updates the view's state with the given function, which is passed a mutable reference and a context.
 68    pub fn update<C, R>(
 69        &self,
 70        cx: &mut C,
 71        f: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
 72    ) -> C::Result<R>
 73    where
 74        C: VisualContext,
 75    {
 76        cx.update_view(self, f)
 77    }
 78
 79    /// Obtain a read-only reference to this view's state.
 80    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
 81        self.model.read(cx)
 82    }
 83
 84    /// Gets a [FocusHandle] for this view when its state implements [FocusableView].
 85    pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle
 86    where
 87        V: FocusableView,
 88    {
 89        self.read(cx).focus_handle(cx)
 90    }
 91}
 92
 93impl<V: Render> Element for View<V> {
 94    type RequestLayoutState = AnyElement;
 95    type PrepaintState = ();
 96
 97    fn id(&self) -> Option<ElementId> {
 98        Some(ElementId::View(self.entity_id()))
 99    }
100
101    fn request_layout(
102        &mut self,
103        _id: Option<&GlobalElementId>,
104        cx: &mut WindowContext,
105    ) -> (LayoutId, Self::RequestLayoutState) {
106        let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
107        let layout_id = element.request_layout(cx);
108        (layout_id, element)
109    }
110
111    fn prepaint(
112        &mut self,
113        _id: Option<&GlobalElementId>,
114        _: Bounds<Pixels>,
115        element: &mut Self::RequestLayoutState,
116        cx: &mut WindowContext,
117    ) {
118        cx.set_view_id(self.entity_id());
119        element.prepaint(cx);
120    }
121
122    fn paint(
123        &mut self,
124        _id: Option<&GlobalElementId>,
125        _: Bounds<Pixels>,
126        element: &mut Self::RequestLayoutState,
127        _: &mut Self::PrepaintState,
128        cx: &mut WindowContext,
129    ) {
130        element.paint(cx);
131    }
132}
133
134impl<V> Clone for View<V> {
135    fn clone(&self) -> Self {
136        Self {
137            model: self.model.clone(),
138        }
139    }
140}
141
142impl<T> std::fmt::Debug for View<T> {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        f.debug_struct(&format!("View<{}>", type_name::<T>()))
145            .field("entity_id", &self.model.entity_id)
146            .finish_non_exhaustive()
147    }
148}
149
150impl<V> Hash for View<V> {
151    fn hash<H: Hasher>(&self, state: &mut H) {
152        self.model.hash(state);
153    }
154}
155
156impl<V> PartialEq for View<V> {
157    fn eq(&self, other: &Self) -> bool {
158        self.model == other.model
159    }
160}
161
162impl<V> Eq for View<V> {}
163
164/// A weak variant of [View] which does not prevent the view from being released.
165pub struct WeakView<V> {
166    pub(crate) model: WeakModel<V>,
167}
168
169impl<V: 'static> WeakView<V> {
170    /// Gets the entity id associated with this handle.
171    pub fn entity_id(&self) -> EntityId {
172        self.model.entity_id
173    }
174
175    /// Obtain a strong handle for the view if it hasn't been released.
176    pub fn upgrade(&self) -> Option<View<V>> {
177        Entity::upgrade_from(self)
178    }
179
180    /// Updates this view's state if it hasn't been released.
181    /// Returns an error if this view has been released.
182    pub fn update<C, R>(
183        &self,
184        cx: &mut C,
185        f: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
186    ) -> Result<R>
187    where
188        C: VisualContext,
189        Result<C::Result<R>>: Flatten<R>,
190    {
191        let view = self.upgrade().context("error upgrading view")?;
192        Ok(view.update(cx, f)).flatten()
193    }
194
195    /// Assert that the view referenced by this handle has been released.
196    #[cfg(any(test, feature = "test-support"))]
197    pub fn assert_released(&self) {
198        self.model.assert_released()
199    }
200}
201
202impl<V> Clone for WeakView<V> {
203    fn clone(&self) -> Self {
204        Self {
205            model: self.model.clone(),
206        }
207    }
208}
209
210impl<V> Hash for WeakView<V> {
211    fn hash<H: Hasher>(&self, state: &mut H) {
212        self.model.hash(state);
213    }
214}
215
216impl<V> PartialEq for WeakView<V> {
217    fn eq(&self, other: &Self) -> bool {
218        self.model == other.model
219    }
220}
221
222impl<V> Eq for WeakView<V> {}
223
224/// A dynamically-typed handle to a view, which can be downcast to a [View] for a specific type.
225#[derive(Clone, Debug)]
226pub struct AnyView {
227    model: AnyModel,
228    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
229    cached_style: Option<StyleRefinement>,
230}
231
232impl AnyView {
233    /// Indicate that this view should be cached when using it as an element.
234    /// When using this method, the view's previous layout and paint will be recycled from the previous frame if [ViewContext::notify] has not been called since it was rendered.
235    /// The one exception is when [WindowContext::refresh] is called, in which case caching is ignored.
236    pub fn cached(mut self, style: StyleRefinement) -> Self {
237        self.cached_style = Some(style);
238        self
239    }
240
241    /// Convert this to a weak handle.
242    pub fn downgrade(&self) -> AnyWeakView {
243        AnyWeakView {
244            model: self.model.downgrade(),
245            render: self.render,
246        }
247    }
248
249    /// Convert this to a [View] of a specific type.
250    /// If this handle does not contain a view of the specified type, returns itself in an `Err` variant.
251    pub fn downcast<T: 'static>(self) -> Result<View<T>, Self> {
252        match self.model.downcast() {
253            Ok(model) => Ok(View { model }),
254            Err(model) => Err(Self {
255                model,
256                render: self.render,
257                cached_style: self.cached_style,
258            }),
259        }
260    }
261
262    /// Gets the [TypeId] of the underlying view.
263    pub fn entity_type(&self) -> TypeId {
264        self.model.entity_type
265    }
266
267    /// Gets the entity id of this handle.
268    pub fn entity_id(&self) -> EntityId {
269        self.model.entity_id()
270    }
271}
272
273impl<V: Render> From<View<V>> for AnyView {
274    fn from(value: View<V>) -> Self {
275        AnyView {
276            model: value.model.into_any(),
277            render: any_view::render::<V>,
278            cached_style: None,
279        }
280    }
281}
282
283impl PartialEq for AnyView {
284    fn eq(&self, other: &Self) -> bool {
285        self.model == other.model
286    }
287}
288
289impl Eq for AnyView {}
290
291impl Element for AnyView {
292    type RequestLayoutState = Option<AnyElement>;
293    type PrepaintState = Option<AnyElement>;
294
295    fn id(&self) -> Option<ElementId> {
296        Some(ElementId::View(self.entity_id()))
297    }
298
299    fn request_layout(
300        &mut self,
301        _id: Option<&GlobalElementId>,
302        cx: &mut WindowContext,
303    ) -> (LayoutId, Self::RequestLayoutState) {
304        if let Some(style) = self.cached_style.as_ref() {
305            let mut root_style = Style::default();
306            root_style.refine(style);
307            let layout_id = cx.request_layout(root_style, None);
308            (layout_id, None)
309        } else {
310            let mut element = (self.render)(self, cx);
311            let layout_id = element.request_layout(cx);
312            (layout_id, Some(element))
313        }
314    }
315
316    fn prepaint(
317        &mut self,
318        global_id: Option<&GlobalElementId>,
319        bounds: Bounds<Pixels>,
320        element: &mut Self::RequestLayoutState,
321        cx: &mut WindowContext,
322    ) -> Option<AnyElement> {
323        cx.set_view_id(self.entity_id());
324        if self.cached_style.is_some() {
325            cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
326                let content_mask = cx.content_mask();
327                let text_style = cx.text_style();
328
329                if let Some(mut element_state) = element_state {
330                    if element_state.cache_key.bounds == bounds
331                        && element_state.cache_key.content_mask == content_mask
332                        && element_state.cache_key.text_style == text_style
333                        && !cx.window.dirty_views.contains(&self.entity_id())
334                        && !cx.window.refreshing
335                    {
336                        let prepaint_start = cx.prepaint_index();
337                        cx.reuse_prepaint(element_state.prepaint_range.clone());
338                        let prepaint_end = cx.prepaint_index();
339                        element_state.prepaint_range = prepaint_start..prepaint_end;
340                        return (None, element_state);
341                    }
342                }
343
344                let prepaint_start = cx.prepaint_index();
345                let mut element = (self.render)(self, cx);
346                element.layout_as_root(bounds.size.into(), cx);
347                element.prepaint_at(bounds.origin, cx);
348                let prepaint_end = cx.prepaint_index();
349
350                (
351                    Some(element),
352                    AnyViewState {
353                        prepaint_range: prepaint_start..prepaint_end,
354                        paint_range: PaintIndex::default()..PaintIndex::default(),
355                        cache_key: ViewCacheKey {
356                            bounds,
357                            content_mask,
358                            text_style,
359                        },
360                    },
361                )
362            })
363        } else {
364            let mut element = element.take().unwrap();
365            element.prepaint(cx);
366            Some(element)
367        }
368    }
369
370    fn paint(
371        &mut self,
372        global_id: Option<&GlobalElementId>,
373        _bounds: Bounds<Pixels>,
374        _: &mut Self::RequestLayoutState,
375        element: &mut Self::PrepaintState,
376        cx: &mut WindowContext,
377    ) {
378        if self.cached_style.is_some() {
379            cx.with_element_state::<AnyViewState, _>(global_id.unwrap(), |element_state, cx| {
380                let mut element_state = element_state.unwrap();
381
382                let paint_start = cx.paint_index();
383
384                if let Some(element) = element {
385                    element.paint(cx);
386                } else {
387                    cx.reuse_paint(element_state.paint_range.clone());
388                }
389
390                let paint_end = cx.paint_index();
391                element_state.paint_range = paint_start..paint_end;
392
393                ((), element_state)
394            })
395        } else {
396            element.as_mut().unwrap().paint(cx);
397        }
398    }
399}
400
401impl<V: 'static + Render> IntoElement for View<V> {
402    type Element = View<V>;
403
404    fn into_element(self) -> Self::Element {
405        self
406    }
407}
408
409impl IntoElement for AnyView {
410    type Element = Self;
411
412    fn into_element(self) -> Self::Element {
413        self
414    }
415}
416
417/// A weak, dynamically-typed view handle that does not prevent the view from being released.
418pub struct AnyWeakView {
419    model: AnyWeakModel,
420    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
421}
422
423impl AnyWeakView {
424    /// Convert to a strongly-typed handle if the referenced view has not yet been released.
425    pub fn upgrade(&self) -> Option<AnyView> {
426        let model = self.model.upgrade()?;
427        Some(AnyView {
428            model,
429            render: self.render,
430            cached_style: None,
431        })
432    }
433}
434
435impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
436    fn from(view: WeakView<V>) -> Self {
437        Self {
438            model: view.model.into(),
439            render: any_view::render::<V>,
440        }
441    }
442}
443
444impl PartialEq for AnyWeakView {
445    fn eq(&self, other: &Self) -> bool {
446        self.model == other.model
447    }
448}
449
450impl std::fmt::Debug for AnyWeakView {
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        f.debug_struct("AnyWeakView")
453            .field("entity_id", &self.model.entity_id)
454            .finish_non_exhaustive()
455    }
456}
457
458mod any_view {
459    use crate::{AnyElement, AnyView, IntoElement, Render, WindowContext};
460
461    pub(crate) fn render<V: 'static + Render>(
462        view: &AnyView,
463        cx: &mut WindowContext,
464    ) -> AnyElement {
465        let view = view.clone().downcast::<V>().unwrap();
466        view.update(cx, |view, cx| view.render(cx).into_any_element())
467    }
468}
469
470/// A view that renders nothing
471pub struct EmptyView;
472
473impl Render for EmptyView {
474    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
475        Empty
476    }
477}