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