WIP

Antonio Scandurra created

Change summary

crates/feature_flags2/src/feature_flags2.rs |  24 +-
crates/gpui2/src/app.rs                     | 179 ++++++++++++----------
crates/gpui2/src/app/model_context.rs       |  20 +-
crates/gpui2/src/gpui2.rs                   |  10 -
crates/gpui2/src/subscription.rs            |  11 
crates/gpui2/src/window.rs                  |  60 ++++++-
6 files changed, 176 insertions(+), 128 deletions(-)

Detailed changes

crates/feature_flags2/src/feature_flags2.rs 🔗

@@ -25,16 +25,18 @@ impl FeatureFlag for ChannelsAlpha {
 pub trait FeatureFlagViewExt<V: 'static> {
     fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
     where
-        F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static;
+        F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static;
 }
 
-impl<V: 'static> FeatureFlagViewExt<V> for ViewContext<'_, '_, V> {
+impl<V> FeatureFlagViewExt<V> for ViewContext<'_, '_, V>
+where
+    V: 'static + Send + Sync,
+{
     fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
     where
-        F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static,
+        F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static,
     {
-        self.observe_global::<FeatureFlags, _>(move |v, cx| {
-            let feature_flags = cx.global::<FeatureFlags>();
+        self.observe_global::<FeatureFlags>(move |v, feature_flags, cx| {
             callback(feature_flags.has_flag(<T as FeatureFlag>::NAME), v, cx);
         })
     }
@@ -49,16 +51,14 @@ pub trait FeatureFlagAppExt {
 
 impl FeatureFlagAppExt for AppContext {
     fn update_flags(&mut self, staff: bool, flags: Vec<String>) {
-        self.update_default_global::<FeatureFlags, _, _>(|feature_flags, _| {
-            feature_flags.staff = staff;
-            feature_flags.flags = flags;
-        })
+        let feature_flags = self.default_global::<FeatureFlags>();
+        feature_flags.staff = staff;
+        feature_flags.flags = flags;
     }
 
     fn set_staff(&mut self, staff: bool) {
-        self.update_default_global::<FeatureFlags, _, _>(|feature_flags, _| {
-            feature_flags.staff = staff;
-        })
+        let feature_flags = self.default_global::<FeatureFlags>();
+        feature_flags.staff = staff;
     }
 
     fn has_flag<T: FeatureFlag>(&self) -> bool {

crates/gpui2/src/app.rs 🔗

@@ -11,7 +11,7 @@ use smallvec::SmallVec;
 use crate::{
     current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context,
     DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap,
-    LayoutId, MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
+    LayoutId, MainThread, MainThreadOnly, Platform, SharedString, SubscriberSet, Subscription,
     SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
     WindowHandle, WindowId,
 };
@@ -75,18 +75,20 @@ impl App {
                 svg_renderer: SvgRenderer::new(asset_source),
                 image_cache: ImageCache::new(http_client),
                 text_style_stack: Vec::new(),
-                global_stacks_by_type: HashMap::default(),
+                globals_by_type: HashMap::default(),
                 unit_entity,
                 entities,
                 windows: SlotMap::with_key(),
                 keymap: Arc::new(RwLock::new(Keymap::default())),
                 global_action_listeners: HashMap::default(),
                 action_builders: HashMap::default(),
-                pending_notifications: Default::default(),
-                pending_effects: Default::default(),
+                pending_effects: VecDeque::new(),
+                pending_notifications: HashSet::default(),
+                pending_global_notifications: HashSet::default(),
                 observers: SubscriberSet::new(),
-                event_handlers: SubscriberSet::new(),
-                release_handlers: SubscriberSet::new(),
+                event_listeners: SubscriberSet::new(),
+                release_listeners: SubscriberSet::new(),
+                global_observers: SubscriberSet::new(),
                 layout_id_buffer: Default::default(),
                 propagate_event: true,
             })
@@ -154,8 +156,8 @@ impl App {
 }
 
 type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
-type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
-type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
+type Listener = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
+type ReleaseListener = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
 type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
 type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
 
@@ -171,7 +173,7 @@ pub struct AppContext {
     pub(crate) svg_renderer: SvgRenderer,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) global_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
+    pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
     pub(crate) unit_entity: Handle<()>,
     pub(crate) entities: EntityMap,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
@@ -179,11 +181,13 @@ pub struct AppContext {
     pub(crate) global_action_listeners:
         HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self) + Send + Sync>>>,
     action_builders: HashMap<SharedString, ActionBuilder>,
-    pub(crate) pending_notifications: HashSet<EntityId>,
     pending_effects: VecDeque<Effect>,
+    pub(crate) pending_notifications: HashSet<EntityId>,
+    pub(crate) pending_global_notifications: HashSet<TypeId>,
     pub(crate) observers: SubscriberSet<EntityId, Handler>,
-    pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
-    pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
+    pub(crate) event_listeners: SubscriberSet<EntityId, Listener>,
+    pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
+    pub(crate) global_observers: SubscriberSet<TypeId, Listener>,
     pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
     pub(crate) propagate_event: bool,
 }
@@ -194,7 +198,7 @@ impl AppContext {
     }
 
     pub fn refresh(&mut self) {
-        self.push_effect(Effect::Refresh);
+        self.pending_effects.push_back(Effect::Refresh);
     }
 
     pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
@@ -250,14 +254,19 @@ impl AppContext {
     pub(crate) fn push_effect(&mut self, effect: Effect) {
         match &effect {
             Effect::Notify { emitter } => {
-                if self.pending_notifications.insert(*emitter) {
-                    self.pending_effects.push_back(effect);
+                if !self.pending_notifications.insert(*emitter) {
+                    return;
                 }
             }
-            Effect::Emit { .. } => self.pending_effects.push_back(effect),
-            Effect::FocusChanged { .. } => self.pending_effects.push_back(effect),
-            Effect::Refresh => self.pending_effects.push_back(effect),
-        }
+            Effect::NotifyGlobalObservers { global_type } => {
+                if !self.pending_global_notifications.insert(*global_type) {
+                    return;
+                }
+            }
+            _ => {}
+        };
+
+        self.pending_effects.push_back(effect);
     }
 
     fn flush_effects(&mut self) {
@@ -266,13 +275,18 @@ impl AppContext {
             self.release_dropped_focus_handles();
             if let Some(effect) = self.pending_effects.pop_front() {
                 match effect {
-                    Effect::Notify { emitter } => self.apply_notify_effect(emitter),
+                    Effect::Notify { emitter } => {
+                        self.apply_notify_effect(emitter);
+                    }
                     Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
                     Effect::FocusChanged { window_id, focused } => {
-                        self.apply_focus_changed(window_id, focused)
+                        self.apply_focus_changed_effect(window_id, focused);
                     }
                     Effect::Refresh => {
-                        self.apply_refresh();
+                        self.apply_refresh_effect();
+                    }
+                    Effect::NotifyGlobalObservers { global_type } => {
+                        self.apply_notify_global_observers_effect(global_type);
                     }
                 }
             } else {
@@ -307,8 +321,8 @@ impl AppContext {
 
             for (entity_id, mut entity) in dropped {
                 self.observers.remove(&entity_id);
-                self.event_handlers.remove(&entity_id);
-                for release_callback in self.release_handlers.remove(&entity_id) {
+                self.event_listeners.remove(&entity_id);
+                for release_callback in self.release_listeners.remove(&entity_id) {
                     release_callback(&mut entity, self);
                 }
             }
@@ -348,12 +362,12 @@ impl AppContext {
     }
 
     fn apply_emit_effect(&mut self, emitter: EntityId, event: Box<dyn Any>) {
-        self.event_handlers
+        self.event_listeners
             .clone()
             .retain(&emitter, |handler| handler(&event, self));
     }
 
-    fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option<FocusId>) {
+    fn apply_focus_changed_effect(&mut self, window_id: WindowId, focused: Option<FocusId>) {
         self.update_window(window_id, |cx| {
             if cx.window.focus == focused {
                 let mut listeners = mem::take(&mut cx.window.focus_listeners);
@@ -379,7 +393,7 @@ impl AppContext {
         .ok();
     }
 
-    fn apply_refresh(&mut self) {
+    fn apply_refresh_effect(&mut self) {
         for window in self.windows.values_mut() {
             if let Some(window) = window.as_mut() {
                 window.dirty = true;
@@ -387,6 +401,15 @@ impl AppContext {
         }
     }
 
+    fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
+        self.pending_global_notifications.insert(type_id);
+        let global = self.globals_by_type.remove(&type_id).unwrap();
+        self.global_observers
+            .clone()
+            .retain(&type_id, |observer| observer(global.as_ref(), self));
+        self.globals_by_type.insert(type_id, global);
+    }
+
     pub fn to_async(&self) -> AsyncAppContext {
         AsyncAppContext {
             app: unsafe { mem::transmute(self.this.clone()) },
@@ -460,94 +483,89 @@ impl AppContext {
     }
 
     pub fn has_global<G: 'static>(&self) -> bool {
-        self.global_stacks_by_type
-            .get(&TypeId::of::<G>())
-            .map_or(false, |stack| !stack.is_empty())
+        self.globals_by_type.contains_key(&TypeId::of::<G>())
     }
 
     pub fn global<G: 'static>(&self) -> &G {
-        self.global_stacks_by_type
+        self.globals_by_type
             .get(&TypeId::of::<G>())
-            .and_then(|stack| stack.last())
             .map(|any_state| any_state.downcast_ref::<G>().unwrap())
             .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
             .unwrap()
     }
 
     pub fn try_global<G: 'static>(&self) -> Option<&G> {
-        self.global_stacks_by_type
+        self.globals_by_type
             .get(&TypeId::of::<G>())
-            .and_then(|stack| stack.last())
             .map(|any_state| any_state.downcast_ref::<G>().unwrap())
     }
 
     pub fn global_mut<G: 'static>(&mut self) -> &mut G {
-        self.global_stacks_by_type
-            .get_mut(&TypeId::of::<G>())
-            .and_then(|stack| stack.last_mut())
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type
+            .get_mut(&global_type)
             .and_then(|any_state| any_state.downcast_mut::<G>())
             .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
             .unwrap()
     }
 
     pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
-        let stack = self
-            .global_stacks_by_type
-            .entry(TypeId::of::<G>())
-            .or_default();
-        if stack.is_empty() {
-            stack.push(Box::new(G::default()));
-        }
-        stack.last_mut().unwrap().downcast_mut::<G>().unwrap()
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type
+            .insert(global_type, Box::new(G::default()));
+        self.globals_by_type
+            .get_mut(&global_type)
+            .unwrap()
+            .downcast_mut::<G>()
+            .unwrap()
     }
 
     pub fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
-        let global = Box::new(global);
-        let stack = self
-            .global_stacks_by_type
-            .entry(TypeId::of::<T>())
-            .or_default();
-        if let Some(last) = stack.last_mut() {
-            *last = global;
-        } else {
-            stack.push(global)
-        }
+        let global_type = TypeId::of::<T>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type.insert(global_type, Box::new(global));
     }
 
     pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
     where
         G: 'static + Send + Sync,
     {
-        let mut global = self
-            .global_stacks_by_type
-            .get_mut(&TypeId::of::<G>())
-            .and_then(|stack| stack.pop())
-            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
-            .unwrap();
-        let result = f(global.downcast_mut().unwrap(), self);
-        self.global_stacks_by_type
-            .get_mut(&TypeId::of::<G>())
-            .unwrap()
-            .push(global);
+        let mut global = self.lease_global::<G>();
+        let result = f(global.as_mut(), self);
+        self.restore_global(global);
         result
     }
 
-    pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, global: T) {
-        self.global_stacks_by_type
-            .entry(TypeId::of::<T>())
-            .or_default()
-            .push(Box::new(global));
-    }
-
-    pub(crate) fn pop_global<T: 'static>(&mut self) -> Box<T> {
-        self.global_stacks_by_type
-            .get_mut(&TypeId::of::<T>())
-            .and_then(|stack| stack.pop())
-            .expect("state stack underflow")
+    pub fn observe_global<G: 'static>(
+        &mut self,
+        f: impl Fn(&G, &mut Self) + Send + Sync + 'static,
+    ) -> Subscription {
+        self.global_observers.insert(
+            TypeId::of::<G>(),
+            Box::new(move |global, cx| {
+                f(global.downcast_ref::<G>().unwrap(), cx);
+                true
+            }),
+        )
+    }
+
+    pub(crate) fn lease_global<G: 'static + Send + Sync>(&mut self) -> Box<G> {
+        self.globals_by_type
+            .remove(&TypeId::of::<G>())
+            .ok_or_else(|| anyhow!("no global registered of type {}", type_name::<G>()))
+            .unwrap()
             .downcast()
             .unwrap()
     }
 
+    pub(crate) fn restore_global<G: 'static + Send + Sync>(&mut self, global: Box<G>) {
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type.insert(global_type, global);
+    }
+
     pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
         self.text_style_stack.push(text_style);
     }
@@ -558,7 +576,7 @@ impl AppContext {
 
     pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
         self.keymap.write().add_bindings(bindings);
-        self.push_effect(Effect::Refresh);
+        self.pending_effects.push_back(Effect::Refresh);
     }
 
     pub fn on_action<A: Action>(
@@ -711,6 +729,9 @@ pub(crate) enum Effect {
         focused: Option<FocusId>,
     },
     Refresh,
+    NotifyGlobalObservers {
+        global_type: TypeId,
+    },
 }
 
 #[cfg(test)]

crates/gpui2/src/app/model_context.rs 🔗

@@ -53,7 +53,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
     ) -> Subscription {
         let this = self.handle();
         let handle = handle.downgrade();
-        self.app.event_handlers.insert(
+        self.app.event_listeners.insert(
             handle.id,
             Box::new(move |event, cx| {
                 let event = event.downcast_ref().expect("invalid event type");
@@ -71,7 +71,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
         &mut self,
         on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
     ) -> Subscription {
-        self.app.release_handlers.insert(
+        self.app.release_listeners.insert(
             self.entity_id,
             Box::new(move |this, cx| {
                 let this = this.downcast_mut().expect("invalid entity type");
@@ -86,7 +86,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
         on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
     ) -> Subscription {
         let this = self.handle();
-        self.app.release_handlers.insert(
+        self.app.release_listeners.insert(
             handle.id,
             Box::new(move |entity, cx| {
                 let entity = entity.downcast_mut().expect("invalid entity type");
@@ -98,18 +98,20 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
     }
 
     pub fn notify(&mut self) {
-        self.app.push_effect(Effect::Notify {
-            emitter: self.entity_id,
-        });
+        if self.app.pending_notifications.insert(self.entity_id) {
+            self.app.pending_effects.push_back(Effect::Notify {
+                emitter: self.entity_id,
+            });
+        }
     }
 
     pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
     where
         G: 'static + Send + Sync,
     {
-        let mut global = self.app.pop_global::<G>();
+        let mut global = self.app.lease_global::<G>();
         let result = f(global.as_mut(), self);
-        self.app.push_global(global);
+        self.app.restore_global(global);
         result
     }
 
@@ -128,7 +130,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
 
 impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> {
     pub fn emit(&mut self, event: T::Event) {
-        self.app.push_effect(Effect::Emit {
+        self.app.pending_effects.push_back(Effect::Emit {
             emitter: self.entity_id,
             event: Box::new(event),
         });

crates/gpui2/src/gpui2.rs 🔗

@@ -153,16 +153,6 @@ pub trait BorrowAppContext {
         result
     }
 
-    fn with_global<T: Send + Sync + 'static, F, R>(&mut self, global: T, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        self.app_mut().push_global(global);
-        let result = f(self);
-        self.app_mut().pop_global::<T>();
-        result
-    }
-
     fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
         self.app_mut().set_global(global)
     }

crates/gpui2/src/subscription.rs 🔗

@@ -32,21 +32,21 @@ where
         })))
     }
 
-    pub fn insert(&self, emitter: EmitterKey, callback: Callback) -> Subscription {
+    pub fn insert(&self, emitter_key: EmitterKey, callback: Callback) -> Subscription {
         let mut lock = self.0.lock();
         let subscriber_id = post_inc(&mut lock.next_subscriber_id);
         lock.subscribers
-            .entry(emitter.clone())
+            .entry(emitter_key.clone())
             .or_default()
             .insert(subscriber_id, callback);
         let this = self.0.clone();
         Subscription {
             unsubscribe: Some(Box::new(move || {
                 let mut lock = this.lock();
-                if let Some(subscribers) = lock.subscribers.get_mut(&emitter) {
+                if let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) {
                     subscribers.remove(&subscriber_id);
                     if subscribers.is_empty() {
-                        lock.subscribers.remove(&emitter);
+                        lock.subscribers.remove(&emitter_key);
                         return;
                     }
                 }
@@ -54,7 +54,8 @@ where
                 // We didn't manage to remove the subscription, which means it was dropped
                 // while invoking the callback. Mark it as dropped so that we can remove it
                 // later.
-                lock.dropped_subscribers.insert((emitter, subscriber_id));
+                lock.dropped_subscribers
+                    .insert((emitter_key, subscriber_id));
             })),
         }
     }

crates/gpui2/src/window.rs 🔗

@@ -325,7 +325,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let window_id = self.window.handle.id;
         self.window.focus = Some(handle.id);
-        self.push_effect(Effect::FocusChanged {
+        self.app.push_effect(Effect::FocusChanged {
             window_id,
             focused: Some(handle.id),
         });
@@ -339,7 +339,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let window_id = self.window.handle.id;
         self.window.focus = None;
-        self.push_effect(Effect::FocusChanged {
+        self.app.push_effect(Effect::FocusChanged {
             window_id,
             focused: None,
         });
@@ -430,9 +430,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     where
         G: 'static + Send + Sync,
     {
-        let mut global = self.app.pop_global::<G>();
+        let global_type = TypeId::of::<G>();
+        let mut global = self.app.lease_global::<G>();
         let result = f(global.as_mut(), self);
-        self.app.push_global(global);
+        self.app.set_global(global);
         result
     }
 
@@ -1017,6 +1018,20 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         key_match
     }
 
+    pub fn observe_global<G: 'static>(
+        &mut self,
+        f: impl Fn(&G, &mut WindowContext<'_, '_>) + Send + Sync + 'static,
+    ) -> Subscription {
+        let window_id = self.window.handle.id;
+        self.global_observers.insert(
+            TypeId::of::<G>(),
+            Box::new(move |global, cx| {
+                let global = global.downcast_ref::<G>().unwrap();
+                cx.update_window(window_id, |cx| f(global, cx)).is_ok()
+            }),
+        )
+    }
+
     fn dispatch_action(
         &mut self,
         action: Box<dyn Action>,
@@ -1364,7 +1379,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         let this = self.handle();
         let handle = handle.downgrade();
         let window_handle = self.window.handle;
-        self.app.event_handlers.insert(
+        self.app.event_listeners.insert(
             handle.id,
             Box::new(move |event, cx| {
                 cx.update_window(window_handle.id, |cx| {
@@ -1386,7 +1401,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static,
     ) -> Subscription {
         let window_handle = self.window.handle;
-        self.app.release_handlers.insert(
+        self.app.release_listeners.insert(
             self.entity_id,
             Box::new(move |this, cx| {
                 let this = this.downcast_mut().expect("invalid entity type");
@@ -1403,7 +1418,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
     ) -> Subscription {
         let this = self.handle();
         let window_handle = self.window.handle;
-        self.app.release_handlers.insert(
+        self.app.release_listeners.insert(
             handle.id,
             Box::new(move |entity, cx| {
                 let entity = entity.downcast_mut().expect("invalid entity type");
@@ -1556,12 +1571,30 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
     where
         G: 'static + Send + Sync,
     {
-        let mut global = self.app.pop_global::<G>();
+        let mut global = self.app.lease_global::<G>();
         let result = f(global.as_mut(), self);
-        self.app.push_global(global);
+        self.app.restore_global(global);
         result
     }
 
+    pub fn observe_global<G: 'static>(
+        &mut self,
+        f: impl Fn(&mut V, &G, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
+    ) -> Subscription {
+        let window_id = self.window.handle.id;
+        let handle = self.handle();
+        self.global_observers.insert(
+            TypeId::of::<G>(),
+            Box::new(move |global, cx| {
+                let global = global.downcast_ref::<G>().unwrap();
+                cx.update_window(window_id, |cx| {
+                    handle.update(cx, |view, cx| f(view, global, cx)).is_ok()
+                })
+                .unwrap_or(false)
+            }),
+        )
+    }
+
     pub fn on_mouse_event<Event: 'static>(
         &mut self,
         handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
@@ -1575,10 +1608,11 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
     }
 }
 
-impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {
-    pub fn emit(&mut self, event: S::Event) {
-        self.window_cx.app.push_effect(Effect::Emit {
-            emitter: self.entity_id,
+impl<'a, 'w, V: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, V> {
+    pub fn emit(&mut self, event: V::Event) {
+        let emitter = self.entity_id;
+        self.app.push_effect(Effect::Emit {
+            emitter,
             event: Box::new(event),
         });
     }