1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-19 18:57:59 +03:00

Detect mouse leaving the window (#1679)

* Detect mouse leaving the window

* Implement leave

* Use new API

* Fix mouse leave

* Fix mouse leave on Wayland

* Mouse leave on X11

* Detect mouse leaving window on macOS

* Fix example

refs:  #1434
This commit is contained in:
David Rios 2022-03-11 17:26:09 +01:00 committed by GitHub
parent 3b05dac0c6
commit aaad2be606
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 5 deletions

View File

@ -837,6 +837,10 @@ impl TermWindow {
self.mouse_event_impl(event, window);
Ok(true)
}
WindowEvent::MouseLeave => {
self.mouse_leave_impl(window);
Ok(true)
}
WindowEvent::Resized {
dimensions,
window_state,

View File

@ -199,6 +199,11 @@ impl super::TermWindow {
}
}
pub fn mouse_leave_impl(&mut self, context: &dyn WindowOps) {
self.current_mouse_event = None;
context.invalidate();
}
fn drag_split(
&mut self,
mut item: UIItem,

View File

@ -80,7 +80,8 @@ impl MyWindow {
WindowEvent::AppearanceChanged(_)
| WindowEvent::AdviseDeadKeyStatus(_)
| WindowEvent::Notification(_)
| WindowEvent::FocusChanged(_) => {}
| WindowEvent::FocusChanged(_)
| WindowEvent::MouseLeave => {}
}
}
}

View File

