Use platform API to request mouse position

Nathan Sobo and Conrad Irwin created

Co-Authored-By: Conrad Irwin <conrad@zed.dev>

Change summary

crates/gpui/playground/src/hoverable.rs     |  2 +-
crates/gpui/src/app.rs                      |  9 +--------
crates/gpui/src/app/window.rs               |  7 ++++---
crates/gpui/src/platform.rs                 |  2 +-
crates/gpui/src/platform/mac/platform.rs    |  9 ++-------
crates/gpui/src/platform/mac/status_item.rs |  4 ++++
crates/gpui/src/platform/mac/window.rs      | 18 ++++++++++++++++++
crates/gpui/src/platform/test.rs            |  8 ++++----
8 files changed, 35 insertions(+), 24 deletions(-)

Detailed changes

crates/gpui/playground/src/hoverable.rs 🔗

@@ -66,7 +66,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
 
         let hovered = self.hovered.clone();
         cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
-            if bounds.contains_point(event.position) != hovered.get() {
+            if bounds.contains_point(cx.mouse_position()) != hovered.get() {
                 cx.repaint();
             }
         });

crates/gpui/src/app.rs 🔗

@@ -1363,14 +1363,7 @@ impl AppContext {
             window: handle,
         }));
 
-        let mouse_position = self.platform.mouse_position();
-        let mut window = Window::new(
-            handle,
-            platform_window,
-            mouse_position,
-            self,
-            build_root_view,
-        );
+        let mut window = Window::new(handle, platform_window, self, build_root_view);
         let mut cx = WindowContext::mutable(self, &mut window, handle);
         cx.layout(false).expect("initial layout should not error");
         let scene = cx.paint().expect("initial paint should not error");

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

@@ -70,7 +70,6 @@ impl Window {
     pub fn new<V, F>(
         handle: AnyWindowHandle,
         platform_window: Box<dyn platform::Window>,
-        mouse_position: Vector2F,
         cx: &mut AppContext,
         build_view: F,
     ) -> Self
@@ -98,7 +97,7 @@ impl Window {
             hovered_region_ids: Default::default(),
             clicked_region_ids: Default::default(),
             clicked_region: None,
-            mouse_position,
+            mouse_position: Default::default(),
             titlebar_height,
             appearance,
         };
@@ -508,7 +507,9 @@ impl<'a> WindowContext<'a> {
     }
 
     pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
-        self.dispatch_to_new_event_handlers(&event);
+        if !event_reused {
+            self.dispatch_to_new_event_handlers(&event);
+        }
 
         let mut mouse_events = SmallVec::<[_; 2]>::new();
         let mut notified_views: HashSet<usize> = Default::default();

crates/gpui/src/platform.rs 🔗

@@ -75,7 +75,6 @@ pub trait Platform: Send + Sync {
     fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
     fn delete_credentials(&self, url: &str) -> Result<()>;
 
-    fn mouse_position(&self) -> Vector2F;
     fn set_cursor_style(&self, style: CursorStyle);
     fn should_auto_hide_scrollbars(&self) -> bool;
 
@@ -147,6 +146,7 @@ pub trait Window {
     fn titlebar_height(&self) -> f32;
     fn appearance(&self) -> Appearance;
     fn screen(&self) -> Rc<dyn Screen>;
+    fn mouse_position(&self) -> Vector2F;
 
     fn as_any_mut(&mut self) -> &mut dyn Any;
     fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);

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

@@ -18,7 +18,7 @@ use cocoa::{
     },
     base::{id, nil, selector, BOOL, YES},
     foundation::{
-        NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSPoint, NSProcessInfo, NSString,
+        NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString,
         NSUInteger, NSURL,
     },
 };
@@ -37,7 +37,7 @@ use objc::{
     runtime::{Class, Object, Sel},
     sel, sel_impl,
 };
-use pathfinder_geometry::vector::{vec2f, Vector2F};
+
 use postage::oneshot;
 use ptr::null_mut;
 use std::{
@@ -785,11 +785,6 @@ impl platform::Platform for MacPlatform {
         Ok(())
     }
 
-    fn mouse_position(&self) -> Vector2F {
-        let position: NSPoint = unsafe { msg_send![class!(NSEvent), mouseLocation] };
-        vec2f(position.x as f32, position.y as f32)
-    }
-
     fn set_cursor_style(&self, style: CursorStyle) {
         unsafe {
             let new_cursor: id = match style {

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

@@ -202,6 +202,10 @@ impl platform::Window for StatusItem {
         }
     }
 
+    fn mouse_position(&self) -> Vector2F {
+        unimplemented!()
+    }
+
     fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
         self
     }

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

@@ -221,6 +221,14 @@ unsafe fn build_classes() {
     };
 }
 
+pub fn convert_mouse_position(position: NSPoint, window_height: f32) -> Vector2F {
+    vec2f(
+        position.x as f32,
+        // MacOS screen coordinates are relative to bottom left
+        window_height - position.y as f32,
+    )
+}
+
 unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
     let mut decl = ClassDecl::new(name, superclass).unwrap();
     decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
@@ -661,6 +669,16 @@ impl platform::Window for MacWindow {
         }
     }
 
+    fn mouse_position(&self) -> Vector2F {
+        let position = unsafe {
+            self.0
+                .borrow()
+                .native_window
+                .mouseLocationOutsideOfEventStream()
+        };
+        convert_mouse_position(position, self.content_size().y())
+    }
+
     fn as_any_mut(&mut self) -> &mut dyn Any {
         self
     }

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

@@ -195,10 +195,6 @@ impl super::Platform for Platform {
         Ok(())
     }
 
-    fn mouse_position(&self) -> Vector2F {
-        Vector2F::zero()
-    }
-
     fn set_cursor_style(&self, style: CursorStyle) {
         *self.cursor.lock() = style;
     }
@@ -336,6 +332,10 @@ impl super::Window for Window {
         Rc::new(Screen)
     }
 
+    fn mouse_position(&self) -> Vector2F {
+        Vector2F::zero()
+    }
+
     fn as_any_mut(&mut self) -> &mut dyn Any {
         self
     }