Improve cli screens dev integration

This commit is contained in:
Pierre HUBERT 2022-10-10 17:17:57 +02:00
parent 5d4940adc6
commit 276f4508c0
5 changed files with 116 additions and 76 deletions

1
rust/.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
.vscode
.idea .idea

View File

@ -1,4 +1,10 @@
use clap::Parser; use clap::{Parser, ValueEnum};
#[derive(Clone, Copy, Debug, ValueEnum)]
pub enum TestDevScreen {
Popup,
Input,
}
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct CliArgs { pub struct CliArgs {
@ -15,6 +21,9 @@ pub struct CliArgs {
/// Local server listen address /// Local server listen address
#[clap(short, long, default_value = "127.0.0.1:5679")] #[clap(short, long, default_value = "127.0.0.1:5679")]
pub listen_address: String, pub listen_address: String,
#[clap(long, value_enum)]
pub dev_screen: Option<TestDevScreen>,
} }
impl CliArgs { impl CliArgs {

View File

@ -1,7 +1,9 @@
use std::error::Error; use std::error::Error;
use std::fmt::Debug;
use std::io; use std::io;
use std::io::ErrorKind; use std::io::ErrorKind;
use cli_player::cli_args::{cli_args, TestDevScreen};
use crossterm::event::DisableMouseCapture; use crossterm::event::DisableMouseCapture;
use crossterm::event::EnableMouseCapture; use crossterm::event::EnableMouseCapture;
use crossterm::execute; use crossterm::execute;
@ -13,25 +15,44 @@ use tui::backend::{Backend, CrosstermBackend};
use tui::Terminal; use tui::Terminal;
use cli_player::server::start_server_if_missing; use cli_player::server::start_server_if_missing;
use cli_player::ui_screens::popup_screen::PopupScreen;
use cli_player::ui_screens::*; use cli_player::ui_screens::*;
use sea_battle_backend::data::GameRules; use sea_battle_backend::data::GameRules;
async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Result<(), Box<dyn Error>> { async fn run_dev<B: Backend>(
terminal: &mut Terminal<B>,
d: TestDevScreen,
) -> Result<(), Box<dyn Error>> {
let res = match d {
TestDevScreen::Popup => PopupScreen::new("Welcome there!!")
.show(terminal)?
.as_string(),
TestDevScreen::Input => input_screen::InputScreen::new("Whas it your name ?")
.set_title("A custom title")
.show(terminal)?
.as_string(),
};
// Temporary code // Temporary code
// let res = configure_game_rules::configure_play_rules(GameRules::default(), terminal)?; // select_bot_type::select_bot_type(terminal)?; // let res = configure_game_rules::configure_play_rules(GameRules::default(), terminal)?; // select_bot_type::select_bot_type(terminal)?;
/*let mut rules = GameRules::default(); /*let mut rules = GameRules::default();
rules.boats_can_touch = true; rules.boats_can_touch = true;
let res = set_boats_layout::set_boat_layout(&rules, terminal)?; // select_bot_type::select_bot_type(terminal)?;*/ let res = set_boats_layout::set_boat_layout(&rules, terminal)?; // select_bot_type::select_bot_type(terminal)?;*/
/*let res = popup_screen::popup_screen("Hi\nWelcome there", terminal);*/
// let res = confirm_dialog::confirm_dialog("Do you really want to interrupt game ?", terminal)?; // select_bot_type::select_bot_type(terminal)?; // let res = confirm_dialog::confirm_dialog("Do you really want to interrupt game ?", terminal)?; // select_bot_type::select_bot_type(terminal)?;
let res = input_screen::InputScreen::new("Whas it your name ?") // select_bot_type::select_bot_type(terminal)?;
.set_title("custom title")
.show(terminal)?; // select_bot_type::select_bot_type(terminal)?;
Err(io::Error::new( Err(io::Error::new(
ErrorKind::Other, ErrorKind::Other,
format!("result: {:?}", res), format!("DEV result: {:?}", res),
))?; ))?
Ok(()) }
async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Result<(), Box<dyn Error>> {
if let Some(d) = cli_args().dev_screen {
run_dev(terminal, d).await
} else {
// TODO : run app
Ok(())
}
} }
#[tokio::main] #[tokio::main]

View File

@ -1,3 +1,5 @@
use std::fmt::Debug;
pub mod configure_game_rules; pub mod configure_game_rules;
pub mod confirm_dialog; pub mod confirm_dialog;
pub mod input_screen; pub mod input_screen;
@ -12,3 +14,9 @@ pub enum ScreenResult<E> {
Ok(E), Ok(E),
Canceled, Canceled,
} }
impl<E: Debug> ScreenResult<E> {
pub fn as_string(&self) -> String {
format!("{:#?}", self)
}
}

View File

@ -14,77 +14,78 @@ use crate::ui_screens::utils::centered_rect_size;
use crate::ui_screens::ScreenResult; use crate::ui_screens::ScreenResult;
use crate::ui_widgets::button_widget::ButtonWidget; use crate::ui_widgets::button_widget::ButtonWidget;
struct PopupScreen<'a> { pub struct PopupScreen<'a> {
title: &'a str, title: &'a str,
msg: &'a str, msg: &'a str,
} }
pub fn popup_screen<B: Backend>( impl<'a> PopupScreen<'a> {
msg: &str, pub fn new(msg: &'a str) -> Self {
terminal: &mut Terminal<B>, Self {
) -> io::Result<ScreenResult<()>> { title: "Message",
let mut model = PopupScreen { msg,
title: "Message",
msg,
};
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui(f, &mut model))?;
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()? {
match key.code {
KeyCode::Char('q') => return Ok(ScreenResult::Canceled),
KeyCode::Enter => {
return Ok(ScreenResult::Ok(()));
}
_ => {}
}
}
}
if last_tick.elapsed() >= TICK_RATE {
last_tick = Instant::now();
} }
} }
}
pub fn show<B: Backend>(mut self, terminal: &mut Terminal<B>) -> io::Result<ScreenResult<()>> {
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut PopupScreen) { let mut last_tick = Instant::now();
// Preprocess message loop {
let lines = textwrap::wrap(model.msg, f.size().width as usize - 20); terminal.draw(|f| self.ui(f))?;
let line_max_len = lines.iter().map(|l| l.len()).max().unwrap();
let timeout = TICK_RATE
let area = centered_rect_size(line_max_len as u16 + 4, 5 + lines.len() as u16, &f.size()); .checked_sub(last_tick.elapsed())
.unwrap_or_else(|| Duration::from_secs(0));
let block = Block::default().borders(Borders::ALL).title(model.title);
f.render_widget(block, area); if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
// Create two chunks with equal horizontal screen space match key.code {
let chunks = Layout::default() KeyCode::Char('q') => return Ok(ScreenResult::Canceled),
.direction(Direction::Vertical) KeyCode::Enter => {
.constraints( return Ok(ScreenResult::Ok(()));
[ }
Constraint::Length(lines.len() as u16), _ => {}
Constraint::Length(3), }
] }
.as_ref(), }
) if last_tick.elapsed() >= TICK_RATE {
.split(area.inner(&Margin { last_tick = Instant::now();
horizontal: 2, }
vertical: 1, }
})); }
let text = lines fn ui<B: Backend>(&mut self, f: &mut Frame<B>) {
.iter() // Preprocess message
.map(|s| Spans::from(s.as_ref())) let lines = textwrap::wrap(self.msg, f.size().width as usize - 20);
.collect::<Vec<_>>(); let line_max_len = lines.iter().map(|l| l.len()).max().unwrap();
let paragraph = Paragraph::new(text);
f.render_widget(paragraph, chunks[0]); let area = centered_rect_size(line_max_len as u16 + 4, 5 + lines.len() as u16, &f.size());
let ok_button = ButtonWidget::new("OK", true); let block = Block::default().borders(Borders::ALL).title(self.title);
f.render_widget(ok_button, chunks[1]); f.render_widget(block, area);
// Create two chunks with equal horizontal screen space
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(lines.len() as u16),
Constraint::Length(3),
]
.as_ref(),
)
.split(area.inner(&Margin {
horizontal: 2,
vertical: 1,
}));
let text = lines
.iter()
.map(|s| Spans::from(s.as_ref()))
.collect::<Vec<_>>();
let paragraph = Paragraph::new(text);
f.render_widget(paragraph, chunks[0]);
let ok_button = ButtonWidget::new("OK", true);
f.render_widget(ok_button, chunks[1]);
}
} }