generated from GarandPLG/rust-flake-template
Add side panel and refactor title helpers
Introduce a `side_panel` flag throughout the skirmish state, board creation, and cell area calculation to enable a detachable side panel. Refactor the title helper to own its strings and use a static separator, updating the single‑title helper accordingly. Add a new `SidePanelWidget` and expose the `skirmish_main_area_layout` helper. Extend keybindings with `Tab` and `ShiftTab` actions under a new `Opener` group. Update structures and units to implement a `get_name` method and adjust related traits and imports.
This commit is contained in:
+4
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,53 +5,19 @@ use ratatui::{
|
||||
|
||||
/// Creates a styled title `Line` for a UI block.
|
||||
///
|
||||
/// This helper builds a `Line` that looks like:
|
||||
/// ```text
|
||||
/// [ <title‑1> <separator> <title‑2> ... ]
|
||||
/// ```
|
||||
/// 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
|
||||
/// [ <text> ]
|
||||
/// ```
|
||||
/// where `<text>` 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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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('_'),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -47,4 +47,8 @@ impl Structure for BaseBuilding {
|
||||
fn get_stress(&self) -> u8 {
|
||||
self.stress
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Base"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,8 @@ impl Structure for Stone {
|
||||
fn get_stress(&self) -> u8 {
|
||||
self.stress
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Stone"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -42,4 +42,8 @@ impl Structure for Tunnel {
|
||||
fn get_stress(&self) -> u8 {
|
||||
self.stress
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Tunnel"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,8 @@ impl Unit for MinerUnit {
|
||||
fn get_tag(&self) -> char {
|
||||
'M'
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Miner"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Units> {
|
||||
fn try_get_tag(&self) -> char {
|
||||
impl Unit for Option<Units> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+13
-4
@@ -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<Units> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<char> = 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user