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