diff --git a/CHANGELOG.md b/CHANGELOG.md index 6503694..4708d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed + +- Fixed the rerendering issue of the cursor by removing it from the render method and placing it to the draw method. + ## [1.1.0] - 2025-01-01 ### Added diff --git a/src/apps/core.rs b/src/apps/core.rs index 1d9a5cb..215c014 100644 --- a/src/apps/core.rs +++ b/src/apps/core.rs @@ -10,7 +10,13 @@ use crate::apps::handlers::flags::Flags; use crate::utils::connect::connect_to_saved_network; use crate::utils::disconnect_connection::disconnect_connected_network; use crate::utils::scan::scan_networks; +use crossterm::cursor; +use crossterm::cursor::DisableBlinking; +use crossterm::cursor::EnableBlinking; +use crossterm::execute; use ratatui::Frame; +use ratatui::layout::Position; +use std::io; use std::sync::{Arc, Mutex}; mod delete_handler; mod help_handlers; @@ -53,10 +59,50 @@ impl Default for App { } impl App { + /// Render the application UI and manage terminal cursor visibility and position for SSID/password popups. + /// + /// When an SSID or password popup is visible, this sets the terminal cursor to the popup's input position + /// and enables cursor blinking; otherwise it hides the cursor and disables blinking. + /// + /// # Examples + /// + /// ```no_run + /// // Assuming `app` is an initialized `App` and `frame` is a mutable `ratatui::Frame`: + /// // app.draw(&mut frame); + /// ``` fn draw(&self, frame: &mut Frame) { frame.render_widget(self, frame.area()); + + // Set cursor position for SSID or Password popups + if self.wifi_credentials.flags.show_ssid_popup + || self.wifi_credentials.flags.show_password_popup + { + frame.set_cursor_position(Position::new( + frame.area().x + frame.area().width / 4 + self.wifi_credentials.cursor_pos + 1, + frame.area().y + frame.area().height / 4 + 1, + )); + // The reason for using io::stdout() here is that the crossterm execute! macro needs a writable output target to send + // terminal commands to. io::stdout() provides a handle to the standard output (the terminal), so the commands (Show, MoveTo, EnableBlinking) + // are sent to the terminal for immediate effect. + // + // If you remove io::stdout(), execute! won’t know where to send the commands, resulting in a compilation error. + // You must provide a valid output stream (like io::stdout()) for terminal control commands to work. + // EnableBlinking + let _ = execute!(io::stdout(), cursor::Show, EnableBlinking); + } else { + let _ = execute!(io::stdout(), cursor::Hide, DisableBlinking); + } } + /// Mark the application to exit by setting its internal exit flag. + /// + /// # Examples + /// + /// ``` + /// let mut app = App::default(); + /// app.exit(); + /// assert!(app.app_state.exit); + /// ``` fn exit(&mut self) { self.app_state.exit = true; } diff --git a/src/apps/core/widget.rs b/src/apps/core/widget.rs index 3944bfe..02a7465 100644 --- a/src/apps/core/widget.rs +++ b/src/apps/core/widget.rs @@ -1,9 +1,5 @@ use super::App; -use crossterm::cursor::DisableBlinking; -use crossterm::cursor::EnableBlinking; -use crossterm::cursor::{self, MoveTo}; -use crossterm::execute; use ratatui::widgets::Clear; use ratatui::{ buffer::Buffer, @@ -12,14 +8,13 @@ use ratatui::{ text::Line, widgets::{Block, Paragraph, Row, Table, TableState, Widget}, }; -use std::io; const INFO_TEXT: [&str; 2] = [ "[Esc] quit | (Ctrl+R) scan for networks | (h) help ", "(Enter) connect to network | (↑) move up | (↓) move down", ]; -const HELP_TEST: [&str; 11] = [ +const HELP_TEXT: [&str; 12] = [ "[Esc] quit", "(Ctrl+c) force quit", "(Ctrl+R) scan for networks", @@ -31,15 +26,15 @@ const HELP_TEST: [&str; 11] = [ "(h) help", "(?) help", "(s) view saved networks", + "(x) disconnect from current network", ]; impl Widget for &App { - /// Render the application's terminal UI into the provided drawing area buffer. + /// Render the application's terminal UI into the provided drawing area. /// - /// This draws the main network table and, depending on internal flags and state, - /// overlays the saved-connections list, help menu, delete-confirmation popup, - /// hidden-SSID input popup, password input popup, and status popup. Cursor - /// visibility and blinking are adjusted as needed for input popups. + /// Draws the main network table and, depending on the app's flags and state, + /// conditionally overlays the saved-connections list, help menu, delete-confirmation + /// popup, hidden-SSID input popup, password input popup, and status popup. /// /// # Examples /// @@ -114,7 +109,6 @@ impl Widget for &App { // handle the render of the saved connections list if self.flags.show_saved { Clear.render(area, buf); - let _ = execute!(io::stdout(), cursor::Hide, DisableBlinking); let saved_block = Block::default() .title("Saved Connections") .borders(ratatui::widgets::Borders::ALL) @@ -177,12 +171,10 @@ impl Widget for &App { height: area.height * 2 / 3, }; - let help_paragraph = Paragraph::new(HELP_TEST.join("\n")) + let help_paragraph = Paragraph::new(HELP_TEXT.join("\n")) .block(help_block) .style(ratatui::style::Style::default().fg(ratatui::style::Color::White)); - let _ = execute!(io::stdout(), cursor::Hide, DisableBlinking); - help_paragraph.render(help_area, buf); } @@ -208,8 +200,6 @@ impl Widget for &App { .block(popup_block) .style(ratatui::style::Style::default().fg(ratatui::style::Color::White)); - let _ = execute!(io::stdout(), cursor::Hide, DisableBlinking); - confirmation_paragraph.render(popup_area, buf); } @@ -233,16 +223,6 @@ impl Widget for &App { .block(popup_block) .style(ratatui::style::Style::default().fg(ratatui::style::Color::White)); - let _ = execute!( - io::stdout(), - cursor::Show, - MoveTo( - popup_area.x + self.wifi_credentials.cursor_pos + 1, - popup_area.y + 1, - ), - EnableBlinking - ); - ssid_paragraph.render(popup_area, buf); } @@ -267,16 +247,6 @@ impl Widget for &App { .block(popup_block) .style(ratatui::style::Style::default().fg(ratatui::style::Color::White)); - let _ = execute!( - io::stdout(), - cursor::Show, - MoveTo( - popup_area.x + self.wifi_credentials.cursor_pos + 1, - popup_area.y + 1, - ), - EnableBlinking - ); - password_paragraph.render(popup_area, buf); } @@ -305,8 +275,6 @@ impl Widget for &App { .block(status_block) .style(ratatui::style::Style::default().fg(ratatui::style::Color::White)); - let _ = execute!(io::stdout(), cursor::Hide, DisableBlinking); - status_paragraph.render(status_area, buf); } }