Add keyboard navigation for launcher grid
This commit is contained in:
parent
8bba628d98
commit
4dc5842949
4 changed files with 106 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
|||
//! Main GTK4 application for the launcher
|
||||
|
||||
use gtk4::glib;
|
||||
use gtk4::gdk;
|
||||
use gtk4::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -41,6 +42,14 @@ window {
|
|||
border-color: #4a90d9;
|
||||
}
|
||||
|
||||
.launcher-tile:focus,
|
||||
.launcher-tile:focus-visible {
|
||||
background: #1f3460;
|
||||
background-color: #1f3460;
|
||||
border-color: #f8c24e;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.launcher-tile:active {
|
||||
background: #0f3460;
|
||||
background-color: #0f3460;
|
||||
|
|
@ -156,6 +165,61 @@ impl LauncherApp {
|
|||
|
||||
window.set_child(Some(&stack));
|
||||
|
||||
let grid_for_keys = grid.downgrade();
|
||||
let window_for_keys = window.downgrade();
|
||||
let key_controller = gtk4::EventControllerKey::new();
|
||||
key_controller.connect_key_pressed(move |_, key, _, modifiers| {
|
||||
let Some(window) = window_for_keys.upgrade() else {
|
||||
return glib::Propagation::Proceed;
|
||||
};
|
||||
let Some(grid) = grid_for_keys.upgrade() else {
|
||||
return glib::Propagation::Proceed;
|
||||
};
|
||||
|
||||
let is_alt = modifiers.contains(gdk::ModifierType::ALT_MASK);
|
||||
let is_ctrl = modifiers.contains(gdk::ModifierType::CONTROL_MASK);
|
||||
|
||||
if (key == gdk::Key::w || key == gdk::Key::W) && is_ctrl {
|
||||
window.close();
|
||||
return glib::Propagation::Stop;
|
||||
}
|
||||
|
||||
let handled = match key {
|
||||
gdk::Key::Up | gdk::Key::KP_Up | gdk::Key::w | gdk::Key::W => {
|
||||
grid.move_focus(gtk4::DirectionType::Up);
|
||||
true
|
||||
}
|
||||
gdk::Key::Down | gdk::Key::KP_Down | gdk::Key::s | gdk::Key::S => {
|
||||
grid.move_focus(gtk4::DirectionType::Down);
|
||||
true
|
||||
}
|
||||
gdk::Key::Left | gdk::Key::KP_Left | gdk::Key::a | gdk::Key::A => {
|
||||
grid.move_focus(gtk4::DirectionType::Left);
|
||||
true
|
||||
}
|
||||
gdk::Key::Right | gdk::Key::KP_Right | gdk::Key::d | gdk::Key::D => {
|
||||
grid.move_focus(gtk4::DirectionType::Right);
|
||||
true
|
||||
}
|
||||
gdk::Key::Home | gdk::Key::KP_Home => {
|
||||
window.close();
|
||||
true
|
||||
}
|
||||
gdk::Key::F4 if is_alt => {
|
||||
window.close();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if handled {
|
||||
glib::Propagation::Stop
|
||||
} else {
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
});
|
||||
window.add_controller(key_controller);
|
||||
|
||||
// Create shared state
|
||||
let state = SharedState::new();
|
||||
let state_receiver = state.subscribe();
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ mod imp {
|
|||
self.flow_box.set_valign(gtk4::Align::Center);
|
||||
self.flow_box.set_hexpand(true);
|
||||
self.flow_box.set_vexpand(true);
|
||||
self.flow_box.set_can_focus(true);
|
||||
self.flow_box.add_css_class("launcher-grid");
|
||||
|
||||
// Wrap in a scrolled window
|
||||
|
|
@ -125,6 +126,8 @@ impl LauncherGrid {
|
|||
imp.flow_box.insert(&tile, -1);
|
||||
imp.tiles.borrow_mut().push(tile);
|
||||
}
|
||||
|
||||
self.focus_first_tile();
|
||||
}
|
||||
|
||||
/// Enable or disable all tiles
|
||||
|
|
@ -133,6 +136,31 @@ impl LauncherGrid {
|
|||
tile.set_sensitive(sensitive);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_focus(&self, direction: gtk4::DirectionType) {
|
||||
self.ensure_focus();
|
||||
self.imp().flow_box.child_focus(direction);
|
||||
}
|
||||
|
||||
fn ensure_focus(&self) {
|
||||
if self
|
||||
.imp()
|
||||
.tiles
|
||||
.borrow()
|
||||
.iter()
|
||||
.any(|tile| tile.has_focus())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.focus_first_tile();
|
||||
}
|
||||
|
||||
fn focus_first_tile(&self) {
|
||||
if let Some(tile) = self.imp().tiles.borrow().first() {
|
||||
tile.grab_focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LauncherGrid {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ mod imp {
|
|||
obj.add_css_class("launcher-tile");
|
||||
obj.add_css_class("flat");
|
||||
obj.set_size_request(160, 160);
|
||||
obj.set_can_focus(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# Launcher Keyboard & Controller Navigation
|
||||
|
||||
Issue: <https://github.com/aarmea/shepherd-launcher/issues/20>
|
||||
|
||||
Summary:
|
||||
- Added keyboard/controller navigation for the launcher grid (arrow keys, WASD, D-pad) with focus movement between tiles.
|
||||
- Enabled focus styling on tiles and ensured the grid focuses the first tile on state updates.
|
||||
- Added keyboard shortcuts to close the launcher (Alt+F4, Ctrl+W, Home).
|
||||
|
||||
Key files:
|
||||
- crates/shepherd-launcher-ui/src/app.rs
|
||||
- crates/shepherd-launcher-ui/src/grid.rs
|
||||
- crates/shepherd-launcher-ui/src/tile.rs
|
||||
Loading…
Reference in a new issue