diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 8d6f70f27e..45c26b0ba8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -380,6 +380,11 @@ impl AppContext { }) } + pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription { + let (subscription, activate) = self.observers.insert(key, value); + self.defer(move |_| activate()); + subscription + } pub(crate) fn observe_internal( &mut self, entity: &E, @@ -391,7 +396,7 @@ impl AppContext { { let entity_id = entity.entity_id(); let handle = entity.downgrade(); - let (subscription, activate) = self.observers.insert( + self.new_observer( entity_id, Box::new(move |cx| { if let Some(handle) = E::upgrade_from(&handle) { @@ -400,9 +405,7 @@ impl AppContext { false } }), - ); - self.defer(move |_| activate()); - subscription + ) } /// Arrange for the given callback to be invoked whenever the given model or view emits an event of a given type. @@ -423,6 +426,15 @@ impl AppContext { }) } + pub(crate) fn new_subscription( + &mut self, + key: EntityId, + value: (TypeId, Listener), + ) -> Subscription { + let (subscription, activate) = self.event_listeners.insert(key, value); + self.defer(move |_| activate()); + subscription + } pub(crate) fn subscribe_internal( &mut self, entity: &E, @@ -435,7 +447,7 @@ impl AppContext { { let entity_id = entity.entity_id(); let entity = entity.downgrade(); - let (subscription, activate) = self.event_listeners.insert( + self.new_subscription( entity_id, ( TypeId::of::(), @@ -448,9 +460,7 @@ impl AppContext { } }), ), - ); - self.defer(move |_| activate()); - subscription + ) } /// Returns handles to all open windows in the application. @@ -930,13 +940,22 @@ impl AppContext { self.globals_by_type.insert(global_type, lease.global); } + pub(crate) fn new_view_observer( + &mut self, + key: TypeId, + value: NewViewListener, + ) -> Subscription { + let (subscription, activate) = self.new_view_observers.insert(key, value); + activate(); + subscription + } /// Arrange for the given function to be invoked whenever a view of the specified type is created. /// The function will be passed a mutable reference to the view along with an appropriate context. pub fn observe_new_views( &mut self, on_new: impl 'static + Fn(&mut V, &mut ViewContext), ) -> Subscription { - let (subscription, activate) = self.new_view_observers.insert( + self.new_view_observer( TypeId::of::(), Box::new(move |any_view: AnyView, cx: &mut WindowContext| { any_view @@ -946,9 +965,7 @@ impl AppContext { on_new(view_state, cx); }) }), - ); - activate(); - subscription + ) } /// Observe the release of a model or view. The callback is invoked after the model or view @@ -980,9 +997,15 @@ impl AppContext { &mut self, f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static, ) -> Subscription { - let (subscription, activate) = self.keystroke_observers.insert((), Box::new(f)); - activate(); - subscription + fn inner( + keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>, + handler: KeystrokeObserver, + ) -> Subscription { + let (subscription, activate) = keystroke_observers.insert((), handler); + activate(); + subscription + } + inner(&mut self.keystroke_observers, Box::new(f)) } pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 47068eacc7..4b7c43e113 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -451,6 +451,12 @@ impl Window { pending_input: None, } } + fn new_focus_listener( + &mut self, + value: AnyWindowFocusListener, + ) -> (Subscription, impl FnOnce()) { + self.focus_listeners.insert((), value) + } } /// Indicates which region of the window is visible. Content falling outside of this mask will not be @@ -635,7 +641,7 @@ impl<'a> WindowContext<'a> { let entity_id = entity.entity_id(); let entity = entity.downgrade(); let window_handle = self.window.handle; - let (subscription, activate) = self.app.event_listeners.insert( + self.app.new_subscription( entity_id, ( TypeId::of::(), @@ -653,9 +659,7 @@ impl<'a> WindowContext<'a> { .unwrap_or(false) }), ), - ); - self.app.defer(move |_| activate()); - subscription + ) } /// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across @@ -1747,13 +1751,15 @@ impl VisualContext for WindowContext<'_> { let entity = build_view_state(&mut cx); cx.entities.insert(slot, entity); - cx.new_view_observers - .clone() - .retain(&TypeId::of::(), |observer| { - let any_view = AnyView::from(view.clone()); - (observer)(any_view, self); + // Non-generic part to avoid leaking SubscriberSet to invokers of `new_view`. + fn notify_observers(cx: &mut WindowContext, tid: TypeId, view: AnyView) { + cx.new_view_observers.clone().retain(&tid, |observer| { + let any_view = view.clone(); + (observer)(any_view, cx); true }); + } + notify_observers(self, TypeId::of::(), AnyView::from(view.clone())); view } @@ -1955,7 +1961,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { let entity_id = entity.entity_id(); let entity = entity.downgrade(); let window_handle = self.window.handle; - let (subscription, activate) = self.app.observers.insert( + self.app.new_observer( entity_id, Box::new(move |cx| { window_handle @@ -1969,9 +1975,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { }) .unwrap_or(false) }), - ); - self.app.defer(move |_| activate()); - subscription + ) } /// Subscribe to events emitted by another model or view. @@ -1991,7 +1995,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { let entity_id = entity.entity_id(); let handle = entity.downgrade(); let window_handle = self.window.handle; - let (subscription, activate) = self.app.event_listeners.insert( + self.app.new_subscription( entity_id, ( TypeId::of::(), @@ -2009,9 +2013,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { .unwrap_or(false) }), ), - ); - self.app.defer(move |_| activate()); - subscription + ) } /// Register a callback to be invoked when the view is released. @@ -2136,9 +2138,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { ) -> Subscription { let view = self.view.downgrade(); let focus_id = handle.id; - let (subscription, activate) = self.window.focus_listeners.insert( - (), - Box::new(move |event, cx| { + let (subscription, activate) = + self.window.new_focus_listener(Box::new(move |event, cx| { view.update(cx, |view, cx| { if event.previous_focus_path.last() != Some(&focus_id) && event.current_focus_path.last() == Some(&focus_id) @@ -2147,9 +2148,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { } }) .is_ok() - }), - ); - self.app.defer(move |_| activate()); + })); + self.app.defer(|_| activate()); subscription } @@ -2162,9 +2162,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { ) -> Subscription { let view = self.view.downgrade(); let focus_id = handle.id; - let (subscription, activate) = self.window.focus_listeners.insert( - (), - Box::new(move |event, cx| { + let (subscription, activate) = + self.window.new_focus_listener(Box::new(move |event, cx| { view.update(cx, |view, cx| { if !event.previous_focus_path.contains(&focus_id) && event.current_focus_path.contains(&focus_id) @@ -2173,8 +2172,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } }) .is_ok() - }), - ); + })); self.app.defer(move |_| activate()); subscription } @@ -2188,9 +2186,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { ) -> Subscription { let view = self.view.downgrade(); let focus_id = handle.id; - let (subscription, activate) = self.window.focus_listeners.insert( - (), - Box::new(move |event, cx| { + let (subscription, activate) = + self.window.new_focus_listener(Box::new(move |event, cx| { view.update(cx, |view, cx| { if event.previous_focus_path.last() == Some(&focus_id) && event.current_focus_path.last() != Some(&focus_id) @@ -2199,8 +2196,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } }) .is_ok() - }), - ); + })); self.app.defer(move |_| activate()); subscription } @@ -2231,9 +2227,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { ) -> Subscription { let view = self.view.downgrade(); let focus_id = handle.id; - let (subscription, activate) = self.window.focus_listeners.insert( - (), - Box::new(move |event, cx| { + let (subscription, activate) = + self.window.new_focus_listener(Box::new(move |event, cx| { view.update(cx, |view, cx| { if event.previous_focus_path.contains(&focus_id) && !event.current_focus_path.contains(&focus_id) @@ -2242,8 +2237,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } }) .is_ok() - }), - ); + })); self.app.defer(move |_| activate()); subscription }