diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 8132f6b9..2d2459cb 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -337,166 +337,6 @@ where Ok(()) } -/// State and methods representing the interactive disk usage analyser for the terminal -pub struct TerminalApp { - pub traversal: Traversal, - pub display: DisplayOptions, - pub state: AppState, - pub window: MainWindow, -} - -type KeyboardInputAndApp = (crossbeam::channel::Receiver, TerminalApp); - -impl TerminalApp { - pub fn refresh_view(&mut self, terminal: &mut Terminal) - where - B: Backend, - { - // Use an event that does nothing to trigger a refresh - self.state - .process_events( - &mut self.window, - &mut self.traversal, - &mut self.display, - terminal, - std::iter::once(Event::Key(refresh_key())), - ) - .ok(); - } - - pub fn process_events( - &mut self, - terminal: &mut Terminal, - events: impl Iterator, - ) -> Result - where - B: Backend, - { - match self.state.process_events( - &mut self.window, - &mut self.traversal, - &mut self.display, - terminal, - events, - )? { - ProcessingResult::Finished(res) | ProcessingResult::ExitRequested(res) => Ok(res), - } - } - - pub fn initialize( - terminal: &mut Terminal, - options: WalkOptions, - input_paths: Vec, - keys_rx: Receiver, - ) -> Result> - where - B: Backend, - { - terminal.hide_cursor()?; - terminal.clear()?; - - let mut display: DisplayOptions = options.clone().into(); - display.byte_vis = ByteVisualization::PercentageAndBar; - - let mut window = MainWindow::default(); - - // #[inline] - // fn fetch_buffered_key_events(keys_rx: &Receiver) -> Vec { - // let mut keys = Vec::new(); - // while let Ok(key) = keys_rx.try_recv() { - // keys.push(key); - // } - // keys - // } - - let mut state = AppState { - is_scanning: false, - ..Default::default() - }; - - // let mut received_events = false; - // let traversal = - // Traversal::from_walk(options, input_paths, &keys_rx, |traversal, event| { - // if !received_events { - // state.navigation_mut().view_root = traversal.root_index; - // } - // state.entries = sorted_entries( - // &traversal.tree, - // state.navigation().view_root, - // state.sorting, - // state.glob_root(), - // ); - // if !received_events { - // state.navigation_mut().selected = state.entries.first().map(|b| b.index); - // } - // state.reset_message(); // force "scanning" to appear - - // let mut events = fetch_buffered_key_events(&keys_rx); - // if let Some(event) = event { - // // This update is triggered by a user event, insert it - // // before any events fetched later. - // events.insert(0, event); - // } - // received_events |= !events.is_empty(); - - // let should_exit = match state.process_events( - // &mut window, - // traversal, - // &mut display, - // terminal, - // events.into_iter(), - // )? { - // ProcessingResult::ExitRequested(_) => true, - // ProcessingResult::Finished(_) => false, - // }; - - // Ok(should_exit) - // })?; - - // let traversal = match traversal { - // Some(t) => t, - // None => return Ok(None), - // }; - - // state.is_scanning = false; - // if !received_events { - // } - - let traversal = { - let mut tree = Tree::new(); - let root_index = tree.add_node(EntryData::default()); - Traversal { - tree, - root_index, - entries_traversed: 0, - start: std::time::Instant::now(), - elapsed: None, - io_errors: 0, - total_bytes: None, - } - }; - - state.navigation_mut().view_root = traversal.root_index; - state.entries = sorted_entries( - &traversal.tree, - state.navigation().view_root, - state.sorting, - state.glob_root(), - ); - state.navigation_mut().selected = state.entries.first().map(|b| b.index); - - let mut app = TerminalApp { - state, - display, - traversal, - window, - }; - app.refresh_view(terminal); - - Ok(Some((keys_rx, app))) - } -} - -fn refresh_key() -> KeyEvent { +pub fn refresh_key() -> KeyEvent { KeyEvent::new(KeyCode::Char('\r'), KeyModifiers::ALT) } diff --git a/src/interactive/app/mod.rs b/src/interactive/app/mod.rs index 8d27e466..73eb2f26 100644 --- a/src/interactive/app/mod.rs +++ b/src/interactive/app/mod.rs @@ -5,6 +5,7 @@ mod handlers; pub mod input; mod navigation; pub mod tree_view; +pub mod terminal_app; pub use bytevis::*; pub use common::*; diff --git a/src/interactive/app/terminal_app.rs b/src/interactive/app/terminal_app.rs new file mode 100644 index 00000000..b19bf1b0 --- /dev/null +++ b/src/interactive/app/terminal_app.rs @@ -0,0 +1,173 @@ +use std::path::PathBuf; + +use crossbeam::channel::Receiver; +use crosstermion::input::Event; +use dua::{traverse::{Traversal, Tree, EntryData}, WalkResult, WalkOptions}; +use tui::prelude::Backend; +use tui_react::Terminal; +use anyhow::Result; + +use crate::interactive::widgets::MainWindow; + +use super::{DisplayOptions, AppState, ProcessingResult, ByteVisualization, sorted_entries, refresh_key}; + + +/// State and methods representing the interactive disk usage analyser for the terminal +pub struct TerminalApp { + pub traversal: Traversal, + pub display: DisplayOptions, + pub state: AppState, + pub window: MainWindow, +} + +type KeyboardInputAndApp = (crossbeam::channel::Receiver, TerminalApp); + +impl TerminalApp { + pub fn refresh_view(&mut self, terminal: &mut Terminal) + where + B: Backend, + { + // Use an event that does nothing to trigger a refresh + self.state + .process_events( + &mut self.window, + &mut self.traversal, + &mut self.display, + terminal, + std::iter::once(Event::Key(refresh_key())), + ) + .ok(); + } + + pub fn process_events( + &mut self, + terminal: &mut Terminal, + events: impl Iterator, + ) -> Result + where + B: Backend, + { + match self.state.process_events( + &mut self.window, + &mut self.traversal, + &mut self.display, + terminal, + events, + )? { + ProcessingResult::Finished(res) | ProcessingResult::ExitRequested(res) => Ok(res), + } + } + + pub fn initialize( + terminal: &mut Terminal, + options: WalkOptions, + input_paths: Vec, + keys_rx: Receiver, + ) -> Result> + where + B: Backend, + { + terminal.hide_cursor()?; + terminal.clear()?; + + let mut display: DisplayOptions = options.clone().into(); + display.byte_vis = ByteVisualization::PercentageAndBar; + + let mut window = MainWindow::default(); + + // #[inline] + // fn fetch_buffered_key_events(keys_rx: &Receiver) -> Vec { + // let mut keys = Vec::new(); + // while let Ok(key) = keys_rx.try_recv() { + // keys.push(key); + // } + // keys + // } + + let mut state = AppState { + is_scanning: false, + ..Default::default() + }; + + // let mut received_events = false; + // let traversal = + // Traversal::from_walk(options, input_paths, &keys_rx, |traversal, event| { + // if !received_events { + // state.navigation_mut().view_root = traversal.root_index; + // } + // state.entries = sorted_entries( + // &traversal.tree, + // state.navigation().view_root, + // state.sorting, + // state.glob_root(), + // ); + // if !received_events { + // state.navigation_mut().selected = state.entries.first().map(|b| b.index); + // } + // state.reset_message(); // force "scanning" to appear + + // let mut events = fetch_buffered_key_events(&keys_rx); + // if let Some(event) = event { + // // This update is triggered by a user event, insert it + // // before any events fetched later. + // events.insert(0, event); + // } + // received_events |= !events.is_empty(); + + // let should_exit = match state.process_events( + // &mut window, + // traversal, + // &mut display, + // terminal, + // events.into_iter(), + // )? { + // ProcessingResult::ExitRequested(_) => true, + // ProcessingResult::Finished(_) => false, + // }; + + // Ok(should_exit) + // })?; + + // let traversal = match traversal { + // Some(t) => t, + // None => return Ok(None), + // }; + + // state.is_scanning = false; + // if !received_events { + // } + + let traversal = { + let mut tree = Tree::new(); + let root_index = tree.add_node(EntryData::default()); + Traversal { + tree, + root_index, + entries_traversed: 0, + start: std::time::Instant::now(), + elapsed: None, + io_errors: 0, + total_bytes: None, + } + }; + + state.navigation_mut().view_root = traversal.root_index; + state.entries = sorted_entries( + &traversal.tree, + state.navigation().view_root, + state.sorting, + state.glob_root(), + ); + state.navigation_mut().selected = state.entries.first().map(|b| b.index); + + let mut app = TerminalApp { + state, + display, + traversal, + window, + }; + app.refresh_view(terminal); + + Ok(Some((keys_rx, app))) + } +} diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs index f8657507..82cf00bc 100644 --- a/src/interactive/app/tests/utils.rs +++ b/src/interactive/app/tests/utils.rs @@ -18,7 +18,7 @@ use std::{ use tui::backend::TestBackend; use tui_react::Terminal; -use crate::interactive::{app::tests::FIXTURE_PATH, TerminalApp}; +use crate::interactive::{app::tests::FIXTURE_PATH, terminal_app::TerminalApp}; pub fn into_keys<'a>( codes: impl IntoIterator + 'a, diff --git a/src/main.rs b/src/main.rs index 6b9ffa33..636b01a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use std::fs::OpenOptions; use std::{fs, io, io::Write, path::PathBuf, process}; use crate::interactive::input::input_channel; +use crate::interactive::terminal_app::TerminalApp; mod crossdev; #[cfg(feature = "tui-crossplatform")] @@ -53,7 +54,6 @@ fn main() -> Result<()> { let res = match opt.command { #[cfg(feature = "tui-crossplatform")] Some(Interactive { input }) => { - use crate::interactive::{TerminalApp}; use anyhow::{anyhow, Context}; use crosstermion::terminal::{tui::new_terminal, AlternateRawScreen};