Refactor skirmish focus handling and CellWidget API

Introduce a MoveFocusedCell enum and a BoardState.change_focused_cell
method to
centralize focus movement logic. Update skirmish keybindings to use this
new
method and simplify board rendering by directly using stored CellWidget
state.
Make CellWidget setters chainable and expose the new enum in the module
re‑exports. Remove duplicated max_offset logic and old move_* methods.
This commit is contained in:
2026-04-07 23:20:11 +02:00
parent e3fea75983
commit 48483da1a4
6 changed files with 88 additions and 57 deletions
+38 -25
View File
@@ -1,7 +1,10 @@
use crate::app::{ use crate::app::{
App, App,
keybindings::{Action, common_keybindings, event_to_action}, keybindings::{Action, common_keybindings, event_to_action},
states::ZoomLevel, states::{
ZoomLevel,
skirmish_states::{BoardState, MoveFocusedCell},
},
}; };
use ratatui::crossterm::event::KeyEvent; use ratatui::crossterm::event::KeyEvent;
@@ -13,48 +16,58 @@ pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) {
return; return;
}; };
let board: &mut BoardState = &mut states.skirmish.board;
match action { match action {
Action::Up => states.skirmish.board.focused_cell.move_up(), Action::Up => {
Action::Down => states.skirmish.board.focused_cell.move_down(), board.change_focused_cell(MoveFocusedCell::Up);
Action::Left => states.skirmish.board.focused_cell.move_left(), }
Action::Right => states.skirmish.board.focused_cell.move_right(), Action::Down => {
board.change_focused_cell(MoveFocusedCell::Down);
}
Action::Left => {
board.change_focused_cell(MoveFocusedCell::Left);
}
Action::Right => {
board.change_focused_cell(MoveFocusedCell::Right);
}
Action::ScrollUp => { Action::ScrollUp => {
states.skirmish.board.vertical_offset.prev(); board.vertical_offset.prev();
states.skirmish.board.focused_cell.move_up(); board.change_focused_cell(MoveFocusedCell::Up);
} }
Action::ScrollDown => { Action::ScrollDown => {
states.skirmish.board.vertical_offset.next(); board.vertical_offset.next();
states.skirmish.board.focused_cell.move_down(); board.change_focused_cell(MoveFocusedCell::Down);
} }
Action::ScrollLeft => { Action::ScrollLeft => {
states.skirmish.board.horizontal_offset.prev(); board.horizontal_offset.prev();
states.skirmish.board.focused_cell.move_left(); board.change_focused_cell(MoveFocusedCell::Left);
} }
Action::ScrollRight => { Action::ScrollRight => {
states.skirmish.board.horizontal_offset.next(); board.horizontal_offset.next();
states.skirmish.board.focused_cell.move_right(); board.change_focused_cell(MoveFocusedCell::Right);
} }
Action::ZoomIn => match states.skirmish.board.zoom_level { Action::ZoomIn => match board.zoom_level {
ZoomLevel::ZoomedIn => {} ZoomLevel::ZoomedIn => {}
ZoomLevel::Default => { ZoomLevel::Default => {
states.skirmish.board.zoom_change(ZoomLevel::ZoomedIn); board.zoom_change(ZoomLevel::ZoomedIn);
states.skirmish.board.vertical_offset.next(); board.vertical_offset.next();
} }
ZoomLevel::ZoomedOut => { ZoomLevel::ZoomedOut => {
states.skirmish.board.zoom_change(ZoomLevel::Default); board.zoom_change(ZoomLevel::Default);
states.skirmish.board.vertical_offset.next(); board.vertical_offset.next();
states.skirmish.board.vertical_offset.next(); board.vertical_offset.next();
} }
}, },
Action::ZoomOut => match states.skirmish.board.zoom_level { Action::ZoomOut => match board.zoom_level {
ZoomLevel::ZoomedIn => { ZoomLevel::ZoomedIn => {
states.skirmish.board.zoom_change(ZoomLevel::Default); board.zoom_change(ZoomLevel::Default);
states.skirmish.board.vertical_offset.prev(); board.vertical_offset.prev();
} }
ZoomLevel::Default => { ZoomLevel::Default => {
states.skirmish.board.zoom_change(ZoomLevel::ZoomedOut); board.zoom_change(ZoomLevel::ZoomedOut);
states.skirmish.board.vertical_offset.prev(); board.vertical_offset.prev();
states.skirmish.board.vertical_offset.prev(); board.vertical_offset.prev();
} }
ZoomLevel::ZoomedOut => {} ZoomLevel::ZoomedOut => {}
}, },
+19 -4
View File
@@ -1,6 +1,6 @@
use crate::app::{ use crate::app::{
helpers::{CellSizes, cell_size_helper, cells_area_helper}, helpers::{CellSizes, cell_size_helper, cells_area_helper},
states::{FocusedCell, Offset, ZoomLevel}, states::{FocusedCell, Offset, ZoomLevel, skirmish_states::MoveFocusedCell},
widgets::CellWidget, widgets::CellWidget,
}; };
use ratatui::layout::Rect; use ratatui::layout::Rect;
@@ -45,7 +45,13 @@ impl BoardState {
for row in 0..map_height { for row in 0..map_height {
for col in 0..map_width { for col in 0..map_width {
cells.push(CellWidget::new(row, col, zoom_level)); cells.push(*CellWidget::new(row, col, zoom_level).set_selected(
if row == focused_cell.get_row() && col == focused_cell.get_col() {
true
} else {
false
},
));
} }
} }
@@ -69,6 +75,10 @@ impl BoardState {
&mut self.cells[row * self.map_width + col] &mut self.cells[row * self.map_width + col]
} }
fn max_offset(map_size: usize, size: usize) -> usize {
if map_size > size { map_size - size } else { 0 }
}
pub fn zoom_change(&mut self, new_zoom_level: ZoomLevel) { pub fn zoom_change(&mut self, new_zoom_level: ZoomLevel) {
self.zoom_level = new_zoom_level; self.zoom_level = new_zoom_level;
@@ -100,7 +110,12 @@ impl BoardState {
} }
} }
fn max_offset(map_size: usize, size: usize) -> usize { pub fn change_focused_cell(&mut self, direction: MoveFocusedCell) {
if map_size > size { map_size - size } else { 0 } let old_row: usize = self.focused_cell.get_row();
let old_col: usize = self.focused_cell.get_col();
let (new_row, new_col) = self.focused_cell.move_focused_cell(direction);
self.get_mut_cell(old_row, old_col).set_selected(false);
self.get_mut_cell(new_row, new_col).set_selected(true);
} }
} }
+23 -13
View File
@@ -1,3 +1,10 @@
pub enum MoveFocusedCell {
Up,
Down,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FocusedCell { pub struct FocusedCell {
row: usize, row: usize,
@@ -24,19 +31,22 @@ impl FocusedCell {
self.col self.col
} }
pub fn move_up(&mut self) { pub fn move_focused_cell(&mut self, direction: MoveFocusedCell) -> (usize, usize) {
self.row = self.row.saturating_sub(1).max(0); match direction {
} MoveFocusedCell::Up => {
self.row = self.row.saturating_sub(1).max(0);
}
MoveFocusedCell::Down => {
self.row = self.row.saturating_add(1).min(self.max_row - 1);
}
MoveFocusedCell::Left => {
self.col = self.col.saturating_sub(1).max(0);
}
MoveFocusedCell::Right => {
self.col = self.col.saturating_add(1).min(self.max_col - 1);
}
}
pub fn move_down(&mut self) { (self.row, self.col)
self.row = self.row.saturating_add(1).min(self.max_row - 1);
}
pub fn move_left(&mut self) {
self.col = self.col.saturating_sub(1).max(0);
}
pub fn move_right(&mut self) {
self.col = self.col.saturating_add(1).min(self.max_col - 1);
} }
} }
+1 -1
View File
@@ -3,5 +3,5 @@ pub mod focused_cell;
pub mod offset; pub mod offset;
pub use board::BoardState; pub use board::BoardState;
pub use focused_cell::FocusedCell; pub use focused_cell::{FocusedCell, MoveFocusedCell};
pub use offset::Offset; pub use offset::Offset;
+2 -10
View File
@@ -1,4 +1,4 @@
use crate::app::{states::skirmish_states::BoardState, widgets::CellWidget}; use crate::app::states::skirmish_states::BoardState;
use ratatui::{ use ratatui::{
buffer::Buffer, buffer::Buffer,
layout::{Constraint, Layout, Rect}, layout::{Constraint, Layout, Rect},
@@ -37,19 +37,11 @@ impl Widget for BoardWidget<'_> {
let map_row: usize = row_idx + self.state.vertical_offset.get_value(); let map_row: usize = row_idx + self.state.vertical_offset.get_value();
let map_col: usize = col_idx + self.state.horizontal_offset.get_value(); let map_col: usize = col_idx + self.state.horizontal_offset.get_value();
if let Some(template) = self if let Some(cell) = self
.state .state
.cells .cells
.get(map_row * self.state.map_width + map_col) .get(map_row * self.state.map_width + map_col)
{ {
let mut cell: CellWidget = template.clone();
if map_row == self.state.focused_cell.get_row()
&& map_col == self.state.focused_cell.get_col()
{
cell.set_selected(true);
}
cell.render(*cell_area, buf); cell.render(*cell_area, buf);
} }
} }
+5 -4
View File
@@ -15,13 +15,12 @@ use ratatui::{
// Base, // Base,
// } // }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CellWidget { pub struct CellWidget {
row: usize, row: usize,
col: usize, col: usize,
selected: bool, selected: bool,
zoom_level: ZoomLevel, zoom_level: ZoomLevel,
// text_area: Vec<Line>,
// pub tags: Vec<CellTags>, // pub tags: Vec<CellTags>,
} }
@@ -35,12 +34,14 @@ 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) -> &mut Self {
self.zoom_level = zoom_level; self.zoom_level = zoom_level;
self
} }
fn col_to_letters(&self) -> String { fn col_to_letters(&self) -> String {