1use crate::{
2 AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference,
3 Subscription, Task, WeakHandle,
4};
5use std::{future::Future, marker::PhantomData};
6
7pub struct ModelContext<'a, T> {
8 app: Reference<'a, AppContext>,
9 entity_type: PhantomData<T>,
10 entity_id: EntityId,
11}
12
13impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
14 pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
15 Self {
16 app: Reference::Mutable(app),
17 entity_type: PhantomData,
18 entity_id,
19 }
20 }
21
22 pub fn handle(&self) -> WeakHandle<T> {
23 self.app.entities.weak_handle(self.entity_id)
24 }
25
26 pub fn observe<E: Send + Sync + 'static>(
27 &mut self,
28 handle: &Handle<E>,
29 on_notify: impl Fn(&mut T, Handle<E>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
30 ) -> Subscription {
31 let this = self.handle();
32 let handle = handle.downgrade();
33 self.app.observers.insert(
34 handle.id,
35 Box::new(move |cx| {
36 if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
37 this.update(cx, |this, cx| on_notify(this, handle, cx));
38 true
39 } else {
40 false
41 }
42 }),
43 )
44 }
45
46 pub fn subscribe<E: EventEmitter + Send + Sync + 'static>(
47 &mut self,
48 handle: &Handle<E>,
49 on_event: impl Fn(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
50 + Send
51 + Sync
52 + 'static,
53 ) -> Subscription {
54 let this = self.handle();
55 let handle = handle.downgrade();
56 self.app.event_listeners.insert(
57 handle.id,
58 Box::new(move |event, cx| {
59 let event = event.downcast_ref().expect("invalid event type");
60 if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
61 this.update(cx, |this, cx| on_event(this, handle, event, cx));
62 true
63 } else {
64 false
65 }
66 }),
67 )
68 }
69
70 pub fn on_release(
71 &mut self,
72 on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
73 ) -> Subscription {
74 self.app.release_listeners.insert(
75 self.entity_id,
76 Box::new(move |this, cx| {
77 let this = this.downcast_mut().expect("invalid entity type");
78 on_release(this, cx);
79 }),
80 )
81 }
82
83 pub fn observe_release<E: Send + Sync + 'static>(
84 &mut self,
85 handle: &Handle<E>,
86 on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
87 ) -> Subscription {
88 let this = self.handle();
89 self.app.release_listeners.insert(
90 handle.id,
91 Box::new(move |entity, cx| {
92 let entity = entity.downcast_mut().expect("invalid entity type");
93 if let Some(this) = this.upgrade() {
94 this.update(cx, |this, cx| on_release(this, entity, cx));
95 }
96 }),
97 )
98 }
99
100 pub fn notify(&mut self) {
101 if self.app.pending_notifications.insert(self.entity_id) {
102 self.app.pending_effects.push_back(Effect::Notify {
103 emitter: self.entity_id,
104 });
105 }
106 }
107
108 pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
109 where
110 G: 'static + Send + Sync,
111 {
112 let mut global = self.app.lease_global::<G>();
113 let result = f(global.as_mut(), self);
114 self.app.restore_global(global);
115 result
116 }
117
118 pub fn spawn<Fut, R>(
119 &self,
120 f: impl FnOnce(WeakHandle<T>, AsyncAppContext) -> Fut + Send + 'static,
121 ) -> Task<R>
122 where
123 Fut: Future<Output = R> + Send + 'static,
124 R: Send + 'static,
125 {
126 let this = self.handle();
127 self.app.spawn(|cx| f(this, cx))
128 }
129}
130
131impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> {
132 pub fn emit(&mut self, event: T::Event) {
133 self.app.pending_effects.push_back(Effect::Emit {
134 emitter: self.entity_id,
135 event: Box::new(event),
136 });
137 }
138}
139
140impl<'a, T: 'static> Context for ModelContext<'a, T> {
141 type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
142 type Result<U> = U;
143
144 fn entity<U: Send + Sync + 'static>(
145 &mut self,
146 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
147 ) -> Handle<U> {
148 self.app.entity(build_entity)
149 }
150
151 fn update_entity<U: Send + Sync + 'static, R>(
152 &mut self,
153 handle: &Handle<U>,
154 update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
155 ) -> R {
156 self.app.update_entity(handle, update)
157 }
158}