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}