view.rs

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