Merge pull request #1205 from zed-industries/bypass-app-menu

Antonio Scandurra created

Override `performKeyEquivalent` to handle key events

Change summary

crates/gpui/src/app.rs                 |  6 +
crates/gpui/src/platform.rs            |  2 
crates/gpui/src/platform/mac/window.rs | 65 ++++++++++++++++++---------
crates/gpui/src/platform/test.rs       |  4 
crates/gpui/src/presenter.rs           |  8 ++
5 files changed, 56 insertions(+), 29 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -1774,11 +1774,13 @@ impl MutableAppContext {
                                 presenter.borrow().dispatch_path(cx.as_ref()),
                                 keystroke,
                             ) {
-                                return;
+                                return true;
                             }
                         }
 
-                        presenter.borrow_mut().dispatch_event(event, cx);
+                        presenter.borrow_mut().dispatch_event(event, cx)
+                    } else {
+                        false
                     }
                 })
             }));

crates/gpui/src/platform.rs 🔗

@@ -90,7 +90,7 @@ pub trait Dispatcher: Send + Sync {
 
 pub trait Window: WindowContext {
     fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event)>);
+    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
     fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
     fn on_resize(&mut self, callback: Box<dyn FnMut()>);
     fn on_close(&mut self, callback: Box<dyn FnOnce()>);

crates/gpui/src/platform/mac/window.rs 🔗

@@ -92,8 +92,8 @@ unsafe fn build_classes() {
         decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
 
         decl.add_method(
-            sel!(keyDown:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
+            sel!(performKeyEquivalent:),
+            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
         );
         decl.add_method(
             sel!(mouseDown:),
@@ -164,7 +164,7 @@ pub struct Window(Rc<RefCell<WindowState>>);
 struct WindowState {
     id: usize,
     native_window: id,
-    event_callback: Option<Box<dyn FnMut(Event)>>,
+    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
     resize_callback: Option<Box<dyn FnMut()>>,
     close_callback: Option<Box<dyn FnOnce()>>,
@@ -331,7 +331,7 @@ impl platform::Window for Window {
         self
     }
 
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event)>) {
+    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
         self.0.as_ref().borrow_mut().event_callback = Some(callback);
     }
 
@@ -528,6 +528,45 @@ extern "C" fn dealloc_view(this: &Object, _: Sel) {
     }
 }
 
+extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
+    let window_state = unsafe { get_window_state(this) };
+    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+
+    let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
+    if let Some(event) = event {
+        match &event {
+            Event::KeyDown {
+                keystroke,
+                input,
+                is_held,
+            } => {
+                let keydown = (keystroke.clone(), input.clone());
+                // Ignore events from held-down keys after some of the initially-pressed keys
+                // were released.
+                if *is_held {
+                    if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
+                        return YES;
+                    }
+                } else {
+                    window_state_borrow.last_fresh_keydown = Some(keydown);
+                }
+            }
+            _ => return NO,
+        }
+
+        if let Some(mut callback) = window_state_borrow.event_callback.take() {
+            drop(window_state_borrow);
+            let handled = callback(event);
+            window_state.borrow_mut().event_callback = Some(callback);
+            handled as BOOL
+        } else {
+            NO
+        }
+    } else {
+        NO
+    }
+}
+
 extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let window_state = unsafe { get_window_state(this) };
     let weak_window_state = Rc::downgrade(&window_state);
@@ -551,24 +590,6 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
             Event::LeftMouseUp { .. } => {
                 window_state_borrow.synthetic_drag_counter += 1;
             }
-
-            // Ignore events from held-down keys after some of the initially-pressed keys
-            // were released.
-            Event::KeyDown {
-                input,
-                keystroke,
-                is_held,
-            } => {
-                let keydown = (keystroke.clone(), input.clone());
-                if *is_held {
-                    if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
-                        return;
-                    }
-                } else {
-                    window_state_borrow.last_fresh_keydown = Some(keydown);
-                }
-            }
-
             _ => {}
         }
 

crates/gpui/src/platform/test.rs 🔗

@@ -34,7 +34,7 @@ pub struct Window {
     size: Vector2F,
     scale_factor: f32,
     current_scene: Option<crate::Scene>,
-    event_handlers: Vec<Box<dyn FnMut(super::Event)>>,
+    event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
     resize_handlers: Vec<Box<dyn FnMut()>>,
     close_handlers: Vec<Box<dyn FnOnce()>>,
     pub(crate) title: Option<String>,
@@ -233,7 +233,7 @@ impl super::Window for Window {
         self
     }
 
-    fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event)>) {
+    fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
         self.event_handlers.push(callback);
     }
 

crates/gpui/src/presenter.rs 🔗

@@ -224,7 +224,7 @@ impl Presenter {
         }
     }
 
-    pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) {
+    pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) -> bool {
         if let Some(root_view_id) = cx.root_view_id(self.window_id) {
             let mut invalidated_views = Vec::new();
             let mut mouse_down_out_handlers = Vec::new();
@@ -366,7 +366,7 @@ impl Presenter {
             }
 
             if !handled {
-                event_cx.dispatch_event(root_view_id, &event);
+                handled = event_cx.dispatch_event(root_view_id, &event);
             }
 
             invalidated_views.extend(event_cx.invalidated_views);
@@ -384,6 +384,10 @@ impl Presenter {
                 }
                 cx.dispatch_action_any(self.window_id, &dispatch_path, directive.action.as_ref());
             }
+
+            handled
+        } else {
+            false
         }
     }