Merge pull request #2054 from zed-industries/notification-mouse-events

Notification mouse events
This commit is contained in:
Mikayla Maki 2023-01-20 16:41:27 -08:00 committed by GitHub
commit 94b2f8e07f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 9 deletions

View file

@ -178,6 +178,21 @@ impl MouseMovedEvent {
} }
} }
#[derive(Clone, Copy, Debug, Default)]
pub struct MouseExitedEvent {
pub position: Vector2F,
pub pressed_button: Option<MouseButton>,
pub modifiers: Modifiers,
}
impl Deref for MouseExitedEvent {
type Target = Modifiers;
fn deref(&self) -> &Self::Target {
&self.modifiers
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Event { pub enum Event {
KeyDown(KeyDownEvent), KeyDown(KeyDownEvent),
@ -186,6 +201,7 @@ pub enum Event {
MouseDown(MouseButtonEvent), MouseDown(MouseButtonEvent),
MouseUp(MouseButtonEvent), MouseUp(MouseButtonEvent),
MouseMoved(MouseMovedEvent), MouseMoved(MouseMovedEvent),
MouseExited(MouseExitedEvent),
ScrollWheel(ScrollWheelEvent), ScrollWheel(ScrollWheelEvent),
} }
@ -197,6 +213,7 @@ impl Event {
Event::ModifiersChanged { .. } => None, Event::ModifiersChanged { .. } => None,
Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position), Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position),
Event::MouseMoved(event) => Some(event.position), Event::MouseMoved(event) => Some(event.position),
Event::MouseExited(event) => Some(event.position),
Event::ScrollWheel(event) => Some(event.position), Event::ScrollWheel(event) => Some(event.position),
} }
} }

View file

@ -3,7 +3,7 @@ use crate::{
keymap_matcher::Keystroke, keymap_matcher::Keystroke,
platform::{Event, NavigationDirection}, platform::{Event, NavigationDirection},
KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton, MouseButtonEvent, KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton, MouseButtonEvent,
MouseMovedEvent, ScrollDelta, ScrollWheelEvent, TouchPhase, MouseExitedEvent, MouseMovedEvent, ScrollDelta, ScrollWheelEvent, TouchPhase,
}; };
use cocoa::{ use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType}, appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
@ -221,6 +221,16 @@ impl Event {
modifiers: read_modifiers(native_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, _ => None,
} }
} }

View file

@ -66,6 +66,14 @@ const NSNormalWindowLevel: NSInteger = 0;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
const NSPopUpWindowLevel: NSInteger = 101; const NSPopUpWindowLevel: NSInteger = 101;
#[allow(non_upper_case_globals)] #[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; const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
#[repr(C)] #[repr(C)]
@ -164,6 +172,10 @@ unsafe fn build_classes() {
sel!(mouseMoved:), sel!(mouseMoved:),
handle_view_event as extern "C" fn(&Object, Sel, id), 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( decl.add_method(
sel!(mouseDragged:), sel!(mouseDragged:),
handle_view_event as extern "C" fn(&Object, Sel, id), handle_view_event as extern "C" fn(&Object, Sel, id),
@ -316,6 +328,7 @@ enum ImeState {
struct WindowState { struct WindowState {
id: usize, id: usize,
native_window: id, native_window: id,
kind: WindowKind,
event_callback: Option<Box<dyn FnMut(Event) -> bool>>, event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
activate_callback: Option<Box<dyn FnMut(bool)>>, activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut()>>, resize_callback: Option<Box<dyn FnMut()>>,
@ -337,7 +350,6 @@ struct WindowState {
ime_state: ImeState, ime_state: ImeState,
//Retains the last IME Text //Retains the last IME Text
ime_text: Option<String>, ime_text: Option<String>,
accepts_first_mouse: bool,
} }
struct InsertText { struct InsertText {
@ -422,6 +434,7 @@ impl Window {
let window = Self(Rc::new(RefCell::new(WindowState { let window = Self(Rc::new(RefCell::new(WindowState {
id, id,
native_window, native_window,
kind: options.kind,
event_callback: None, event_callback: None,
resize_callback: None, resize_callback: None,
should_close_callback: None, should_close_callback: None,
@ -437,7 +450,6 @@ impl Window {
scene_to_render: Default::default(), scene_to_render: Default::default(),
renderer: Renderer::new(true, fonts), renderer: Renderer::new(true, fonts),
last_fresh_keydown: None, last_fresh_keydown: None,
accepts_first_mouse: options.kind == WindowKind::PopUp,
traffic_light_position: options traffic_light_position: options
.titlebar .titlebar
.as_ref() .as_ref()
@ -470,8 +482,6 @@ impl Window {
native_window.setTitlebarAppearsTransparent_(YES); native_window.setTitlebarAppearsTransparent_(YES);
} }
native_window.setAcceptsMouseMovedEvents_(YES);
native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable); native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
native_view.setWantsBestResolutionOpenGLSurface_(YES); native_view.setWantsBestResolutionOpenGLSurface_(YES);
@ -494,8 +504,25 @@ impl Window {
} }
match options.kind { match options.kind {
WindowKind::Normal => native_window.setLevel_(NSNormalWindowLevel), WindowKind::Normal => {
native_window.setLevel_(NSNormalWindowLevel);
native_window.setAcceptsMouseMovedEvents_(YES);
}
WindowKind::PopUp => { 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); native_window.setLevel_(NSPopUpWindowLevel);
let _: () = msg_send![ let _: () = msg_send![
native_window, 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 window_height = window_state_borrow.content_size().y();
let event = unsafe { Event::from_native(native_event, Some(window_height)) }; let event = unsafe { Event::from_native(native_event, Some(window_height)) };
if let Some(event) = event { if let Some(event) = event {
match &event { match &event {
Event::MouseMoved( Event::MouseMoved(
@ -985,7 +1011,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
.detach(); .detach();
} }
Event::MouseMoved(_) if !is_active => return, Event::MouseMoved(_)
if !(is_active || window_state_borrow.kind == WindowKind::PopUp) =>
{
return
}
Event::MouseUp(MouseButtonEvent { Event::MouseUp(MouseButtonEvent {
button: MouseButton::Left, button: MouseButton::Left,
@ -1408,7 +1438,7 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
unsafe { unsafe {
let state = get_window_state(this); let state = get_window_state(this);
let state_borrow = state.as_ref().borrow(); let state_borrow = state.as_ref().borrow();
return state_borrow.accepts_first_mouse as BOOL; return state_borrow.kind == WindowKind::PopUp;
} }
} }

View file

@ -360,6 +360,21 @@ impl Presenter {
self.last_mouse_moved_event = Some(event.clone()); 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 { Event::ScrollWheel(e) => mouse_events.push(MouseEvent::ScrollWheel(MouseScrollWheel {
region: Default::default(), region: Default::default(),
platform_event: e.clone(), platform_event: e.clone(),