Compare commits
1 commit
main
...
codex/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dc5842949 |
4 changed files with 106 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
//! Main GTK4 application for the launcher
|
//! Main GTK4 application for the launcher
|
||||||
|
|
||||||
use gtk4::glib;
|
use gtk4::glib;
|
||||||
|
use gtk4::gdk;
|
||||||
use gtk4::prelude::*;
|
use gtk4::prelude::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -41,6 +42,14 @@ window {
|
||||||
border-color: #4a90d9;
|
border-color: #4a90d9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.launcher-tile:focus,
|
||||||
|
.launcher-tile:focus-visible {
|
||||||
|
background: #1f3460;
|
||||||
|
background-color: #1f3460;
|
||||||
|
border-color: #f8c24e;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.launcher-tile:active {
|
.launcher-tile:active {
|
||||||
background: #0f3460;
|
background: #0f3460;
|
||||||
background-color: #0f3460;
|
background-color: #0f3460;
|
||||||
|
|
@ -156,6 +165,61 @@ impl LauncherApp {
|
||||||
|
|
||||||
window.set_child(Some(&stack));
|
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
|
// Create shared state
|
||||||
let state = SharedState::new();
|
let state = SharedState::new();
|
||||||
let state_receiver = state.subscribe();
|
let state_receiver = state.subscribe();
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ mod imp {
|
||||||
self.flow_box.set_valign(gtk4::Align::Center);
|
self.flow_box.set_valign(gtk4::Align::Center);
|
||||||
self.flow_box.set_hexpand(true);
|
self.flow_box.set_hexpand(true);
|
||||||
self.flow_box.set_vexpand(true);
|
self.flow_box.set_vexpand(true);
|
||||||
|
self.flow_box.set_can_focus(true);
|
||||||
self.flow_box.add_css_class("launcher-grid");
|
self.flow_box.add_css_class("launcher-grid");
|
||||||
|
|
||||||
// Wrap in a scrolled window
|
// Wrap in a scrolled window
|
||||||
|
|
@ -125,6 +126,8 @@ impl LauncherGrid {
|
||||||
imp.flow_box.insert(&tile, -1);
|
imp.flow_box.insert(&tile, -1);
|
||||||
imp.tiles.borrow_mut().push(tile);
|
imp.tiles.borrow_mut().push(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.focus_first_tile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable or disable all tiles
|
/// Enable or disable all tiles
|
||||||
|
|
@ -133,6 +136,31 @@ impl LauncherGrid {
|
||||||
tile.set_sensitive(sensitive);
|
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 {
|
impl Default for LauncherGrid {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ mod imp {
|
||||||
obj.add_css_class("launcher-tile");
|
obj.add_css_class("launcher-tile");
|
||||||
obj.add_css_class("flat");
|
obj.add_css_class("flat");
|
||||||
obj.set_size_request(160, 160);
|
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