diff --git a/src/app/app.rs b/src/app/app.rs
index 78a4bee..bf84680 100644
--- a/src/app/app.rs
+++ b/src/app/app.rs
@@ -60,7 +60,10 @@ impl App {
let Some(state) = self.states_mut() else {
panic!("State issue")
};
- state.skirmish.board.change_resize(&window_area);
+ state
+ .skirmish
+ .board
+ .change_resize(&window_area, state.skirmish.side_panel);
}
}
}
diff --git a/src/app/helpers/block_title.rs b/src/app/helpers/block_title.rs
index 515e03d..ab320d1 100644
--- a/src/app/helpers/block_title.rs
+++ b/src/app/helpers/block_title.rs
@@ -5,53 +5,19 @@ use ratatui::{
/// Creates a styled title `Line` for a UI block.
///
-/// This helper builds a `Line` that looks like:
-/// ```text
-/// [
... ]
-/// ```
-/// where each title fragment is rendered in the color supplied by the caller,
-/// and the surrounding brackets and any separator are rendered in gray.
-///
-/// # Arguments
-///
-/// * `texts` – A slice of tuples. Each tuple contains a title fragment (`&str`)
-/// and a `Color` that should be applied to that fragment. The order of the
-/// tuples determines the order of the fragments in the final line.
-///
-/// * `separator` – An optional string that will be inserted (in gray) between
-/// successive title fragments. If `None` the fragments are concatenated
-/// directly without any separator.
-///
-/// # Returns
-///
-/// A `Line<'a>` that starts with a gray “\[ ”, contains the colored title
-/// fragments separated (if requested) by a gray separator, and ends with a
-/// gray “ \]”. The line can be passed directly to widgets such as `Block::title`.
-///
-/// # Example
-///
-/// ```rust
-/// use war_in_tunnels::app::helpers::block_title::block_title_helper;
-/// use ratatui::style::Color;
-/// use ratatui::text::Line;
-///
-/// let title: Line<'_> = block_title_helper(
-/// &[
-/// ("Skirmish", Color::Magenta),
-/// ("Map", Color::Green),
-/// ],
-/// Some(" - ")
-/// );
-/// // `title` now represents: [ Skirmish - Map ] with the appropriate colors.
-/// ```
-pub fn block_title_helper<'a>(texts: &[(&'a str, Color)], separator: Option<&'a str>) -> Line<'a> {
- let mut line: Line<'a> = Line::default();
+/// `texts` is a slice of `(String, Color)` tuples. The `String`s are cloned into
+/// the spans, so the returned `Line` owns all its data and does not borrow from
+/// the caller.
+pub fn block_title_helper(
+ texts: &[(String, Color)],
+ separator: Option<&'static str>,
+) -> Line<'static> {
+ let mut line: Line<'static> = Line::default();
line.spans.push(Span::from("[ ").gray());
for (i, (text, color)) in texts.iter().enumerate() {
- let span: Span<'a> = Span::styled(*text, Style::new().fg(*color));
-
+ let span = Span::styled(text.clone(), Style::new().fg(*color));
line.spans.push(span);
if let Some(sep) = separator {
@@ -66,38 +32,11 @@ pub fn block_title_helper<'a>(texts: &[(&'a str, Color)], separator: Option<&'a
line
}
-/// Convenience wrapper for `block_title_helper` that creates a title line for a
-/// single piece of text.
+/// Convenience wrapper for a single‑title line.
///
-/// This function builds a `Line` that looks like:
-/// ```text
-/// [ ]
-/// ```
-/// where `` is rendered with the supplied color, and the surrounding
-/// brackets are rendered in gray. It simply forwards the arguments to
-/// `block_title_helper` with `separator` set to `None`.
-///
-/// # Arguments
-///
-/// * `text` – The title string to display.
-///
-/// * `color` – The `Color` that should be applied to the title text.
-///
-/// # Returns
-///
-/// A `Line<'a>` containing the grey brackets and the coloured title text,
-/// ready to be passed to widgets such as `Block::title`.
-///
-/// # Example
-///
-/// ```rust
-/// use war_in_tunnels::app::helpers::block_title::block_single_title_helper;
-/// use ratatui::style::Color;
-/// use ratatui::text::Line;
-///
-/// let title: Line<'_> = block_single_title_helper("Keybindings", Color::Magenta);
-/// // `title` now represents: [ Keybindings ] with “Keybindings” coloured magenta.
-/// ```
-pub fn block_single_title_helper<'a>(text: &'a str, color: Color) -> Line<'a> {
+/// The function builds a slice that contains the supplied `String` and then
+/// forwards it to `block_title_helper`. Because `block_title_helper` clones the
+/// string, the temporary slice is safe.
+pub fn block_single_title_helper(text: String, color: Color) -> Line<'static> {
block_title_helper(&[(text, color)], None)
}
diff --git a/src/app/helpers/cells_area.rs b/src/app/helpers/cells_area.rs
index aed40d2..1598c90 100644
--- a/src/app/helpers/cells_area.rs
+++ b/src/app/helpers/cells_area.rs
@@ -1,13 +1,15 @@
-use crate::app::views::skirmish_layout;
+use crate::app::views::{skirmish_layout, skirmish_main_area_layout};
use ratatui::{
layout::{Margin, Rect},
widgets::{Block, Borders},
};
-pub fn cells_area_helper(area: &Rect) -> Rect {
+pub fn cells_area_helper(area: &Rect, side_panel: bool) -> Rect {
+ let board_area: Rect = skirmish_main_area_layout(skirmish_layout(*area)[1], side_panel)[0];
+
Block::new()
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT)
- .inner(skirmish_layout(*area)[1])
+ .inner(board_area)
.inner(Margin {
horizontal: 1,
vertical: 1,
diff --git a/src/app/keybindings/keybindings.rs b/src/app/keybindings/keybindings.rs
index c93d691..f9c223c 100644
--- a/src/app/keybindings/keybindings.rs
+++ b/src/app/keybindings/keybindings.rs
@@ -43,6 +43,10 @@ pub enum Action {
ZoomIn,
/// Zoom the view out.
ZoomOut,
+ /// Open side panel.
+ Tab,
+ /// Toggle skirmish board/skills view.
+ ShiftTab,
/// Mute music.
Mute,
/// Volume up.
@@ -71,6 +75,8 @@ pub enum Group {
Zoom,
/// Music related bindings.
Music,
+ /// Opens/Closes/Toggles elements in current view.
+ Opener,
/// Quit related bindings.
Quit,
}
@@ -282,6 +288,24 @@ static KEYBINDINGS: &[KeyBinding] = &[
symbol: "n",
description: "🔉",
},
+ KeyBinding {
+ action: Action::Tab,
+ code: KeyCode::Tab,
+ kind: KeyEventKind::Press,
+ modifiers: KeyModifiers::NONE,
+ group: Group::Opener,
+ symbol: "Tab",
+ description: "Toggle side panel",
+ },
+ KeyBinding {
+ action: Action::ShiftTab,
+ code: KeyCode::Tab,
+ kind: KeyEventKind::Press,
+ modifiers: KeyModifiers::SHIFT,
+ group: Group::Opener,
+ symbol: "Shift + Tab",
+ description: "Toggle Map/Skills window",
+ },
KeyBinding {
action: Action::WildCard('_'),
code: KeyCode::Char('_'),
diff --git a/src/app/keybindings/skirmish.rs b/src/app/keybindings/skirmish.rs
index 8418376..044208f 100644
--- a/src/app/keybindings/skirmish.rs
+++ b/src/app/keybindings/skirmish.rs
@@ -3,12 +3,14 @@ use crate::app::{
keybindings::{Action, common_keybindings, event_to_action},
states::skirmish_states::{BoardState, MoveFocusedCell, ZoomLevel},
};
-use ratatui::crossterm::event::KeyEvent;
+use ratatui::{crossterm::event::KeyEvent, layout::Rect};
pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) {
if let Some(action) = event_to_action(&key_event) {
common_keybindings(app, action);
+ let window_area: Rect = app.window_area;
+
let Some(states) = app.states_mut() else {
return;
};
@@ -86,6 +88,10 @@ pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) {
board.marking_cells = false;
board.clear_marked_cells()
}
+ Action::Tab => {
+ states.skirmish.side_panel = !states.skirmish.side_panel;
+ board.change_resize(&window_area, states.skirmish.side_panel);
+ }
_ => (),
}
}
diff --git a/src/app/state.rs b/src/app/state.rs
index ae98cb8..870a269 100644
--- a/src/app/state.rs
+++ b/src/app/state.rs
@@ -32,7 +32,9 @@ impl GameStates {
args.map_width as usize,
args.map_height as usize,
args.zoom_level,
+ false,
),
+ side_panel: false,
},
perk_decks: PerkDecksState {
id: 2,
diff --git a/src/app/states/skirmish.rs b/src/app/states/skirmish.rs
index 41737bd..2bd381b 100644
--- a/src/app/states/skirmish.rs
+++ b/src/app/states/skirmish.rs
@@ -6,6 +6,7 @@ pub struct SkirmishState {
pub id: usize,
pub name: &'static str,
pub board: BoardState,
+ pub side_panel: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
diff --git a/src/app/states/skirmish_states/board.rs b/src/app/states/skirmish_states/board.rs
index b793a4f..dea56fb 100644
--- a/src/app/states/skirmish_states/board.rs
+++ b/src/app/states/skirmish_states/board.rs
@@ -33,8 +33,14 @@ pub struct BoardState {
}
impl BoardState {
- pub fn new(area: &Rect, map_width: usize, map_height: usize, zoom_level: ZoomLevel) -> Self {
- let cells_area: Rect = cells_area_helper(area);
+ pub fn new(
+ area: &Rect,
+ map_width: usize,
+ map_height: usize,
+ zoom_level: ZoomLevel,
+ side_panel: bool,
+ ) -> Self {
+ let cells_area: Rect = cells_area_helper(area, side_panel);
let cell_width: usize = zoom_level.get_cell_size(CellSizes::Width);
let cell_height: usize = zoom_level.get_cell_size(CellSizes::Height);
@@ -114,6 +120,10 @@ impl BoardState {
}
}
+ pub fn get_ref_cell(&self, row: usize, col: usize) -> &CellWidget {
+ &self.cells[row][col]
+ }
+
fn get_mut_cell(&mut self, row: usize, col: usize) -> &mut CellWidget {
&mut self.cells[row][col]
}
@@ -188,6 +198,10 @@ impl BoardState {
if map_size > size { map_size - size } else { 0 }
}
+ pub fn get_focused_cell(&self) -> &FocusedCell {
+ &self.focused_cell
+ }
+
pub fn is_focused_cell_visible(&self) -> bool {
let vertical_offset: usize = self.vertical_offset.get_value();
let horizontal_offset: usize = self.horizontal_offset.get_value();
@@ -209,14 +223,14 @@ impl BoardState {
true
}
- pub fn change_resize(&mut self, area: &Rect) {
- self.cells_area = cells_area_helper(area);
+ pub fn change_resize(&mut self, area: &Rect, side_panel: bool) {
+ self.cells_area = cells_area_helper(area, side_panel);
self.cols = (self.cells_area.width / self.cell_width as u16) as usize;
self.rows = (self.cells_area.height / self.cell_height as u16) as usize;
- let h_max_offset: usize = Self::max_offset(self.map_height, self.cols);
- let v_max_offset: usize = Self::max_offset(self.map_width, self.rows);
+ let v_max_offset: usize = Self::max_offset(self.map_height, self.rows);
+ let h_max_offset: usize = Self::max_offset(self.map_width, self.cols);
self.horizontal_offset =
Offset::new(Some(self.horizontal_offset.get_value()), Some(h_max_offset));
diff --git a/src/app/states/skirmish_states/structures/base.rs b/src/app/states/skirmish_states/structures/base.rs
index 4742e58..7ffa4d3 100644
--- a/src/app/states/skirmish_states/structures/base.rs
+++ b/src/app/states/skirmish_states/structures/base.rs
@@ -47,4 +47,8 @@ impl Structure for BaseBuilding {
fn get_stress(&self) -> u8 {
self.stress
}
+
+ fn get_name(&self) -> &'static str {
+ "Base"
+ }
}
diff --git a/src/app/states/skirmish_states/structures/stone.rs b/src/app/states/skirmish_states/structures/stone.rs
index 901889a..27a096c 100644
--- a/src/app/states/skirmish_states/structures/stone.rs
+++ b/src/app/states/skirmish_states/structures/stone.rs
@@ -36,4 +36,8 @@ impl Structure for Stone {
fn get_stress(&self) -> u8 {
self.stress
}
+
+ fn get_name(&self) -> &'static str {
+ "Stone"
+ }
}
diff --git a/src/app/states/skirmish_states/structures/structures_enum.rs b/src/app/states/skirmish_states/structures/structures_enum.rs
index 350440a..c4dc44b 100644
--- a/src/app/states/skirmish_states/structures/structures_enum.rs
+++ b/src/app/states/skirmish_states/structures/structures_enum.rs
@@ -8,28 +8,52 @@ pub enum Structures {
Stone(Stone),
}
-impl Structures {
- pub fn get_color(&self) -> Color {
+impl Structure for Structures {
+ fn get_color(&self) -> Color {
match self {
- Structures::Base(b) => b.get_color(),
- Structures::Tunnel(t) => t.get_color(),
- Structures::Stone(s) => s.get_color(),
+ Self::Base(b) => b.get_color(),
+ Self::Tunnel(t) => t.get_color(),
+ Self::Stone(s) => s.get_color(),
}
}
- pub fn get_tag(&self) -> char {
+ fn get_tag(&self) -> char {
match self {
- Structures::Base(b) => b.get_tag(),
- Structures::Tunnel(t) => t.get_tag(),
- Structures::Stone(s) => s.get_tag(),
+ Self::Base(b) => b.get_tag(),
+ Self::Tunnel(t) => t.get_tag(),
+ Self::Stone(s) => s.get_tag(),
}
}
- pub fn get_level(&self) -> char {
+ fn get_level(&self) -> char {
match self {
- Structures::Base(b) => b.get_level(),
- Structures::Tunnel(t) => t.get_level(),
- Structures::Stone(s) => s.get_level(),
+ Self::Base(b) => b.get_level(),
+ Self::Tunnel(t) => t.get_level(),
+ Self::Stone(s) => s.get_level(),
+ }
+ }
+
+ fn get_name(&self) -> &'static str {
+ match self {
+ Self::Base(b) => b.get_name(),
+ Self::Tunnel(t) => t.get_name(),
+ Self::Stone(s) => s.get_name(),
+ }
+ }
+
+ fn get_durability(&self) -> u16 {
+ match self {
+ Self::Base(b) => b.get_durability(),
+ Self::Tunnel(t) => t.get_durability(),
+ Self::Stone(s) => s.get_durability(),
+ }
+ }
+
+ fn get_stress(&self) -> u8 {
+ match self {
+ Self::Base(b) => b.get_stress(),
+ Self::Tunnel(t) => t.get_stress(),
+ Self::Stone(s) => s.get_stress(),
}
}
}
diff --git a/src/app/states/skirmish_states/structures/structures_trait.rs b/src/app/states/skirmish_states/structures/structures_trait.rs
index f911a9f..39d6b2e 100644
--- a/src/app/states/skirmish_states/structures/structures_trait.rs
+++ b/src/app/states/skirmish_states/structures/structures_trait.rs
@@ -6,4 +6,5 @@ pub trait Structure {
fn get_level(&self) -> char;
fn get_stress(&self) -> u8;
fn get_durability(&self) -> u16;
+ fn get_name(&self) -> &'static str;
}
diff --git a/src/app/states/skirmish_states/structures/tunnel.rs b/src/app/states/skirmish_states/structures/tunnel.rs
index 547d1e8..129da67 100644
--- a/src/app/states/skirmish_states/structures/tunnel.rs
+++ b/src/app/states/skirmish_states/structures/tunnel.rs
@@ -42,4 +42,8 @@ impl Structure for Tunnel {
fn get_stress(&self) -> u8 {
self.stress
}
+
+ fn get_name(&self) -> &'static str {
+ "Tunnel"
+ }
}
diff --git a/src/app/states/skirmish_states/units/miner.rs b/src/app/states/skirmish_states/units/miner.rs
index 06f2031..a306e41 100644
--- a/src/app/states/skirmish_states/units/miner.rs
+++ b/src/app/states/skirmish_states/units/miner.rs
@@ -19,4 +19,8 @@ impl Unit for MinerUnit {
fn get_tag(&self) -> char {
'M'
}
+
+ fn get_name(&self) -> &'static str {
+ "Miner"
+ }
}
diff --git a/src/app/states/skirmish_states/units/mod.rs b/src/app/states/skirmish_states/units/mod.rs
index 491ebe6..fc493ca 100644
--- a/src/app/states/skirmish_states/units/mod.rs
+++ b/src/app/states/skirmish_states/units/mod.rs
@@ -4,4 +4,4 @@ mod units_trait;
pub use miner::MinerUnit;
pub use units_enum::Units;
-pub use units_trait::{OptionalUnit, Unit};
+pub use units_trait::Unit;
diff --git a/src/app/states/skirmish_states/units/units_enum.rs b/src/app/states/skirmish_states/units/units_enum.rs
index 951426d..4fef7f1 100644
--- a/src/app/states/skirmish_states/units/units_enum.rs
+++ b/src/app/states/skirmish_states/units/units_enum.rs
@@ -1,20 +1,43 @@
-use crate::app::states::skirmish_states::units::{MinerUnit, OptionalUnit, Unit};
+use crate::app::states::{
+ Players,
+ skirmish_states::units::{MinerUnit, Unit},
+};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Units {
Miner(MinerUnit),
}
-impl Units {
- pub fn get_tag(&self) -> char {
+impl Unit for Units {
+ fn get_tag(&self) -> char {
match self {
- Units::Miner(m) => m.get_tag(),
+ Self::Miner(m) => m.get_tag(),
+ }
+ }
+
+ fn get_name(&self) -> &'static str {
+ match self {
+ Self::Miner(m) => m.get_name(),
+ }
+ }
+
+ fn get_owner(&self) -> Players {
+ match self {
+ Self::Miner(m) => m.get_owner(),
}
}
}
-impl OptionalUnit for Option {
- fn try_get_tag(&self) -> char {
+impl Unit for Option {
+ fn get_tag(&self) -> char {
self.map_or(' ', |u| u.get_tag())
}
+
+ fn get_name(&self) -> &'static str {
+ self.map_or("", |u| u.get_name())
+ }
+
+ fn get_owner(&self) -> Players {
+ self.map_or(Players::Enemy, |u| u.get_owner())
+ }
}
diff --git a/src/app/states/skirmish_states/units/units_trait.rs b/src/app/states/skirmish_states/units/units_trait.rs
index 1641fc8..272f57c 100644
--- a/src/app/states/skirmish_states/units/units_trait.rs
+++ b/src/app/states/skirmish_states/units/units_trait.rs
@@ -3,8 +3,5 @@ use crate::app::states::Players;
pub trait Unit {
fn get_owner(&self) -> Players;
fn get_tag(&self) -> char;
-}
-
-pub trait OptionalUnit {
- fn try_get_tag(&self) -> char;
+ fn get_name(&self) -> &'static str;
}
diff --git a/src/app/views/mod.rs b/src/app/views/mod.rs
index d62764a..9fd4adc 100644
--- a/src/app/views/mod.rs
+++ b/src/app/views/mod.rs
@@ -4,4 +4,4 @@ mod skirmish;
pub use default::default_view;
pub use main_menu::main_menu_view;
-pub use skirmish::{skirmish_layout, skirmish_view};
+pub use skirmish::{skirmish_layout, skirmish_main_area_layout, skirmish_view};
diff --git a/src/app/views/skirmish.rs b/src/app/views/skirmish.rs
index 3f766bb..446261e 100644
--- a/src/app/views/skirmish.rs
+++ b/src/app/views/skirmish.rs
@@ -2,7 +2,8 @@ use crate::app::{
App,
helpers::block_title_helper,
keybindings::{Action, count_largest_group},
- widgets::{BoardWidget, KeybindingsWidget},
+ states::skirmish_states::{structures::Structure, units::Unit},
+ widgets::{BoardWidget, KeybindingsWidget, SidePanelWidget},
};
use ratatui::{
buffer::Buffer,
@@ -29,6 +30,7 @@ const ACTIONS: &[Action] = &[
Action::Backspace,
Action::Delete,
Action::Mute,
+ Action::Tab,
Action::Quit,
Action::Quit2,
Action::Esc,
@@ -43,6 +45,16 @@ pub fn skirmish_layout(area: Rect) -> [Rect; 3] {
.areas(area)
}
+pub fn skirmish_main_area_layout(area: Rect, side_panel: bool) -> [Rect; 2] {
+ let constraint: [Constraint; 2] = if side_panel {
+ [Constraint::Percentage(80), Constraint::Percentage(20)]
+ } else {
+ [Constraint::Percentage(100), Constraint::Percentage(0)]
+ };
+
+ Layout::horizontal(constraint).areas(area)
+}
+
pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
let Some(states) = app.states() else { return };
@@ -80,16 +92,22 @@ pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
}
{
- let board_block: Block = Block::new()
+ let main_block: Block = Block::new()
.gray()
.title(block_title_helper(
- &[("Skirmish", Color::Magenta), ("Map", Color::Green)],
+ &[
+ ("Skirmish".to_string(), Color::Magenta),
+ ("Map".to_string(), Color::Green),
+ ],
Some(" - "),
))
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT);
- let board_area: Rect = board_block.inner(main_area);
- board_block.render(main_area, buf);
+ let main_inner_area: Rect = main_block.inner(main_area);
+ main_block.render(main_area, buf);
+
+ let [board_area, side_panel_area] =
+ skirmish_main_area_layout(main_inner_area, states.skirmish.side_panel);
let cells_area: Rect = board_area.inner(Margin {
horizontal: 1,
@@ -105,6 +123,26 @@ pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
// );
BoardWidget::new(&states.skirmish.board).render(cells_area, buf);
+
+ if states.skirmish.side_panel {
+ let row: usize = states.skirmish.board.get_focused_cell().get_row();
+ let col: usize = states.skirmish.board.get_focused_cell().get_col();
+ let coords: (usize, usize) = (row, col);
+ let structure_name: &str = states
+ .skirmish
+ .board
+ .get_ref_cell(row, col)
+ .get_structure()
+ .get_name();
+ let unit_name: &str = states
+ .skirmish
+ .board
+ .get_ref_cell(row, col)
+ .get_unit_option()
+ .get_name();
+
+ SidePanelWidget::new(coords, structure_name, unit_name).render(side_panel_area, buf);
+ }
}
{
diff --git a/src/app/widgets/cell.rs b/src/app/widgets/cell.rs
index fa57896..9619e9f 100644
--- a/src/app/widgets/cell.rs
+++ b/src/app/widgets/cell.rs
@@ -1,14 +1,14 @@
use crate::app::states::skirmish_states::{
ZoomLevel,
- structures::{Stone, Structures},
- units::{OptionalUnit, Units},
+ structures::{Stone, Structure, Structures},
+ units::{Unit, Units},
};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Rect},
style::{Color, Style, Stylize},
text::{Line, Span, ToSpan},
- widgets::{Block, Borders, Paragraph, Widget},
+ widgets::{Block, BorderType, Borders, Paragraph, Widget},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -52,6 +52,14 @@ impl CellWidget {
self
}
+ pub fn get_structure(&self) -> Structures {
+ self.structure
+ }
+
+ pub fn get_unit_option(&self) -> Option {
+ self.unit
+ }
+
pub fn set_structure(&mut self, sctructure: Structures) -> &mut Self {
self.structure = sctructure;
self
@@ -102,7 +110,7 @@ impl CellWidget {
self.zoom_level.get_cell_text_area(
self.structure.get_tag(),
self.structure.get_level(),
- self.unit.try_get_tag(),
+ self.unit.get_tag(),
)
}
@@ -111,6 +119,7 @@ impl CellWidget {
.borders(Borders::ALL)
.style(Style::default().fg(self.fg_color()))
.title(self.display_coords())
+ .border_type(BorderType::Rounded)
}
}
diff --git a/src/app/widgets/keybindings.rs b/src/app/widgets/keybindings.rs
index 8760c39..6829b1e 100644
--- a/src/app/widgets/keybindings.rs
+++ b/src/app/widgets/keybindings.rs
@@ -115,9 +115,13 @@ impl Widget for KeybindingsWidget {
return;
}
- let block: Block<'_> = Block::default()
- .borders(Borders::ALL)
- .title(block_single_title_helper("Keybindings", Color::Magenta));
+ let block: Block<'_> =
+ Block::default()
+ .borders(Borders::ALL)
+ .title(block_single_title_helper(
+ "Keybindings".to_string(),
+ Color::Magenta,
+ ));
let inner: Rect = block.inner(area);
diff --git a/src/app/widgets/mod.rs b/src/app/widgets/mod.rs
index e6a0ef5..e414e61 100644
--- a/src/app/widgets/mod.rs
+++ b/src/app/widgets/mod.rs
@@ -1,7 +1,9 @@
mod board;
mod cell;
mod keybindings;
+mod side_panel;
pub use board::BoardWidget;
pub use cell::CellWidget;
pub use keybindings::KeybindingsWidget;
+pub use side_panel::SidePanelWidget;
diff --git a/src/app/widgets/side_panel.rs b/src/app/widgets/side_panel.rs
new file mode 100644
index 0000000..938829f
--- /dev/null
+++ b/src/app/widgets/side_panel.rs
@@ -0,0 +1,74 @@
+use ratatui::{
+ buffer::Buffer,
+ layout::{Alignment, Rect},
+ style::Color,
+ text::Line,
+ widgets::{Block, BorderType, Borders, Paragraph, Widget},
+};
+
+use crate::app::helpers::block_title_helper;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SidePanelWidget {
+ coords: (usize, usize),
+ structure_name: &'static str,
+ unit_name: &'static str,
+}
+
+impl SidePanelWidget {
+ pub fn new(
+ coords: (usize, usize),
+ structure_name: &'static str,
+ unit_name: &'static str,
+ ) -> Self {
+ Self {
+ coords,
+ structure_name,
+ unit_name,
+ }
+ }
+
+ fn col_to_letters(&self) -> String {
+ let mut col: usize = self.coords.1 + 1;
+ let mut letters: Vec = Vec::new();
+
+ while col > 0 {
+ letters.push((b'A' + ((col - 1) % 26) as u8) as char);
+ col = (col - 1) / 26;
+ }
+
+ letters.iter().rev().collect()
+ }
+
+ fn get_title(&self) -> Line<'_> {
+ let cell_coords: String = format!("{}{}", self.col_to_letters(), self.coords.0);
+
+ let mut texts: Vec<(String, Color)> = Vec::with_capacity(3);
+
+ texts.push((cell_coords, Color::Yellow));
+ texts.push((self.structure_name.to_string(), Color::Cyan));
+
+ if !self.unit_name.is_empty() {
+ texts.push((self.unit_name.to_string(), Color::Green));
+ }
+
+ block_title_helper(&texts, Some(" - "))
+ }
+
+ fn get_block(&self) -> Block<'_> {
+ Block::default()
+ .borders(Borders::ALL)
+ .border_type(BorderType::Double)
+ .title(self.get_title())
+ .title_alignment(Alignment::Center)
+ }
+}
+
+impl Widget for SidePanelWidget {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ Paragraph::default()
+ .alignment(Alignment::Center)
+ .block(self.get_block())
+ .render(area, buf);
+ }
+}