diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ad1fad85b1938220e4f2259ff623dc274748aac5..0e5697c30bc1d1fbe2b8dcfeb4003254bd460a64 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -21,6 +21,7 @@ use std::{ use anyhow::{anyhow, Context, Result}; use lazy_static::lazy_static; use parking_lot::Mutex; +use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use smallvec::SmallVec; use smol::prelude::*; @@ -865,6 +866,12 @@ impl MutableAppContext { } } + pub fn screen_position(&self, window_id: usize, view_position: &Vector2F) -> Option { + self.presenters_and_platform_windows + .get(&window_id) + .map(|(_, window)| window.screen_position(view_position)) + } + pub fn window_ids(&self) -> impl Iterator + '_ { self.cx.windows.keys().cloned() } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 99d607e4070c5eebb2039dba3754e6100a59b30a..0601fd97640559e16f4b1eb6ba39f61fff5a4af2 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -64,7 +64,7 @@ pub trait Platform: Send + Sync { fn read_credentials(&self, url: &str) -> Result)>>; fn delete_credentials(&self, url: &str) -> Result<()>; - fn set_cursor_style(&self, style: CursorStyle); + fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F); fn should_auto_hide_scrollbars(&self) -> bool; fn local_timezone(&self) -> UtcOffset; @@ -145,6 +145,7 @@ pub trait Window { fn present_scene(&mut self, scene: Scene); fn appearance(&self) -> Appearance; fn on_appearance_changed(&mut self, callback: Box); + fn screen_position(&self, view_position: &Vector2F) -> Vector2F; } #[derive(Debug)] diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 37406858ecec6ed277ed0f964c5e9d4ef86af2ee..f1b7f75c03f62e4b43fc6022c19ed7c618353dba 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -37,6 +37,7 @@ use objc::{ runtime::{Class, Object, Sel}, sel, sel_impl, }; +use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use ptr::null_mut; use std::{ @@ -695,7 +696,19 @@ impl platform::Platform for MacPlatform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle) { + fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F) { + let top_most = Window::window_id_under(screen_position); + if top_most.is_none() { + return; + } + if top_most.unwrap() != window_id { + return; + } + + dbg!(top_most.unwrap(), window_id); + + dbg!(style); + unsafe { let cursor: id = match style { CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], diff --git a/crates/gpui/src/platform/mac/status_item.rs b/crates/gpui/src/platform/mac/status_item.rs index 33feb4808f17c15136df7daeee287a90af921f3a..d05c559772651a3e8aa0ce20b149da31ac9e746e 100644 --- a/crates/gpui/src/platform/mac/status_item.rs +++ b/crates/gpui/src/platform/mac/status_item.rs @@ -258,6 +258,10 @@ impl platform::Window for StatusItem { crate::Appearance::from_native(appearance) } } + + fn screen_position(&self, _view_position: &Vector2F) -> Vector2F { + unimplemented!() + } } impl StatusItemState { diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 6126533644578ca0a023657eef851f26c16432a3..032b88507bca39044ca5bf7f17c9f8e16ec333f8 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -559,6 +559,26 @@ impl Window { } } } + + pub fn window_id_under(screen_position: &Vector2F) -> Option { + unsafe { + let app = NSApplication::sharedApplication(nil); + + let point = NSPoint::new(screen_position.x() as f64, screen_position.y() as f64); + let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:point belowWindowWithWindowNumber:0 as NSInteger]; + // For some reason this API doesn't work when our two windows are on top of each other + let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number]; + dbg!(top_most_window); + let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; + let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; + if is_panel | is_window { + let id = get_window_state(&*top_most_window).borrow().id; + Some(id) + } else { + None + } + } + } } impl Drop for Window { @@ -755,6 +775,16 @@ impl platform::Window for Window { fn on_appearance_changed(&mut self, callback: Box) { self.0.borrow_mut().appearance_changed_callback = Some(callback); } + + fn screen_position(&self, view_position: &Vector2F) -> Vector2F { + let self_borrow = self.0.borrow_mut(); + unsafe { + let point = NSPoint::new(view_position.x() as f64, view_position.y() as f64); + let screen_point: NSPoint = + msg_send![self_borrow.native_window, convertPointToScreen: point]; + vec2f(screen_point.x as f32, screen_point.y as f32) + } + } } impl WindowState { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 00cd524c1de6b99e9acfef3a43f1e1f3c3b02630..7295bf525bc1321cd378069ead2f2d1dde15f775 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -178,7 +178,7 @@ impl super::Platform for Platform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle) { + fn set_cursor_style(&self, style: CursorStyle, _window_id: usize, _position: &Vector2F) { *self.cursor.lock() = style; } @@ -332,6 +332,10 @@ impl super::Window for Window { } fn on_appearance_changed(&mut self, _: Box) {} + + fn screen_position(&self, view_position: &Vector2F) -> Vector2F { + view_position.clone() + } } pub fn platform() -> Platform { diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 0909d95fd0a5ac4f53e5de600ca7bc5c540dfc00..f74eef463a7aab26637f1217d84334424bedabaf 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -316,7 +316,14 @@ impl Presenter { break; } } - cx.platform().set_cursor_style(style_to_assign); + + if let Some(screen_position) = cx.screen_position(self.window_id, position) { + cx.platform().set_cursor_style( + style_to_assign, + self.window_id, + &screen_position, + ); + } if !event_reused { if pressed_button.is_some() { diff --git a/crates/gpui/src/presenter/event_dispatcher.rs b/crates/gpui/src/presenter/event_dispatcher.rs index 4c72334910b514a257be8213410d1aa9dbb05b79..960c565bd49f02a90f41a50f3fb595adc6579b93 100644 --- a/crates/gpui/src/presenter/event_dispatcher.rs +++ b/crates/gpui/src/presenter/event_dispatcher.rs @@ -209,6 +209,7 @@ impl EventDispatcher { break; } } + cx.platform().set_cursor_style(style_to_assign); if !event_reused { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ec7ba8fae07ec149c378d1f4e9dc2dde4e4b6a25..71c5ef97a209e866a9d6ad4ba69d491af59bcb6e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,8 +31,9 @@ use futures::{ }; use gpui::{ actions, + color::Color, elements::*, - geometry::vector::Vector2F, + geometry::vector::{vec2f, Vector2F}, impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, WindowOptions}, @@ -263,6 +264,59 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { client.add_view_request_handler(Workspace::handle_follow); client.add_view_message_handler(Workspace::handle_unfollow); client.add_view_message_handler(Workspace::handle_update_followers); + + // REMEMBER TO DELETE THE SHOW NOTIF + cx.add_action( + |_workspace: &mut Workspace, _: &ShowNotif, cx: &mut ViewContext| { + struct DummyView; + + impl Entity for DummyView { + type Event = (); + } + + impl View for DummyView { + fn ui_name() -> &'static str { + "DummyView" + } + fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { + MouseEventHandler::::new(0, cx, |state, _cx| { + Empty::new() + .contained() + .with_background_color(if state.hovered() { + Color::red() + } else { + Color::blue() + }) + .constrained() + .with_width(200.) + .with_height(200.) + .boxed() + }) + .on_click(MouseButton::Left, |_, _| { + println!("click"); + }) + .with_cursor_style(CursorStyle::ResizeUpDown) + .boxed() + } + } + + cx.add_window( + WindowOptions { + bounds: gpui::WindowBounds::Fixed(gpui::geometry::rect::RectF::new( + vec2f(400., 200.), + vec2f(300., 200.), + )), + titlebar: None, + center: false, + focus: false, + kind: gpui::WindowKind::PopUp, + is_movable: true, + screen: None, + }, + |_cx| DummyView, + ) + }, + ) } type ProjectItemBuilders = HashMap<