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