Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/core/view/handle.rs
StratoSDK / crates / strato-ui-core / src / core / view / handle.rs
1use std::{
2 any::TypeId,
3 fmt::{self, Debug},
4 marker::PhantomData,
5 sync::{Arc, Weak},
6};
7 
8use parking_lot::Mutex;
9 
10use crate::{core::RefCounts, AppContext, EntityId, WindowId};
11 
12use super::{context::ViewContext, View};
13 
14/// A strong reference to a particular [`View`] instance within the application.
15///
16/// Handles structures are used in place of references (e.g.: `&View`) to avoid
17/// the complexity of reference lifetimes and appeasing the borrow checker. A
18/// handle can be combined with a reference to the application state (e.g.:
19/// [`AppContext`]) to get access to the actual [`View`] instance behind the
20/// handle.
21pub struct ViewHandle<T> {
22 window_id: WindowId,
23 view_id: EntityId,
24 view_type: PhantomData<T>,
25 ref_counts: Weak<Mutex<RefCounts>>,
26}
27 
28impl<T: View> ViewHandle<T> {
29 pub(in crate::core) fn new(
30 window_id: WindowId,
31 view_id: EntityId,
32 ref_counts: &Arc<Mutex<RefCounts>>,
33 ) -> Self {
34 ref_counts.lock().inc_entity(view_id);
35 Self {
36 window_id,
37 view_id,
38 view_type: PhantomData,
39 ref_counts: Arc::downgrade(ref_counts),
40 }
41 }
42 
43 pub fn downgrade(&self) -> WeakViewHandle<T> {
44 WeakViewHandle::new(self.view_id)
45 }
46 
47 /// Returns the current window this view belongs to.
48 ///
49 /// This looks up the window from the view_to_window mapping, which may differ
50 /// from the window where the view was originally created if the view has been
51 /// transferred between windows.
52 pub fn window_id(&self, app: &AppContext) -> WindowId {
53 app.view_to_window
54 .get(&self.view_id)
55 .copied()
56 .unwrap_or(self.window_id)
57 }
58 
59 pub fn id(&self) -> EntityId {
60 self.view_id
61 }
62 
63 /// Convert a ViewHandle to a reference of the underlying View.
64 pub fn as_ref<'a, A: ViewAsRef>(&self, app: &'a A) -> &'a T {
65 app.view(self)
66 }
67 
68 /// Try to convert a ViewHandle to a reference of the underlying View.
69 /// Returns `None` if the view is currently borrowed (circular reference).
70 pub fn try_as_ref<'a, A: ViewAsRef>(&self, app: &'a A) -> Option<&'a T> {
71 app.try_view(self)
72 }
73 
74 /// Reads a value out of the underlying View. This is especially useful when the view
75 /// has a function that requires a `ViewContext` since `as_ref` does not create a `ViewHandle`
76 /// to the view.
77 pub fn read<A, F, S>(&self, app: &A, read: F) -> S
78 where
79 A: crate::ReadView,
80 F: FnOnce(&T, &AppContext) -> S,
81 {
82 app.read_view(self, read)
83 }
84 
85 /// Updates a value within the underlying View.
86 pub fn update<A, F, S>(&self, app: &mut A, update: F) -> S
87 where
88 A: UpdateView,
89 F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
90 {
91 app.update_view(self, update)
92 }
93 
94 pub fn is_focused(&self, app: &AppContext) -> bool {
95 app.focused_view_id(self.window_id(app)) == Some(self.view_id)
96 }
97 
98 // TODO: This is the same as the `is_self_or_child_focused` function in ViewContext.
99 // Moving forward we should figure out a better interface to check whether a specific
100 // view is focused or not.
101 pub fn is_self_or_child_focused(&self, app: &mut AppContext) -> bool {
102 let window_id = self.window_id(app);
103 app.check_view_or_child_focused(window_id, &self.view_id)
104 }
105}
106 
107impl<T> Clone for ViewHandle<T> {
108 fn clone(&self) -> Self {
109 if let Some(ref_counts) = self.ref_counts.upgrade() {
110 ref_counts.lock().inc_entity(self.view_id);
111 }
112 
113 Self {
114 window_id: self.window_id,
115 view_id: self.view_id,
116 view_type: PhantomData,
117 ref_counts: self.ref_counts.clone(),
118 }
119 }
120}
121 
122impl<T> PartialEq for ViewHandle<T> {
123 fn eq(&self, other: &Self) -> bool {
124 self.window_id == other.window_id && self.view_id == other.view_id
125 }
126}
127 
128impl<T> Eq for ViewHandle<T> {}
129 
130impl<T> Debug for ViewHandle<T> {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 f.debug_struct(&format!("ViewHandle<{}>", core::any::type_name::<T>()))
133 .field("window_id", &self.window_id)
134 .field("view_id", &self.view_id)
135 .finish()
136 }
137}
138 
139impl<T> Drop for ViewHandle<T> {
140 fn drop(&mut self) {
141 if let Some(ref_counts) = self.ref_counts.upgrade() {
142 ref_counts.lock().dec_view(self.window_id, self.view_id);
143 }
144 }
145}
146 
147unsafe impl<T> Send for ViewHandle<T> {}
148unsafe impl<T> Sync for ViewHandle<T> {}
149 
150/// A type-erased strong reference to a particular [`View`] instance within the
151/// application.
152///
153/// `AnyViewHandle` is used within the core UI framework in places where we need
154/// to hold a strong reference to a `View`, but don't want to add a generic type
155/// parameter to the containing structure. See `root_view` in
156/// [`Window`](crate::core::Window) for an example.
157pub(in crate::core) struct AnyViewHandle {
158 window_id: WindowId,
159 view_id: EntityId,
160 view_type: TypeId,
161 ref_counts: Weak<Mutex<RefCounts>>,
162}
163 
164impl AnyViewHandle {
165 pub fn id(&self) -> EntityId {
166 self.view_id
167 }
168 
169 /// Returns the current window this view belongs to.
170 pub fn window_id(&self, app: &AppContext) -> WindowId {
171 app.view_to_window
172 .get(&self.view_id)
173 .copied()
174 .unwrap_or(self.window_id)
175 }
176 
177 pub fn is<T: 'static>(&self) -> bool {
178 TypeId::of::<T>() == self.view_type
179 }
180 
181 pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
182 if self.is::<T>() {
183 if let Some(ref_counts) = self.ref_counts.upgrade() {
184 return Some(ViewHandle::new(self.window_id, self.view_id, &ref_counts));
185 }
186 }
187 None
188 }
189}
190 
191impl Clone for AnyViewHandle {
192 fn clone(&self) -> Self {
193 if let Some(ref_counts) = self.ref_counts.upgrade() {
194 ref_counts.lock().inc_entity(self.view_id);
195 }
196 
197 Self {
198 view_id: self.view_id,
199 window_id: self.window_id,
200 view_type: self.view_type,
201 ref_counts: self.ref_counts.clone(),
202 }
203 }
204}
205 
206impl<T: View> From<&ViewHandle<T>> for AnyViewHandle {
207 fn from(handle: &ViewHandle<T>) -> Self {
208 if let Some(ref_counts) = handle.ref_counts.upgrade() {
209 ref_counts.lock().inc_entity(handle.view_id);
210 }
211 AnyViewHandle {
212 window_id: handle.window_id,
213 view_id: handle.view_id,
214 view_type: TypeId::of::<T>(),
215 ref_counts: handle.ref_counts.clone(),
216 }
217 }
218}
219 
220impl<T: View> From<ViewHandle<T>> for AnyViewHandle {
221 fn from(handle: ViewHandle<T>) -> Self {
222 (&handle).into()
223 }
224}
225 
226impl Drop for AnyViewHandle {
227 fn drop(&mut self) {
228 if let Some(ref_counts) = self.ref_counts.upgrade() {
229 ref_counts.lock().dec_view(self.window_id, self.view_id);
230 }
231 }
232}
233 
234/// A weak reference to a particular [`View`] instance within the application.
235///
236/// `WeakViewHandle` is useful when a view wants to hold onto its own handle -
237/// holding a strong reference via `ViewHandle` would create a reference cycle
238/// that prevents the application from ever dropping the view.
239pub struct WeakViewHandle<T> {
240 view_id: EntityId,
241 view_type: PhantomData<T>,
242}
243 
244impl<T: View> WeakViewHandle<T> {
245 pub(super) fn new(view_id: EntityId) -> Self {
246 Self {
247 view_id,
248 view_type: PhantomData,
249 }
250 }
251 
252 pub fn upgrade(&self, app: &AppContext) -> Option<ViewHandle<T>> {
253 // Look up the current window for this view
254 let window_id = app.view_to_window.get(&self.view_id).copied()?;
255 
256 if app
257 .windows
258 .get(&window_id)
259 .and_then(|w| w.views.get(&self.view_id))
260 .is_some()
261 {
262 Some(ViewHandle::new(window_id, self.view_id, &app.ref_counts))
263 } else {
264 None
265 }
266 }
267 
268 pub fn id(&self) -> EntityId {
269 self.view_id
270 }
271 
272 /// Returns the current window this view belongs to, if any.
273 pub fn window_id(&self, app: &AppContext) -> Option<WindowId> {
274 app.view_to_window.get(&self.view_id).copied()
275 }
276}
277 
278impl<T> Clone for WeakViewHandle<T> {
279 fn clone(&self) -> Self {
280 Self {
281 view_id: self.view_id,
282 view_type: PhantomData,
283 }
284 }
285}
286 
287impl<T> Debug for WeakViewHandle<T> {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 f.debug_struct(&format!("WeakViewHandle<{}>", core::any::type_name::<T>()))
290 .field("view_id", &self.view_id)
291 .finish()
292 }
293}
294 
295unsafe impl<T> Send for WeakViewHandle<T> {}
296unsafe impl<T> Sync for WeakViewHandle<T> {}
297 
298pub trait ViewAsRef {
299 fn view<T: View>(&self, handle: &ViewHandle<T>) -> &T;
300 
301 /// Try to get a reference to the view. Returns `None` if the view is
302 /// currently borrowed (e.g., during a circular reference scenario).
303 fn try_view<T: View>(&self, handle: &ViewHandle<T>) -> Option<&T>;
304}
305 
306pub trait ReadView: ViewAsRef {
307 fn read_view<T, F, S>(&self, handle: &ViewHandle<T>, read: F) -> S
308 where
309 T: View,
310 F: FnOnce(&T, &AppContext) -> S;
311}
312 
313pub trait UpdateView: ReadView {
314 fn update_view<T, F, S>(&mut self, handle: &ViewHandle<T>, update: F) -> S
315 where
316 T: View,
317 F: FnOnce(&mut T, &mut ViewContext<T>) -> S;
318}
319