@@ -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 subscribe_global<E, F>(&mut self, mut callback: F) -> Subscription
+ where
+ E: Any,
+ 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,12 @@ 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 +3839,9 @@ impl Subscription {
Subscription::Subscription { subscriptions, .. } => {
subscriptions.take();
}
+ Subscription::GlobalSubscription { subscriptions, .. } => {
+ subscriptions.take();
+ }
Subscription::Observation { observations, .. } => {
observations.take();
}
@@ -3794,18 +3855,29 @@ impl Subscription {
impl Drop for Subscription {
fn drop(&mut self) {
match self {
- Subscription::Observation {
+ Subscription::Subscription {
id,
entity_id,
- observations,
+ subscriptions,
} => {
- if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) {
- if let Some(observations) = observations.lock().get_mut(entity_id) {
- observations.remove(id);
+ 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::ReleaseObservation {
+ 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,
observations,
@@ -3816,14 +3888,14 @@ impl Drop for Subscription {
}
}
}
- Subscription::Subscription {
+ Subscription::ReleaseObservation {
id,
entity_id,
- subscriptions,
+ observations,
} => {
- if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) {
- if let Some(subscriptions) = subscriptions.lock().get_mut(entity_id) {
- subscriptions.remove(id);
+ if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) {
+ if let Some(observations) = observations.lock().get_mut(entity_id) {
+ observations.remove(id);
}
}
}
@@ -1511,6 +1511,8 @@ fn open(action: &Open, cx: &mut MutableAppContext) {
.detach();
}
+pub struct WorkspaceCreated(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(WorkspaceCreated(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(WorkspaceCreated(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(WorkspaceCreated(workspace.downgrade()));
cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone()));
}