Refactor view handling and keybinding API

Move the `View` enum to a dedicated module and implement `Widget` for
`&App` there.
Rename `default_view_keybindings` to `default_keybindings` and adjust
its
signature. Redesign `KeybindingsWidget` to accept an explicit list of
bindings instead of a `View`. Update imports, function signatures, and
rendering
logic across the codebase to reflect these changes.
This commit is contained in:
2026-03-13 20:10:49 +01:00
parent d563bbc966
commit 61e9dedfe8
15 changed files with 109 additions and 115 deletions
+3 -11
View File
@@ -1,5 +1,5 @@
use crate::{
app::{GameStates, handle_keybindings},
app::{GameStates, handle_keybindings, view::View},
cli::Cli,
};
use clap::ValueEnum;
@@ -34,15 +34,6 @@ pub struct App {
pub skill_points_limit: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
pub enum View {
MainMenu,
Skirmish,
PerkDecks,
SkillsConfig,
Settings,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
pub enum GameMode {
LastManStanding,
@@ -100,7 +91,8 @@ impl App {
}
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
handle_keybindings(self, key_event)
handle_keybindings(self, key_event);
Ok(())
}
}
+5 -7
View File
@@ -1,16 +1,14 @@
use crate::app::{
App, View,
keybindings::{default_view_keybindings, main_menu_keybindings, settings_keybindings},
App,
keybindings::{default_keybindings, main_menu_keybindings, settings_keybindings},
view::View,
};
use ratatui::crossterm::event::KeyEvent;
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) {
match app.view {
View::MainMenu => main_menu_keybindings(app, &key_event),
View::Settings => settings_keybindings(app, &key_event),
_ => default_view_keybindings(app, &key_event),
_ => default_keybindings(app, &key_event),
}
Ok(())
}
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::app::{
};
use ratatui::crossterm::event::KeyEvent;
pub fn default_view_keybindings(app: &mut App, key_event: &KeyEvent) {
pub fn default_keybindings(app: &mut App, key_event: &KeyEvent) {
if let Some(action) = event_to_action(&key_event) {
match action {
Action::Quit => app.exit = true,
-50
View File
@@ -1,6 +1,4 @@
use crate::app::View;
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Action {
@@ -115,51 +113,3 @@ pub fn event_to_action(event: &KeyEvent) -> Option<Action> {
.find(|b| b.code == event.code && b.kind == event.kind && b.modifiers == event.modifiers)
.map(|b| b.action)
}
fn used_groups_in_view(keybindings: &Vec<Option<&'static KeyBinding>>) -> HashSet<Group> {
keybindings
.iter()
.filter_map(|b| b.map(|binding| binding.group))
.collect()
}
pub fn binding_for_view(view: View) -> (HashSet<Group>, Vec<Option<&'static KeyBinding>>) {
let main_menu_keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Up),
binding_for(Action::Down),
binding_for(Action::Space),
binding_for(Action::Quit),
binding_for(Action::Quit2),
];
let settings_keybinding: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Up),
binding_for(Action::Down),
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 {
View::MainMenu => (
used_groups_in_view(&main_menu_keybindings),
main_menu_keybindings,
),
View::Settings => (
used_groups_in_view(&settings_keybinding),
settings_keybinding,
),
_ => (
used_groups_in_view(&default_keybindings),
default_keybindings,
),
}
}
+2 -4
View File
@@ -3,9 +3,7 @@ pub mod keybindings;
pub mod main_menu;
pub mod settings;
pub use default::default_view_keybindings;
pub use keybindings::{
Action, Group, KEYBINDINGS, KeyBinding, binding_for, binding_for_view, event_to_action,
};
pub use default::default_keybindings;
pub use keybindings::{Action, Group, KEYBINDINGS, KeyBinding, binding_for, event_to_action};
pub use main_menu::main_menu_keybindings;
pub use settings::settings_keybindings;
+4 -2
View File
@@ -2,11 +2,13 @@ pub mod app;
pub mod game_states;
pub mod keybind;
pub mod keybindings;
pub mod widget;
pub mod view;
pub mod views;
pub mod widgets;
pub use app::{App, Event, GameMode, PerkDecks, View, handle_input_events};
pub use app::{App, Event, GameMode, PerkDecks, handle_input_events};
pub use game_states::{
GameStates, MainMenuState, PerkDecksState, SettingsState, SkillsConfigState, SkirmishState,
};
pub use keybind::handle_keybindings;
pub use view::View;
+28
View File
@@ -0,0 +1,28 @@
use crate::app::{
App,
views::{default_view, main_menu_view, settings_view},
};
use clap::ValueEnum;
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
pub enum View {
MainMenu,
Skirmish,
PerkDecks,
SkillsConfig,
Settings,
}
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer)
where
Self: Sized,
{
match self.view {
View::MainMenu => main_menu_view(self, area, buf),
View::Settings => settings_view(self, area, buf),
_ => default_view(area, buf),
}
}
}
@@ -1,4 +1,7 @@
use crate::app::{App, widgets::KeybindingsWidget};
use crate::app::{
keybindings::{Action, KeyBinding, binding_for},
widgets::KeybindingsWidget,
};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
@@ -6,7 +9,7 @@ use ratatui::{
widgets::{Block, Borders, Paragraph, Widget},
};
pub fn default_view_widget(app: &App, area: Rect, buf: &mut Buffer) {
pub fn default_view(area: Rect, buf: &mut Buffer) {
let vertical_layout: Layout = Layout::vertical([Constraint::Fill(1), Constraint::Length(4)]);
let [main_area, keybindings_area] = vertical_layout.areas(area);
@@ -21,5 +24,13 @@ pub fn default_view_widget(app: &App, area: Rect, buf: &mut Buffer) {
)
.render(main_area, buf);
KeybindingsWidget::new(app.view).render(keybindings_area, buf);
{
let keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Quit),
binding_for(Action::Quit2),
binding_for(Action::Esc),
];
KeybindingsWidget::new(keybindings).render(keybindings_area, buf);
}
}
@@ -1,4 +1,8 @@
use crate::app::{App, View, widgets::KeybindingsWidget};
use crate::app::{
App, View,
keybindings::{Action, KeyBinding, binding_for},
widgets::KeybindingsWidget,
};
use clap::ValueEnum;
use ratatui::{
buffer::Buffer,
@@ -26,7 +30,7 @@ fn view_options() -> Vec<(usize, String)> {
.collect()
}
pub fn main_menu_widget(app: &App, area: Rect, buf: &mut Buffer) {
pub fn main_menu_view(app: &App, area: Rect, buf: &mut Buffer) {
let vertical_layout: Layout = Layout::vertical([Constraint::Fill(1), Constraint::Length(4)]);
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
@@ -81,5 +85,15 @@ pub fn main_menu_widget(app: &App, area: Rect, buf: &mut Buffer) {
.render(options_area, buf);
}
KeybindingsWidget::new(View::MainMenu).render(keybindings_area, buf);
{
let keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Up),
binding_for(Action::Down),
binding_for(Action::Space),
binding_for(Action::Quit),
binding_for(Action::Quit2),
];
KeybindingsWidget::new(keybindings).render(keybindings_area, buf);
}
}
+7
View File
@@ -0,0 +1,7 @@
pub mod default;
pub mod main_menu;
pub mod settings;
pub use default::default_view;
pub use main_menu::main_menu_view;
pub use settings::settings_view;
@@ -1,4 +1,8 @@
use crate::app::{App, widgets::KeybindingsWidget};
use crate::app::{
App,
keybindings::{Action, KeyBinding, binding_for},
widgets::KeybindingsWidget,
};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
@@ -6,7 +10,7 @@ use ratatui::{
widgets::{Block, Borders, Padding, Paragraph, Widget},
};
pub fn settings_widget(app: &App, area: Rect, buf: &mut Buffer) {
pub fn settings_view(app: &App, area: Rect, buf: &mut Buffer) {
let vertical_layout: Layout = Layout::vertical([
Constraint::Length(10),
Constraint::Fill(1),
@@ -37,5 +41,17 @@ pub fn settings_widget(app: &App, area: Rect, buf: &mut Buffer) {
)
.render(main_area, buf);
KeybindingsWidget::new(app.view).render(keybindings_area, buf);
{
let keybindings: Vec<Option<&'static KeyBinding>> = vec![
binding_for(Action::Up),
binding_for(Action::Down),
binding_for(Action::Space),
binding_for(Action::Enter),
binding_for(Action::Quit),
binding_for(Action::Quit2),
binding_for(Action::Esc),
];
KeybindingsWidget::new(keybindings).render(keybindings_area, buf);
}
}
-18
View File
@@ -1,18 +0,0 @@
use crate::app::{
App, View,
widgets::{default_view_widget, main_menu_widget, settings_widget},
};
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer)
where
Self: Sized,
{
match self.view {
View::MainMenu => main_menu_widget(self, area, buf),
View::Settings => settings_widget(self, area, buf),
_ => default_view_widget(self, area, buf),
}
}
}
+8 -6
View File
@@ -1,7 +1,6 @@
use crate::app::{
View,
keybindings::{Group, binding_for_view},
};
use std::collections::HashSet;
use crate::app::keybindings::{Group, KeyBinding};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Rect},
@@ -15,8 +14,11 @@ pub struct KeybindingsWidget {
}
impl KeybindingsWidget {
pub fn new(view: View) -> Self {
let (used_groups, keybindings) = binding_for_view(view);
pub fn new(keybindings: Vec<Option<&'static KeyBinding>>) -> Self {
let used_groups: HashSet<Group> = keybindings
.iter()
.filter_map(|b| b.map(|binding| binding.group))
.collect();
let mut grouped_keybindings: Vec<Paragraph<'static>> = Vec::new();
for (i, group) in Group::iter().enumerate() {
-6
View File
@@ -1,9 +1,3 @@
pub mod default;
pub mod keybindings;
pub mod main_menu;
pub mod settings;
pub use default::default_view_widget;
pub use keybindings::KeybindingsWidget;
pub use main_menu::main_menu_widget;
pub use settings::settings_widget;
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::app::{GameMode, PerkDecks, View};
use crate::app::{GameMode, PerkDecks, view::View};
use clap::Parser;
#[derive(Parser, Debug)]