✨ feat(launcher): add result item hover selection and mouse navigation support

This commit is contained in:
sankalp committed 2025-07-20 21:47:13 +05:45
1 parent 54c5c52026
commit b7b3e8c596
4 files changed
+105 -24

No files matched your search

+31 -2
View File
@@ -668,11 +668,14 @@ class Launcher(Window):
else:
# Create normal result item
result_item = ResultItem(
result=result, selected=(i == self.selected_index)
result=result, selected=(i == self.selected_index), index=i
)
result_item.clicked.connect(
lambda _, idx=i: self._on_result_clicked(result_item, idx)
)
result_item.hovered.connect(
lambda _, idx=i: self._on_result_hovered(idx)
)
self.results_box.add(result_item)
self.results_box.show_all()
@@ -1032,13 +1035,21 @@ class Launcher(Window):
self.selected_index = index
self._activate_selected()
def _on_result_hovered(self, index):
"""Handle result item hover."""
# Update selection when mouse hovers over a result (without scrolling)
if 0 <= index < len(self.results):
self.selected_index = index
self.focus_mode = "results" # Switch to results focus mode
self._update_selection_visual_only()
def _is_mouse_over_results(self):
"""Check if mouse is over the results area."""
# Simple check - if we have results visible, assume user might be interacting
return len(self.results) > 0 and self.results_scroll.get_visible()
def _update_selection(self):
"""Update the visual selection of results."""
"""Update the visual selection of results with scrolling (for keyboard navigation)."""
children = self.results_box.get_children()
selected_widget = None
@@ -1065,6 +1076,24 @@ class Launcher(Window):
# Also schedule a more accurate scroll after layout is complete
GLib.idle_add(self._ensure_selected_visible)
def _update_selection_visual_only(self):
"""Update the visual selection of results without scrolling (for mouse hover)."""
children = self.results_box.get_children()
for i, child in enumerate(children):
if isinstance(child, ResultItem):
is_selected = i == self.selected_index
child.set_selected(is_selected)
# For custom widgets, we don't need to handle selection visually
# since they manage their own interaction
# Focus custom widgets when selected for keyboard interaction
if self.results and 0 <= self.selected_index < len(self.results):
result = self.results[self.selected_index]
if result.custom_widget and result.custom_widget.get_can_focus():
# Give focus to the custom widget for keyboard interaction
result.custom_widget.grab_focus()
def _scroll_to_widget(self, widget):
"""Scroll the results container to make the widget visible."""
if not widget or not self.results_scroll.get_visible():
+16 -3
View File
@@ -20,11 +20,17 @@ class ResultItem(EventBox):
"""Emitted when result is clicked."""
pass
def __init__(self, result: Result, selected: bool = False, **kwargs):
@Signal
def hovered(self, index: int) -> None:
"""Emitted when result is hovered."""
pass
def __init__(self, result: Result, selected: bool = False, index: int = 0, **kwargs):
super().__init__(name="launcher-result-item", **kwargs)
self.result = result
self._selected = selected
self.index = index
# Setup UI
self._setup_ui()
@@ -32,6 +38,7 @@ class ResultItem(EventBox):
# Connect signals
self.connect("button-press-event", self._on_button_press)
self.connect("enter-notify-event", self._on_enter)
self.connect("leave-notify-event", self._on_leave)
# Set initial selection state
self.set_selected(selected)
@@ -138,11 +145,17 @@ class ResultItem(EventBox):
def _on_button_press(self, widget, event):
"""Handle button press events."""
if event.button == 1: # Left click
self.clicked.emit(0) # Emit with dummy index, parent will handle
self.clicked.emit(self.index)
return True
return False
def _on_enter(self, widget, event):
"""Handle mouse enter events."""
# Could be used for hover effects
# Emit hover signal to update selection
self.hovered.emit(self.index)
return False
def _on_leave(self, widget, event):
"""Handle mouse leave events."""
# Could be used for hover effects cleanup
return False