diff --git a/rm-main/src/tui/app.rs b/rm-main/src/tui/app.rs index c71a41f..4e987af 100644 --- a/rm-main/src/tui/app.rs +++ b/rm-main/src/tui/app.rs @@ -1,9 +1,8 @@ use std::sync::Arc; -use crate::tui::ui::components::Component; use crate::{ transmission::{self, TorrentAction}, - tui::Tui, + tui::components::Component, }; use rm_config::CONFIG; @@ -14,8 +13,7 @@ use crossterm::event::{Event, KeyCode, KeyModifiers}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use transmission_rpc::{types::SessionGet, TransClient}; -use super::ui::components::tabs::CurrentTab; -use super::ui::MainWindow; +use super::{components::tabs::CurrentTab, main_window::MainWindow, terminal::Tui}; #[derive(Clone)] pub struct Ctx { diff --git a/rm-main/src/tui/ui/components/mod.rs b/rm-main/src/tui/components/mod.rs similarity index 95% rename from rm-main/src/tui/ui/components/mod.rs rename to rm-main/src/tui/components/mod.rs index 6e60556..a8fdd47 100644 --- a/rm-main/src/tui/ui/components/mod.rs +++ b/rm-main/src/tui/components/mod.rs @@ -6,7 +6,6 @@ use ratatui::Frame; use rm_shared::action::Action; use rm_shared::action::UpdateAction; -pub use tabs::TabComponent; #[derive(Clone, Copy, PartialEq, Eq)] pub enum ComponentAction { diff --git a/rm-main/src/tui/ui/components/table.rs b/rm-main/src/tui/components/table.rs similarity index 100% rename from rm-main/src/tui/ui/components/table.rs rename to rm-main/src/tui/components/table.rs diff --git a/rm-main/src/tui/ui/components/tabs.rs b/rm-main/src/tui/components/tabs.rs similarity index 100% rename from rm-main/src/tui/ui/components/tabs.rs rename to rm-main/src/tui/components/tabs.rs diff --git a/rm-main/src/tui/ui/global_popups/error.rs b/rm-main/src/tui/global_popups/error.rs similarity index 97% rename from rm-main/src/tui/ui/global_popups/error.rs rename to rm-main/src/tui/global_popups/error.rs index ae78082..fbd57e7 100644 --- a/rm-main/src/tui/ui/global_popups/error.rs +++ b/rm-main/src/tui/global_popups/error.rs @@ -5,9 +5,9 @@ use ratatui::{ use rm_shared::action::Action; -use crate::tui::ui::{ - centered_rect, +use crate::tui::{ components::{Component, ComponentAction}, + main_window::centered_rect, }; #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/rm-main/src/tui/ui/global_popups/help.rs b/rm-main/src/tui/global_popups/help.rs similarity index 98% rename from rm-main/src/tui/ui/global_popups/help.rs rename to rm-main/src/tui/global_popups/help.rs index 76ff921..6bd7692 100644 --- a/rm-main/src/tui/ui/global_popups/help.rs +++ b/rm-main/src/tui/global_popups/help.rs @@ -16,10 +16,8 @@ use rm_shared::action::Action; use crate::tui::{ app, - ui::{ - centered_rect, - components::{Component, ComponentAction}, - }, + components::{Component, ComponentAction}, + main_window::centered_rect, }; macro_rules! add_line { diff --git a/rm-main/src/tui/ui/global_popups/mod.rs b/rm-main/src/tui/global_popups/mod.rs similarity index 100% rename from rm-main/src/tui/ui/global_popups/mod.rs rename to rm-main/src/tui/global_popups/mod.rs diff --git a/rm-main/src/tui/ui/mod.rs b/rm-main/src/tui/main_window.rs similarity index 89% rename from rm-main/src/tui/ui/mod.rs rename to rm-main/src/tui/main_window.rs index b47e380..1ed5a07 100644 --- a/rm-main/src/tui/ui/mod.rs +++ b/rm-main/src/tui/main_window.rs @@ -1,21 +1,15 @@ -pub mod components; -pub mod global_popups; -pub mod tabs; - -use components::ComponentAction; -use global_popups::ErrorPopup; use ratatui::prelude::*; -use tabs::torrents::TorrentsTab; use rm_shared::action::{Action, UpdateAction}; -use self::{ - components::{tabs::CurrentTab, Component, TabComponent}, - global_popups::GlobalPopupManager, - tabs::search::SearchTab, -}; +use crate::tui::components::tabs::CurrentTab; -use super::app; +use super::{ + app, + components::{tabs::TabComponent, Component, ComponentAction}, + global_popups::{ErrorPopup, GlobalPopupManager}, + tabs::{search::SearchTab, torrents::TorrentsTab}, +}; pub struct MainWindow { pub tabs: TabComponent, @@ -97,7 +91,7 @@ impl Component for MainWindow { } } -fn centered_rect(r: Rect, percent_x: u16, percent_y: u16) -> Rect { +pub fn centered_rect(r: Rect, percent_x: u16, percent_y: u16) -> Rect { let popup_layout = Layout::vertical([ Constraint::Percentage((100 - percent_y) / 2), Constraint::Percentage(percent_y), diff --git a/rm-main/src/tui/mod.rs b/rm-main/src/tui/mod.rs index 334a5b2..f10aad6 100644 --- a/rm-main/src/tui/mod.rs +++ b/rm-main/src/tui/mod.rs @@ -1,110 +1,6 @@ pub mod app; -mod ui; - -use std::{io, time::Duration}; - -use anyhow::Result; -use crossterm::{ - cursor, - event::{Event, KeyEventKind}, - terminal::{EnterAlternateScreen, LeaveAlternateScreen}, -}; -use futures::{FutureExt, StreamExt}; -use ratatui::{backend::CrosstermBackend as Backend, Terminal}; -use tokio::{ - sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, - task::JoinHandle, -}; -use tokio_util::sync::CancellationToken; - -pub struct Tui { - pub terminal: Terminal>, - pub task: JoinHandle>, - pub cancellation_token: CancellationToken, - pub event_rx: UnboundedReceiver, - pub event_tx: UnboundedSender, -} - -impl Tui { - pub(crate) fn new() -> Result { - let terminal = Terminal::new(Backend::new(std::io::stdout()))?; - let (event_tx, event_rx) = mpsc::unbounded_channel(); - let cancellation_token = CancellationToken::new(); - let task = tokio::spawn(async { Ok(()) }); - Ok(Self { - terminal, - task, - cancellation_token, - event_rx, - event_tx, - }) - } - - fn handle_crossterm_event( - event: Option>, - event_tx: &UnboundedSender, - ) -> Result<()> { - match event { - Some(Ok(Event::Key(key))) => { - if key.kind == KeyEventKind::Press { - event_tx.send(Event::Key(key)).unwrap(); - } - } - Some(Ok(Event::Resize(x, y))) => event_tx.send(Event::Resize(x, y)).unwrap(), - Some(Err(e)) => Err(e)?, - _ => (), - } - Ok(()) - } - - pub(crate) fn enter(&mut self) -> Result<()> { - crossterm::terminal::enable_raw_mode()?; - crossterm::execute!(std::io::stdout(), EnterAlternateScreen, cursor::Hide)?; - self.start()?; - Ok(()) - } - - pub fn start(&mut self) -> Result<()> { - self.cancellation_token = CancellationToken::new(); - let cancellation_token = self.cancellation_token.clone(); - let event_tx = self.event_tx.clone(); - - self.task = tokio::spawn(async move { - let mut reader = crossterm::event::EventStream::new(); - loop { - let crossterm_event = reader.next().fuse(); - tokio::select! { - _ = cancellation_token.cancelled() => break, - event = crossterm_event => Self::handle_crossterm_event(event, &event_tx)?, - } - } - Ok(()) - }); - Ok(()) - } - - pub(crate) fn exit(&mut self) -> Result<()> { - self.cancellation_token.cancel(); - let mut counter = 0; - while !self.task.is_finished() { - std::thread::sleep(Duration::from_millis(1)); - counter += 1; - if counter > 50 { - self.task.abort(); - } - if counter > 100 { - break; - } - } - if crossterm::terminal::is_raw_mode_enabled()? { - self.terminal.flush()?; - crossterm::execute!(std::io::stdout(), LeaveAlternateScreen, cursor::Show)?; - crossterm::terminal::disable_raw_mode()?; - } - Ok(()) - } - - pub async fn next(&mut self) -> Option { - self.event_rx.recv().await - } -} +mod components; +mod global_popups; +pub mod main_window; +mod tabs; +pub mod terminal; diff --git a/rm-main/src/tui/ui/tabs/mod.rs b/rm-main/src/tui/tabs/mod.rs similarity index 100% rename from rm-main/src/tui/ui/tabs/mod.rs rename to rm-main/src/tui/tabs/mod.rs diff --git a/rm-main/src/tui/ui/tabs/search/bottom_bar.rs b/rm-main/src/tui/tabs/search/bottom_bar.rs similarity index 98% rename from rm-main/src/tui/ui/tabs/search/bottom_bar.rs rename to rm-main/src/tui/tabs/search/bottom_bar.rs index 3c47b2d..f93ca82 100644 --- a/rm-main/src/tui/ui/tabs/search/bottom_bar.rs +++ b/rm-main/src/tui/tabs/search/bottom_bar.rs @@ -9,7 +9,7 @@ use rm_config::CONFIG; use rm_shared::action::{Action, UpdateAction}; use throbber_widgets_tui::ThrobberState; -use crate::tui::{app, ui::components::Component}; +use crate::tui::{app, components::Component}; use super::{ConfiguredProvider, ProviderState}; diff --git a/rm-main/src/tui/ui/tabs/search/mod.rs b/rm-main/src/tui/tabs/search/mod.rs similarity index 99% rename from rm-main/src/tui/ui/tabs/search/mod.rs rename to rm-main/src/tui/tabs/search/mod.rs index d90cbe7..8f03f59 100644 --- a/rm-main/src/tui/ui/tabs/search/mod.rs +++ b/rm-main/src/tui/tabs/search/mod.rs @@ -22,7 +22,7 @@ use crate::{ transmission::TorrentAction, tui::{ app, - ui::components::{table::GenericTable, Component, ComponentAction}, + components::{table::GenericTable, Component, ComponentAction}, }, }; use rm_shared::{ diff --git a/rm-main/src/tui/ui/tabs/search/popups/mod.rs b/rm-main/src/tui/tabs/search/popups/mod.rs similarity index 94% rename from rm-main/src/tui/ui/tabs/search/popups/mod.rs rename to rm-main/src/tui/tabs/search/popups/mod.rs index c11fe8f..c49f544 100644 --- a/rm-main/src/tui/ui/tabs/search/popups/mod.rs +++ b/rm-main/src/tui/tabs/search/popups/mod.rs @@ -6,8 +6,8 @@ use ratatui::Frame; use rm_shared::action::Action; use crate::tui::app; -use crate::tui::ui::components::Component; -use crate::tui::ui::components::ComponentAction; +use crate::tui::components::Component; +use crate::tui::components::ComponentAction; use super::ConfiguredProvider; diff --git a/rm-main/src/tui/ui/tabs/search/popups/providers.rs b/rm-main/src/tui/tabs/search/popups/providers.rs similarity index 98% rename from rm-main/src/tui/ui/tabs/search/popups/providers.rs rename to rm-main/src/tui/tabs/search/popups/providers.rs index 9df1855..781c454 100644 --- a/rm-main/src/tui/ui/tabs/search/popups/providers.rs +++ b/rm-main/src/tui/tabs/search/popups/providers.rs @@ -13,9 +13,9 @@ use ratatui::{ use rm_config::CONFIG; use rm_shared::action::Action; -use crate::tui::ui::{ - centered_rect, +use crate::tui::{ components::{Component, ComponentAction}, + main_window::centered_rect, tabs::search::{ConfiguredProvider, ProviderState}, }; diff --git a/rm-main/src/tui/ui/tabs/torrents/bottom_stats.rs b/rm-main/src/tui/tabs/torrents/bottom_stats.rs similarity index 98% rename from rm-main/src/tui/ui/tabs/torrents/bottom_stats.rs rename to rm-main/src/tui/tabs/torrents/bottom_stats.rs index 6a4fe01..2d9fb76 100644 --- a/rm-main/src/tui/ui/tabs/torrents/bottom_stats.rs +++ b/rm-main/src/tui/tabs/torrents/bottom_stats.rs @@ -8,7 +8,7 @@ use ratatui::{ use rm_shared::utils::bytes_to_human_format; use transmission_rpc::types::{FreeSpace, SessionStats}; -use crate::tui::ui::components::Component; +use crate::tui::components::Component; use super::table_manager::TableManager; diff --git a/rm-main/src/tui/ui/tabs/torrents/input_manager.rs b/rm-main/src/tui/tabs/torrents/input_manager.rs similarity index 97% rename from rm-main/src/tui/ui/tabs/torrents/input_manager.rs rename to rm-main/src/tui/tabs/torrents/input_manager.rs index 1865ec5..0f1706f 100644 --- a/rm-main/src/tui/ui/tabs/torrents/input_manager.rs +++ b/rm-main/src/tui/tabs/torrents/input_manager.rs @@ -6,7 +6,7 @@ use ratatui::{ use rm_config::CONFIG; use tui_input::{backend::crossterm::to_input_request, Input, InputResponse}; -use crate::tui::ui::components::Component; +use crate::tui::components::Component; pub struct InputManager { input: Input, diff --git a/rm-main/src/tui/ui/tabs/torrents/mod.rs b/rm-main/src/tui/tabs/torrents/mod.rs similarity index 99% rename from rm-main/src/tui/ui/tabs/torrents/mod.rs rename to rm-main/src/tui/tabs/torrents/mod.rs index 7e2c699..56a0624 100644 --- a/rm-main/src/tui/ui/tabs/torrents/mod.rs +++ b/rm-main/src/tui/tabs/torrents/mod.rs @@ -8,7 +8,7 @@ pub mod tasks; use crate::transmission::TorrentAction; use crate::tui::app; -use crate::tui::ui::components::{Component, ComponentAction}; +use crate::tui::components::{Component, ComponentAction}; use popups::stats::StatisticsPopup; use ratatui::prelude::*; diff --git a/rm-main/src/tui/ui/tabs/torrents/popups/files.rs b/rm-main/src/tui/tabs/torrents/popups/files.rs similarity index 99% rename from rm-main/src/tui/ui/tabs/torrents/popups/files.rs rename to rm-main/src/tui/tabs/torrents/popups/files.rs index cc29998..7c801c2 100644 --- a/rm-main/src/tui/ui/tabs/torrents/popups/files.rs +++ b/rm-main/src/tui/tabs/torrents/popups/files.rs @@ -17,10 +17,8 @@ use crate::{ transmission::TorrentAction, tui::{ app, - ui::{ - centered_rect, - components::{Component, ComponentAction}, - }, + components::{Component, ComponentAction}, + main_window::centered_rect, }, }; use rm_shared::{ diff --git a/rm-main/src/tui/ui/tabs/torrents/popups/mod.rs b/rm-main/src/tui/tabs/torrents/popups/mod.rs similarity index 97% rename from rm-main/src/tui/ui/tabs/torrents/popups/mod.rs rename to rm-main/src/tui/tabs/torrents/popups/mod.rs index ee68e87..e3874d2 100644 --- a/rm-main/src/tui/ui/tabs/torrents/popups/mod.rs +++ b/rm-main/src/tui/tabs/torrents/popups/mod.rs @@ -1,6 +1,6 @@ use crate::tui::{ app, - ui::components::{Component, ComponentAction}, + components::{Component, ComponentAction}, }; use self::{files::FilesPopup, stats::StatisticsPopup}; diff --git a/rm-main/src/tui/ui/tabs/torrents/popups/stats.rs b/rm-main/src/tui/tabs/torrents/popups/stats.rs similarity index 97% rename from rm-main/src/tui/ui/tabs/torrents/popups/stats.rs rename to rm-main/src/tui/tabs/torrents/popups/stats.rs index 2df6052..9f03a47 100644 --- a/rm-main/src/tui/ui/tabs/torrents/popups/stats.rs +++ b/rm-main/src/tui/tabs/torrents/popups/stats.rs @@ -13,9 +13,9 @@ use transmission_rpc::types::SessionStats; use rm_shared::{action::Action, utils::bytes_to_human_format}; -use crate::tui::ui::{ - centered_rect, +use crate::tui::{ components::{Component, ComponentAction}, + main_window::centered_rect, }; pub struct StatisticsPopup { diff --git a/rm-main/src/tui/ui/tabs/torrents/rustmission_torrent.rs b/rm-main/src/tui/tabs/torrents/rustmission_torrent.rs similarity index 100% rename from rm-main/src/tui/ui/tabs/torrents/rustmission_torrent.rs rename to rm-main/src/tui/tabs/torrents/rustmission_torrent.rs diff --git a/rm-main/src/tui/ui/tabs/torrents/table_manager.rs b/rm-main/src/tui/tabs/torrents/table_manager.rs similarity index 99% rename from rm-main/src/tui/ui/tabs/torrents/table_manager.rs rename to rm-main/src/tui/tabs/torrents/table_manager.rs index f868cee..69017a9 100644 --- a/rm-main/src/tui/ui/tabs/torrents/table_manager.rs +++ b/rm-main/src/tui/tabs/torrents/table_manager.rs @@ -4,7 +4,7 @@ use rm_config::CONFIG; use rm_shared::header::Header; use std::collections::HashMap; -use crate::tui::ui::components::table::GenericTable; +use crate::tui::components::table::GenericTable; use super::rustmission_torrent::RustmissionTorrent; diff --git a/rm-main/src/tui/ui/tabs/torrents/task_manager.rs b/rm-main/src/tui/tabs/torrents/task_manager.rs similarity index 99% rename from rm-main/src/tui/ui/tabs/torrents/task_manager.rs rename to rm-main/src/tui/tabs/torrents/task_manager.rs index 6a64515..bc6a7e4 100644 --- a/rm-main/src/tui/ui/tabs/torrents/task_manager.rs +++ b/rm-main/src/tui/tabs/torrents/task_manager.rs @@ -9,7 +9,7 @@ use rm_shared::{ use crate::tui::{ app, - ui::components::{Component, ComponentAction}, + components::{Component, ComponentAction}, }; use super::{ diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/add_magnet.rs b/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs similarity index 95% rename from rm-main/src/tui/ui/tabs/torrents/tasks/add_magnet.rs rename to rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs index 906bd6b..54bf916 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/add_magnet.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs @@ -5,10 +5,8 @@ use crate::{ transmission::TorrentAction, tui::{ app, - ui::{ - components::{Component, ComponentAction}, - tabs::torrents::input_manager::InputManager, - }, + components::{Component, ComponentAction}, + tabs::torrents::input_manager::InputManager, }, }; use rm_shared::{ diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/default.rs b/rm-main/src/tui/tabs/torrents/tasks/default.rs similarity index 92% rename from rm-main/src/tui/ui/tabs/torrents/tasks/default.rs rename to rm-main/src/tui/tabs/torrents/tasks/default.rs index 2209fc5..40126e1 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/default.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/default.rs @@ -1,7 +1,7 @@ use ratatui::prelude::*; use rm_config::CONFIG; -use crate::tui::ui::components::Component; +use crate::tui::components::Component; pub struct DefaultBar {} diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/delete_torrent.rs b/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs similarity index 95% rename from rm-main/src/tui/ui/tabs/torrents/tasks/delete_torrent.rs rename to rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs index 4b5204c..d97cf28 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/delete_torrent.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs @@ -4,8 +4,8 @@ use transmission_rpc::types::Id; use crate::transmission::TorrentAction; use crate::tui::app; -use crate::tui::ui::components::{Component, ComponentAction}; -use crate::tui::ui::tabs::torrents::input_manager::InputManager; +use crate::tui::components::{Component, ComponentAction}; +use crate::tui::tabs::torrents::input_manager::InputManager; use rm_shared::action::{Action, UpdateAction}; use rm_shared::status_task::StatusTask; diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/filter.rs b/rm-main/src/tui/tabs/torrents/tasks/filter.rs similarity index 91% rename from rm-main/src/tui/ui/tabs/torrents/tasks/filter.rs rename to rm-main/src/tui/tabs/torrents/tasks/filter.rs index 1b42129..bc4c727 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/filter.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/filter.rs @@ -5,10 +5,8 @@ use rm_shared::action::{Action, UpdateAction}; use crate::tui::{ app, - ui::{ - components::{Component, ComponentAction}, - tabs::torrents::{input_manager::InputManager, table_manager::Filter}, - }, + components::{Component, ComponentAction}, + tabs::torrents::{input_manager::InputManager, table_manager::Filter}, }; pub struct FilterBar { diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/mod.rs b/rm-main/src/tui/tabs/torrents/tasks/mod.rs similarity index 100% rename from rm-main/src/tui/ui/tabs/torrents/tasks/mod.rs rename to rm-main/src/tui/tabs/torrents/tasks/mod.rs diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/move_torrent.rs b/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs similarity index 93% rename from rm-main/src/tui/ui/tabs/torrents/tasks/move_torrent.rs rename to rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs index d84537a..c456ac9 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/move_torrent.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs @@ -10,10 +10,8 @@ use crate::{ transmission::TorrentAction, tui::{ app, - ui::{ - components::{Component, ComponentAction}, - tabs::torrents::input_manager::InputManager, - }, + components::{Component, ComponentAction}, + tabs::torrents::input_manager::InputManager, }, }; diff --git a/rm-main/src/tui/ui/tabs/torrents/tasks/status.rs b/rm-main/src/tui/tabs/torrents/tasks/status.rs similarity index 98% rename from rm-main/src/tui/ui/tabs/torrents/tasks/status.rs rename to rm-main/src/tui/tabs/torrents/tasks/status.rs index bdf3849..1a124eb 100644 --- a/rm-main/src/tui/ui/tabs/torrents/tasks/status.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/status.rs @@ -6,7 +6,7 @@ use rm_shared::{ use throbber_widgets_tui::ThrobberState; use tokio::time::{self, Instant}; -use crate::tui::{app, ui::components::Component}; +use crate::tui::{app, components::Component}; pub struct StatusBar { task: StatusTask, diff --git a/rm-main/src/tui/terminal.rs b/rm-main/src/tui/terminal.rs new file mode 100644 index 0000000..35c31f7 --- /dev/null +++ b/rm-main/src/tui/terminal.rs @@ -0,0 +1,107 @@ +use std::{io, time::Duration}; + +use anyhow::Result; +use crossterm::{ + cursor, + event::{Event, KeyEventKind}, + terminal::{EnterAlternateScreen, LeaveAlternateScreen}, +}; +use futures::{FutureExt, StreamExt}; +use ratatui::{backend::CrosstermBackend as Backend, Terminal}; +use tokio::{ + sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, + task::JoinHandle, +}; +use tokio_util::sync::CancellationToken; + +pub struct Tui { + pub terminal: Terminal>, + pub task: JoinHandle>, + pub cancellation_token: CancellationToken, + pub event_rx: UnboundedReceiver, + pub event_tx: UnboundedSender, +} + +impl Tui { + pub(crate) fn new() -> Result { + let terminal = Terminal::new(Backend::new(std::io::stdout()))?; + let (event_tx, event_rx) = mpsc::unbounded_channel(); + let cancellation_token = CancellationToken::new(); + let task = tokio::spawn(async { Ok(()) }); + Ok(Self { + terminal, + task, + cancellation_token, + event_rx, + event_tx, + }) + } + + fn handle_crossterm_event( + event: Option>, + event_tx: &UnboundedSender, + ) -> Result<()> { + match event { + Some(Ok(Event::Key(key))) => { + if key.kind == KeyEventKind::Press { + event_tx.send(Event::Key(key)).unwrap(); + } + } + Some(Ok(Event::Resize(x, y))) => event_tx.send(Event::Resize(x, y)).unwrap(), + Some(Err(e)) => Err(e)?, + _ => (), + } + Ok(()) + } + + pub(crate) fn enter(&mut self) -> Result<()> { + crossterm::terminal::enable_raw_mode()?; + crossterm::execute!(std::io::stdout(), EnterAlternateScreen, cursor::Hide)?; + self.start()?; + Ok(()) + } + + pub fn start(&mut self) -> Result<()> { + self.cancellation_token = CancellationToken::new(); + let cancellation_token = self.cancellation_token.clone(); + let event_tx = self.event_tx.clone(); + + self.task = tokio::spawn(async move { + let mut reader = crossterm::event::EventStream::new(); + loop { + let crossterm_event = reader.next().fuse(); + tokio::select! { + _ = cancellation_token.cancelled() => break, + event = crossterm_event => Self::handle_crossterm_event(event, &event_tx)?, + } + } + Ok(()) + }); + Ok(()) + } + + pub(crate) fn exit(&mut self) -> Result<()> { + self.cancellation_token.cancel(); + let mut counter = 0; + while !self.task.is_finished() { + std::thread::sleep(Duration::from_millis(1)); + counter += 1; + if counter > 50 { + self.task.abort(); + } + if counter > 100 { + break; + } + } + if crossterm::terminal::is_raw_mode_enabled()? { + self.terminal.flush()?; + crossterm::execute!(std::io::stdout(), LeaveAlternateScreen, cursor::Show)?; + crossterm::terminal::disable_raw_mode()?; + } + Ok(()) + } + + pub async fn next(&mut self) -> Option { + self.event_rx.recv().await + } +}