Add Settings view, widget, and keybindings

Introduce Settings view handling in key events and widgets.
Add `Enter` action with its keybinding.
Refactor keybinding lookup to use `Option<&KeyBinding>` for missing
entries.
Update view option generation to safely filter possible values.
Export `settings_keybindings` and `settings_widget` from their modules.
This commit is contained in:
2026-03-13 09:31:55 +01:00
parent 7226a09bb4
commit d563bbc966
9 changed files with 123 additions and 27 deletions
+2 -1
View File
@@ -1,6 +1,6 @@
use crate::app::{ use crate::app::{
App, View, App, View,
keybindings::{default_view_keybindings, main_menu_keybindings}, keybindings::{default_view_keybindings, main_menu_keybindings, settings_keybindings},
}; };
use ratatui::crossterm::event::KeyEvent; use ratatui::crossterm::event::KeyEvent;
use std::io::Result; use std::io::Result;
@@ -8,6 +8,7 @@ use std::io::Result;
pub fn handle_keybindings(app: &mut App, key_event: KeyEvent) -> Result<()> { pub fn handle_keybindings(app: &mut App, key_event: KeyEvent) -> Result<()> {
match app.view { match app.view {
View::MainMenu => main_menu_keybindings(app, &key_event), View::MainMenu => main_menu_keybindings(app, &key_event),
View::Settings => settings_keybindings(app, &key_event),
_ => default_view_keybindings(app, &key_event), _ => default_view_keybindings(app, &key_event),
} }
+40 -13
View File
@@ -9,6 +9,7 @@ pub enum Action {
Up, Up,
Down, Down,
Space, Space,
Enter,
Esc, Esc,
} }
@@ -84,6 +85,15 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
symbol: "Space", symbol: "Space",
description: "Select", description: "Select",
}, },
KeyBinding {
action: Action::Enter,
code: KeyCode::Enter,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
group: Group::Select,
symbol: "Enter",
description: "Submit",
},
KeyBinding { KeyBinding {
action: Action::Esc, action: Action::Esc,
code: KeyCode::Esc, code: KeyCode::Esc,
@@ -106,23 +116,36 @@ pub fn event_to_action(event: &KeyEvent) -> Option<Action> {
.map(|b| b.action) .map(|b| b.action)
} }
fn used_groups_in_view(keybindings: &Vec<&'static KeyBinding>) -> HashSet<Group> { fn used_groups_in_view(keybindings: &Vec<Option<&'static KeyBinding>>) -> HashSet<Group> {
keybindings.iter().map(|b| b.group).collect() keybindings
.iter()
.filter_map(|b| b.map(|binding| binding.group))
.collect()
} }
pub fn binding_for_view(view: View) -> (HashSet<Group>, Vec<&'static KeyBinding>) { pub fn binding_for_view(view: View) -> (HashSet<Group>, Vec<Option<&'static KeyBinding>>) {
let main_menu_keybindings: Vec<&'static KeyBinding> = vec![ let main_menu_keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Up).unwrap(), binding_for(Action::Up),
binding_for(Action::Down).unwrap(), binding_for(Action::Down),
binding_for(Action::Space).unwrap(), binding_for(Action::Space),
binding_for(Action::Quit).unwrap(), binding_for(Action::Quit),
binding_for(Action::Quit2).unwrap(), binding_for(Action::Quit2),
]; ];
let default_keybindings: Vec<&'static KeyBinding> = vec![ let settings_keybinding: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Quit).unwrap(), binding_for(Action::Up),
binding_for(Action::Quit2).unwrap(), binding_for(Action::Down),
binding_for(Action::Esc).unwrap(), binding_for(Action::Space),
binding_for(Action::Enter),
binding_for(Action::Quit),
binding_for(Action::Quit2),
binding_for(Action::Esc),
];
let default_keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Quit),
binding_for(Action::Quit2),
binding_for(Action::Esc),
]; ];
match view { match view {
@@ -130,6 +153,10 @@ pub fn binding_for_view(view: View) -> (HashSet<Group>, Vec<&'static KeyBinding>
used_groups_in_view(&main_menu_keybindings), used_groups_in_view(&main_menu_keybindings),
main_menu_keybindings, main_menu_keybindings,
), ),
View::Settings => (
used_groups_in_view(&settings_keybinding),
settings_keybinding,
),
_ => ( _ => (
used_groups_in_view(&default_keybindings), used_groups_in_view(&default_keybindings),
default_keybindings, default_keybindings,
+2
View File
@@ -1,9 +1,11 @@
pub mod default; pub mod default;
pub mod keybindings; pub mod keybindings;
pub mod main_menu; pub mod main_menu;
pub mod settings;
pub use default::default_view_keybindings; pub use default::default_view_keybindings;
pub use keybindings::{ pub use keybindings::{
Action, Group, 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; pub use main_menu::main_menu_keybindings;
pub use settings::settings_keybindings;
+16
View File
@@ -0,0 +1,16 @@
use crate::app::{
App, View,
keybindings::{Action, event_to_action},
};
use ratatui::crossterm::event::KeyEvent;
pub fn settings_keybindings(app: &mut App, key_event: &KeyEvent) {
if let Some(action) = event_to_action(&key_event) {
match action {
Action::Quit => app.exit = true,
Action::Quit2 => app.exit = true,
Action::Esc => app.view = View::MainMenu,
_ => (),
}
}
}
+2 -1
View File
@@ -1,6 +1,6 @@
use crate::app::{ use crate::app::{
App, View, App, View,
widgets::{default_view_widget, main_menu_widget}, widgets::{default_view_widget, main_menu_widget, settings_widget},
}; };
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
@@ -11,6 +11,7 @@ impl Widget for &App {
{ {
match self.view { match self.view {
View::MainMenu => main_menu_widget(self, area, buf), View::MainMenu => main_menu_widget(self, area, buf),
View::Settings => settings_widget(self, area, buf),
_ => default_view_widget(self, area, buf), _ => default_view_widget(self, area, buf),
} }
} }
+9 -3
View File
@@ -26,9 +26,15 @@ impl KeybindingsWidget {
let grouped_lines: Vec<Line<'_>> = keybindings let grouped_lines: Vec<Line<'_>> = keybindings
.iter() .iter()
.filter(|b| b.group == group) .filter(|b| b.as_ref().map_or(false, |kb| kb.group == group))
.map(|b| { .filter_map(|b| {
Line::from_iter([b.symbol.red().bold(), " - ".into(), b.description.into()]) b.as_ref().map(|kb| {
Line::from_iter([
kb.symbol.red().bold(),
" - ".into(),
kb.description.into(),
])
})
}) })
.collect(); .collect();
+9 -9
View File
@@ -12,15 +12,15 @@ fn view_options() -> Vec<(usize, String)> {
View::value_variants() View::value_variants()
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, v)| { .filter_map(|(i, v)| {
let name = v v.to_possible_value().map(|possible_value| {
.to_possible_value() let name = possible_value
.unwrap() .get_name()
.get_name() .replace('-', " ")
.replace('-', " ") .to_uppercase()
.to_uppercase() .to_string();
.to_string(); (i, name)
(i, name) })
}) })
.filter(|(_, v)| v != "MAIN MENU") .filter(|(_, v)| v != "MAIN MENU")
.collect() .collect()
+2
View File
@@ -1,7 +1,9 @@
pub mod default; pub mod default;
pub mod keybindings; pub mod keybindings;
pub mod main_menu; pub mod main_menu;
pub mod settings;
pub use default::default_view_widget; pub use default::default_view_widget;
pub use keybindings::KeybindingsWidget; pub use keybindings::KeybindingsWidget;
pub use main_menu::main_menu_widget; pub use main_menu::main_menu_widget;
pub use settings::settings_widget;
+41
View File
@@ -0,0 +1,41 @@
use crate::app::{App, widgets::KeybindingsWidget};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
style::Stylize,
widgets::{Block, Borders, Padding, Paragraph, Widget},
};
pub fn settings_widget(app: &App, area: Rect, buf: &mut Buffer) {
let vertical_layout: Layout = Layout::vertical([
Constraint::Length(10),
Constraint::Fill(1),
Constraint::Length(4),
]);
let [title_area, main_area, keybindings_area] = vertical_layout.areas(area);
Paragraph::new("War in tunnels")
.alignment(Alignment::Left)
.yellow()
.block(
Block::new()
.gray()
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT)
.padding(Padding::new(1, 1, 1, 1)),
)
.render(title_area, buf);
Paragraph::new("Settings")
.alignment(Alignment::Center)
.yellow()
.block(
Block::new()
.gray()
.borders(Borders::LEFT | Borders::RIGHT)
.padding(Padding::new(1, 1, 1, 1)),
)
.render(main_area, buf);
KeybindingsWidget::new(app.view).render(keybindings_area, buf);
}