Add lifetime parameter and MarkedCells struct

Refactor App, GameStates, SkirmishState and BoardState to carry a
generic
lifetime `'a`. Introduce a new `MarkedCells` type that holds marking
state
and selected unit, replacing the previous `marking_cells` flag and raw
deque. Update related methods, keybindings, view implementations and
module exports to use the new structures.
This commit is contained in:
2026-05-06 21:39:53 +02:00
parent b66c1d8e25
commit cd1822d05e
11 changed files with 78 additions and 38 deletions
+5 -5
View File
@@ -10,16 +10,16 @@ use std::{
time::Duration, time::Duration,
}; };
pub struct App { pub struct App<'a> {
pub exit: bool, pub exit: bool,
pub view: View, pub view: View,
pub window_area: Rect, pub window_area: Rect,
pub args: Cli, pub args: Cli,
pub states: Option<GameStates>, pub states: Option<GameStates<'a>>,
pub audio_tx: Sender<AudioCmd>, pub audio_tx: Sender<AudioCmd>,
} }
impl App { impl<'a> App<'a> {
pub fn new(args: Cli, audio_tx: Sender<AudioCmd>) -> Self { pub fn new(args: Cli, audio_tx: Sender<AudioCmd>) -> Self {
Self { Self {
exit: false, exit: false,
@@ -31,11 +31,11 @@ impl App {
} }
} }
pub fn states(&self) -> Option<&GameStates> { pub fn states_ref(&self) -> Option<&GameStates<'a>> {
self.states.as_ref() self.states.as_ref()
} }
pub fn states_mut(&mut self) -> Option<&mut GameStates> { pub fn states_mut(&mut self) -> Option<&mut GameStates<'a>> {
self.states.as_mut() self.states.as_mut()
} }
+1 -1
View File
@@ -85,7 +85,7 @@ pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) {
Action::Space => board.toggle_marking(), Action::Space => board.toggle_marking(),
Action::Backspace => board.undo_marked_cell(), Action::Backspace => board.undo_marked_cell(),
Action::Delete => { Action::Delete => {
board.marking_cells = false; board.marked_cells.marking_cells = false;
board.clear_marked_cells() board.clear_marked_cells()
} }
Action::Tab => { Action::Tab => {
+3 -3
View File
@@ -8,15 +8,15 @@ use crate::{
use ratatui::layout::Rect; use ratatui::layout::Rect;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct GameStates { pub struct GameStates<'a> {
pub main_menu: MainMenuState, pub main_menu: MainMenuState,
pub skirmish: SkirmishState, pub skirmish: SkirmishState<'a>,
pub perk_decks: PerkDecksState, pub perk_decks: PerkDecksState,
pub skills_config: SkillsConfigState, pub skills_config: SkillsConfigState,
pub settings: SettingsState, pub settings: SettingsState,
} }
impl GameStates { impl GameStates<'_> {
pub fn new(args: &Cli, area: &Rect) -> Self { pub fn new(args: &Cli, area: &Rect) -> Self {
Self { Self {
main_menu: MainMenuState { main_menu: MainMenuState {
+3 -3
View File
@@ -1,15 +1,15 @@
use crate::app::states::skirmish_states::BoardState; use crate::app::states::skirmish_states::BoardState;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct SkirmishState { pub struct SkirmishState<'a> {
pub id: usize, pub id: usize,
pub name: &'static str, pub name: &'static str,
pub board: BoardState, pub board: BoardState<'a>,
pub side_panel: bool, pub side_panel: bool,
pub turn_counter: u64, pub turn_counter: u64,
} }
impl SkirmishState { impl SkirmishState<'_> {
pub fn tick_update(&mut self) { pub fn tick_update(&mut self) {
// self.board.advance_turn(); // self.board.advance_turn();
+36 -22
View File
@@ -3,7 +3,7 @@ use crate::app::{
states::{ states::{
FocusedCell, Offset, FocusedCell, Offset,
skirmish_states::{ skirmish_states::{
CellSizes, MoveFocusedCell, Players, ZoomLevel, CellSizes, MarkedCells, MoveFocusedCell, Players, ZoomLevel,
structures::{BaseBuilding, Ore, Stone, Structures}, structures::{BaseBuilding, Ore, Stone, Structures},
units::{MinerUnit, Units}, units::{MinerUnit, Units},
}, },
@@ -14,7 +14,7 @@ use ratatui::layout::Rect;
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct BoardState { pub struct BoardState<'a> {
cells_area: Rect, cells_area: Rect,
pub cell_width: usize, pub cell_width: usize,
pub cell_height: usize, pub cell_height: usize,
@@ -29,11 +29,12 @@ pub struct BoardState {
focused_cell: FocusedCell, focused_cell: FocusedCell,
player_base_coords: (usize, usize), player_base_coords: (usize, usize),
enemy_base_coords: (usize, usize), enemy_base_coords: (usize, usize),
pub marking_cells: bool, // pub marking_cells: bool,
marked_cells: VecDeque<(usize, usize)>, // marked_cells: VecDeque<(usize, usize)>,
pub marked_cells: MarkedCells<'a>,
} }
impl BoardState { impl BoardState<'_> {
pub fn new( pub fn new(
area: &Rect, area: &Rect,
map_width: usize, map_width: usize,
@@ -105,8 +106,7 @@ impl BoardState {
cells.push(rows); cells.push(rows);
} }
let marking_cells: bool = false; let marked_cells: MarkedCells<'_> = MarkedCells::new();
let marked_cells: VecDeque<(usize, usize)> = VecDeque::new();
Self { Self {
cells_area, cells_area,
@@ -123,7 +123,6 @@ impl BoardState {
focused_cell, focused_cell,
player_base_coords, player_base_coords,
enemy_base_coords, enemy_base_coords,
marking_cells,
marked_cells, marked_cells,
} }
} }
@@ -136,17 +135,22 @@ impl BoardState {
&mut self.cells[row][col] &mut self.cells[row][col]
} }
pub fn get_marked_cells(&self) -> Vec<&CellWidget> { pub fn get_marked_cells_widgets(&self) -> VecDeque<&CellWidget> {
self.marked_cells self.marked_cells
.marked_cells
.iter() .iter()
.map(move |&(row, col)| &self.cells[row][col]) .map(move |&(row, col)| &self.cells[row][col])
.collect() .collect()
} }
pub fn toggle_marking(&mut self) { pub fn get_marked_cells(&self) -> VecDeque<(usize, usize)> {
self.marking_cells = !self.marking_cells; self.marked_cells.marked_cells.clone()
}
if self.marking_cells { pub fn toggle_marking(&mut self) {
self.marked_cells.marking_cells = !self.marked_cells.marking_cells;
if self.marked_cells.marking_cells {
self.start_marking_cells(); self.start_marking_cells();
} else { } else {
self.clear_marked_cells(); self.clear_marked_cells();
@@ -160,17 +164,27 @@ impl BoardState {
cell.set_marked(true); cell.set_marked(true);
} }
self.marked_cells.push_back((new_cell.0, new_cell.1)); self.marked_cells
.marked_cells
.push_back((new_cell.0, new_cell.1));
} }
pub fn undo_marked_cell(&mut self) { pub fn undo_marked_cell(&mut self) {
if self.marked_cells.len() < 2 { if self.marked_cells.marked_cells.len() < 2 {
return; return;
} }
let old: (usize, usize) = self.marked_cells[self.marked_cells.len() - 1]; let old: (usize, usize) =
let new: (usize, usize) = self.marked_cells[self.marked_cells.len() - 2]; self.marked_cells.marked_cells[self.marked_cells.marked_cells.len() - 1];
let old_is_unique: bool = self.marked_cells.iter().filter(|x| **x == old).count() == 1; let new: (usize, usize) =
self.marked_cells.marked_cells[self.marked_cells.marked_cells.len() - 2];
let old_is_unique: bool = self
.marked_cells
.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); let old_cell: &mut CellWidget = self.get_mut_cell(old.0, old.1).set_selected(false);
if old_is_unique { if old_is_unique {
@@ -179,7 +193,7 @@ impl BoardState {
self.get_mut_cell(new.0, new.1).set_selected(true); self.get_mut_cell(new.0, new.1).set_selected(true);
self.marked_cells.pop_back(); self.marked_cells.marked_cells.pop_back();
self.focused_cell.set_focused_cell(new); self.focused_cell.set_focused_cell(new);
} }
@@ -191,15 +205,15 @@ impl BoardState {
let cell: &mut CellWidget = self.get_mut_cell(row, col); let cell: &mut CellWidget = self.get_mut_cell(row, col);
cell.set_marked(true); cell.set_marked(true);
self.marked_cells.push_back((row, col)); self.marked_cells.marked_cells.push_back((row, col));
} }
pub fn clear_marked_cells(&mut self) { pub fn clear_marked_cells(&mut self) {
for (row, col) in self.marked_cells.clone() { for (row, col) in self.marked_cells.marked_cells.clone() {
self.get_mut_cell(row, col).set_marked(false); self.get_mut_cell(row, col).set_marked(false);
} }
self.marked_cells.clear(); self.marked_cells.marked_cells.clear();
} }
fn max_offset(map_size: usize, size: usize) -> usize { fn max_offset(map_size: usize, size: usize) -> usize {
@@ -285,7 +299,7 @@ impl BoardState {
.set_selected(false); .set_selected(false);
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.marked_cells.marking_cells {
self.set_marked_cell(new_cell); self.set_marked_cell(new_cell);
} }
} }
@@ -0,0 +1,19 @@
use crate::app::states::skirmish_states::units::Units;
use std::collections::VecDeque;
#[derive(Debug, Clone, PartialEq)]
pub struct MarkedCells<'a> {
pub marking_cells: bool,
pub marked_cells: VecDeque<(usize, usize)>,
pub selected_unit: &'a Option<Units>,
}
impl MarkedCells<'_> {
pub fn new() -> Self {
Self {
marking_cells: false,
marked_cells: VecDeque::new(),
selected_unit: &None,
}
}
}
+2
View File
@@ -1,6 +1,7 @@
mod board; mod board;
mod focused_cell; mod focused_cell;
mod game_mode; mod game_mode;
mod marked_cells;
mod offset; mod offset;
mod players; mod players;
pub mod structures; pub mod structures;
@@ -11,6 +12,7 @@ pub mod zoom_level;
pub use board::BoardState; pub use board::BoardState;
pub use focused_cell::{FocusedCell, MoveFocusedCell}; pub use focused_cell::{FocusedCell, MoveFocusedCell};
pub use game_mode::GameMode; pub use game_mode::GameMode;
pub use marked_cells::MarkedCells;
pub use offset::Offset; pub use offset::Offset;
pub use players::Players; pub use players::Players;
pub use zoom_level::{CellSizes, ZoomLevel}; pub use zoom_level::{CellSizes, ZoomLevel};
+1 -1
View File
@@ -13,7 +13,7 @@ pub enum View {
SkillsConfig, SkillsConfig,
} }
impl Widget for &mut App { impl Widget for &mut App<'_> {
fn render(self, area: Rect, buf: &mut Buffer) fn render(self, area: Rect, buf: &mut Buffer)
where where
Self: Sized, Self: Sized,
+3 -1
View File
@@ -33,7 +33,9 @@ fn main_menu_layout(area: Rect) -> [Rect; 2] {
} }
pub fn main_menu_view(app: &App, area: Rect, buf: &mut Buffer) { pub fn main_menu_view(app: &App, area: Rect, buf: &mut Buffer) {
let Some(states) = app.states() else { return }; let Some(states) = app.states_ref() else {
return;
};
let [main_menu_area, keybindings_area] = main_menu_layout(area); let [main_menu_area, keybindings_area] = main_menu_layout(area);
+4 -1
View File
@@ -56,7 +56,10 @@ pub fn skirmish_main_area_layout(area: Rect, side_panel: bool) -> [Rect; 2] {
} }
pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) { pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
let Some(states) = app.states() else { return }; let Some(states) = app.states_ref() else {
return;
};
let board: &BoardState = &states.skirmish.board; let board: &BoardState = &states.skirmish.board;
let [title_area, main_area, keybindings_area] = skirmish_layout(area); let [title_area, main_area, keybindings_area] = skirmish_layout(area);
+1 -1
View File
@@ -7,7 +7,7 @@ use ratatui::{
use std::rc::Rc; use std::rc::Rc;
pub struct BoardWidget<'a> { pub struct BoardWidget<'a> {
state: &'a BoardState, state: &'a BoardState<'a>,
} }
impl<'a> BoardWidget<'a> { impl<'a> BoardWidget<'a> {