Add global events to MutableAppContext and raise global event when new workspace is created

This commit is contained in:
Keith Simmons 2022-03-10 20:03:01 -08:00
parent b62daebde1
commit 81fc812221
2 changed files with 95 additions and 19 deletions

View file

@ -740,6 +740,7 @@ type ActionCallback =
type GlobalActionCallback = dyn FnMut(&dyn AnyAction, &mut MutableAppContext);
type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext) -> bool>;
type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
@ -757,6 +758,7 @@ pub struct MutableAppContext {
next_subscription_id: usize,
frame_count: usize,
subscriptions: Arc<Mutex<HashMap<usize, BTreeMap<usize, SubscriptionCallback>>>>,
global_subscriptions: Arc<Mutex<HashMap<TypeId, BTreeMap<usize, GlobalSubscriptionCallback>>>>,
observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ObservationCallback>>>>,
release_observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>,
presenters_and_platform_windows:
@ -804,6 +806,7 @@ impl MutableAppContext {
next_subscription_id: 0,
frame_count: 0,
subscriptions: Default::default(),
global_subscriptions: Default::default(),
observations: Default::default(),
release_observations: Default::default(),
presenters_and_platform_windows: HashMap::new(),
@ -1062,6 +1065,12 @@ impl MutableAppContext {
self.foreground_platform.prompt_for_new_path(directory)
}
pub fn emit_global<E: Any>(&mut self, payload: E) {
self.pending_effects.push_back(Effect::GlobalEvent {
payload: Box::new(payload),
});
}
pub fn subscribe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
where
E: Entity,
@ -1075,6 +1084,31 @@ impl MutableAppContext {
})
}
pub fn global_subscribe<E, F>(&mut self, mut callback: F) -> Subscription
where
E: Any + Copy,
F: 'static + FnMut(&E, &mut Self),
{
let id = post_inc(&mut self.next_subscription_id);
let type_id = TypeId::of::<E>();
self.global_subscriptions
.lock()
.entry(type_id)
.or_default()
.insert(
id,
Box::new(move |payload, cx| {
let payload = payload.downcast_ref().expect("downcast is type safe");
callback(payload, cx)
}));
Subscription::GlobalSubscription {
id,
type_id,
subscriptions: Some(Arc::downgrade(&self.global_subscriptions))
}
}
pub fn observe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
where
E: Entity,
@ -1573,6 +1607,7 @@ impl MutableAppContext {
if let Some(effect) = self.pending_effects.pop_front() {
match effect {
Effect::Event { entity_id, payload } => self.emit_event(entity_id, payload),
Effect::GlobalEvent { payload } => self.emit_global_event(payload),
Effect::ModelNotification { model_id } => {
self.notify_model_observers(model_id)
}
@ -1700,6 +1735,16 @@ impl MutableAppContext {
}
}
fn emit_global_event(&mut self, payload: Box<dyn Any>) {
let type_id = (&*payload).type_id();
let callbacks = self.global_subscriptions.lock().remove(&type_id);
if let Some(callbacks) = callbacks {
for (_, mut callback) in callbacks {
callback(payload.as_ref(), self)
}
}
}
fn notify_model_observers(&mut self, observed_id: usize) {
let callbacks = self.observations.lock().remove(&observed_id);
if let Some(callbacks) = callbacks {
@ -2071,6 +2116,9 @@ pub enum Effect {
entity_id: usize,
payload: Box<dyn Any>,
},
GlobalEvent {
payload: Box<dyn Any>,
},
ModelNotification {
model_id: usize,
},
@ -2104,6 +2152,10 @@ impl Debug for Effect {
.debug_struct("Effect::Event")
.field("entity_id", entity_id)
.finish(),
Effect::GlobalEvent { payload, .. } => f
.debug_struct("Effect::GlobalEvent")
.field("type_id", &(&*payload).type_id())
.finish(),
Effect::ModelNotification { model_id } => f
.debug_struct("Effect::ModelNotification")
.field("model_id", model_id)
@ -3762,6 +3814,11 @@ pub enum Subscription {
entity_id: usize,
subscriptions: Option<Weak<Mutex<HashMap<usize, BTreeMap<usize, SubscriptionCallback>>>>>,
},
GlobalSubscription {
id: usize,
type_id: TypeId,
subscriptions: Option<Weak<Mutex<HashMap<TypeId, BTreeMap<usize, GlobalSubscriptionCallback>>>>>,
},
Observation {
id: usize,
entity_id: usize,
@ -3781,6 +3838,9 @@ impl Subscription {
Subscription::Subscription { subscriptions, .. } => {
subscriptions.take();
}
Subscription::GlobalSubscription { subscriptions, .. } => {
subscriptions.take();
}
Subscription::Observation { observations, .. } => {
observations.take();
}
@ -3794,6 +3854,28 @@ impl Subscription {
impl Drop for Subscription {
fn drop(&mut self) {
match self {
Subscription::Subscription {
id,
entity_id,
subscriptions,
} => {
if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) {
if let Some(subscriptions) = subscriptions.lock().get_mut(entity_id) {
subscriptions.remove(id);
}
}
}
Subscription::GlobalSubscription {
id,
type_id,
subscriptions,
} => {
if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) {
if let Some(subscriptions) = subscriptions.lock().get_mut(type_id) {
subscriptions.remove(id);
}
}
}
Subscription::Observation {
id,
entity_id,
@ -3816,17 +3898,6 @@ impl Drop for Subscription {
}
}
}
Subscription::Subscription {
id,
entity_id,
subscriptions,
} => {
if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) {
if let Some(subscriptions) = subscriptions.lock().get_mut(entity_id) {
subscriptions.remove(id);
}
}
}
}
}
}

View file

@ -1511,6 +1511,8 @@ fn open(action: &Open, cx: &mut MutableAppContext) {
.detach();
}
pub struct WorkspaceBuilt(WeakViewHandle<Workspace>);
pub fn open_paths(
abs_paths: &[PathBuf],
app_state: &Arc<AppState>,
@ -1537,7 +1539,7 @@ pub fn open_paths(
}
let workspace = existing.unwrap_or_else(|| {
cx.add_window((app_state.build_window_options)(), |cx| {
let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
let project = Project::local(
app_state.client.clone(),
app_state.user_store.clone(),
@ -1546,8 +1548,9 @@ pub fn open_paths(
cx,
);
(app_state.build_workspace)(project, &app_state, cx)
})
.1
});
cx.emit_global(WorkspaceBuilt(workspace.downgrade()));
workspace
});
let task = workspace.update(cx, |workspace, cx| workspace.open_paths(abs_paths, cx));
@ -1581,12 +1584,13 @@ pub fn join_project(
&mut cx,
)
.await?;
let (_, workspace) = cx.update(|cx| {
cx.add_window((app_state.build_window_options)(), |cx| {
Ok(cx.update(|cx| {
let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
(app_state.build_workspace)(project, &app_state, cx)
})
});
Ok(workspace)
});
cx.emit_global(WorkspaceBuilt(workspace.downgrade()));
workspace
}))
})
}
@ -1601,5 +1605,6 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
);
(app_state.build_workspace)(project, &app_state, cx)
});
cx.emit_global(WorkspaceBuilt(workspace.downgrade()));
cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone()));
}