Add mouse interaction
This commit is contained in:
		@@ -1,8 +1,9 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::time::{Duration, Instant};
 | 
			
		||||
 | 
			
		||||
use crossterm::event;
 | 
			
		||||
use crossterm::event::{Event, KeyCode};
 | 
			
		||||
use crossterm::event::{Event, KeyCode, MouseButton, MouseEventKind};
 | 
			
		||||
use tui::backend::Backend;
 | 
			
		||||
use tui::style::*;
 | 
			
		||||
use tui::{Frame, Terminal};
 | 
			
		||||
@@ -19,6 +20,8 @@ struct SetBotsLayoutScreen {
 | 
			
		||||
    layout: BoatsLayout,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CoordinatesMapper = HashMap<Coordinates, Coordinates>;
 | 
			
		||||
 | 
			
		||||
pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
    rules: &GameRules,
 | 
			
		||||
    terminal: &mut Terminal<B>,
 | 
			
		||||
@@ -29,16 +32,19 @@ pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
            .expect("Failed to generate initial boats layout"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut coordinates_mapper = CoordinatesMapper::default();
 | 
			
		||||
 | 
			
		||||
    let mut last_tick = Instant::now();
 | 
			
		||||
    loop {
 | 
			
		||||
        terminal.draw(|f| ui(f, &mut model, rules))?;
 | 
			
		||||
        terminal.draw(|f| coordinates_mapper = ui(f, &mut model, rules))?;
 | 
			
		||||
 | 
			
		||||
        let timeout = TICK_RATE
 | 
			
		||||
            .checked_sub(last_tick.elapsed())
 | 
			
		||||
            .unwrap_or_else(|| Duration::from_secs(0));
 | 
			
		||||
 | 
			
		||||
        if event::poll(timeout)? {
 | 
			
		||||
            if let Event::Key(key) = event::read()? {
 | 
			
		||||
            let event = event::read()?;
 | 
			
		||||
            if let Event::Key(key) = &event {
 | 
			
		||||
                match key.code {
 | 
			
		||||
                    KeyCode::Char('q') => return Ok(ScreenResult::Canceled),
 | 
			
		||||
                    KeyCode::Enter => {
 | 
			
		||||
@@ -51,6 +57,22 @@ pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                model.curr_boat %= model.layout.number_of_boats();
 | 
			
		||||
            } else if let Event::Mouse(mouse) = event {
 | 
			
		||||
                if MouseEventKind::Up(MouseButton::Left) == mouse.kind {
 | 
			
		||||
                    let src_pos = Coordinates::new(mouse.column, mouse.row);
 | 
			
		||||
                    if let Some(pos) = coordinates_mapper.get(&src_pos) {
 | 
			
		||||
                        match model.layout.find_boat_at_position(*pos).cloned() {
 | 
			
		||||
                            // Change of selected boat
 | 
			
		||||
                            Some(b) if b != model.layout.0[model.curr_boat] => {
 | 
			
		||||
                                model.curr_boat =
 | 
			
		||||
                                    model.layout.0.iter().position(|s| s == &b).unwrap();
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Move current boat
 | 
			
		||||
                            _ => model.layout.0[model.curr_boat].start = *pos,
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if last_tick.elapsed() >= TICK_RATE {
 | 
			
		||||
@@ -59,7 +81,11 @@ pub fn set_boat_layout<B: Backend>(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SetBotsLayoutScreen, rules: &GameRules) {
 | 
			
		||||
fn ui<B: Backend>(
 | 
			
		||||
    f: &mut Frame<B>,
 | 
			
		||||
    model: &mut SetBotsLayoutScreen,
 | 
			
		||||
    rules: &GameRules,
 | 
			
		||||
) -> CoordinatesMapper {
 | 
			
		||||
    let current_boat = ColoredCells {
 | 
			
		||||
        color: Color::Green,
 | 
			
		||||
        cells: model.layout.0[model.curr_boat].all_coordinates(),
 | 
			
		||||
@@ -77,11 +103,20 @@ fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SetBotsLayoutScreen, rules: &Gam
 | 
			
		||||
        cells: other_boats_cells,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut coordinates_mapper = HashMap::new();
 | 
			
		||||
 | 
			
		||||
    let game_map_widget = GameMapWidget::new(rules)
 | 
			
		||||
        .set_default_empty_char(' ')
 | 
			
		||||
        .add_colored_cells(current_boat)
 | 
			
		||||
        .add_colored_cells(other_boats)
 | 
			
		||||
        .set_title("Choose your boat layout")
 | 
			
		||||
        .set_yield_func(|c, r| {
 | 
			
		||||
            for i in 0..r.width {
 | 
			
		||||
                for j in 0..r.height {
 | 
			
		||||
                    coordinates_mapper.insert(Coordinates::new(r.x + i, r.y + j), c);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .set_legend(
 | 
			
		||||
            "n        next boat     \n\
 | 
			
		||||
                     r        rotate boat    \n\n\
 | 
			
		||||
@@ -92,4 +127,6 @@ fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SetBotsLayoutScreen, rules: &Gam
 | 
			
		||||
    let (w, h) = game_map_widget.estimated_size();
 | 
			
		||||
    let area = centered_rect_size(w, h, &f.size());
 | 
			
		||||
    f.render_widget(game_map_widget, area);
 | 
			
		||||
 | 
			
		||||
    coordinates_mapper
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ pub struct GameMapWidget<'a> {
 | 
			
		||||
    colored_cells: Vec<ColoredCells>,
 | 
			
		||||
    title: Option<String>,
 | 
			
		||||
    legend: Option<String>,
 | 
			
		||||
    yield_coordinates: Option<Box<dyn 'a + FnMut(Coordinates, Rect)>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> GameMapWidget<'a> {
 | 
			
		||||
@@ -30,6 +31,7 @@ impl<'a> GameMapWidget<'a> {
 | 
			
		||||
            colored_cells: vec![],
 | 
			
		||||
            title: None,
 | 
			
		||||
            legend: None,
 | 
			
		||||
            yield_coordinates: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +55,14 @@ impl<'a> GameMapWidget<'a> {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_yield_func<F>(mut self, func: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: 'a + FnMut(Coordinates, Rect),
 | 
			
		||||
    {
 | 
			
		||||
        self.yield_coordinates = Some(Box::new(func));
 | 
			
		||||
        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;
 | 
			
		||||
@@ -76,7 +86,7 @@ impl<'a> GameMapWidget<'a> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
    fn render(self, area: Rect, buf: &mut Buffer) {
 | 
			
		||||
    fn render(mut self, area: Rect, buf: &mut Buffer) {
 | 
			
		||||
        let alphabet = PlayConfiguration::default().ordinate_alphabet;
 | 
			
		||||
 | 
			
		||||
        let symbols = BorderType::line_symbols(BorderType::Plain);
 | 
			
		||||
@@ -92,12 +102,16 @@ impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
 | 
			
		||||
        // Paint game grid
 | 
			
		||||
        for y in 0..(self.rules.map_height + 1) {
 | 
			
		||||
            // Header (ordinate)
 | 
			
		||||
            if y < self.rules.map_height {
 | 
			
		||||
                buf.get_mut(area.x, start_y + 2 + (y as u16 * 2))
 | 
			
		||||
                    .set_char(alphabet.chars().nth(y).unwrap());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for x in 0..(self.rules.map_width + 1) {
 | 
			
		||||
                let coordinates = Coordinates::new(x as i32, y as i32);
 | 
			
		||||
 | 
			
		||||
                // Header (abscissa)
 | 
			
		||||
                if x < self.rules.map_width {
 | 
			
		||||
                    buf.set_string(
 | 
			
		||||
                        area.x + 2 + (x as u16 * 2) - (x as u16) / 10,
 | 
			
		||||
@@ -110,6 +124,11 @@ impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
                let o_x = 1 + area.x + (x as u16 * 2);
 | 
			
		||||
                let o_y = 1 + start_y + (y as u16 * 2);
 | 
			
		||||
 | 
			
		||||
                let color = self
 | 
			
		||||
                    .colored_cells
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .find(|c| c.cells.contains(&coordinates));
 | 
			
		||||
 | 
			
		||||
                buf.get_mut(o_x, o_y).set_symbol(match (x, y) {
 | 
			
		||||
                    (0, 0) => symbols.top_left,
 | 
			
		||||
 | 
			
		||||
@@ -142,13 +161,13 @@ impl<'a> Widget for GameMapWidget<'a> {
 | 
			
		||||
                        .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)))
 | 
			
		||||
                    {
 | 
			
		||||
                    if let Some(c) = color {
 | 
			
		||||
                        cell.set_bg(c.color);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if let Some(f) = self.yield_coordinates.as_mut() {
 | 
			
		||||
                        f(coordinates, Rect::new(o_x + 1, o_y + 1, 1, 1));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,12 +18,7 @@ pub enum BoatDirection {
 | 
			
		||||
    BottomRight,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _BOATS_DIRECTION: [BoatDirection; 4] = [
 | 
			
		||||
    BoatDirection::Left,
 | 
			
		||||
    BoatDirection::Right,
 | 
			
		||||
    BoatDirection::Up,
 | 
			
		||||
    BoatDirection::Down,
 | 
			
		||||
];
 | 
			
		||||
const _BOATS_DIRECTION: [BoatDirection; 2] = [BoatDirection::Right, BoatDirection::Down];
 | 
			
		||||
 | 
			
		||||
const _ALL_DIRECTIONS: [BoatDirection; 8] = [
 | 
			
		||||
    BoatDirection::Left,
 | 
			
		||||
@@ -54,8 +49,8 @@ impl BoatDirection {
 | 
			
		||||
 | 
			
		||||
    pub fn shift_coordinates(&self, coordinates: Coordinates, nth: usize) -> Coordinates {
 | 
			
		||||
        let shift = match self {
 | 
			
		||||
            BoatDirection::Left => (1, 0),
 | 
			
		||||
            BoatDirection::Right => (-1, 0),
 | 
			
		||||
            BoatDirection::Left => (-1, 0),
 | 
			
		||||
            BoatDirection::Right => (1, 0),
 | 
			
		||||
            BoatDirection::Up => (0, -1),
 | 
			
		||||
            BoatDirection::Down => (0, 1),
 | 
			
		||||
            BoatDirection::UpLeft => (-1, -1),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user