From 8af1294ba51d6c8b114d06d3f8a1aa7cfbcc20ae Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 20 Jan 2023 09:37:09 -0800 Subject: [PATCH 1/2] Changed platform mouse moved handling to only fire on active or popup windows co-authored-by: Antonio --- crates/gpui/src/platform/mac/window.rs | 12 ++++++++---- crates/workspace/src/workspace.rs | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 4104b0ebf8..0da2e4351e 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -316,6 +316,7 @@ enum ImeState { struct WindowState { id: usize, native_window: id, + kind: WindowKind, event_callback: Option bool>>, activate_callback: Option>, resize_callback: Option>, @@ -337,7 +338,6 @@ struct WindowState { ime_state: ImeState, //Retains the last IME Text ime_text: Option, - accepts_first_mouse: bool, } struct InsertText { @@ -422,6 +422,7 @@ impl Window { let window = Self(Rc::new(RefCell::new(WindowState { id, native_window, + kind: options.kind, event_callback: None, resize_callback: None, should_close_callback: None, @@ -437,7 +438,6 @@ impl Window { scene_to_render: Default::default(), renderer: Renderer::new(true, fonts), last_fresh_keydown: None, - accepts_first_mouse: options.kind == WindowKind::PopUp, traffic_light_position: options .titlebar .as_ref() @@ -985,7 +985,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { .detach(); } - Event::MouseMoved(_) if !is_active => return, + Event::MouseMoved(_) + if !(is_active || window_state_borrow.kind == WindowKind::PopUp) => + { + return + } Event::MouseUp(MouseButtonEvent { button: MouseButton::Left, @@ -1408,7 +1412,7 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL { unsafe { let state = get_window_state(this); let state_borrow = state.as_ref().borrow(); - return state_borrow.accepts_first_mouse as BOOL; + return state_borrow.kind == WindowKind::PopUp; } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8735f33fbe..9665db6be6 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,14 +31,18 @@ use futures::{ }; use gpui::{ actions, + color::Color, elements::*, - geometry::vector::Vector2F, + geometry::{ + rect::RectF, + vector::{vec2f, Vector2F}, + }, impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, WindowOptions}, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MouseButton, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, SizeConstraint, - Task, View, ViewContext, ViewHandle, WeakViewHandle, + Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowBounds, WindowKind, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use language::LanguageRegistry; From 310d867aab01e6b0cc8b7f0954d13c68d88123c4 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 20 Jan 2023 10:32:44 -0800 Subject: [PATCH 2/2] Switch PopUp windows to use the NSTrackingArea API and add support for the mouseExited event Co-authored-by: Antonio --- crates/gpui/src/platform/event.rs | 17 +++++++++++++ crates/gpui/src/platform/mac/event.rs | 12 ++++++++- crates/gpui/src/platform/mac/window.rs | 34 +++++++++++++++++++++++--- crates/gpui/src/presenter.rs | 15 ++++++++++++ crates/workspace/src/workspace.rs | 8 ++---- 5 files changed, 75 insertions(+), 11 deletions(-) diff --git a/crates/gpui/src/platform/event.rs b/crates/gpui/src/platform/event.rs index 0c08af4497..c39c76dc34 100644 --- a/crates/gpui/src/platform/event.rs +++ b/crates/gpui/src/platform/event.rs @@ -178,6 +178,21 @@ impl MouseMovedEvent { } } +#[derive(Clone, Copy, Debug, Default)] +pub struct MouseExitedEvent { + pub position: Vector2F, + pub pressed_button: Option, + pub modifiers: Modifiers, +} + +impl Deref for MouseExitedEvent { + type Target = Modifiers; + + fn deref(&self) -> &Self::Target { + &self.modifiers + } +} + #[derive(Clone, Debug)] pub enum Event { KeyDown(KeyDownEvent), @@ -186,6 +201,7 @@ pub enum Event { MouseDown(MouseButtonEvent), MouseUp(MouseButtonEvent), MouseMoved(MouseMovedEvent), + MouseExited(MouseExitedEvent), ScrollWheel(ScrollWheelEvent), } @@ -197,6 +213,7 @@ impl Event { Event::ModifiersChanged { .. } => None, Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position), Event::MouseMoved(event) => Some(event.position), + Event::MouseExited(event) => Some(event.position), Event::ScrollWheel(event) => Some(event.position), } } diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index c527fe8d25..2f29898c26 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -3,7 +3,7 @@ use crate::{ keymap_matcher::Keystroke, platform::{Event, NavigationDirection}, KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton, MouseButtonEvent, - MouseMovedEvent, ScrollDelta, ScrollWheelEvent, TouchPhase, + MouseExitedEvent, MouseMovedEvent, ScrollDelta, ScrollWheelEvent, TouchPhase, }; use cocoa::{ appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType}, @@ -221,6 +221,16 @@ impl Event { modifiers: read_modifiers(native_event), }) }), + NSEventType::NSMouseExited => window_height.map(|window_height| { + Self::MouseExited(MouseExitedEvent { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + pressed_button: None, + modifiers: read_modifiers(native_event), + }) + }), _ => None, } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 0da2e4351e..e93e63be92 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -66,6 +66,14 @@ const NSNormalWindowLevel: NSInteger = 0; #[allow(non_upper_case_globals)] const NSPopUpWindowLevel: NSInteger = 101; #[allow(non_upper_case_globals)] +const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01; +#[allow(non_upper_case_globals)] +const NSTrackingMouseMoved: NSUInteger = 0x02; +#[allow(non_upper_case_globals)] +const NSTrackingActiveAlways: NSUInteger = 0x80; +#[allow(non_upper_case_globals)] +const NSTrackingInVisibleRect: NSUInteger = 0x200; +#[allow(non_upper_case_globals)] const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4; #[repr(C)] @@ -164,6 +172,10 @@ unsafe fn build_classes() { sel!(mouseMoved:), handle_view_event as extern "C" fn(&Object, Sel, id), ); + decl.add_method( + sel!(mouseExited:), + handle_view_event as extern "C" fn(&Object, Sel, id), + ); decl.add_method( sel!(mouseDragged:), handle_view_event as extern "C" fn(&Object, Sel, id), @@ -470,8 +482,6 @@ impl Window { native_window.setTitlebarAppearsTransparent_(YES); } - native_window.setAcceptsMouseMovedEvents_(YES); - native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable); native_view.setWantsBestResolutionOpenGLSurface_(YES); @@ -494,8 +504,25 @@ impl Window { } match options.kind { - WindowKind::Normal => native_window.setLevel_(NSNormalWindowLevel), + WindowKind::Normal => { + native_window.setLevel_(NSNormalWindowLevel); + native_window.setAcceptsMouseMovedEvents_(YES); + } WindowKind::PopUp => { + // Use a tracking area to allow receiving MouseMoved events even when + // the window or application aren't active, which is often the case + // e.g. for notification windows. + let tracking_area: id = msg_send![class!(NSTrackingArea), alloc]; + let _: () = msg_send![ + tracking_area, + initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)) + options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect + owner: native_view + userInfo: nil + ]; + let _: () = + msg_send![native_view, addTrackingArea: tracking_area.autorelease()]; + native_window.setLevel_(NSPopUpWindowLevel); let _: () = msg_send![ native_window, @@ -965,7 +992,6 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { let window_height = window_state_borrow.content_size().y(); let event = unsafe { Event::from_native(native_event, Some(window_height)) }; - if let Some(event) = event { match &event { Event::MouseMoved( diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 73e371c50b..0909d95fd0 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -360,6 +360,21 @@ impl Presenter { self.last_mouse_moved_event = Some(event.clone()); } + Event::MouseExited(event) => { + // When the platform sends a MouseExited event, synthesize + // a MouseMoved event whose position is outside the window's + // bounds so that hover and cursor state can be updated. + return self.dispatch_event( + Event::MouseMoved(MouseMovedEvent { + position: event.position, + pressed_button: event.pressed_button, + modifiers: event.modifiers, + }), + event_reused, + cx, + ); + } + Event::ScrollWheel(e) => mouse_events.push(MouseEvent::ScrollWheel(MouseScrollWheel { region: Default::default(), platform_event: e.clone(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9665db6be6..8735f33fbe 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,18 +31,14 @@ use futures::{ }; use gpui::{ actions, - color::Color, elements::*, - geometry::{ - rect::RectF, - vector::{vec2f, Vector2F}, - }, + geometry::vector::Vector2F, impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, WindowOptions}, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MouseButton, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, SizeConstraint, - Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowBounds, WindowKind, + Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use language::LanguageRegistry;