@ -168,6 +168,7 @@ pub enum WindowEvent {
KeyEvent(KeyEvent),
MouseEvent(MouseEvent),
MouseLeave,
AppearanceChanged(Appearance),

View File

@ -414,6 +414,7 @@ impl Window {
screen_changed: false,
gl_context_pair: None,
text_cursor_position: Rect::new(Point::new(0, 0), Size::new(0, 0)),
tracking_rect_tag: 0,
hscroll_remainder: 0.,
vscroll_remainder: 0.,
last_wheel: Instant::now(),
@ -1045,6 +1046,7 @@ struct Inner {
screen_changed: bool,
gl_context_pair: Option<GlContextPair>,
text_cursor_position: Rect,
tracking_rect_tag: NSInteger,
hscroll_remainder: f64,
vscroll_remainder: f64,
last_wheel: Instant,
@ -1679,6 +1681,35 @@ impl WindowView {
}
}
extern "C" fn update_tracking_areas(this: &mut Object, _sel: Sel) {
let frame = unsafe { NSView::frame(this as *mut _) };
if let Some(this) = Self::get_this(this) {
let mut inner = this.inner.borrow_mut();
if let Some(ref view) = inner.view_id {
let view = view.load();
if view.is_null() {
return;
}
let tag = inner.tracking_rect_tag;
if tag != 0 {
unsafe {
let () = msg_send![*view, removeTrackingRect: tag];
}
}
let rect = NSRect::new(
NSPoint::new(0.0, 0.0),
NSSize::new(frame.size.width, frame.size.height),
);
inner.tracking_rect_tag = unsafe {
msg_send![*view, addTrackingRect: rect owner: *view userData: nil assumeInside: NO]
};
}
}
}
extern "C" fn window_should_close(this: &mut Object, _sel: Sel, _id: id) -> BOOL {
unsafe {
let () = msg_send![this, setNeedsDisplay: YES];
@ -1881,6 +1912,16 @@ impl WindowView {
Self::mouse_common(this, nsevent, MouseEventKind::Move);
}
extern "C" fn mouse_exited(this: &mut Object, _sel: Sel, _nsevent: id) {
if let Some(myself) = Self::get_this(this) {
myself
.inner
.borrow_mut()
.events
.dispatch(WindowEvent::MouseLeave);
}
}
fn key_common(this: &mut Object, nsevent: id, key_is_down: bool) {
let is_a_repeat = unsafe { nsevent.isARepeat() == YES };
let chars = unsafe { nsstring_to_str(nsevent.characters()) };
@ -2443,6 +2484,10 @@ impl WindowView {
sel!(scrollWheel:),
Self::scroll_wheel as extern "C" fn(&mut Object, Sel, id),
);
cls.add_method(
sel!(mouseExited:),
Self::mouse_exited as extern "C" fn(&mut Object, Sel, id),
);
cls.add_method(
sel!(keyDown:),
@ -2468,6 +2513,11 @@ impl WindowView {
Self::view_did_change_effective_appearance as extern "C" fn(&mut Object, Sel),
);
cls.add_method(
sel!(updateTrackingAreas),
Self::update_tracking_areas as extern "C" fn(&mut Object, Sel),
);
// NSTextInputClient
cls.add_method(

View File

@ -104,6 +104,7 @@ pub struct PendingMouse {
surface_coords: Option<(f64, f64)>,
button: Vec<(MousePress, ButtonState)>,
scroll: Option<(f64, f64)>,
in_window: bool,
}
impl PendingMouse {
@ -114,6 +115,7 @@ impl PendingMouse {
button: vec![],
scroll: None,
surface_coords: None,
in_window: false,
}))
}
@ -126,8 +128,15 @@ impl PendingMouse {
.lock()
.unwrap()
.update_last_serial(serial);
self.in_window = true;
false
}
PointerEvent::Leave { .. } => {
let changed = self.in_window;
self.surface_coords = None;
self.in_window = false;
changed
}
PointerEvent::Motion {
surface_x,
surface_y,
@ -204,6 +213,10 @@ impl PendingMouse {
pub fn scroll(pending: &Arc<Mutex<Self>>) -> Option<(f64, f64)> {
pending.lock().unwrap().scroll.take()
}
pub fn in_window(pending: &Arc<Mutex<Self>>) -> bool {
pending.lock().unwrap().in_window
}
}
impl PointerDispatcher {

View File

@ -537,6 +537,11 @@ impl WaylandWindowInner {
self.events.dispatch(WindowEvent::MouseEvent(event));
}
}
if !PendingMouse::in_window(&pending_mouse) {
self.events.dispatch(WindowEvent::MouseLeave);
self.refresh_frame();
}
}
fn get_dpi_factor(&self) -> i32 {

View File

@ -61,6 +61,7 @@ pub(crate) struct WindowInner {
in_size_move: bool,
dead_pending: Option<(Modifiers, u32)>,
saved_placement: Option<WINDOWPLACEMENT>,
track_mouse_leave: bool,
keyboard_info: KeyboardLayoutInfo,
appearance: Appearance,
@ -415,6 +416,7 @@ impl Window {
in_size_move: false,
dead_pending: None,
saved_placement: None,
track_mouse_leave: false,
config: config.clone(),
}));
@ -1133,6 +1135,21 @@ unsafe fn mouse_button(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
unsafe fn mouse_move(hwnd: HWND, _msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
if !inner.track_mouse_leave {
inner.track_mouse_leave = true;
let mut trk = TRACKMOUSEEVENT {
cbSize: std::mem::size_of::<TRACKMOUSEEVENT>() as u32,
dwFlags: TME_LEAVE,
hwndTrack: hwnd,
dwHoverTime: 0,
};
inner.track_mouse_leave = TrackMouseEvent(&mut trk) == winapi::shared::minwindef::TRUE;
}
let (modifiers, mouse_buttons) = mods_and_buttons(wparam);
let coords = mouse_coords(lparam);
let event = MouseEvent {
@ -1143,10 +1160,20 @@ unsafe fn mouse_move(hwnd: HWND, _msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
modifiers,
};
inner
.borrow_mut()
.events
.dispatch(WindowEvent::MouseEvent(event));
inner.events.dispatch(WindowEvent::MouseEvent(event));
Some(0)
} else {
None
}
}
unsafe fn mouse_leave(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> {
if let Some(inner) = rc_from_hwnd(hwnd) {
let mut inner = inner.borrow_mut();
inner.track_mouse_leave = false;
inner.events.dispatch(WindowEvent::MouseLeave);
Some(0)
} else {
None
@ -2000,6 +2027,7 @@ unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
WM_SETTINGCHANGE => apply_theme(hwnd),
WM_IME_COMPOSITION => ime_composition(hwnd, msg, wparam, lparam),
WM_MOUSEMOVE => mouse_move(hwnd, msg, wparam, lparam),
WM_MOUSELEAVE => mouse_leave(hwnd, msg, wparam, lparam),
WM_MOUSEHWHEEL | WM_MOUSEWHEEL => mouse_wheel(hwnd, msg, wparam, lparam),
WM_LBUTTONDBLCLK | WM_RBUTTONDBLCLK | WM_MBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP
| WM_RBUTTONDOWN | WM_RBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP => {

View File

@ -138,6 +138,10 @@ fn window_id_from_event(event: &xcb::GenericEvent) -> Option<xcb::xproto::Window
let msg: &xcb::FocusOutEvent = unsafe { xcb::cast_event(event) };
Some(msg.event())
}
xcb::LEAVE_NOTIFY => {
let msg: &xcb::LeaveNotifyEvent = unsafe { xcb::cast_event(event) };
Some(msg.event())
}
_ => None,
}
}

View File

@ -414,6 +414,9 @@ impl XWindowInner {
log::trace!("Calling focus_change(false)");
self.events.dispatch(WindowEvent::FocusChanged(false));
}
xcb::LEAVE_NOTIFY => {
self.events.dispatch(WindowEvent::MouseLeave);
}
_ => {
eprintln!("unhandled: {:x}", r);
}
@ -825,6 +828,7 @@ impl XWindow {
| xcb::EVENT_MASK_BUTTON_PRESS
| xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_POINTER_MOTION
| xcb::EVENT_MASK_LEAVE_WINDOW
| xcb::EVENT_MASK_BUTTON_MOTION
| xcb::EVENT_MASK_KEY_RELEASE
| xcb::EVENT_MASK_PROPERTY_CHANGE