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