Fix dispatching global actions from a window (#9502)

Antonio Scandurra and Conrad created

Fixes https://github.com/zed-industries/zed/issues/9313

Release Notes:

- Fixed a regression that caused global actions to stop working when
invoked from a window (preview-only)
([#9313](https://github.com/zed-industries/zed/issues/9313)).

Co-authored-by: Conrad <conrad@zed.dev>

Change summary

crates/gpui/src/app.rs    |  2 
crates/gpui/src/window.rs | 60 ++++++++++++++++++++++++++++++++++++----
2 files changed, 55 insertions(+), 7 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -1155,7 +1155,7 @@ impl AppContext {
         }
     }
 
-    pub(crate) fn dispatch_global_action(&mut self, action: &dyn Action) {
+    fn dispatch_global_action(&mut self, action: &dyn Action) {
         self.propagate_event = true;
 
         if let Some(mut global_listeners) = self

crates/gpui/src/window.rs 🔗

@@ -705,7 +705,6 @@ impl<'a> WindowContext<'a> {
 
         let window = self.window.handle;
         self.app.defer(move |cx| {
-            cx.propagate_event = true;
             window
                 .update(cx, |_, cx| {
                     let node_id = focus_handle
@@ -720,9 +719,6 @@ impl<'a> WindowContext<'a> {
                     cx.dispatch_action_on_node(node_id, action.as_ref());
                 })
                 .log_err();
-            if cx.propagate_event {
-                cx.dispatch_global_action(action.as_ref());
-            }
         })
     }
 
@@ -1467,7 +1463,34 @@ impl<'a> WindowContext<'a> {
             .dispatch_tree
             .dispatch_path(node_id);
 
-        // Capture phase
+        // Capture phase for global actions.
+        self.propagate_event = true;
+        if let Some(mut global_listeners) = self
+            .global_action_listeners
+            .remove(&action.as_any().type_id())
+        {
+            for listener in &global_listeners {
+                listener(action.as_any(), DispatchPhase::Capture, self);
+                if !self.propagate_event {
+                    break;
+                }
+            }
+
+            global_listeners.extend(
+                self.global_action_listeners
+                    .remove(&action.as_any().type_id())
+                    .unwrap_or_default(),
+            );
+
+            self.global_action_listeners
+                .insert(action.as_any().type_id(), global_listeners);
+        }
+
+        if !self.propagate_event {
+            return;
+        }
+
+        // Capture phase for window actions.
         for node_id in &dispatch_path {
             let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
             for DispatchActionListener {
@@ -1487,7 +1510,8 @@ impl<'a> WindowContext<'a> {
                 }
             }
         }
-        // Bubble phase
+
+        // Bubble phase for window actions.
         for node_id in dispatch_path.iter().rev() {
             let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
             for DispatchActionListener {
@@ -1509,6 +1533,30 @@ impl<'a> WindowContext<'a> {
                 }
             }
         }
+
+        // Bubble phase for global actions.
+        if let Some(mut global_listeners) = self
+            .global_action_listeners
+            .remove(&action.as_any().type_id())
+        {
+            for listener in global_listeners.iter().rev() {
+                self.propagate_event = false; // Actions stop propagation by default during the bubble phase
+
+                listener(action.as_any(), DispatchPhase::Bubble, self);
+                if !self.propagate_event {
+                    break;
+                }
+            }
+
+            global_listeners.extend(
+                self.global_action_listeners
+                    .remove(&action.as_any().type_id())
+                    .unwrap_or_default(),
+            );
+
+            self.global_action_listeners
+                .insert(action.as_any().type_id(), global_listeners);
+        }
     }
 
     /// Register the given handler to be invoked whenever the global of the given type