view.rs

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