✨ 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

+19 -19
View File
@@ -1,27 +1,27 @@
$wallpaper = /home/sankalp/Pictures/wallpapers/example-1.jpg
$background = 1a1110
$foreground = f1dedd
$wallpaper = /home/sankalp/Pictures/wallpapers/3.jpg
$background = 141218
$foreground = e7e0e8
$primary = ffb3ad
$secondary = e7bdb9
$tertiary = e1c28c
$surface = 1a1110
$surface_bright = 423736
$outline = a08c8a
$primary = d1bcfd
$secondary = ccc2db
$tertiary = f0b8c6
$surface = 141218
$surface_bright = 3b383e
$outline = 948f99
$error = ff5945
$shadow = 000000
$red = ffb4aa
$green = b7d085
$red = ffb2b9
$green = 95d5a7
$yellow = dec56e
$blue = cebdfe
$magenta = fcb0d5
$cyan = 84d5c4
$blue = c8bfff
$magenta = e4b7f3
$cyan = 82d3e2
$white = 82d3e0
$red_dim = ff5a44
$green_dim = 89ad42
$red_dim = ff4c5c
$green_dim = 4cb86a
$yellow_dim = bb9b2b
$blue_dim = 8359fc
$magenta_dim = f84ea1
$cyan_dim = 3eb59c
$blue_dim = 7059ff
$magenta_dim = c360e4
$cyan_dim = 30b5ce
+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
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""
Test script to verify mouse navigation works in the launcher.
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from modules.launcher.main import Launcher
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib
def test_mouse_navigation():
"""Test mouse navigation functionality."""
print("Testing mouse navigation in launcher...")
# Create launcher instance
launcher = Launcher()
# Show launcher with a trigger to get some results
launcher.show_launcher("app")
print("Launcher opened with 'app' trigger")
print("You should be able to:")
print("1. Hover over result items to see selection change (without scrolling)")
print("2. Click on result items to activate them")
print("3. Use keyboard navigation (arrow keys) which should scroll")
print("Press Ctrl+C to exit test")
try:
Gtk.main()
except KeyboardInterrupt:
print("\nTest completed!")
launcher.close_launcher()
if __name__ == "__main__":
test_mouse_navigation()