Can show legend on game map
This commit is contained in:
		@@ -140,7 +140,7 @@ pub fn configure_play_rules<B: Backend>(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut GameRulesConfigurationScreen) {
 | 
			
		||||
    let area = centered_rect_size(50, 16, f.size());
 | 
			
		||||
    let area = centered_rect_size(50, 16, &f.size());
 | 
			
		||||
 | 
			
		||||
    let block = Block::default().title("Game rules").borders(Borders::ALL);
 | 
			
		||||
    f.render_widget(block, area);
 | 
			
		||||
@@ -218,7 +218,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, model: &mut GameRulesConfigurationScreen) {
 | 
			
		||||
 | 
			
		||||
    // Error message (if any)
 | 
			
		||||
    if let Some(msg) = model.rules.get_errors().first() {
 | 
			
		||||
        let area = centered_rect_size(msg.len() as u16, 1, *chunks.last().unwrap());
 | 
			
		||||
        let area = centered_rect_size(msg.len() as u16, 1, chunks.last().unwrap());
 | 
			
		||||
        let err = Paragraph::new(*msg).style(Style::default().fg(Color::Red));
 | 
			
		||||
        f.render_widget(err, area);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ pub fn select_bot_type<B: Backend>(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SelectPlayModeScreen) {
 | 
			
		||||
    let area = centered_rect_size(60, model.types.len() as u16 * 2 + 2, f.size());
 | 
			
		||||
    let area = centered_rect_size(60, model.types.len() as u16 * 2 + 2, &f.size());
 | 
			
		||||
 | 
			
		||||
    // Create a List from all list items and highlight the currently selected one
 | 
			
		||||
    let items = model
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ pub fn select_play_mode<B: Backend>(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SelectPlayModeScreen) {
 | 
			
		||||
    let area = centered_rect_size(50, 5, f.size());
 | 
			
		||||
    let area = centered_rect_size(50, 5, &f.size());
 | 
			
		||||
 | 
			
		||||
    // Create a List from all list items and highlight the currently selected one
 | 
			
		||||
    let items = AVAILABLE_PLAY_MODES
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,6 @@ use crossterm::event;
 | 
			
		||||
use crossterm::event::{Event, KeyCode};
 | 
			
		||||
use tui::backend::Backend;
 | 
			
		||||
use tui::style::*;
 | 
			
		||||
use tui::text::*;
 | 
			
		||||
use tui::widgets::*;
 | 
			
		||||
use tui::{Frame, Terminal};
 | 
			
		||||
 | 
			
		||||
use sea_battle_backend::data::*;
 | 
			
		||||
@@ -14,7 +12,7 @@ use sea_battle_backend::data::*;
 | 
			
		||||
use crate::constants::*;
 | 
			
		||||
use crate::ui_screens::utils::centered_rect_size;
 | 
			
		||||
use crate::ui_screens::ScreenResult;
 | 
			
		||||
use crate::ui_widgets::game_map_widget::GameMapWidget;
 | 
			
		||||
use crate::ui_widgets::game_map_widget::{ColoredCells, GameMapWidget};
 | 
			
		||||
 | 
			
		||||
struct SetBotsLayoutScreen {
 | 
			
		||||
    curr_boat: usize,
 | 
			
		||||
@@ -48,8 +46,7 @@ pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
                            return Ok(ScreenResult::Ok(model.layout));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    KeyCode::Down => model.curr_boat -= 1,
 | 
			
		||||
                    KeyCode::Up => model.curr_boat += model.layout.number_of_boats() - 1,
 | 
			
		||||
                    KeyCode::Char('n') => model.curr_boat += model.layout.number_of_boats() - 1,
 | 
			
		||||
                    _ => {}
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -63,13 +60,30 @@ pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SetBotsLayoutScreen, rules: &GameRules) {
 | 
			
		||||
    let area = centered_rect_size(
 | 
			
		||||
        rules.map_width as u16 + 10,
 | 
			
		||||
        rules.map_height as u16 + 10,
 | 
			
		||||
        f.size(),
 | 
			
		||||
    );
 | 
			
		||||
    let current_boat = ColoredCells {
 | 
			
		||||
        color: Color::Green,
 | 
			
		||||
        cells: model.layout.0[model.curr_boat].all_coordinates(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let game_map_widget = GameMapWidget::new(rules);
 | 
			
		||||
    let mut other_boats_cells = vec![];
 | 
			
		||||
    for boat in &model.layout.0 {
 | 
			
		||||
        for pos in boat.all_coordinates() {
 | 
			
		||||
            other_boats_cells.push(pos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let other_boats = ColoredCells {
 | 
			
		||||
        color: Color::DarkGray,
 | 
			
		||||
        cells: other_boats_cells,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let game_map_widget = GameMapWidget::new(rules)
 | 
			
		||||
        .set_default_empty_char(' ')
 | 
			
		||||
        .add_colored_cells(current_boat)
 | 
			
		||||
        .add_colored_cells(other_boats)
 | 
			
		||||
        .set_legend("n next boat \nr rotate boat \n\n↓ ↑ → ← move boat \n\nEnter confirm layout");
 | 
			
		||||
 | 
			
		||||
    let (w, h) = game_map_widget.estimated_size();
 | 
			
		||||
    let area = centered_rect_size(w, h, &f.size());
 | 
			
		||||
    f.render_widget(game_map_widget, area);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ pub fn centered_rect_percentage(percent_x: u16, percent_y: u16, r: Rect) -> Rect
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// helper function to create a centered rect using up certain container size
 | 
			
		||||
pub fn centered_rect_size(width: u16, height: u16, parent: Rect) -> Rect {
 | 
			
		||||
pub fn centered_rect_size(width: u16, height: u16, parent: &Rect) -> Rect {
 | 
			
		||||
    if parent.width < width || parent.height < height {
 | 
			
		||||
        return Rect {
 | 
			
		||||
            x: parent.x,
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ impl Widget for ButtonWidget {
 | 
			
		||||
    fn render(self, area: Rect, buf: &mut Buffer) {
 | 
			
		||||
        let label = format!(" {} ", self.label);
 | 
			
		||||
 | 
			
		||||
        let area = centered_rect_size(label.len() as u16, 1, area);
 | 
			
		||||
        let area = centered_rect_size(label.len() as u16, 1, &area);
 | 
			
		||||
 | 
			
		||||
        let input = Paragraph::new(label.as_ref()).style(match (self.disabled, self.is_hovered) {
 | 
			
		||||
            (true, _) => Style::default(),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,63 @@
 | 
			
		||||
use sea_battle_backend::data::{GameRules, PlayConfiguration};
 | 
			
		||||
use crate::ui_screens::utils::centered_rect_size;
 | 
			
		||||
use sea_battle_backend::data::{Coordinates, GameRules, PlayConfiguration};
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
use tui::buffer::Buffer;
 | 
			
		||||
use tui::layout::Rect;
 | 
			
		||||
use tui::style::{Color, Style};
 | 
			
		||||
use tui::widgets::{BorderType, Widget};
 | 
			
		||||
 | 
			
		||||
pub struct ColoredCells {
 | 
			
		||||
    pub color: Color,
 | 
			
		||||
    pub cells: Vec<Coordinates>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct GameMapWidget<'a> {
 | 
			
		||||
    rules: &'a GameRules,
 | 
			
		||||
    default_empty_character: char,
 | 
			
		||||
    colored_cells: Vec<ColoredCells>,
 | 
			
		||||
    legend: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> GameMapWidget<'a> {
 | 
			
		||||
    pub fn new(rules: &'a GameRules) -> Self {
 | 
			
		||||
        Self { rules }
 | 
			
		||||
        Self {
 | 
			
		||||
            rules,
 | 
			
		||||
            default_empty_character: '.',
 | 
			
		||||
            colored_cells: vec![],
 | 
			
		||||
            legend: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_default_empty_char(mut self, c: char) -> Self {
 | 
			
		||||
        self.default_empty_character = c;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_colored_cells(mut self, c: ColoredCells) -> Self {
 | 
			
		||||
        self.colored_cells.push(c);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_legend<D: Display>(mut self, legend: D) -> Self {
 | 
			
		||||
        self.legend = Some(legend.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn grid_size(&self) -> (u16, u16) {
 | 
			
		||||
        let w = self.rules.map_width as u16 * 2 + 1;
 | 
			
		||||
        let h = self.rules.map_height as u16 * 2 + 1;
 | 
			
		||||
 | 
			
		||||
        (w, h)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn estimated_size(&self) -> (u16, u16) {
 | 
			
		||||
        let (w, mut h) = self.grid_size();
 | 
			
		||||
 | 
			
		||||
        if let Some(l) = &self.legend {
 | 
			
		||||
            h += 1 + l.split('\n').count() as u16;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (w, h)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -22,8 +70,9 @@ impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
        // Paint game grid
 | 
			
		||||
        for y in 0..(self.rules.map_height + 1) {
 | 
			
		||||
            for x in 0..(self.rules.map_width + 1) {
 | 
			
		||||
                let o_x = x as u16 * 2;
 | 
			
		||||
                let o_y = y as u16 * 2;
 | 
			
		||||
                let o_x = area.x + (x as u16 * 2);
 | 
			
		||||
                let o_y = area.y + (y as u16 * 2);
 | 
			
		||||
 | 
			
		||||
                buf.get_mut(o_x, o_y).set_symbol(match (x, y) {
 | 
			
		||||
                    (0, 0) => symbols.top_left,
 | 
			
		||||
 | 
			
		||||
@@ -52,9 +101,32 @@ impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if x < self.rules.map_width && y < self.rules.map_height {
 | 
			
		||||
                    buf.get_mut(o_x + 1, o_y + 1).set_char('.');
 | 
			
		||||
                    let cell = buf
 | 
			
		||||
                        .get_mut(o_x + 1, o_y + 1)
 | 
			
		||||
                        .set_char(self.default_empty_character);
 | 
			
		||||
 | 
			
		||||
                    if let Some(c) = self
 | 
			
		||||
                        .colored_cells
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .find(|c| c.cells.contains(&Coordinates::new(x as i32, y as i32)))
 | 
			
		||||
                    {
 | 
			
		||||
                        cell.set_bg(c.color);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Paint legend (if any)
 | 
			
		||||
        if let Some(legend) = &self.legend {
 | 
			
		||||
            let (_, mut y) = self.grid_size();
 | 
			
		||||
            y += area.y + 1;
 | 
			
		||||
 | 
			
		||||
            for line in legend.split('\n') {
 | 
			
		||||
                let center_rect = centered_rect_size(line.len() as u16, 1, &area);
 | 
			
		||||
                let x = center_rect.x;
 | 
			
		||||
                buf.set_string(x, y, line, Style::default());
 | 
			
		||||
                y += 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user