Add text input handling to settings popup

Introduce a `selected_setting_new_value` field in `SettingsState` and
initialize it.
Add `Backspace` and `WildCard(char)` actions with an `Input` group, and
corresponding keybindings.
Update `event_to_action` to map wildcard characters to
`Action::WildCard`.
Handle backspace and character input in `settings_keybindings` to edit
the new field.
Display the edited value in the settings popup and include the new
actions in the UI.
This commit is contained in:
2026-03-15 22:37:11 +01:00
parent 9d4ff19b6e
commit 2dbe378470
5 changed files with 112 additions and 46 deletions
+4 -2
View File
@@ -1,4 +1,4 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GameStates { pub struct GameStates {
pub main_menu_state: MainMenuState, pub main_menu_state: MainMenuState,
pub skirmish_state: SkirmishState, pub skirmish_state: SkirmishState,
@@ -34,12 +34,13 @@ pub struct SkillsConfigState {
pub selected_skill: usize, pub selected_skill: usize,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SettingsState { pub struct SettingsState {
pub id: usize, pub id: usize,
pub name: &'static str, pub name: &'static str,
pub selected_setting: usize, pub selected_setting: usize,
pub show_popup: bool, pub show_popup: bool,
pub selected_setting_new_value: String,
} }
impl GameStates { impl GameStates {
@@ -69,6 +70,7 @@ impl GameStates {
name: "Settings", name: "Settings",
selected_setting: 0, selected_setting: 0,
show_popup: false, show_popup: false,
selected_setting_new_value: String::new(),
}, },
} }
} }
+37 -2
View File
@@ -10,12 +10,15 @@ pub enum Action {
Space, Space,
Enter, Enter,
Esc, Esc,
Backspace,
WildCard(char),
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
pub enum Group { pub enum Group {
Movement, Movement,
Select, Select,
Input,
Quit, Quit,
} }
@@ -94,6 +97,24 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
symbol: "Esc", symbol: "Esc",
description: "Go back", description: "Go back",
}, },
KeyBinding {
action: Action::Backspace,
code: KeyCode::Backspace,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
group: Group::Input,
symbol: "Backspace",
description: "Delete character",
},
KeyBinding {
action: Action::WildCard('_'),
code: KeyCode::Char('_'),
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
group: Group::Input,
symbol: "*",
description: "All keyboard characters",
},
]; ];
pub fn binding_for(action: Action) -> Option<&'static KeyBinding> { pub fn binding_for(action: Action) -> Option<&'static KeyBinding> {
@@ -101,8 +122,22 @@ pub fn binding_for(action: Action) -> Option<&'static KeyBinding> {
} }
pub fn event_to_action(event: &KeyEvent) -> Option<Action> { pub fn event_to_action(event: &KeyEvent) -> Option<Action> {
KEYBINDINGS if let Some(b) = KEYBINDINGS
.iter() .iter()
.find(|b| b.code == event.code && b.kind == event.kind && b.modifiers == event.modifiers) .find(|b| b.code == event.code && b.kind == event.kind && b.modifiers == event.modifiers)
.map(|b| b.action) {
return Some(b.action);
}
if KEYBINDINGS.iter().any(|b| {
matches!(b.action, Action::WildCard(_))
&& b.kind == event.kind
&& b.modifiers == event.modifiers
}) {
if let KeyCode::Char(c) = event.code {
return Some(Action::WildCard(c));
}
}
None
} }
+18
View File
@@ -7,6 +7,8 @@ use ratatui::crossterm::event::KeyEvent;
pub fn settings_keybindings(app: &mut App, key_event: &KeyEvent) { pub fn settings_keybindings(app: &mut App, key_event: &KeyEvent) {
if let Some(action) = event_to_action(&key_event) { if let Some(action) = event_to_action(&key_event) {
match action { match action {
// Action::Up,
// Action::Down,
Action::Quit => app.exit = true, Action::Quit => app.exit = true,
Action::Quit2 => app.exit = true, Action::Quit2 => app.exit = true,
Action::Esc => app.view = View::MainMenu, Action::Esc => app.view = View::MainMenu,
@@ -14,6 +16,22 @@ pub fn settings_keybindings(app: &mut App, key_event: &KeyEvent) {
app.game_states.settings_state.show_popup = app.game_states.settings_state.show_popup =
!app.game_states.settings_state.show_popup !app.game_states.settings_state.show_popup
} }
Action::Backspace => {
if app.game_states.settings_state.show_popup {
app.game_states
.settings_state
.selected_setting_new_value
.pop();
}
}
Action::WildCard(c) => {
if app.game_states.settings_state.show_popup {
app.game_states
.settings_state
.selected_setting_new_value
.push(c);
}
}
_ => (), _ => (),
} }
} }
+11 -9
View File
@@ -11,15 +11,17 @@ pub fn default_view(area: Rect, buf: &mut Buffer) {
let [main_area, keybindings_area] = vertical_layout.areas(area); let [main_area, keybindings_area] = vertical_layout.areas(area);
Paragraph::new("Work in progress") {
.alignment(Alignment::Center) Paragraph::new("Work in progress")
.yellow() .alignment(Alignment::Center)
.block( .yellow()
Block::new() .block(
.gray() Block::new()
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT), .gray()
) .borders(Borders::LEFT | Borders::TOP | Borders::RIGHT),
.render(main_area, buf); )
.render(main_area, buf);
}
{ {
let actions: Vec<Action> = vec![Action::Quit, Action::Quit2, Action::Esc]; let actions: Vec<Action> = vec![Action::Quit, Action::Quit2, Action::Esc];
+42 -33
View File
@@ -15,36 +15,42 @@ pub fn settings_view(app: &App, area: Rect, buf: &mut Buffer) {
let [title_area, main_area, keybindings_area] = vertical_layout.areas(area); let [title_area, main_area, keybindings_area] = vertical_layout.areas(area);
Paragraph::new("War in tunnels") {
.alignment(Alignment::Left) Paragraph::new("War in tunnels")
.yellow() .alignment(Alignment::Left)
.block( .yellow()
Block::new() .block(
.gray() Block::new()
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT) .gray()
.padding(Padding::new(1, 1, 1, 1)), .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);
let popup_area: Rect = Rect {
x: main_area.width / 4,
y: main_area.height / 3,
width: main_area.width / 2,
height: main_area.height / 3,
};
let popup: Paragraph<'_> = Paragraph::new(
app.game_states
.settings_state
.selected_setting_new_value
.clone(),
) )
.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);
let popup_area = Rect {
x: main_area.width / 4,
y: main_area.height / 3,
width: main_area.width / 2,
height: main_area.height / 3,
};
let popup = Paragraph::new("Test popup")
.wrap(Wrap { trim: true }) .wrap(Wrap { trim: true })
.style(Style::default().yellow()) .style(Style::default().yellow())
.block( .block(
@@ -55,19 +61,22 @@ pub fn settings_view(app: &App, area: Rect, buf: &mut Buffer) {
.border_style(Style::default().blue()), .border_style(Style::default().blue()),
); );
if app.game_states.settings_state.show_popup { if app.game_states.settings_state.show_popup {
popup.render(popup_area, buf); popup.render(popup_area, buf);
}
} }
{ {
let actions: Vec<Action> = vec![ let actions: Vec<Action> = vec![
Action::Up, Action::Up,
Action::Down, Action::Down,
Action::Space,
Action::Enter,
Action::Quit, Action::Quit,
Action::Quit2, Action::Quit2,
Action::Esc, Action::Esc,
Action::Space,
Action::Enter,
Action::Backspace,
Action::WildCard('_'),
]; ];
KeybindingsWidget::new(actions).render(keybindings_area, buf); KeybindingsWidget::new(actions).render(keybindings_area, buf);