Rename crate

This commit is contained in:
2022-10-17 19:13:16 +02:00
parent cf1d77f445
commit dfaa5ce30b
27 changed files with 55 additions and 37 deletions

@ -0,0 +1,63 @@
use std::fmt::Display;
use crate::constants::HIGHLIGHT_COLOR;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Color, Style};
use tui::widgets::*;
use crate::ui_screens::utils::centered_rect_size;
pub struct ButtonWidget {
is_hovered: bool,
label: String,
disabled: bool,
min_width: usize,
}
impl ButtonWidget {
pub fn new<D: Display>(label: D, is_hovered: bool) -> Self {
Self {
label: label.to_string(),
is_hovered,
disabled: false,
min_width: 0,
}
}
pub fn set_disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn set_min_width(mut self, min_width: usize) -> Self {
self.min_width = min_width;
self
}
pub fn estimated_size(&self) -> (u16, u16) {
((self.label.len() + 2).max(self.min_width) as u16, 1)
}
}
impl Widget for ButtonWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
let expected_len = self.estimated_size().0;
let mut label = self.label.clone();
while label.len() < expected_len as usize {
label.insert(0, ' ');
label.push(' ');
}
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(),
(_, false) => Style::default().bg(Color::DarkGray),
(_, true) => Style::default().fg(Color::White).bg(HIGHLIGHT_COLOR),
});
input.render(area, buf);
}
}

@ -0,0 +1,57 @@
use crate::constants::HIGHLIGHT_COLOR;
use std::fmt::Display;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::*;
use tui::widgets::*;
pub struct CheckboxWidget {
is_editing: bool,
checked: bool,
label: String,
is_radio: bool,
}
impl CheckboxWidget {
pub fn new<D: Display>(label: D, checked: bool, is_editing: bool) -> Self {
Self {
is_editing,
checked,
label: label.to_string(),
is_radio: false,
}
}
pub fn set_radio(mut self) -> Self {
self.is_radio = true;
self
}
}
impl Widget for CheckboxWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
let paragraph = format!(
"{}{}{} {}",
match self.is_radio {
true => "(",
false => "[",
},
match self.checked {
true => "X",
false => " ",
},
match self.is_radio {
true => ")",
false => "]",
},
self.label
);
let input = Paragraph::new(paragraph.as_ref()).style(match &self.is_editing {
false => Style::default(),
true => Style::default().fg(HIGHLIGHT_COLOR),
});
input.render(area, buf);
}
}

@ -0,0 +1,204 @@
use std::collections::HashMap;
use std::fmt::Display;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Color, Style};
use tui::widgets::{BorderType, Widget};
use sea_battle_backend::data::{Coordinates, GameRules, PlayConfiguration};
use crate::ui_screens::utils::centered_rect_size;
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>,
title: Option<String>,
legend: Option<String>,
yield_coordinates: Option<Box<dyn 'a + FnMut(Coordinates, Rect)>>,
chars: HashMap<Coordinates, char>,
}
impl<'a> GameMapWidget<'a> {
pub fn new(rules: &'a GameRules) -> Self {
Self {
rules,
default_empty_character: '.',
colored_cells: vec![],
title: None,
legend: None,
yield_coordinates: None,
chars: Default::default(),
}
}
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_title<D: Display>(mut self, title: D) -> Self {
self.title = Some(title.to_string());
self
}
pub fn set_legend<D: Display>(mut self, legend: D) -> Self {
self.legend = Some(legend.to_string());
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 set_char(mut self, coordinates: Coordinates, c: char) -> Self {
self.chars.insert(coordinates, c);
self
}
pub fn set_char_no_overwrite(mut self, coordinates: Coordinates, c: char) -> Self {
self.chars.entry(coordinates).or_insert(c);
self
}
pub fn grid_size(&self) -> (u16, u16) {
let w = (self.rules.map_width as u16 * 2) + 2;
let h = (self.rules.map_height as u16 * 2) + 2;
(w, h)
}
pub fn estimated_size(&self) -> (u16, u16) {
let (w, mut h) = self.grid_size();
if self.title.is_some() {
h += 2;
}
if let Some(l) = &self.legend {
h += 1 + l.split('\n').count() as u16;
}
(w, h)
}
}
impl<'a> Widget for GameMapWidget<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
let alphabet = PlayConfiguration::default().ordinate_alphabet;
let symbols = BorderType::line_symbols(BorderType::Plain);
let mut start_y = area.y;
// Render title
if let Some(title) = &self.title {
let x = centered_rect_size(title.len() as u16, 1, &area).x;
buf.set_string(x, start_y, title, Style::default());
start_y += 2;
}
// 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,
start_y,
x.to_string(),
Style::default(),
);
}
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,
(x, 0) if x == self.rules.map_width => symbols.top_right,
(0, y) if y == self.rules.map_height => symbols.bottom_left,
(0, _) => symbols.vertical_right,
(_, 0) => symbols.horizontal_down,
(x, y) if x == self.rules.map_width && y == self.rules.map_height => {
symbols.bottom_right
}
(x, _) if x == self.rules.map_width => symbols.vertical_left,
(_, y) if y == self.rules.map_height => symbols.horizontal_up,
_ => symbols.cross,
});
if x < self.rules.map_width {
buf.get_mut(o_x + 1, o_y).set_symbol(symbols.horizontal);
}
if y < self.rules.map_height {
buf.get_mut(o_x, o_y + 1).set_symbol(symbols.vertical);
}
if x < self.rules.map_width && y < self.rules.map_height {
let cell = buf.get_mut(o_x + 1, o_y + 1).set_char(
*self
.chars
.get(&coordinates)
.unwrap_or(&self.default_empty_character),
);
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));
}
}
}
}
start_y += self.grid_size().1;
// Paint legend (if any)
if let Some(legend) = &self.legend {
start_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, start_y, line, Style::default());
start_y += 1;
}
}
}
}

@ -0,0 +1,4 @@
pub mod button_widget;
pub mod checkbox_widget;
pub mod game_map_widget;
pub mod text_editor_widget;

@ -0,0 +1,65 @@
use std::fmt::Display;
use crate::constants::HIGHLIGHT_COLOR;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::*;
use tui::text::*;
use tui::widgets::{Block, Borders, Paragraph, Widget};
#[derive(Eq, PartialEq)]
pub enum InputMode {
Normal,
Editing,
}
pub struct TextEditorWidget {
input_mode: InputMode,
value: String,
label: String,
}
impl TextEditorWidget {
pub fn new<D: Display>(label: D, value: D, is_editing: bool) -> Self {
Self {
input_mode: match is_editing {
true => InputMode::Editing,
false => InputMode::Normal,
},
value: value.to_string(),
label: label.to_string(),
}
}
}
impl Widget for TextEditorWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
let span = Span::styled(
self.value.to_string(),
match &self.input_mode {
InputMode::Normal => Style::default(),
InputMode::Editing => Style::default().fg(HIGHLIGHT_COLOR),
},
);
let mut spans = vec![span];
// Add cursor if field is highlighted
if self.input_mode == InputMode::Editing {
spans.push(Span::styled(" ", Style::default().bg(HIGHLIGHT_COLOR)))
}
let text = Text::from(Spans::from(spans));
let input = Paragraph::new(text).block(
Block::default()
.borders(Borders::ALL)
.border_style(match self.input_mode {
InputMode::Normal => Style::default(),
InputMode::Editing => Style::default().fg(HIGHLIGHT_COLOR),
})
.title(self.label.as_ref()),
);
input.render(area, buf);
}
}