Refactor cell marking, add undo, update keybindings

CellWidget::set_selected and set_marked now return &mut Self for method
chaining. Added BoardState::undo_marked_cell to remove the last marked
cell and restore focus. Backspace triggers undo, Delete clears marking.
This commit is contained in:
2026-04-13 16:57:44 +02:00
parent d5f4d03264
commit 1393f282e8
6 changed files with 74 additions and 34 deletions
+32 -21
View File
@@ -29,14 +29,16 @@ pub enum Action {
ScrollLeft, ScrollLeft,
/// Scroll right without moving the cursor. /// Scroll right without moving the cursor.
ScrollRight, ScrollRight,
/// Select the current item. /// Toggle selecting cell.
Space, Space,
/// Unmark last selected cell.
Backspace,
/// Cancel marking cell.
Delete,
/// Submit or confirm the current choice. /// Submit or confirm the current choice.
Enter, Enter,
/// Return to the main menu or previous screen. /// Return to the main menu or previous screen.
Esc, Esc,
/// Delete the character before the cursor.
Backspace,
/// Zoom the view in. /// Zoom the view in.
ZoomIn, ZoomIn,
/// Zoom the view out. /// Zoom the view out.
@@ -199,15 +201,33 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
symbol: "Space", symbol: "Space",
description: "Marking cells", description: "Marking cells",
}, },
// KeyBinding { KeyBinding {
// action: Action::Enter, action: Action::Backspace,
// code: KeyCode::Enter, code: KeyCode::Backspace,
// kind: KeyEventKind::Press, kind: KeyEventKind::Press,
// modifiers: KeyModifiers::NONE, modifiers: KeyModifiers::NONE,
// group: Group::Select, group: Group::Select,
// symbol: "Enter", symbol: "Backspace",
// description: "Submit", description: "Unmark cell",
// }, },
KeyBinding {
action: Action::Delete,
code: KeyCode::Delete,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
group: Group::Select,
symbol: "Delete",
description: "Cancel marking",
},
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,
@@ -217,15 +237,6 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
symbol: "Esc", symbol: "Esc",
description: "Main menu", description: "Main menu",
}, },
KeyBinding {
action: Action::Backspace,
code: KeyCode::Backspace,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
group: Group::Select,
symbol: "Backspace",
description: "Cancel marking",
},
KeyBinding { KeyBinding {
action: Action::ZoomIn, action: Action::ZoomIn,
code: KeyCode::Char(','), code: KeyCode::Char(','),
+2 -1
View File
@@ -84,7 +84,8 @@ pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) {
ZoomLevel::ZoomedOut => {} ZoomLevel::ZoomedOut => {}
}, },
Action::Space => board.toggle_marking(), Action::Space => board.toggle_marking(),
Action::Backspace => { Action::Backspace => board.undo_marked_cell(),
Action::Delete => {
board.marking_cells = false; board.marking_cells = false;
board.clear_marked_cells() board.clear_marked_cells()
} }
+26 -7
View File
@@ -116,16 +116,35 @@ impl BoardState {
} }
} }
pub fn set_marked_cell(&mut self, new_cell: (usize, usize), old_cell: (usize, usize)) { pub fn set_marked_cell(&mut self, new_cell: (usize, usize)) {
let cell: &mut CellWidget = self.get_mut_cell(new_cell.0, new_cell.1); let cell: &mut CellWidget = self.get_mut_cell(new_cell.0, new_cell.1);
if cell.get_marked() && old_cell != new_cell { if !cell.get_marked() {
self.get_mut_cell(old_cell.0, old_cell.1).set_marked(false);
self.marked_cells.pop();
} else {
cell.set_marked(true); cell.set_marked(true);
self.marked_cells.push((new_cell.0, new_cell.1));
} }
self.marked_cells.push((new_cell.0, new_cell.1));
}
pub fn undo_marked_cell(&mut self) {
if self.marked_cells.len() < 2 {
return;
}
let old: (usize, usize) = self.marked_cells[self.marked_cells.len() - 1];
let new: (usize, usize) = self.marked_cells[self.marked_cells.len() - 2];
let old_is_unique: bool = self.marked_cells.iter().filter(|x| **x == old).count() == 1;
let old_cell: &mut CellWidget = self.get_mut_cell(old.0, old.1).set_selected(false);
if old_is_unique {
old_cell.set_marked(false);
}
self.get_mut_cell(new.0, new.1).set_selected(true);
self.marked_cells.pop();
self.focused_cell.set_focused_cell(new);
} }
pub fn start_marking_cells(&mut self) { pub fn start_marking_cells(&mut self) {
@@ -226,7 +245,7 @@ impl BoardState {
self.get_mut_cell(new_cell.0, new_cell.1).set_selected(true); self.get_mut_cell(new_cell.0, new_cell.1).set_selected(true);
if self.marking_cells { if self.marking_cells {
self.set_marked_cell(new_cell, old_cell); self.set_marked_cell(new_cell);
} }
} }
} }
@@ -49,4 +49,9 @@ impl FocusedCell {
(self.row, self.col) (self.row, self.col)
} }
pub fn set_focused_cell(&mut self, cell: (usize, usize)) {
self.row = cell.0.max(0).min(self.max_row - 1);
self.col = cell.1.max(0).min(self.max_col - 1);
}
} }
+1
View File
@@ -27,6 +27,7 @@ const ACTIONS: &[Action] = &[
Action::VolumeDown, Action::VolumeDown,
Action::Space, Action::Space,
Action::Backspace, Action::Backspace,
Action::Delete,
Action::Mute, Action::Mute,
Action::Quit, Action::Quit,
Action::Quit2, Action::Quit2,
+8 -5
View File
@@ -48,8 +48,9 @@ impl CellWidget {
} }
} }
pub fn set_selected(&mut self, selected: bool) { pub fn set_selected(&mut self, selected: bool) -> &mut Self {
self.selected = selected; self.selected = selected;
self
} }
pub fn set_zoom_level(&mut self, zoom_level: ZoomLevel) { pub fn set_zoom_level(&mut self, zoom_level: ZoomLevel) {
@@ -64,8 +65,9 @@ impl CellWidget {
self.marked self.marked
} }
pub fn set_marked(&mut self, marked: bool) { pub fn set_marked(&mut self, marked: bool) -> &mut Self {
self.marked = marked self.marked = marked;
self
} }
fn col_to_letters(&self) -> String { fn col_to_letters(&self) -> String {
@@ -90,8 +92,9 @@ impl CellWidget {
fn fg_color(&self) -> Color { fn fg_color(&self) -> Color {
match self.tag { match self.tag {
_ if self.marked => Color::LightMagenta, _ if self.marked && self.selected => Color::Magenta,
_ if self.selected => Color::LightYellow, _ if self.marked && !self.selected => Color::LightMagenta,
_ if self.selected && !self.marked => Color::LightYellow,
CellTag::Base(Players::Player) if !self.selected => Color::LightBlue, CellTag::Base(Players::Player) if !self.selected => Color::LightBlue,
CellTag::Base(Players::Enemy) if !self.selected => Color::LightRed, CellTag::Base(Players::Enemy) if !self.selected => Color::LightRed,
CellTag::Tunel if !self.selected => Color::Gray, CellTag::Tunel if !self.selected => Color::Gray,