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 let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
93 let layout_id = element.request_layout(cx);
94 (layout_id, Some(element))
95 }
96
97 fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
98 cx.with_view_id(self.entity_id(), |cx| element.take().unwrap().paint(cx));
99 }
100}
101
102impl<V> Clone for View<V> {
103 fn clone(&self) -> Self {
104 Self {
105 model: self.model.clone(),
106 }
107 }
108}
109
110impl<T> std::fmt::Debug for View<T> {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.debug_struct(&format!("View<{}>", type_name::<T>()))
113 .field("entity_id", &self.model.entity_id)
114 .finish_non_exhaustive()
115 }
116}
117
118impl<V> Hash for View<V> {
119 fn hash<H: Hasher>(&self, state: &mut H) {
120 self.model.hash(state);
121 }
122}
123
124impl<V> PartialEq for View<V> {
125 fn eq(&self, other: &Self) -> bool {
126 self.model == other.model
127 }
128}
129
130impl<V> Eq for View<V> {}
131
132pub struct WeakView<V> {
133 pub(crate) model: WeakModel<V>,
134}
135
136impl<V: 'static> WeakView<V> {
137 pub fn entity_id(&self) -> EntityId {
138 self.model.entity_id
139 }
140
141 pub fn upgrade(&self) -> Option<View<V>> {
142 Entity::upgrade_from(self)
143 }
144
145 pub fn update<C, R>(
146 &self,
147 cx: &mut C,
148 f: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
149 ) -> Result<R>
150 where
151 C: VisualContext,
152 Result<C::Result<R>>: Flatten<R>,
153 {
154 let view = self.upgrade().context("error upgrading view")?;
155 Ok(view.update(cx, f)).flatten()
156 }
157
158 #[cfg(any(test, feature = "test-support"))]
159 pub fn assert_dropped(&self) {
160 self.model.assert_dropped()
161 }
162}
163
164impl<V> Clone for WeakView<V> {
165 fn clone(&self) -> Self {
166 Self {
167 model: self.model.clone(),
168 }
169 }
170}
171
172impl<V> Hash for WeakView<V> {
173 fn hash<H: Hasher>(&self, state: &mut H) {
174 self.model.hash(state);
175 }
176}
177
178impl<V> PartialEq for WeakView<V> {
179 fn eq(&self, other: &Self) -> bool {
180 self.model == other.model
181 }
182}
183
184impl<V> Eq for WeakView<V> {}
185
186#[derive(Clone, Debug)]
187pub struct AnyView {
188 model: AnyModel,
189 request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
190 cache: bool,
191}
192
193impl AnyView {
194 pub fn cached(mut self) -> Self {
195 self.cache = true;
196 self
197 }
198
199 pub fn downgrade(&self) -> AnyWeakView {
200 AnyWeakView {
201 model: self.model.downgrade(),
202 layout: self.request_layout,
203 }
204 }
205
206 pub fn downcast<T: 'static>(self) -> Result<View<T>, Self> {
207 match self.model.downcast() {
208 Ok(model) => Ok(View { model }),
209 Err(model) => Err(Self {
210 model,
211 request_layout: self.request_layout,
212 cache: self.cache,
213 }),
214 }
215 }
216
217 pub fn entity_type(&self) -> TypeId {
218 self.model.entity_type
219 }
220
221 pub fn entity_id(&self) -> EntityId {
222 self.model.entity_id()
223 }
224
225 pub(crate) fn draw(
226 &self,
227 origin: Point<Pixels>,
228 available_space: Size<AvailableSpace>,
229 cx: &mut WindowContext,
230 ) {
231 cx.with_absolute_element_offset(origin, |cx| {
232 let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
233 cx.compute_layout(layout_id, available_space);
234 rendered_element.paint(cx);
235 })
236 }
237}
238
239impl<V: Render> From<View<V>> for AnyView {
240 fn from(value: View<V>) -> Self {
241 AnyView {
242 model: value.model.into_any(),
243 request_layout: any_view::request_layout::<V>,
244 cache: false,
245 }
246 }
247}
248
249impl Element for AnyView {
250 type State = AnyViewState;
251
252 fn request_layout(
253 &mut self,
254 state: Option<Self::State>,
255 cx: &mut WindowContext,
256 ) -> (LayoutId, Self::State) {
257 if self.cache {
258 if let Some(state) = state {
259 let layout_id = cx.request_layout(&state.root_style, None);
260 return (layout_id, state);
261 }
262 }
263
264 let (layout_id, element) = (self.request_layout)(self, cx);
265 let root_style = cx.layout_style(layout_id).unwrap().clone();
266 let state = AnyViewState {
267 root_style,
268 cache_key: None,
269 element: Some(element),
270 };
271 (layout_id, state)
272 }
273
274 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
275 cx.with_view_id(self.entity_id(), |cx| {
276 if !self.cache {
277 state.element.take().unwrap().paint(cx);
278 return;
279 }
280
281 if let Some(cache_key) = state.cache_key.as_mut() {
282 if cache_key.bounds == bounds
283 && cache_key.content_mask == cx.content_mask()
284 && cache_key.stacking_order == *cx.stacking_order()
285 && cache_key.text_style == cx.text_style()
286 && !cx.window.dirty_views.contains(&self.entity_id())
287 {
288 cx.reuse_geometry();
289 return;
290 }
291 }
292
293 let mut element = state
294 .element
295 .take()
296 .unwrap_or_else(|| (self.request_layout)(self, cx).1);
297 element.draw(bounds.origin, bounds.size.into(), cx);
298
299 state.cache_key = Some(ViewCacheKey {
300 bounds,
301 stacking_order: cx.stacking_order().clone(),
302 content_mask: cx.content_mask(),
303 text_style: cx.text_style(),
304 });
305 })
306 }
307}
308
309impl<V: 'static + Render> IntoElement for View<V> {
310 type Element = View<V>;
311
312 fn element_id(&self) -> Option<ElementId> {
313 Some(ElementId::from_entity_id(self.model.entity_id))
314 }
315
316 fn into_element(self) -> Self::Element {
317 self
318 }
319}
320
321impl IntoElement for AnyView {
322 type Element = Self;
323
324 fn element_id(&self) -> Option<ElementId> {
325 Some(ElementId::from_entity_id(self.model.entity_id))
326 }
327
328 fn into_element(self) -> Self::Element {
329 self
330 }
331}
332
333pub struct AnyWeakView {
334 model: AnyWeakModel,
335 layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
336}
337
338impl AnyWeakView {
339 pub fn upgrade(&self) -> Option<AnyView> {
340 let model = self.model.upgrade()?;
341 Some(AnyView {
342 model,
343 request_layout: self.layout,
344 cache: false,
345 })
346 }
347}
348
349impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
350 fn from(view: WeakView<V>) -> Self {
351 Self {
352 model: view.model.into(),
353 layout: any_view::request_layout::<V>,
354 }
355 }
356}
357
358impl PartialEq for AnyWeakView {
359 fn eq(&self, other: &Self) -> bool {
360 self.model == other.model
361 }
362}
363
364impl std::fmt::Debug for AnyWeakView {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 f.debug_struct("AnyWeakView")
367 .field("entity_id", &self.model.entity_id)
368 .finish_non_exhaustive()
369 }
370}
371
372mod any_view {
373 use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
374
375 pub(crate) fn request_layout<V: 'static + Render>(
376 view: &AnyView,
377 cx: &mut WindowContext,
378 ) -> (LayoutId, AnyElement) {
379 let view = view.clone().downcast::<V>().unwrap();
380 let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element());
381 let layout_id = element.request_layout(cx);
382 (layout_id, element)
383 }
384}