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