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