✨ feat(launcher): add result item hover selection and mouse navigation support
This commit is contained in:
4 files changed
+105
-24
No files matched your search
+19
-19
@@ -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
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user