generated from GarandPLG/rust-flake-template
Refactor keybindings with groups and symbols
Introduce a `Group` enum to categorize actions and add `group` and `symbol` fields to `KeyBinding`. Extend `Action` with `Up`, `Down`, `Space`, and `Esc`, updating all keybinding definitions accordingly. Update `binding_for_view` to return the new actions per view and handle `Esc` by returning to the main menu. Rewrite the keybindings widget as a custom `Widget` that renders grouped paragraphs with proper layout. Adjust the main menu layout percentages to accommodate the new widget.
This commit is contained in:
@@ -103,6 +103,7 @@ impl App {
|
||||
if let Some(action) = event_to_action(&key_event) {
|
||||
match action {
|
||||
Action::Quit => self.exit = true,
|
||||
Action::Esc => self.view = View::MainMenu,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,39 @@ use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Action {
|
||||
Quit,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
Up,
|
||||
Down,
|
||||
Space,
|
||||
Esc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Group {
|
||||
Quit,
|
||||
Movement,
|
||||
Select,
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn iter() -> impl Iterator<Item = Group> {
|
||||
[Group::Quit, Group::Movement, Group::Select]
|
||||
.iter()
|
||||
.copied()
|
||||
}
|
||||
|
||||
pub fn len() -> usize {
|
||||
Self::iter().count()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct KeyBinding {
|
||||
pub action: Action,
|
||||
pub code: KeyCode,
|
||||
pub kind: KeyEventKind,
|
||||
pub modifiers: KeyModifiers,
|
||||
pub group: Group,
|
||||
pub symbol: &'static str,
|
||||
pub description: &'static str,
|
||||
}
|
||||
|
||||
@@ -24,29 +46,46 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
|
||||
code: KeyCode::Char('q'),
|
||||
kind: KeyEventKind::Press,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
group: Group::Quit,
|
||||
symbol: "q",
|
||||
description: "Quit",
|
||||
},
|
||||
KeyBinding {
|
||||
action: Action::ScrollUp,
|
||||
action: Action::Up,
|
||||
code: KeyCode::Up,
|
||||
kind: KeyEventKind::Press,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
group: Group::Movement,
|
||||
symbol: "↑",
|
||||
description: "Up",
|
||||
},
|
||||
KeyBinding {
|
||||
action: Action::ScrollDown,
|
||||
action: Action::Down,
|
||||
code: KeyCode::Down,
|
||||
kind: KeyEventKind::Press,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
group: Group::Movement,
|
||||
symbol: "↓",
|
||||
description: "Down",
|
||||
},
|
||||
KeyBinding {
|
||||
action: Action::Select,
|
||||
action: Action::Space,
|
||||
code: KeyCode::Char(' '),
|
||||
kind: KeyEventKind::Press,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
group: Group::Select,
|
||||
symbol: "Space",
|
||||
description: "Select",
|
||||
},
|
||||
KeyBinding {
|
||||
action: Action::Esc,
|
||||
code: KeyCode::Esc,
|
||||
kind: KeyEventKind::Press,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
group: Group::Movement,
|
||||
symbol: "Esc",
|
||||
description: "Go back",
|
||||
},
|
||||
];
|
||||
|
||||
pub fn binding_for(action: Action) -> Option<&'static KeyBinding> {
|
||||
@@ -63,11 +102,14 @@ pub fn event_to_action(event: &KeyEvent) -> Option<Action> {
|
||||
pub fn binding_for_view(view: View) -> Vec<&'static KeyBinding> {
|
||||
match view {
|
||||
View::MainMenu => vec![
|
||||
binding_for(Action::ScrollUp).unwrap(),
|
||||
binding_for(Action::ScrollDown).unwrap(),
|
||||
binding_for(Action::Select).unwrap(),
|
||||
binding_for(Action::Up).unwrap(),
|
||||
binding_for(Action::Down).unwrap(),
|
||||
binding_for(Action::Space).unwrap(),
|
||||
binding_for(Action::Quit).unwrap(),
|
||||
],
|
||||
_ => vec![],
|
||||
_ => vec![
|
||||
binding_for(Action::Quit).unwrap(),
|
||||
binding_for(Action::Esc).unwrap(),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) {
|
||||
if let Some(action) = event_to_action(&event) {
|
||||
match action {
|
||||
Action::Quit => app.exit = true,
|
||||
Action::ScrollUp => {
|
||||
Action::Up => {
|
||||
app.game_states.main_menu_state.selected_view = app
|
||||
.game_states
|
||||
.main_menu_state
|
||||
@@ -16,7 +16,7 @@ pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) {
|
||||
.saturating_sub(1)
|
||||
.max(1);
|
||||
}
|
||||
Action::ScrollDown => {
|
||||
Action::Down => {
|
||||
app.game_states.main_menu_state.selected_view = app
|
||||
.game_states
|
||||
.main_menu_state
|
||||
@@ -24,7 +24,7 @@ pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) {
|
||||
.saturating_add(1)
|
||||
.min(4);
|
||||
}
|
||||
Action::Select => {
|
||||
Action::Space => {
|
||||
let selected_view: usize = app.game_states.main_menu_state.selected_view;
|
||||
|
||||
if selected_view == 1 {
|
||||
@@ -36,7 +36,8 @@ pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) {
|
||||
} else if selected_view == 4 {
|
||||
app.view = View::Settings;
|
||||
}
|
||||
} // _ => (),
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ pub mod keybindings;
|
||||
pub mod main_menu;
|
||||
|
||||
pub use keybindings::{
|
||||
Action, KEYBINDINGS, KeyBinding, binding_for, binding_for_view, event_to_action,
|
||||
Action, Group, KEYBINDINGS, KeyBinding, binding_for, binding_for_view, event_to_action,
|
||||
};
|
||||
pub use main_menu::main_menu_keybindings;
|
||||
|
||||
@@ -1,33 +1,72 @@
|
||||
use crate::app::{View, keybindings::binding_for_view};
|
||||
use crate::app::{
|
||||
View,
|
||||
keybindings::{Group, KeyBinding, binding_for_view},
|
||||
};
|
||||
use ratatui::{
|
||||
crossterm::event::KeyCode,
|
||||
layout::Alignment,
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Constraint, Layout, Rect},
|
||||
style::Stylize,
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
text::Line,
|
||||
widgets::{Block, Borders, Padding, Paragraph, Widget},
|
||||
};
|
||||
|
||||
pub fn keybindings_widget(view: View) -> Paragraph<'static> {
|
||||
let lines: Vec<Line> = binding_for_view(view)
|
||||
.iter()
|
||||
.map(|b| {
|
||||
let key_span = match b.code {
|
||||
KeyCode::Up => Span::raw("↑".to_string()),
|
||||
KeyCode::Down => Span::raw("↓".to_string()),
|
||||
KeyCode::Char(' ') => Span::raw("Space".to_string()),
|
||||
KeyCode::Char(c) => Span::raw(c.to_string()),
|
||||
other => Span::raw(format!("{:?}", other)),
|
||||
}
|
||||
.bold()
|
||||
.red();
|
||||
|
||||
Line::from_iter([key_span, "\t - ".into(), b.description.into()])
|
||||
})
|
||||
.collect();
|
||||
|
||||
Paragraph::new(lines).alignment(Alignment::Left).block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("[ Keybindings ]"),
|
||||
)
|
||||
pub struct KeybindingsWidget {
|
||||
grouped: Vec<Paragraph<'static>>,
|
||||
}
|
||||
|
||||
impl Widget for KeybindingsWidget {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let count: u16 = self.grouped.len() as u16;
|
||||
|
||||
if count == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let block: Block<'_> = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("[ Keybindings ]");
|
||||
|
||||
let inner: Rect = block.inner(area);
|
||||
|
||||
block.render(area, buf);
|
||||
|
||||
let base: u16 = if count == 0 { 0 } else { 10 };
|
||||
|
||||
let constraints: Vec<Constraint> = vec![Constraint::Percentage(base); count as usize];
|
||||
|
||||
let chunks: Vec<Rect> = Layout::horizontal(constraints).split(inner).to_vec();
|
||||
|
||||
for (paragraph, chunk) in self.grouped.into_iter().zip(chunks.into_iter()) {
|
||||
paragraph.render(chunk, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keybindings_widget(view: View) -> KeybindingsWidget {
|
||||
let keybindings: Vec<&'static KeyBinding> = binding_for_view(view);
|
||||
|
||||
let mut grouped_keybindings: Vec<Paragraph<'static>> = Vec::new();
|
||||
for (i, group) in Group::iter().enumerate() {
|
||||
let grouped_lines: Vec<Line<'_>> = keybindings
|
||||
.iter()
|
||||
.filter(|b| b.group == group)
|
||||
.map(|b| Line::from_iter([b.symbol.red().bold(), " - ".into(), b.description.into()]))
|
||||
.collect();
|
||||
|
||||
let mut block: Block<'_> = Block::default().padding(Padding::new(1, 1, 0, 0));
|
||||
|
||||
if i != Group::len() - 1 {
|
||||
block = block.borders(Borders::RIGHT);
|
||||
}
|
||||
|
||||
grouped_keybindings.push(
|
||||
Paragraph::new(grouped_lines)
|
||||
.alignment(Alignment::Center)
|
||||
.block(block),
|
||||
);
|
||||
}
|
||||
|
||||
KeybindingsWidget {
|
||||
grouped: grouped_keybindings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fn view_options() -> Vec<(usize, String)> {
|
||||
|
||||
pub fn main_menu_widget(app: &App, area: Rect, buf: &mut Buffer) {
|
||||
let vertical_layout: Layout =
|
||||
Layout::vertical([Constraint::Percentage(88), Constraint::Percentage(12)]);
|
||||
Layout::vertical([Constraint::Percentage(92), Constraint::Percentage(8)]);
|
||||
|
||||
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user