generated from GarandPLG/rust-flake-template
Refactor to View enum and add keybindings
Replace the `default_window` string field with a `View` enum throughout the application. Introduce a new keybinding module providing `Action`, `KeyBinding` structs and helper functions, and integrate a keybindings widget into the main menu. Update the CLI to accept a `View` value enum and adjust imports and event handling accordingly. Add a release profile in `Cargo.toml` with LTO, single codegen unit, higher optimisation level and binary stripping for smaller, faster builds.
This commit is contained in:
@@ -3,6 +3,12 @@ name = "war_in_tunnels"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = 3
|
||||||
|
strip = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.53", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
ratatui = "0.30.0"
|
ratatui = "0.30.0"
|
||||||
|
|||||||
+21
-4
@@ -1,6 +1,8 @@
|
|||||||
|
use crate::app::{Action, event_to_action};
|
||||||
|
use clap::ValueEnum;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
crossterm::event::{self, KeyCode, KeyEvent, KeyEventKind},
|
crossterm::event::{self, KeyEvent},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io::Result,
|
io::Result,
|
||||||
@@ -9,7 +11,7 @@ use std::{
|
|||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub exit: bool,
|
pub exit: bool,
|
||||||
pub default_window: String,
|
pub window: View,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub game_mode: String,
|
pub game_mode: String,
|
||||||
pub map_width: u8,
|
pub map_width: u8,
|
||||||
@@ -22,6 +24,15 @@ pub struct App {
|
|||||||
pub skill_points_limit: u16,
|
pub skill_points_limit: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, ValueEnum)]
|
||||||
|
pub enum View {
|
||||||
|
MainMenu,
|
||||||
|
Skirmish,
|
||||||
|
PerkDecks,
|
||||||
|
SkillsConfig,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Input(KeyEvent),
|
Input(KeyEvent),
|
||||||
}
|
}
|
||||||
@@ -48,8 +59,14 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
|
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
|
||||||
if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') {
|
// if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') {
|
||||||
self.exit = true;
|
// self.exit = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if let Some(action) = event_to_action(&key_event) {
|
||||||
|
match action {
|
||||||
|
Action::Quit => self.exit = true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -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<Action> {
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-1
@@ -1,5 +1,9 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
|
pub mod keybindings;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
pub mod widgets;
|
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,
|
||||||
|
};
|
||||||
|
|||||||
+4
-3
@@ -1,14 +1,15 @@
|
|||||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
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 {
|
impl Widget for &App {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer)
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
if self.default_window == "main_menu" {
|
match self.window {
|
||||||
main_menu_widget(area, buf);
|
View::MainMenu => main_menu_widget(area, buf),
|
||||||
|
_ => panic!("This window doesn't have widget."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Line> = 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 ]"),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Alignment, Constraint, Layout, Rect},
|
layout::{Alignment, Constraint, Layout, Rect},
|
||||||
style::{Color, Stylize},
|
style::Color,
|
||||||
text::Line,
|
|
||||||
widgets::{Block, Borders, Paragraph, Widget},
|
widgets::{Block, Borders, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::app::{View, widgets::keybindings_widget};
|
||||||
|
|
||||||
pub fn main_menu_widget(area: Rect, buf: &mut Buffer) {
|
pub fn main_menu_widget(area: Rect, buf: &mut Buffer) {
|
||||||
let vertical_layout: Layout =
|
let vertical_layout: Layout =
|
||||||
Layout::vertical([Constraint::Percentage(90), Constraint::Percentage(10)]);
|
Layout::vertical([Constraint::Percentage(90), Constraint::Percentage(10)]);
|
||||||
|
|
||||||
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
|
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
|
||||||
|
|
||||||
|
{
|
||||||
let title_text: String = vec![
|
let title_text: String = vec![
|
||||||
r" __ __ _ _____ _ ",
|
r" __ __ _ _____ _ ",
|
||||||
r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___",
|
r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___",
|
||||||
@@ -31,17 +33,10 @@ pub fn main_menu_widget(area: Rect, buf: &mut Buffer) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
title.render(main_menu_area, buf);
|
title.render(main_menu_area, buf);
|
||||||
|
}
|
||||||
|
|
||||||
let keybindings_text: Vec<Line<'_>> =
|
{
|
||||||
vec![Line::from_iter(["q".bold().red(), "\t - Quit".into()])];
|
let keybindings: Paragraph<'_> = keybindings_widget(View::MainMenu);
|
||||||
|
|
||||||
let keybindings: Paragraph<'_> = Paragraph::new(keybindings_text)
|
|
||||||
.alignment(Alignment::Left)
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.title("[ Keybinding ]"),
|
|
||||||
);
|
|
||||||
|
|
||||||
keybindings.render(keybindings_area, buf);
|
keybindings.render(keybindings_area, buf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pub mod keybindings;
|
||||||
pub mod main_menu;
|
pub mod main_menu;
|
||||||
|
|
||||||
|
pub use keybindings::keybindings_widget;
|
||||||
pub use main_menu::main_menu_widget;
|
pub use main_menu::main_menu_widget;
|
||||||
|
|||||||
+6
-3
@@ -1,15 +1,18 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::app::View;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about = "War in Tunnels", long_about = "War in Tunnels")]
|
#[command(version, about = "War in Tunnels", long_about = "War in Tunnels")]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
help = "Default window",
|
help = "Default window",
|
||||||
value_name = "main_menu | skirmish | skills_config | perk_decks | settings",
|
value_name = "...",
|
||||||
default_value = "main_menu"
|
default_value_t = View::MainMenu,
|
||||||
|
value_enum
|
||||||
)]
|
)]
|
||||||
pub default_window: String,
|
pub window: View,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ fn main() -> Result<()> {
|
|||||||
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
||||||
let mut app: App = App {
|
let mut app: App = App {
|
||||||
exit: false,
|
exit: false,
|
||||||
default_window: args.default_window,
|
window: args.window,
|
||||||
username: args.username,
|
username: args.username,
|
||||||
game_mode: args.game_mode,
|
game_mode: args.game_mode,
|
||||||
map_width: args.map_width,
|
map_width: args.map_width,
|
||||||
|
|||||||
Reference in New Issue
Block a user