Detailed changes
@@ -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)]
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),
}
}
@@ -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,
}
}
@@ -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(
@@ -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(),
@@ -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;