Handle first click on Zed window (#9553)

Daniel Zhu created

Fixes #4336

Change summary

crates/collab/src/tests/integration_tests.rs     |  2 +
crates/gpui/src/app/test_context.rs              |  1 
crates/gpui/src/interactive.rs                   |  3 +
crates/gpui/src/platform/linux/wayland/client.rs |  1 
crates/gpui/src/platform/linux/x11/client.rs     |  1 
crates/gpui/src/platform/mac/events.rs           |  1 
crates/gpui/src/platform/mac/window.rs           | 31 ++++++++++++-----
crates/gpui/src/platform/windows/window.rs       |  2 +
crates/project_panel/src/project_panel.rs        |  2 
9 files changed, 33 insertions(+), 11 deletions(-)

Detailed changes

crates/collab/src/tests/integration_tests.rs 🔗

@@ -5891,6 +5891,7 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
         position: new_tab_button_bounds.center(),
         modifiers: Modifiers::default(),
         click_count: 1,
+        first_mouse: false,
     });
 
     // regression test that the right click menu for tabs does not open.
@@ -5902,6 +5903,7 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) {
         position: tab_bounds.center(),
         modifiers: Modifiers::default(),
         click_count: 1,
+        first_mouse: false,
     });
     assert!(cx.debug_bounds("MENU_ITEM-Close").is_some());
 }

crates/gpui/src/app/test_context.rs 🔗

@@ -665,6 +665,7 @@ impl VisualTestContext {
             modifiers,
             button: MouseButton::Left,
             click_count: 1,
+            first_mouse: false,
         });
         self.simulate_event(MouseUpEvent {
             position,

crates/gpui/src/interactive.rs 🔗

@@ -100,6 +100,9 @@ pub struct MouseDownEvent {
 
     /// The number of times the button has been clicked.
     pub click_count: usize,
+
+    /// Whether this is the first, focusing click.
+    pub first_mouse: bool,
 }
 
 impl Sealed for MouseDownEvent {}

crates/gpui/src/platform/linux/wayland/client.rs 🔗

@@ -933,6 +933,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
                                 position: state.mouse_location,
                                 modifiers: state.modifiers,
                                 click_count: state.click_state.current_count,
+                                first_mouse: false,
                             }),
                         );
                     }

crates/gpui/src/platform/linux/x11/client.rs 🔗

@@ -218,6 +218,7 @@ impl X11Client {
                         position,
                         modifiers,
                         click_count: 1,
+                        first_mouse: false,
                     }));
                 } else if event.detail >= 4 && event.detail <= 5 {
                     // https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11

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

@@ -350,6 +350,8 @@ struct MacWindowState {
     input_during_keydown: Option<SmallVec<[ImeInput; 1]>>,
     previous_keydown_inserted_text: Option<String>,
     external_files_dragged: bool,
+    // Whether the next left-mouse click is also the focusing click.
+    first_mouse: bool,
     minimized: bool,
 }
 
@@ -607,6 +609,7 @@ impl MacWindow {
                 input_during_keydown: None,
                 previous_keydown_inserted_text: None,
                 external_files_dragged: false,
+                first_mouse: false,
                 minimized: false,
             })));
 
@@ -1262,7 +1265,6 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let weak_window_state = Arc::downgrade(&window_state);
     let mut lock = window_state.as_ref().lock();
     let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
-
     let window_height = lock.content_size().height;
     let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
 
@@ -1287,6 +1289,20 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                 };
             }
 
+            // Handles focusing click.
+            PlatformInput::MouseDown(
+                event @ MouseDownEvent {
+                    button: MouseButton::Left,
+                    ..
+                },
+            ) if (lock.first_mouse) => {
+                *event = MouseDownEvent {
+                    first_mouse: true,
+                    ..*event
+                };
+                lock.first_mouse = false;
+            }
+
             // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
             // the ctrl-left_up to avoid having a mismatch in button down/up events if the
             // user is still holding ctrl when releasing the left mouse button
@@ -1745,15 +1761,10 @@ extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
 }
 
 extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
-    unsafe {
-        let state = get_window_state(this);
-        let lock = state.as_ref().lock();
-        if lock.kind == WindowKind::PopUp {
-            YES
-        } else {
-            NO
-        }
-    }
+    let window_state = unsafe { get_window_state(this) };
+    let mut lock = window_state.as_ref().lock();
+    lock.first_mouse = true;
+    YES
 }
 
 extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {

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

@@ -594,6 +594,7 @@ impl WindowsWindowInner {
                 position: logical_point(x, y, scale_factor),
                 modifiers: self.current_modifiers(),
                 click_count: 1,
+                first_mouse: false,
             };
             if callback(PlatformInput::MouseDown(event)).default_prevented {
                 return Some(0);
@@ -1009,6 +1010,7 @@ impl WindowsWindowInner {
                 position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor),
                 modifiers: self.current_modifiers(),
                 click_count: 1,
+                first_mouse: false,
             };
             if callback(PlatformInput::MouseDown(event)).default_prevented {
                 return Some(0);

crates/project_panel/src/project_panel.rs 🔗

@@ -1450,7 +1450,7 @@ impl ProjectPanel {
                         .ml_1(),
                     )
                     .on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
-                        if event.down.button == MouseButton::Right {
+                        if event.down.button == MouseButton::Right || event.down.first_mouse {
                             return;
                         }
                         if !show_editor {