From 98ad8efd3abdd7b382ddbeec477c67418d765c24 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Mon, 7 Jul 2025 17:52:42 +0300 Subject: [PATCH] feat(help): add help popup --- src/app/action.rs | 2 +- src/app/mod.rs | 30 ++++++++++++++-------------- src/handler.rs | 4 ++-- src/lib.rs | 4 ---- src/ui/help.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++ src/ui/mod.rs | 20 +++++-------------- src/ui/popup.rs | 13 ------------ src/ui/table.rs | 3 +-- tests/app.rs | 20 +++++++++---------- 9 files changed, 85 insertions(+), 62 deletions(-) create mode 100644 src/ui/help.rs delete mode 100644 src/ui/popup.rs diff --git a/src/app/action.rs b/src/app/action.rs index dec296b..11eb6a6 100644 --- a/src/app/action.rs +++ b/src/app/action.rs @@ -6,7 +6,7 @@ pub enum Action { NextTorrent, PrevTorrent, SwitchTab(u8), - TogglePopup, + ToggleHelp, // Add this line ToggleTorrent, ToggleAll, PauseAll, diff --git a/src/app/mod.rs b/src/app/mod.rs index 8126882..3e53860 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -17,7 +17,7 @@ pub struct App<'a> { tabs: &'a [Tab], pub state: TableState, pub torrents: Torrents, - pub show_popup: bool, + pub show_help: bool, } impl<'a> App<'a> { @@ -30,7 +30,7 @@ impl<'a> App<'a> { index: 0, state: TableState::default(), torrents: Torrents::new()?, // Handle the Result here - show_popup: false, + show_help: false, }) } @@ -56,7 +56,7 @@ impl<'a> App<'a> { } None => 0, }; - self.close_popup(); + self.close_help(); self.state.select(Some(i)); } @@ -71,19 +71,19 @@ impl<'a> App<'a> { } None => 0, }; - self.close_popup(); + self.close_help(); self.state.select(Some(i)); } /// Switches to the next tab. pub fn next_tab(&mut self) { - self.close_popup(); + self.close_help(); self.index = (self.index + 1) % self.tabs.len(); } /// Switches to the previous tab. pub fn prev_tab(&mut self) { - self.close_popup(); + self.close_help(); if self.index > 0 { self.index -= 1; } else { @@ -93,7 +93,7 @@ impl<'a> App<'a> { /// Switches to the tab whose index is `idx`. pub fn switch_tab(&mut self, idx: usize) { - self.close_popup(); + self.close_help(); self.index = idx } @@ -107,29 +107,29 @@ impl<'a> App<'a> { self.tabs } - pub fn toggle_popup(&mut self) { - self.show_popup = !self.show_popup; + pub fn toggle_help(&mut self) { + self.show_help = !self.show_help; } - pub fn close_popup(&mut self) { - self.show_popup = false; + pub fn close_help(&mut self) { + self.show_help = false; } - pub fn open_popup(&mut self) { - self.show_popup = true; + pub fn open_help(&mut self) { + self.show_help = true; } pub async fn toggle_torrents(&mut self) -> anyhow::Result<()> { let ids = self.selected(false); self.torrents.toggle(ids).await?; - self.close_popup(); + self.close_help(); Ok(()) } pub async fn delete(&mut self, delete_local_data: bool) -> anyhow::Result<()> { let ids = self.selected(false); self.torrents.delete(ids, delete_local_data).await?; - self.close_popup(); + self.close_help(); Ok(()) } diff --git a/src/handler.rs b/src/handler.rs index 3944ff0..2639f7d 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -24,7 +24,7 @@ pub fn get_action(key_event: KeyEvent) -> Option { KeyCode::Char('d') => Some(Action::Delete(false)), KeyCode::Char('D') => Some(Action::Delete(true)), KeyCode::Char(' ') => Some(Action::Select), - // Other handlers you could add here. + KeyCode::Char('?') => Some(Action::ToggleHelp), _ => None, } } @@ -38,7 +38,7 @@ pub async fn update(app: &mut App<'_>, action: Action) -> anyhow::Result<()> { Action::NextTorrent => app.next(), Action::PrevTorrent => app.previous(), Action::SwitchTab(x) => app.switch_tab(x as usize), - Action::TogglePopup => app.toggle_popup(), + Action::ToggleHelp => app.toggle_help(), Action::ToggleTorrent => app.toggle_torrents().await?, Action::ToggleAll => app.torrents.toggle_all().await?, Action::PauseAll => app.torrents.stop_all().await?, diff --git a/src/lib.rs b/src/lib.rs index 5be682c..85ad8bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,3 @@ pub mod tui; /// Event handler. pub mod handler; - - - - diff --git a/src/ui/help.rs b/src/ui/help.rs new file mode 100644 index 0000000..b1a467b --- /dev/null +++ b/src/ui/help.rs @@ -0,0 +1,51 @@ +use ratatui::{prelude::*, widgets::*}; + +pub fn render_help(frame: &mut Frame) { + let block = Block::default() + .title("Help") + .title_alignment(Alignment::Center) + .borders(Borders::ALL) + .border_type(BorderType::Rounded); + + let rows = vec![ + Row::new(vec![Cell::from("?"), Cell::from("Show help")]), + Row::new(vec![Cell::from("q"), Cell::from("Quit")]), + Row::new(vec![Cell::from("h"), Cell::from("Left")]), + Row::new(vec![Cell::from("l"), Cell::from("Right")]), + Row::new(vec![Cell::from("j"), Cell::from("Down")]), + Row::new(vec![Cell::from("k"), Cell::from("Up")]), + Row::new(vec![Cell::from("1"), Cell::from("Switch to All tab")]), + Row::new(vec![Cell::from("2"), Cell::from("Switch to Active tab")]), + Row::new(vec![ + Cell::from("3"), + Cell::from("Switch to Downloading tab"), + ]), + Row::new(vec![Cell::from("t"), Cell::from("Toggle torrent")]), + Row::new(vec![Cell::from("a"), Cell::from("Toggle all torrents")]), + Row::new(vec![Cell::from("d"), Cell::from("Delete torrent")]), + Row::new(vec![Cell::from("D"), Cell::from("Delete torrent and data")]), + Row::new(vec![Cell::from(" "), Cell::from("Select torrent")]), + ]; + + let table = Table::new( + rows, + &[Constraint::Percentage(20), Constraint::Percentage(80)], + ) + .block(block) + .style(Style::default().fg(Color::Green)); + + let area = frame.area(); + let height = 15; // Desired height for the help menu + let width = area.width; // Full width of the screen + + let popup_area = Rect::new( + area.x + (area.width - width) / 2, // Center horizontally + area.y + area.height - height, // Position at the very bottom + width, + height, + ); + + frame.render_widget(Clear, popup_area); + frame.render_widget(table, popup_area); +} + diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0cc38f3..5f6c535 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,16 +1,9 @@ -mod popup; +mod help; mod table; use crate::app::{App, Tab}; -use popup::render_popup; -use ratatui::{ - layout::Alignment, - prelude::{Constraint, Direction, Layout}, - style::{Color, Style}, - text::Line, - widgets::{Block, BorderType, Borders, Clear, Tabs}, - Frame, -}; +use help::render_help; +use ratatui::{prelude::*, widgets::*}; use table::render_table; /// Renders the user interface widgets. @@ -57,10 +50,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { }; frame.render_stateful_widget(table, chunks[1], &mut app.state); // renders table - if app.show_popup { - let block = Block::default().borders(Borders::ALL); - let size = render_popup(size); - frame.render_widget(Clear, size); - frame.render_widget(block, size); + if app.show_help { + render_help(frame); } } diff --git a/src/ui/popup.rs b/src/ui/popup.rs deleted file mode 100644 index aa74ad2..0000000 --- a/src/ui/popup.rs +++ /dev/null @@ -1,13 +0,0 @@ -use ratatui::layout::Rect; - -pub fn render_popup(r: Rect) -> Rect { - let vertical_margin = r.height / 5; - let horizontal_margin = r.width / 5; - - Rect::new( - r.x + horizontal_margin, - r.y + vertical_margin, - r.width - (2 * horizontal_margin), - r.height - (2 * vertical_margin), - ) -} diff --git a/src/ui/table.rs b/src/ui/table.rs index 0627e15..0710bcd 100644 --- a/src/ui/table.rs +++ b/src/ui/table.rs @@ -1,11 +1,10 @@ +use crate::app::{utils::Wrapper, App, Tab}; use ratatui::{ layout::Constraint, style::{Color, Style, Styled}, widgets::{Block, BorderType, Borders, Row, Table}, }; -use crate::app::{utils::Wrapper, App, Tab}; - pub fn render_table<'a>(app: &mut App, tab: Tab) -> Table<'a> { let fields = tab.fields(); let selected = &app.torrents.selected.clone(); diff --git a/tests/app.rs b/tests/app.rs index 96cada3..0efdac5 100644 --- a/tests/app.rs +++ b/tests/app.rs @@ -48,19 +48,19 @@ fn test_app_switch_tab() { #[test] fn test_app_toggle_popup() { let mut app = App::new().unwrap(); - assert!(!app.show_popup); - app.toggle_popup(); - assert!(app.show_popup); - app.toggle_popup(); - assert!(!app.show_popup); + assert!(!app.show_help); + app.toggle_help(); + assert!(app.show_help); + app.toggle_help(); + assert!(!app.show_help); } #[test] fn test_app_open_close_popup() { let mut app = App::new().unwrap(); - assert!(!app.show_popup); - app.open_popup(); - assert!(app.show_popup); - app.close_popup(); - assert!(!app.show_popup); + assert!(!app.show_help); + app.open_help(); + assert!(app.show_help); + app.close_help(); + assert!(!app.show_help); }