bubbletea_widgets/list/
keys.rs

1//! Key bindings for list component navigation and interaction.
2//!
3//! This module defines the `ListKeyMap` which contains all the key bindings used by the list component
4//! for navigation, filtering, and interaction. The key bindings follow common terminal UI conventions
5//! and vim-style navigation patterns.
6//!
7//! ## Navigation Keys
8//!
9//! - **Cursor Movement**: `↑/k` (up), `↓/j` (down)
10//! - **Page Navigation**: `→/l/pgdn/f/d` (next page), `←/h/pgup/b/u` (prev page)
11//! - **Jump Navigation**: `g/home` (go to start), `G/end` (go to end)
12//!
13//! ## Filtering Keys
14//!
15//! - **Start Filter**: `/` (enter filtering mode)
16//! - **Clear Filter**: `esc` (clear active filter)
17//! - **Cancel Filter**: `esc` (cancel filter input)
18//! - **Accept Filter**: `enter/tab/↑/↓` (apply filter and continue)
19//!
20//! ## Help and Quit Keys
21//!
22//! - **Help**: `?` (show/hide help)
23//! - **Quit**: `q/esc` (normal quit)
24//! - **Force Quit**: `ctrl+c` (immediate quit)
25//!
26//! ## Example
27//!
28//! ```rust
29//! use bubbletea_widgets::list::ListKeyMap;
30//! use bubbletea_widgets::key::KeyMap;
31//!
32//! let keymap = ListKeyMap::default();
33//! let help = keymap.short_help(); // Get key bindings for help display
34//! ```
35
36use crate::key;
37use crossterm::event::KeyCode;
38
39/// Key bindings for list navigation, filtering, help, and exit actions.
40#[derive(Debug, Clone)]
41pub struct ListKeyMap {
42    /// Move selection up one item.
43    pub cursor_up: key::Binding,
44    /// Move selection down one item.
45    pub cursor_down: key::Binding,
46    /// Go to the next page of items.
47    pub next_page: key::Binding,
48    /// Go to the previous page of items.
49    pub prev_page: key::Binding,
50    /// Jump to the first item.
51    pub go_to_start: key::Binding,
52    /// Jump to the last item.
53    pub go_to_end: key::Binding,
54    /// Enter filtering mode.
55    pub filter: key::Binding,
56    /// Clear the active filter.
57    pub clear_filter: key::Binding,
58    /// Cancel filtering mode.
59    pub cancel_filter: key::Binding,
60    /// Accept/apply the current filter input.
61    pub accept_filter: key::Binding,
62    /// Show the full help panel.
63    pub show_full_help: key::Binding,
64    /// Close the full help panel.
65    pub close_full_help: key::Binding,
66    /// Quit.
67    pub quit: key::Binding,
68    /// Force quit.
69    pub force_quit: key::Binding,
70}
71
72impl Default for ListKeyMap {
73    fn default() -> Self {
74        Self {
75            cursor_up: key::Binding::new(vec![KeyCode::Up, KeyCode::Char('k')])
76                .with_help("↑/k", "up"),
77            cursor_down: key::Binding::new(vec![KeyCode::Down, KeyCode::Char('j')])
78                .with_help("↓/j", "down"),
79            next_page: key::Binding::new(vec![
80                KeyCode::Right,
81                KeyCode::Char('l'),
82                KeyCode::PageDown,
83                KeyCode::Char('f'),
84                KeyCode::Char('d'),
85            ])
86            .with_help("→/l/pgdn", "next page"),
87            prev_page: key::Binding::new(vec![
88                KeyCode::Left,
89                KeyCode::Char('h'),
90                KeyCode::PageUp,
91                KeyCode::Char('b'),
92                KeyCode::Char('u'),
93            ])
94            .with_help("←/h/pgup", "prev page"),
95            go_to_start: key::Binding::new(vec![KeyCode::Home, KeyCode::Char('g')])
96                .with_help("g/home", "go to start"),
97            go_to_end: key::Binding::new(vec![KeyCode::End, KeyCode::Char('G')])
98                .with_help("G/end", "go to end"),
99            filter: key::Binding::new(vec![KeyCode::Char('/')]).with_help("/", "filter"),
100            clear_filter: key::Binding::new(vec![KeyCode::Esc]).with_help("esc", "clear filter"),
101            cancel_filter: key::Binding::new(vec![KeyCode::Esc]).with_help("esc", "cancel"),
102            // Simplify accept_filter: Enter, Tab, Up/Down
103            accept_filter: key::Binding::new(vec![
104                KeyCode::Enter,
105                KeyCode::Tab,
106                KeyCode::Up,
107                KeyCode::Down,
108            ])
109            .with_help("enter", "apply filter"),
110            show_full_help: key::Binding::new(vec![KeyCode::Char('?')]).with_help("?", "more"),
111            close_full_help: key::Binding::new(vec![KeyCode::Char('?')])
112                .with_help("?", "close help"),
113            quit: key::Binding::new(vec![KeyCode::Char('q'), KeyCode::Esc]).with_help("q", "quit"),
114            // Use parse string for ctrl+c via key module convenience
115            force_quit: key::new_binding(vec![key::with_keys_str(&["ctrl+c"])])
116                .with_help("ctrl+c", "force quit"),
117        }
118    }
119}
120
121impl key::KeyMap for ListKeyMap {
122    fn short_help(&self) -> Vec<&key::Binding> {
123        vec![&self.cursor_up, &self.cursor_down, &self.filter, &self.quit]
124    }
125
126    fn full_help(&self) -> Vec<Vec<&key::Binding>> {
127        vec![
128            // Column 1: Primary Navigation
129            vec![
130                &self.cursor_up,
131                &self.cursor_down,
132                &self.next_page,
133                &self.prev_page,
134                &self.go_to_start,
135                &self.go_to_end,
136            ],
137            // Column 2: Filtering Actions
138            vec![
139                &self.filter,
140                &self.clear_filter,
141                &self.accept_filter,
142                &self.cancel_filter,
143            ],
144            // Column 3: Help and Quit
145            vec![&self.show_full_help, &self.quit],
146        ]
147    }
148}