diff --git a/Cargo.lock b/Cargo.lock index 5132177..e2145ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,7 +709,7 @@ dependencies = [ [[package]] name = "pp" -version = "0.2.1" +version = "0.2.2-PREVIEW" dependencies = [ "ansi_term", "chrono", diff --git a/Cargo.toml b/Cargo.toml index df9f63a..9ebf508 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pp" -version = "0.2.1" +version = "0.2.2-PREVIEW" edition = "2024" [dependencies] diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 1cb6103..ce71e55 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,5 +1,4 @@ pub mod mode_editing; -pub mod mode_exiting; pub mod mode_monitoring; pub mod ratatui_app; pub mod target_state_widget; diff --git a/src/tui/mode_adding.rs b/src/tui/mode_adding.rs index 70de156..33f5b1d 100644 --- a/src/tui/mode_adding.rs +++ b/src/tui/mode_adding.rs @@ -8,7 +8,6 @@ use ratatui::widgets::{Block, Paragraph}; use crate::tui::ratatui_screens::RatatuiScreens::Monitoring; pub struct RatatuiAddingMode {} - impl RatatuiAddingMode { pub fn render(frame: &mut Frame, state: &mut RatatuiApp) { frame.render_widget( @@ -17,7 +16,6 @@ impl RatatuiAddingMode { .centered(), frame.area() ); - } pub fn handle_crossterm_events(app: &mut RatatuiApp) -> Result<()> { diff --git a/src/tui/mode_deleting.rs b/src/tui/mode_deleting.rs index 6e10cfa..e5703c2 100644 --- a/src/tui/mode_deleting.rs +++ b/src/tui/mode_deleting.rs @@ -14,7 +14,14 @@ pub struct RatatuiDeletingMode {} impl RatatuiDeletingMode { pub fn render(frame: &mut Frame, state: &mut RatatuiApp) { let title = Line::from("Delete Host").bold().red().centered(); - let mut body = format!("Do you really want to delete {} (Y/N)", state.selected_host); + let as_list = state.state.clone(); + let mut nice_status = None; + for (index, (label, data)) in as_list.iter().enumerate() { + if index == state.selected_host { + nice_status = Some(data); + } + } + let mut body = format!("Do you really want to delete {} (Y/N)", nice_status.unwrap().name); frame.render_widget( Paragraph::new(body) @@ -29,7 +36,13 @@ impl RatatuiDeletingMode { match event::read()? { Event::Key(key) if key.kind == KeyEventKind::Press => match key.code { KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => { - + let as_list = &app.state.clone(); + for (index, (label, _)) in as_list.iter().enumerate() { + if index == app.selected_host { + app.state.remove(label).unwrap(); + app.set_screen(Monitoring) + } + } } KeyCode::Esc | KeyCode::Char('n') | KeyCode::Char('N') => { app.set_screen(Monitoring) diff --git a/src/tui/mode_exiting.rs b/src/tui/mode_exiting.rs deleted file mode 100644 index 298506b..0000000 --- a/src/tui/mode_exiting.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::time::Duration; -use crossterm::event; -use crossterm::event::{Event, KeyCode, KeyEventKind}; -use ratatui::Frame; -use ratatui::style::Stylize; -use ratatui::text::Line; -use ratatui::widgets::{Block, Paragraph}; -use crate::tui::ratatui_app::RatatuiApp; -use color_eyre::Result; -use crate::tui::ratatui_screens::RatatuiScreens::Monitoring; - -pub struct RatatuiExitingMode {} - -impl RatatuiExitingMode { - pub fn render(frame: &mut Frame) { - let title = Line::from("Exit?").bold().red().centered(); - let mut body = "Do you want to exit? (Y/N)"; - frame.render_widget( - Paragraph::new(body) - .block(Block::bordered().title(title)) - .centered(), - frame.area(), - ); - } - - pub fn handle_crossterm_events(app: &mut RatatuiApp) -> Result<()>{ - if event::poll(Duration::from_millis(100))? { - match event::read()? { - Event::Key(key) if key.kind == KeyEventKind::Press => match key.code { - KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => { - app.set_running(false); - } - KeyCode::Char('n') | KeyCode::Char('N') => { - app.set_screen(Monitoring); - } - _ => {} - }, - _ => {} - } - } - Ok(()) - } -} diff --git a/src/tui/mode_monitoring.rs b/src/tui/mode_monitoring.rs index dfd2f3f..35f5536 100644 --- a/src/tui/mode_monitoring.rs +++ b/src/tui/mode_monitoring.rs @@ -3,11 +3,12 @@ use color_eyre::Result; use crossterm::event; use crossterm::event::{Event, KeyCode, KeyEventKind}; use ratatui::prelude::*; -use ratatui::widgets::{Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState}; +use ratatui::widgets::{Block, Borders, Cell, Clear, List, ListItem, ListState, Paragraph, Row, Table, TableState, Wrap}; use ratatui::Frame; use std::time::{Duration, SystemTime}; +use ratatui::layout::Flex; use crate::tui::ratatui_app::RatatuiApp; -use crate::tui::ratatui_screens::RatatuiScreens::{Deleting, Editing, Exiting}; +use crate::tui::ratatui_screens::RatatuiScreens::{Adding, Deleting, Editing}; pub struct RatatuiMonitoringMode {} @@ -25,20 +26,13 @@ impl RatatuiMonitoringMode { let body_layout = layouts[0]; let logs_layout = layouts[1]; let footer_layout = layouts[2]; - // - // let title = Line::from(format!("PP v{}", env!("CARGO_PKG_VERSION"))) - // .bold() - // .blue() - // .centered(); - // let table_size = layouts[0].area(); - // let columns = Layout::default() - // .direction(Vertical) - // .constraints([Constraint::Min(30), - // Constraint::Min(6), - // Constraint::Min(4), - // Constraint::Min(30)]); - let headers = ["Host", "Alive", "RTT", "Last Change"] + frame.render_widget(Paragraph::new("") + .block(Block::bordered()) + .centered(), + body_layout); + + let headers = ["Host", "RTT", "Last Change"] .iter() .map(|h| Cell::from(*h)); let header = Row::new(headers) @@ -61,7 +55,6 @@ impl RatatuiMonitoringMode { let to_push = vec![ Cell::from(name_field).style(name_style), - Cell::from(current.alive.to_string()), Cell::from(current.last_rtt.to_string()), Cell::from( format!("{} ago.", @@ -83,12 +76,11 @@ impl RatatuiMonitoringMode { .title(Line::from(format!("PP v{}", env!("CARGO_PKG_VERSION")))) .borders(Borders::ALL)) .widths(&[ - Constraint::Min(30), - Constraint::Min(6), + Constraint::Fill(3), Constraint::Min(4), Constraint::Min(30) ]) - .highlight_style( + .row_highlight_style( Style::default() .bg(Color::Blue) .fg(Color::White) @@ -100,7 +92,7 @@ impl RatatuiMonitoringMode { frame.render_stateful_widget(table, layouts[0], &mut make_state(state.selected_host)); // let footer_text = "Press or q to exit"; - let footer_text = format!("Press or q to exit | Press d to delete host | Press a to add host - ({:?})", state.filename); + let footer_text = "Press or q to exit | Press d to delete host | Press a to add host"; let mut list_items = vec![]; for entry in state.get_log_entries(10) { @@ -114,12 +106,8 @@ impl RatatuiMonitoringMode { .fg(Color::Yellow) .add_modifier(Modifier::BOLD), ); - // .highlight_symbol(">> "); - - let mut list_state = ListState::default(); - list_state.select(Some(0)); - - frame.render_stateful_widget(list, logs_layout, &mut list_state); + // Log + frame.render_widget(list, logs_layout); frame.render_widget( Paragraph::new(footer_text) @@ -127,37 +115,103 @@ impl RatatuiMonitoringMode { .centered(), footer_layout, ); + + if state.showing_add_popup { + let block = Block::bordered().title("Add Host"); + let area = RatatuiMonitoringMode::popup_area(frame.area(), 60, 20); + + frame.render_widget(Clear, area); + frame.render_widget(block, area); + } + + if state.trying_to_exit { + let block = Paragraph::new("Are you sure? (Y/N)") + .block(Block::bordered().title("Exit")); + let area = RatatuiMonitoringMode::popup_area(frame.area(), 60, 20); + + frame.render_widget(Clear, area); + frame.render_widget(block, area); + } + } + + fn popup_area(area: Rect, percent_x: u16, percent_y: u16) -> Rect { + let vertical = Layout::vertical([Constraint::Percentage(percent_y)]).flex(Flex::Center); + let horizontal = Layout::horizontal([Constraint::Percentage(percent_y)]).flex(Flex::Center); + let [area] = vertical.areas(area); + let [area] = horizontal.areas(area); + area + } + + fn handle_add_popup_inputs(app: &mut RatatuiApp, key: KeyCode) { + match key { + KeyCode::Esc => { + app.showing_add_popup = false; + }, + KeyCode::Backspace => { + app.add_host_name.remove(app.add_host_name.len()); + }, + KeyCode::Enter => { + dbg!("SAVE THE VALUE TO A NEW TARGETSTATE"); + }, + _ => { + app.add_host_name = format!("{}{}", app.add_host_name, key); + } + } + } + + fn handle_exit_popup_inputs(app: &mut RatatuiApp, key: KeyCode) { + match key { + KeyCode::Char('y') | KeyCode::Char('Y') => { + app.set_running(false); + } + KeyCode::Char('n') | KeyCode::Char('N') => { + app.trying_to_exit = false; + } + _ => {} + } + } + + fn handle_monitoring_screen_inputs(app: &mut RatatuiApp, key: KeyCode) { + // Default monitoring Screen + match key { + KeyCode::Down => { + if app.selected_host + 1 == app.state.len() { + app.selected_host = 0; + } else { + app.selected_host += 1; + } + } + KeyCode::Up => { + if app.selected_host == 0 { + app.selected_host = app.state.len() - 1; + } else { + app.selected_host -= 1; + } + } + KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => { + app.trying_to_exit = true; + } + KeyCode::Char('a') | KeyCode::Char('A') => { + app.showing_add_popup = true; + } + KeyCode::Char('d') | KeyCode::Char('D') => { + app.set_screen(Deleting); + } + _ => {} + } } pub fn handle_crossterm_events(app: &mut RatatuiApp) -> Result<()> { if event::poll(Duration::from_millis(100))? { match event::read()? { Event::Key(key) if key.kind == KeyEventKind::Press => { - match (key.modifiers, key.code) { - (_, KeyCode::Down) => { - if app.selected_host + 1 == app.state.len() { - app.selected_host = 0; - } else { - app.selected_host += 1; - } - } - (_, KeyCode::Up) => { - if app.selected_host == 0 { - app.selected_host = app.state.len() - 1; - } else { - app.selected_host -= 1; - } - } - (_, KeyCode::Esc) | (_, KeyCode::Char('q')) | (_, KeyCode::Char('Q')) => { - app.set_screen(Exiting); - } - (_, KeyCode::Char('e')) | (_, KeyCode::Char('E')) => { - app.set_screen(Editing); - } - (_, KeyCode::Char('d')) | (_, KeyCode::Char('D')) => { - app.set_screen(Deleting); - } - _ => {} + // adding mode + if app.showing_add_popup { + Self::handle_add_popup_inputs(app, key.code); + } else if app.trying_to_exit { + Self::handle_exit_popup_inputs(app, key.code); + } else { + Self::handle_monitoring_screen_inputs(app, key.code); } } _ => {} diff --git a/src/tui/ratatui_app.rs b/src/tui/ratatui_app.rs index a5bb451..b138449 100644 --- a/src/tui/ratatui_app.rs +++ b/src/tui/ratatui_app.rs @@ -19,23 +19,31 @@ use log::debug; use crate::tui::mode_adding::RatatuiAddingMode; use crate::tui::mode_deleting::RatatuiDeletingMode; use crate::tui::mode_editing::RatatuiEditingMode; -use crate::tui::mode_exiting::RatatuiExitingMode; use crate::tui::mode_monitoring::RatatuiMonitoringMode; - - #[derive(Default)] pub struct RatatuiApp { running: bool, pub state: BTreeMap, current_screen: RatatuiScreens, log_entries: Vec, - pub(crate) filename: Option, - pub selected_host: usize + pub filename: Option, + pub selected_host: usize, + pub showing_add_popup: bool, + pub trying_to_exit: bool, + pub trying_to_delete: bool, + pub add_host_cursor_position: usize, + pub add_host_name: String } /// Private Methods impl RatatuiApp { + + pub fn add_new_host(mut self, new_target: TargetState) -> Result<()> { + self.state.insert(new_target.name.clone(), new_target); + Ok(()) + } + pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { self.running = true; // start the 'manager' thread that spawns its ping threads as needed @@ -57,10 +65,6 @@ impl RatatuiApp { .draw(|frame| RatatuiMonitoringMode::render(frame, &mut self))?; RatatuiMonitoringMode::handle_crossterm_events(&mut self)?; } - RatatuiScreens::Exiting => { - terminal.draw(|frame| RatatuiExitingMode::render(frame))?; - RatatuiExitingMode::handle_crossterm_events(&mut self)?; - } RatatuiScreens::Editing => { terminal.draw(|frame| RatatuiEditingMode::render(frame, &mut self.state))?; RatatuiEditingMode::handle_crossterm_events(&mut self)?; @@ -94,6 +98,7 @@ impl RatatuiApp { alive: new_message.success, last_rtt: new_message.rtt, last_alive_change, + ..TargetState::default() }; local_state.insert(name.clone(), new_state.clone()); let success_message = if new_state.alive { @@ -106,8 +111,8 @@ impl RatatuiApp { if did_change { self.log_entries.push(format!( - "{:?} {} for {}", - current_time.format("%Y-%m-%d %H:%M:%S: ").to_string(), + "{} {} for {}", + current_time.format("%Y-%m-%d %H:%M:%S:").to_string(), success_message, new_state.name.clone() )); @@ -129,7 +134,6 @@ impl RatatuiApp { fn render(&mut self, frame: &mut Frame) { match self.current_screen { RatatuiScreens::Monitoring => RatatuiMonitoringMode::render(frame, self), - RatatuiScreens::Exiting => RatatuiExitingMode::render(frame), RatatuiScreens::Editing => RatatuiEditingMode::render(frame, &mut self.state), RatatuiScreens::Deleting => RatatuiDeletingMode::render(frame,self), RatatuiScreens::Adding => RatatuiAddingMode::render(frame, self) @@ -176,9 +180,7 @@ impl RatatuiApp { working.insert(record[1].to_string(), TargetState { name: record[1].to_string(), target: Ipv4Addr::from_str(&record[0]).unwrap(), - alive: false, - last_alive_change: SystemTime::now(), - last_rtt: 0 + ..TargetState::default() }); } @@ -245,6 +247,7 @@ impl RatatuiApp { return_value.push(current); } return_value - } + + } diff --git a/src/tui/ratatui_screens.rs b/src/tui/ratatui_screens.rs index b1a6889..7780fcc 100644 --- a/src/tui/ratatui_screens.rs +++ b/src/tui/ratatui_screens.rs @@ -3,7 +3,6 @@ pub enum RatatuiScreens { #[default] Monitoring, - Exiting, Editing, Deleting, Adding