Use a CallbackCollection for release observations

Co-authored-by: Kay Simmons <kay@zed.dev>
This commit is contained in:
Max Brunsfeld 2023-01-05 18:02:53 -08:00
parent fa620bf98f
commit 82e9f736bd

View file

@ -588,9 +588,9 @@ type GlobalActionCallback = dyn FnMut(&dyn Action, &mut MutableAppContext);
type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext) -> bool>; type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext) -> bool>;
type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>; type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>; type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
type FocusObservationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
type GlobalObservationCallback = Box<dyn FnMut(&mut MutableAppContext)>; type GlobalObservationCallback = Box<dyn FnMut(&mut MutableAppContext)>;
type ReleaseObservationCallback = Box<dyn FnOnce(&dyn Any, &mut MutableAppContext)>; type FocusObservationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>; type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>;
type WindowActivationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>; type WindowActivationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
type WindowFullscreenCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>; type WindowFullscreenCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
@ -615,18 +615,17 @@ pub struct MutableAppContext {
next_subscription_id: usize, next_subscription_id: usize,
frame_count: usize, frame_count: usize,
focus_observations: CallbackCollection<usize, FocusObservationCallback>,
global_subscriptions: CallbackCollection<TypeId, GlobalSubscriptionCallback>,
global_observations: CallbackCollection<TypeId, GlobalObservationCallback>,
subscriptions: CallbackCollection<usize, SubscriptionCallback>, subscriptions: CallbackCollection<usize, SubscriptionCallback>,
global_subscriptions: CallbackCollection<TypeId, GlobalSubscriptionCallback>,
observations: CallbackCollection<usize, ObservationCallback>, observations: CallbackCollection<usize, ObservationCallback>,
global_observations: CallbackCollection<TypeId, GlobalObservationCallback>,
focus_observations: CallbackCollection<usize, FocusObservationCallback>,
release_observations: CallbackCollection<usize, ReleaseObservationCallback>,
action_dispatch_observations: Arc<Mutex<BTreeMap<usize, ActionObservationCallback>>>,
window_activation_observations: CallbackCollection<usize, WindowActivationCallback>, window_activation_observations: CallbackCollection<usize, WindowActivationCallback>,
window_fullscreen_observations: CallbackCollection<usize, WindowFullscreenCallback>, window_fullscreen_observations: CallbackCollection<usize, WindowFullscreenCallback>,
keystroke_observations: CallbackCollection<usize, KeystrokeCallback>, keystroke_observations: CallbackCollection<usize, KeystrokeCallback>,
release_observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>,
action_dispatch_observations: Arc<Mutex<BTreeMap<usize, ActionObservationCallback>>>,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
presenters_and_platform_windows: presenters_and_platform_windows:
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>, HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
@ -1172,22 +1171,18 @@ impl MutableAppContext {
F: 'static + FnOnce(&E, &mut Self), F: 'static + FnOnce(&E, &mut Self),
{ {
let id = post_inc(&mut self.next_subscription_id); let id = post_inc(&mut self.next_subscription_id);
self.release_observations let mut callback = Some(callback);
.lock() self.release_observations.add_callback(
.entry(handle.id()) handle.id(),
.or_default()
.insert(
id,
Box::new(move |released, cx| {
let released = released.downcast_ref().unwrap();
callback(released, cx)
}),
);
Subscription::ReleaseObservation {
id, id,
entity_id: handle.id(), Box::new(move |released, cx| {
observations: Some(Arc::downgrade(&self.release_observations)), let released = released.downcast_ref().unwrap();
} if let Some(callback) = callback.take() {
callback(released, cx)
}
}),
);
Subscription::ReleaseObservation(self.release_observations.subscribe(handle.id(), id))
} }
pub fn observe_actions<F>(&mut self, callback: F) -> Subscription pub fn observe_actions<F>(&mut self, callback: F) -> Subscription
@ -2137,6 +2132,7 @@ impl MutableAppContext {
self.window_activation_observations.gc(); self.window_activation_observations.gc();
self.window_fullscreen_observations.gc(); self.window_fullscreen_observations.gc();
self.keystroke_observations.gc(); self.keystroke_observations.gc();
self.release_observations.gc();
self.remove_dropped_entities(); self.remove_dropped_entities();
@ -2306,12 +2302,13 @@ impl MutableAppContext {
} }
fn handle_entity_release_effect(&mut self, entity_id: usize, entity: &dyn Any) { fn handle_entity_release_effect(&mut self, entity_id: usize, entity: &dyn Any) {
let callbacks = self.release_observations.lock().remove(&entity_id); self.release_observations
if let Some(callbacks) = callbacks { .clone()
for (_, callback) in callbacks { .emit(entity_id, self, |callback, this| {
callback(entity, self); callback(entity, this);
} // Release observations happen one time. So clear the callback by returning false
} false
})
} }
fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) { fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) {
@ -5093,14 +5090,8 @@ pub enum Subscription {
WindowActivationObservation(callback_collection::Subscription<usize, WindowActivationCallback>), WindowActivationObservation(callback_collection::Subscription<usize, WindowActivationCallback>),
WindowFullscreenObservation(callback_collection::Subscription<usize, WindowFullscreenCallback>), WindowFullscreenObservation(callback_collection::Subscription<usize, WindowFullscreenCallback>),
KeystrokeObservation(callback_collection::Subscription<usize, KeystrokeCallback>), KeystrokeObservation(callback_collection::Subscription<usize, KeystrokeCallback>),
ReleaseObservation(callback_collection::Subscription<usize, ReleaseObservationCallback>),
ReleaseObservation {
id: usize,
entity_id: usize,
#[allow(clippy::type_complexity)]
observations:
Option<Weak<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>>,
},
ActionObservation { ActionObservation {
id: usize, id: usize,
observations: Option<Weak<Mutex<BTreeMap<usize, ActionObservationCallback>>>>, observations: Option<Weak<Mutex<BTreeMap<usize, ActionObservationCallback>>>>,
@ -5118,10 +5109,7 @@ impl Subscription {
Subscription::KeystrokeObservation(subscription) => subscription.detach(), Subscription::KeystrokeObservation(subscription) => subscription.detach(),
Subscription::WindowActivationObservation(subscription) => subscription.detach(), Subscription::WindowActivationObservation(subscription) => subscription.detach(),
Subscription::WindowFullscreenObservation(subscription) => subscription.detach(), Subscription::WindowFullscreenObservation(subscription) => subscription.detach(),
Subscription::ReleaseObservation(subscription) => subscription.detach(),
Subscription::ReleaseObservation { observations, .. } => {
observations.take();
}
Subscription::ActionObservation { observations, .. } => { Subscription::ActionObservation { observations, .. } => {
observations.take(); observations.take();
} }
@ -5132,17 +5120,6 @@ impl Subscription {
impl Drop for Subscription { impl Drop for Subscription {
fn drop(&mut self) { fn drop(&mut self) {
match self { match self {
Subscription::ReleaseObservation {
id,
entity_id,
observations,
} => {
if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) {
if let Some(observations) = observations.lock().get_mut(entity_id) {
observations.remove(id);
}
}
}
Subscription::ActionObservation { id, observations } => { Subscription::ActionObservation { id, observations } => {
if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) {
observations.lock().remove(id); observations.lock().remove(id);