diff --git a/Cargo.toml b/Cargo.toml index 5230481..64fea6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,12 @@ name = "war_in_tunnels" version = "0.1.0" edition = "2024" +[profile.release] +codegen-units = 1 +lto = true +opt-level = 3 +strip = true + [dependencies] clap = { version = "4.5.53", features = ["derive"] } ratatui = "0.30.0" diff --git a/src/app/app.rs b/src/app/app.rs index 9c99e1a..967b3b4 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -1,6 +1,8 @@ +use crate::app::{Action, event_to_action}; +use clap::ValueEnum; use ratatui::{ DefaultTerminal, Frame, - crossterm::event::{self, KeyCode, KeyEvent, KeyEventKind}, + crossterm::event::{self, KeyEvent}, }; use std::{ io::Result, @@ -9,7 +11,7 @@ use std::{ pub struct App { pub exit: bool, - pub default_window: String, + pub window: View, pub username: String, pub game_mode: String, pub map_width: u8, @@ -22,6 +24,15 @@ pub struct App { pub skill_points_limit: u16, } +#[derive(Debug, Clone, ValueEnum)] +pub enum View { + MainMenu, + Skirmish, + PerkDecks, + SkillsConfig, + Settings, +} + pub enum Event { Input(KeyEvent), } @@ -48,8 +59,14 @@ impl App { } fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> { - if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { - self.exit = true; + // if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { + // self.exit = true; + // } + + if let Some(action) = event_to_action(&key_event) { + match action { + Action::Quit => self.exit = true, + } } Ok(()) diff --git a/src/app/keybindings.rs b/src/app/keybindings.rs new file mode 100644 index 0000000..277f0b2 --- /dev/null +++ b/src/app/keybindings.rs @@ -0,0 +1,45 @@ +use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; + +use crate::app::View; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Action { + Quit, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct KeyBinding { + pub action: Action, + pub code: KeyCode, + pub kind: KeyEventKind, + pub modifiers: KeyModifiers, + pub description: &'static str, +} + +pub static KEYBINDINGS: &[KeyBinding] = &[KeyBinding { + action: Action::Quit, + code: KeyCode::Char('q'), + kind: KeyEventKind::Press, + modifiers: KeyModifiers::NONE, + description: "Quit", +}]; + +pub fn binding_for(action: Action) -> Option<&'static KeyBinding> { + KEYBINDINGS.iter().find(|b| b.action == action) +} + +pub fn event_to_action(event: &KeyEvent) -> Option { + KEYBINDINGS + .iter() + .find(|b| b.code == event.code && b.kind == event.kind && b.modifiers == event.modifiers) + .map(|b| b.action) +} + +pub fn binding_for_view(view: View) -> Vec<&'static KeyBinding> { + match view { + View::MainMenu => binding_for(Action::Quit) + .map(|b| vec![b]) + .unwrap_or_else(|| vec![]), + _ => vec![], + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 39c8669..f618336 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,5 +1,9 @@ pub mod app; +pub mod keybindings; pub mod widget; pub mod widgets; -pub use app::{App, Event, handle_input_events}; +pub use app::{App, Event, View, handle_input_events}; +pub use keybindings::{ + Action, KEYBINDINGS, KeyBinding, binding_for, binding_for_view, event_to_action, +}; diff --git a/src/app/widget.rs b/src/app/widget.rs index beb7e06..7856aea 100644 --- a/src/app/widget.rs +++ b/src/app/widget.rs @@ -1,14 +1,15 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; -use crate::app::{App, widgets::main_menu_widget}; +use crate::app::{App, View, widgets::main_menu_widget}; impl Widget for &App { fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized, { - if self.default_window == "main_menu" { - main_menu_widget(area, buf); + match self.window { + View::MainMenu => main_menu_widget(area, buf), + _ => panic!("This window doesn't have widget."), } } } diff --git a/src/app/widgets/keybindings.rs b/src/app/widgets/keybindings.rs new file mode 100644 index 0000000..7d73574 --- /dev/null +++ b/src/app/widgets/keybindings.rs @@ -0,0 +1,31 @@ +use ratatui::{ + crossterm::event::KeyCode, + layout::Alignment, + style::Stylize, + text::{Line, Span}, + widgets::{Block, Borders, Paragraph}, +}; + +use crate::app::{View, binding_for_view}; + +pub fn keybindings_widget(view: View) -> Paragraph<'static> { + let lines: Vec = binding_for_view(view) + .iter() + .map(|b| { + let key_span = match b.code { + 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 ]"), + ) +} diff --git a/src/app/widgets/main_menu.rs b/src/app/widgets/main_menu.rs index 71dd8c9..0b69f6b 100644 --- a/src/app/widgets/main_menu.rs +++ b/src/app/widgets/main_menu.rs @@ -1,47 +1,42 @@ use ratatui::{ buffer::Buffer, layout::{Alignment, Constraint, Layout, Rect}, - style::{Color, Stylize}, - text::Line, + style::Color, widgets::{Block, Borders, Paragraph, Widget}, }; +use crate::app::{View, widgets::keybindings_widget}; + pub fn main_menu_widget(area: Rect, buf: &mut Buffer) { let vertical_layout: Layout = Layout::vertical([Constraint::Percentage(90), Constraint::Percentage(10)]); let [main_menu_area, keybindings_area] = vertical_layout.areas(area); - let title_text: String = vec![ - r" __ __ _ _____ _ ", - r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___", - r"\ \/ \/ / _` | '__| | | '_ \ / /\/ | | | '_ \| '_ \ / _ \ / __|", - r" \ /\ / (_| | | | | | | | / / | |_| | | | | | | | __/ \__ \", - r" \/ \/ \__,_|_| |_|_| |_| \/ \__,_|_| |_|_| |_|\___|_|___/", - ] - .join("\n"); + { + let title_text: String = vec![ + r" __ __ _ _____ _ ", + r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___", + r"\ \/ \/ / _` | '__| | | '_ \ / /\/ | | | '_ \| '_ \ / _ \ / __|", + r" \ /\ / (_| | | | | | | | / / | |_| | | | | | | | __/ \__ \", + r" \/ \/ \__,_|_| |_|_| |_| \/ \__,_|_| |_|_| |_|\___|_|___/", + ] + .join("\n"); - let title: Paragraph<'_> = Paragraph::new(title_text) - .alignment(Alignment::Center) - .style(Color::Yellow) - .block( - Block::default() - .borders(Borders::LEFT | Borders::RIGHT | Borders::TOP) - .style(Color::Gray), - ); + let title: Paragraph<'_> = Paragraph::new(title_text) + .alignment(Alignment::Center) + .style(Color::Yellow) + .block( + Block::default() + .borders(Borders::LEFT | Borders::RIGHT | Borders::TOP) + .style(Color::Gray), + ); - title.render(main_menu_area, buf); + title.render(main_menu_area, buf); + } - let keybindings_text: Vec> = - vec![Line::from_iter(["q".bold().red(), "\t - Quit".into()])]; - - let keybindings: Paragraph<'_> = Paragraph::new(keybindings_text) - .alignment(Alignment::Left) - .block( - Block::default() - .borders(Borders::ALL) - .title("[ Keybinding ]"), - ); - - keybindings.render(keybindings_area, buf); + { + let keybindings: Paragraph<'_> = keybindings_widget(View::MainMenu); + keybindings.render(keybindings_area, buf); + } } diff --git a/src/app/widgets/mod.rs b/src/app/widgets/mod.rs index 13ba747..768f7e6 100644 --- a/src/app/widgets/mod.rs +++ b/src/app/widgets/mod.rs @@ -1,3 +1,5 @@ +pub mod keybindings; pub mod main_menu; +pub use keybindings::keybindings_widget; pub use main_menu::main_menu_widget; diff --git a/src/cli.rs b/src/cli.rs index c9d18f5..91a59a3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,15 +1,18 @@ use clap::Parser; +use crate::app::View; + #[derive(Parser, Debug)] #[command(version, about = "War in Tunnels", long_about = "War in Tunnels")] pub struct Cli { #[arg( long, help = "Default window", - value_name = "main_menu | skirmish | skills_config | perk_decks | settings", - default_value = "main_menu" + value_name = "...", + default_value_t = View::MainMenu, + value_enum )] - pub default_window: String, + pub window: View, #[arg( long, diff --git a/src/main.rs b/src/main.rs index 4c5c210..59b08e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() -> Result<()> { let mut terminal: Terminal> = ratatui::init(); let mut app: App = App { exit: false, - default_window: args.default_window, + window: args.window, username: args.username, game_mode: args.game_mode, map_width: args.map_width,