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}