1use crate::{
2 AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, Handle, MainThread,
3 Reference, Subscription, Task, WeakHandle,
4};
5use derive_more::{Deref, DerefMut};
6use futures::FutureExt;
7use std::{
8 any::{Any, TypeId},
9 borrow::{Borrow, BorrowMut},
10 future::Future,
11};
12
13#[derive(Deref, DerefMut)]
14pub struct ModelContext<'a, T> {
15 #[deref]
16 #[deref_mut]
17 app: Reference<'a, AppContext>,
18 model_state: WeakHandle<T>,
19}
20
21impl<'a, T: 'static> ModelContext<'a, T> {
22 pub(crate) fn mutable(app: &'a mut AppContext, model_state: WeakHandle<T>) -> Self {
23 Self {
24 app: Reference::Mutable(app),
25 model_state,
26 }
27 }
28
29 pub fn entity_id(&self) -> EntityId {
30 self.model_state.entity_id
31 }
32
33 pub fn handle(&self) -> Handle<T> {
34 self.weak_handle()
35 .upgrade()
36 .expect("The entity must be alive if we have a model context")
37 }
38
39 pub fn weak_handle(&self) -> WeakHandle<T> {
40 self.model_state.clone()
41 }
42
43 pub fn observe<T2: 'static>(
44 &mut self,
45 handle: &Handle<T2>,
46 mut on_notify: impl FnMut(&mut T, Handle<T2>, &mut ModelContext<'_, T>) + Send + 'static,
47 ) -> Subscription
48 where
49 T: 'static + Send,
50 {
51 let this = self.weak_handle();
52 let handle = handle.downgrade();
53 self.app.observers.insert(
54 handle.entity_id,
55 Box::new(move |cx| {
56 if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
57 this.update(cx, |this, cx| on_notify(this, handle, cx));
58 true
59 } else {
60 false
61 }
62 }),
63 )
64 }
65
66 pub fn subscribe<E: 'static + EventEmitter>(
67 &mut self,
68 handle: &Handle<E>,
69 mut on_event: impl FnMut(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
70 + Send
71 + 'static,
72 ) -> Subscription
73 where
74 T: 'static + Send,
75 {
76 let this = self.weak_handle();
77 let handle = handle.downgrade();
78 self.app.event_listeners.insert(
79 handle.entity_id,
80 Box::new(move |event, cx| {
81 let event: &E::Event = event.downcast_ref().expect("invalid event type");
82 if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
83 this.update(cx, |this, cx| on_event(this, handle, event, cx));
84 true
85 } else {
86 false
87 }
88 }),
89 )
90 }
91
92 pub fn on_release(
93 &mut self,
94 mut on_release: impl FnMut(&mut T, &mut AppContext) + Send + 'static,
95 ) -> Subscription
96 where
97 T: 'static,
98 {
99 self.app.release_listeners.insert(
100 self.model_state.entity_id,
101 Box::new(move |this, cx| {
102 let this = this.downcast_mut().expect("invalid entity type");
103 on_release(this, cx);
104 }),
105 )
106 }
107
108 pub fn observe_release<E: 'static>(
109 &mut self,
110 handle: &Handle<E>,
111 mut on_release: impl FnMut(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + 'static,
112 ) -> Subscription
113 where
114 T: Any + Send,
115 {
116 let this = self.weak_handle();
117 self.app.observe_release(handle, move |entity, cx| {
118 if let Some(this) = this.upgrade() {
119 this.update(cx, |this, cx| on_release(this, entity, cx));
120 }
121 })
122 }
123
124 pub fn observe_global<G: 'static>(
125 &mut self,
126 mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + Send + 'static,
127 ) -> Subscription
128 where
129 T: 'static + Send,
130 {
131 let handle = self.weak_handle();
132 self.global_observers.insert(
133 TypeId::of::<G>(),
134 Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
135 )
136 }
137
138 pub fn on_app_quit<Fut>(
139 &mut self,
140 mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + Send + 'static,
141 ) -> Subscription
142 where
143 Fut: 'static + Future<Output = ()> + Send,
144 T: 'static + Send,
145 {
146 let handle = self.weak_handle();
147 self.app.quit_observers.insert(
148 (),
149 Box::new(move |cx| {
150 let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok();
151 async move {
152 if let Some(future) = future {
153 future.await;
154 }
155 }
156 .boxed()
157 }),
158 )
159 }
160
161 pub fn notify(&mut self) {
162 if self
163 .app
164 .pending_notifications
165 .insert(self.model_state.entity_id)
166 {
167 self.app.pending_effects.push_back(Effect::Notify {
168 emitter: self.model_state.entity_id,
169 });
170 }
171 }
172
173 pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
174 where
175 G: 'static + Send,
176 {
177 let mut global = self.app.lease_global::<G>();
178 let result = f(&mut global, self);
179 self.app.end_global_lease(global);
180 result
181 }
182
183 pub fn spawn<Fut, R>(
184 &self,
185 f: impl FnOnce(WeakHandle<T>, AsyncAppContext) -> Fut + Send + 'static,
186 ) -> Task<R>
187 where
188 T: 'static,
189 Fut: Future<Output = R> + Send + 'static,
190 R: Send + 'static,
191 {
192 let this = self.weak_handle();
193 self.app.spawn(|cx| f(this, cx))
194 }
195
196 pub fn spawn_on_main<Fut, R>(
197 &self,
198 f: impl FnOnce(WeakHandle<T>, MainThread<AsyncAppContext>) -> Fut + Send + 'static,
199 ) -> Task<R>
200 where
201 Fut: Future<Output = R> + 'static,
202 R: Send + 'static,
203 {
204 let this = self.weak_handle();
205 self.app.spawn_on_main(|cx| f(this, cx))
206 }
207}
208
209impl<'a, T> ModelContext<'a, T>
210where
211 T: EventEmitter,
212 T::Event: Send,
213{
214 pub fn emit(&mut self, event: T::Event) {
215 self.app.pending_effects.push_back(Effect::Emit {
216 emitter: self.model_state.entity_id,
217 event: Box::new(event),
218 });
219 }
220}
221
222impl<'a, T> Context for ModelContext<'a, T> {
223 type EntityContext<'b, U> = ModelContext<'b, U>;
224 type Result<U> = U;
225
226 fn entity<U>(
227 &mut self,
228 build_entity: impl FnOnce(&mut Self::EntityContext<'_, U>) -> U,
229 ) -> Handle<U>
230 where
231 U: 'static + Send,
232 {
233 self.app.entity(build_entity)
234 }
235
236 fn update_entity<U: 'static, R>(
237 &mut self,
238 handle: &Handle<U>,
239 update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, U>) -> R,
240 ) -> R {
241 self.app.update_entity(handle, update)
242 }
243}
244
245impl<T> Borrow<AppContext> for ModelContext<'_, T> {
246 fn borrow(&self) -> &AppContext {
247 &self.app
248 }
249}
250
251impl<T> BorrowMut<AppContext> for ModelContext<'_, T> {
252 fn borrow_mut(&mut self) -> &mut AppContext {
253 &mut self.app
254 }
255}