diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index ebbce8f84c9..04692c86a55 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -2860,7 +2860,6 @@ impl Interactivity { } if let Some(hover_listener) = self.hover_listener.take() { - let hitbox = hitbox.clone(); let was_hovered = element_state .hover_listener_state .get_or_insert_with(Default::default) @@ -2869,22 +2868,35 @@ impl Interactivity { .pending_mouse_down .get_or_insert_with(Default::default) .clone(); - - window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| { - if phase != DispatchPhase::Bubble { - return; - } - let is_hovered = has_mouse_down.borrow().is_none() - && !cx.has_active_drag() - && hitbox.is_hovered(window); + let hover_listener = Rc::new(hover_listener); + let update_hover = move |is_hovered: bool, window: &mut Window, cx: &mut App| { let mut was_hovered = was_hovered.borrow_mut(); - if is_hovered != *was_hovered { *was_hovered = is_hovered; drop(was_hovered); - hover_listener(&is_hovered, window, cx); } + }; + + window.on_mouse_event({ + let update_hover = update_hover.clone(); + let hitbox = hitbox.clone(); + move |_: &MouseMoveEvent, phase, window, cx| { + if phase == DispatchPhase::Bubble { + let is_hovered = has_mouse_down.borrow().is_none() + && !cx.has_active_drag() + && hitbox.is_hovered(window); + update_hover(is_hovered, window, cx); + } + } + }); + + // The pointer can leave the window without a final MouseMove, so also + // clear hover on MouseExited. + window.on_mouse_event(move |_: &MouseExitEvent, phase, window, cx| { + if phase == DispatchPhase::Bubble { + update_hover(false, window, cx); + } }); } diff --git a/crates/gpui_linux/src/linux/wayland/client.rs b/crates/gpui_linux/src/linux/wayland/client.rs index 9522ebb8f0d..a4cc9fde9f1 100644 --- a/crates/gpui_linux/src/linux/wayland/client.rs +++ b/crates/gpui_linux/src/linux/wayland/client.rs @@ -1884,8 +1884,9 @@ impl Dispatch for WaylandClientStatePtr { surface_y, .. } => { + let position = point(px(surface_x as f32), px(surface_y as f32)); state.serial_tracker.update(SerialKind::MouseEnter, serial); - state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32))); + state.mouse_location = Some(position); state.button_pressed = None; if let Some(window) = get_window(&mut state, &surface.id()) { @@ -1908,8 +1909,16 @@ impl Dispatch for WaylandClientStatePtr { ); } } + let modifiers = state.modifiers; drop(state); window.set_hovered(true); + // No Motion follows Enter unless the pointer keeps moving, so synthesize + // a MouseMove to establish hover at the entry position. + window.handle_input(PlatformInput::MouseMove(MouseMoveEvent { + position, + pressed_button: None, + modifiers, + })); } } wl_pointer::Event::Leave { .. } => {