diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index bf185b1b6cc20e0f0f484fd0029c78a6211e6a3a..c1bb2011d0bdff432fc5bd0da12b63a79cb9ef5a 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -2589,7 +2589,8 @@ impl Interactivity { let pending_mouse_down = pending_mouse_down.clone(); let source_bounds = hitbox.bounds; move |window: &Window| { - pending_mouse_down.borrow().is_none() + !window.last_input_was_keyboard() + && pending_mouse_down.borrow().is_none() && source_bounds.contains(&window.mouse_position()) } }); diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 2a80f553eb9ff5a36cf1637a1106fd4c13712f15..5d9ea28fcf8c7998dfada055157dc0766c33628a 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -560,7 +560,8 @@ pub enum WindowControlArea { pub struct HitboxId(u64); impl HitboxId { - /// Checks if the hitbox with this ID is currently hovered. Except when handling + /// Checks if the hitbox with this ID is currently hovered. Returns `false` during keyboard + /// input modality so that keyboard navigation suppresses hover highlights. Except when handling /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse /// events or paint hover styles. /// @@ -570,6 +571,9 @@ impl HitboxId { if window.captured_hitbox == Some(self) { return true; } + if window.last_input_was_keyboard() { + return false; + } let hit_test = &window.mouse_hit_test; for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) { if self == *id { @@ -608,13 +612,15 @@ pub struct Hitbox { } impl Hitbox { - /// Checks if the hitbox is currently hovered. Except when handling `ScrollWheelEvent`, this is - /// typically what you want when determining whether to handle mouse events or paint hover - /// styles. + /// Checks if the hitbox is currently hovered. Returns `false` during keyboard input modality + /// so that keyboard navigation suppresses hover highlights. Except when handling + /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse + /// events or paint hover styles. /// /// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of /// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`) or - /// `HitboxBehavior::BlockMouseExceptScroll` (`InteractiveElement::block_mouse_except_scroll`). + /// `HitboxBehavior::BlockMouseExceptScroll` (`InteractiveElement::block_mouse_except_scroll`), + /// or if the current input modality is keyboard (see [`Window::last_input_was_keyboard`]). /// /// Handling of `ScrollWheelEvent` should typically use `should_handle_scroll` instead. /// Concretely, this is due to use-cases like overlays that cause the elements under to be @@ -4028,14 +4034,18 @@ impl Window { /// Dispatch a mouse or keyboard event on the window. #[profiling::function] pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult { - // Track whether this input was keyboard-based for focus-visible styling + // Track input modality for focus-visible styling and hover suppression. + // Hover is suppressed during keyboard modality so that keyboard navigation + // doesn't show hover highlights on the item under the mouse cursor. + let old_modality = self.last_input_modality; self.last_input_modality = match &event { - PlatformInput::KeyDown(_) | PlatformInput::ModifiersChanged(_) => { - InputModality::Keyboard - } - PlatformInput::MouseDown(e) if e.is_focusing() => InputModality::Mouse, + PlatformInput::KeyDown(_) => InputModality::Keyboard, + PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse, _ => self.last_input_modality, }; + if self.last_input_modality != old_modality { + self.refresh(); + } // Handlers may set this to false by calling `stop_propagation`. cx.propagate_event = true; diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index e87ec3415cf6d70d840d8566accb94ac6de1547c..2eb8d71bd4aa14960f8388859c974214f3e85c72 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -788,6 +788,12 @@ impl Picker { this.handle_click(ix, event.modifiers.platform, window, cx) }), ) + .on_hover(cx.listener(move |this, hovered: &bool, window, cx| { + if *hovered { + this.set_selected_index(ix, None, false, window, cx); + cx.notify(); + } + })) .children(self.delegate.render_match( ix, ix == self.delegate.selected_index(),