From 3f3fe77d1679f867928d70d8e844f0041d26bf35 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 5 Jun 2019 12:05:32 +0530 Subject: [PATCH] First moderately working step towards react-tui mode --- src/interactive/app/eventloop.rs | 66 ++++++++++++-------------------- src/interactive/app_test.rs | 4 +- src/interactive/react.rs | 27 +++++-------- src/interactive/widgets/list.rs | 2 +- src/interactive/widgets/main.rs | 28 ++++++++------ src/main.rs | 4 +- src/traverse.rs | 2 +- 7 files changed, 57 insertions(+), 76 deletions(-) diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index a7a98171..7975d934 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -1,6 +1,7 @@ use crate::interactive::{ + react::Terminal, sorted_entries, - widgets::{DrawState, HelpPaneState, MainWindow}, + widgets::{HelpPaneState, ReactMainWindow}, ByteVisualization, DisplayOptions, EntryDataBundle, SortMode, }; use dua::{ @@ -13,7 +14,7 @@ use itertools::Itertools; use petgraph::Direction; use std::{io, path::PathBuf}; use termion::input::{Keys, TermReadEventsAndRaw}; -use tui::{backend::Backend, widgets::Widget, Terminal}; +use tui::backend::Backend; #[derive(Copy, Clone)] pub enum FocussedPane { @@ -43,7 +44,7 @@ pub struct TerminalApp { pub traversal: Traversal, pub display: DisplayOptions, pub state: AppState, - pub draw_state: DrawState, + pub window: ReactMainWindow, } enum CursorDirection { @@ -58,24 +59,9 @@ impl TerminalApp { where B: Backend, { - let Self { - traversal, - display, - state, - ref mut draw_state, - } = self; - - terminal.draw(|mut f| { - let full_screen = f.size(); - MainWindow { - traversal, - display: *display, - state: &state, - draw_state, - } - .render(&mut f, full_screen) - })?; - + let mut window = self.window.clone(); // TODO: fix this - we shouldn't have to pass ourselves as props! + terminal.render(&mut window, &*self)?; + self.window = window; Ok(()) } pub fn process_events( @@ -205,8 +191,8 @@ impl TerminalApp { fn scroll_help(&mut self, direction: CursorDirection) { use CursorDirection::*; - let scroll = self.draw_state.help_scroll; - self.draw_state.help_scroll = match direction { + let scroll = self.window.draw_state.help_scroll; // TODO: don't do this - make it private when ready + self.window.draw_state.help_scroll = match direction { Down => scroll.saturating_add(1), Up => scroll.saturating_sub(1), PageDown => scroll.saturating_add(10), @@ -247,24 +233,22 @@ impl TerminalApp { terminal.hide_cursor()?; let mut display_options: DisplayOptions = options.clone().into(); display_options.byte_vis = ByteVisualization::Bar; + let mut window = ReactMainWindow::default(); + let traversal = Traversal::from_walk(options, input, move |traversal| { - terminal.draw(|mut f| { - let full_screen = f.size(); - let state = AppState { - root: traversal.root_index, - sorting: Default::default(), - message: Some("-> scanning <-".into()), - ..Default::default() - }; - MainWindow { - traversal, - display: display_options, - state: &state, - draw_state: &mut Default::default(), - } - .render(&mut f, full_screen) - })?; - Ok(()) + let state = AppState { + root: traversal.root_index, + sorting: Default::default(), + message: Some("-> scanning <-".into()), + ..Default::default() + }; + let app = TerminalApp { + traversal: traversal.clone(), // TODO absolutely fix this! We should not rely on this anymore when done + display: display_options, + state, + window: Default::default(), + }; + terminal.render(&mut window, &app).map_err(Into::into) })?; let sorting = Default::default(); @@ -282,7 +266,7 @@ impl TerminalApp { }, display: display_options, traversal, - draw_state: Default::default(), + window: Default::default(), }) } } diff --git a/src/interactive/app_test.rs b/src/interactive/app_test.rs index 091dc414..2ebdcf4e 100644 --- a/src/interactive/app_test.rs +++ b/src/interactive/app_test.rs @@ -1,4 +1,4 @@ -use crate::interactive::{SortMode, TerminalApp}; +use crate::interactive::{react::Terminal, SortMode, TerminalApp}; use dua::{ traverse::{EntryData, Tree, TreeIndex}, ByteFormat, Color, TraversalSorting, WalkOptions, @@ -8,7 +8,7 @@ use petgraph::prelude::NodeIndex; use pretty_assertions::assert_eq; use std::{ffi::OsStr, ffi::OsString, fmt, path::Path, path::PathBuf}; use termion::input::TermRead; -use tui::{backend::TestBackend, Terminal}; +use tui::backend::TestBackend; const FIXTURE_PATH: &'static str = "tests/fixtures"; diff --git a/src/interactive/react.rs b/src/interactive/react.rs index 4c0a31f5..f0684e08 100644 --- a/src/interactive/react.rs +++ b/src/interactive/react.rs @@ -1,10 +1,8 @@ -#[allow(unused)] mod terminal { use log::error; use std::{borrow::Borrow, io}; - use std::borrow::BorrowMut; - use tui::{backend::Backend, buffer::Buffer, layout::Rect, widgets::Widget}; + use tui::{backend::Backend, buffer::Buffer, layout::Rect}; pub trait Component { type Props; @@ -17,7 +15,7 @@ mod terminal { where B: Backend, { - backend: B, + pub backend: B, buffers: [Buffer; 2], current: usize, hidden_cursor: bool, @@ -57,14 +55,6 @@ mod terminal { &mut self.buffers[self.current] } - pub fn backend(&self) -> &B { - &self.backend - } - - pub fn backend_mut(&mut self) -> &mut B { - &mut self.backend - } - pub fn reconcile_and_flush(&mut self) -> io::Result<()> { let previous_buffer = &self.buffers[1 - self.current]; let current_buffer = &self.buffers[self.current]; @@ -90,7 +80,7 @@ mod terminal { pub fn render( &mut self, - mut component: &mut C, + component: &mut C, props: impl Borrow, ) -> io::Result<()> where @@ -121,12 +111,15 @@ mod terminal { self.hidden_cursor = false; Ok(()) } + #[allow(unused)] pub fn get_cursor(&mut self) -> io::Result<(u16, u16)> { self.backend.get_cursor() } + #[allow(unused)] pub fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> { self.backend.set_cursor(x, y) } + #[allow(unused)] pub fn clear(&mut self) -> io::Result<()> { self.backend.clear() } @@ -157,14 +150,14 @@ mod terminal { impl Component for StatefulComponent { type Props = usize; - fn render(&mut self, props: impl Borrow, area: Rect, buf: &mut Buffer) { + fn render(&mut self, props: impl Borrow, _area: Rect, _buf: &mut Buffer) { self.x += *props.borrow(); } } impl Component for StatelessComponent { type Props = ComplexProps; - fn render(&mut self, props: impl Borrow, area: Rect, buf: &mut Buffer) { + fn render(&mut self, _props: impl Borrow, _area: Rect, _buf: &mut Buffer) { // does not matter - we want to see it compiles essentially } } @@ -174,11 +167,11 @@ mod terminal { let mut term = Terminal::new(TestBackend::new(20, 20)).unwrap(); let mut c = StatefulComponent::default(); - term.render(&mut c, 3usize); + term.render(&mut c, 3usize).ok(); assert_eq!(c.x, 3); let mut c = StatelessComponent::default(); - term.render(&mut c, ComplexProps::default()); + term.render(&mut c, ComplexProps::default()).ok(); } } } diff --git a/src/interactive/widgets/list.rs b/src/interactive/widgets/list.rs index 578781ec..787b56f4 100644 --- a/src/interactive/widgets/list.rs +++ b/src/interactive/widgets/list.rs @@ -5,7 +5,7 @@ use tui::{ widgets::{Block, Paragraph, Text, Widget}, }; -#[derive(Default)] +#[derive(Default, Clone)] // TODO: remove Clone derive pub struct ListState { /// The index at which the list last started. Used for scrolling pub start_index: usize, diff --git a/src/interactive/widgets/main.rs b/src/interactive/widgets/main.rs index 36a4df8a..8c14d803 100644 --- a/src/interactive/widgets/main.rs +++ b/src/interactive/widgets/main.rs @@ -1,8 +1,10 @@ use crate::interactive::{ + react::Component, widgets::{Entries, Footer, Header, HelpPane, ListState}, - AppState, DisplayOptions, FocussedPane, + FocussedPane, TerminalApp, }; use dua::traverse::Traversal; +use std::borrow::Borrow; use tui::style::{Color, Style}; use tui::{ buffer::Buffer, @@ -13,22 +15,22 @@ use tui::{ /// The state that can be mutated while drawing /// This is easiest compared to alternatives, but at least it's restricted to a subset of the state -#[derive(Default)] +#[derive(Default, Clone)] // TODO: remove Clone derive pub struct DrawState { entries_list: ListState, pub help_scroll: u16, } -pub struct MainWindow<'a, 'b, 'c> { - pub traversal: &'a Traversal, - pub display: DisplayOptions, - pub state: &'b AppState, - pub draw_state: &'c mut DrawState, +#[derive(Default, Clone)] // TODO: remove clone derive +pub struct ReactMainWindow { + pub draw_state: DrawState, } -impl<'a, 'b, 'c> Widget for MainWindow<'a, 'b, 'c> { - fn draw(&mut self, area: Rect, buf: &mut Buffer) { - let Self { +impl<'a, 'b> Component for ReactMainWindow { + type Props = TerminalApp; + + fn render(&mut self, props: impl Borrow, area: Rect, buf: &mut Buffer) { + let TerminalApp { traversal: Traversal { tree, @@ -38,8 +40,10 @@ impl<'a, 'b, 'c> Widget for MainWindow<'a, 'b, 'c> { }, display, state, - ref mut draw_state, - } = self; + .. + } = props.borrow(); + let draw_state = &mut self.draw_state; + let regions = Layout::default() .direction(Direction::Vertical) .constraints( diff --git a/src/main.rs b/src/main.rs index 8d58c0c2..7deb814a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,14 @@ extern crate failure; extern crate failure_tools; extern crate structopt; -use crate::interactive::TerminalApp; +use crate::interactive::{react::Terminal, TerminalApp}; use dua::{ByteFormat, Color, TraversalSorting}; use failure::{Error, ResultExt}; use failure_tools::ok_or_exit; use std::{fs, io, io::Write, path::PathBuf, process}; use structopt::StructOpt; use termion::{input::TermRead, raw::IntoRawMode, screen::AlternateScreen}; -use tui::{backend::TermionBackend, Terminal}; +use tui::backend::TermionBackend; mod interactive; mod options; diff --git a/src/traverse.rs b/src/traverse.rs index d6ef9578..aa60606e 100644 --- a/src/traverse.rs +++ b/src/traverse.rs @@ -19,7 +19,7 @@ pub struct EntryData { const REFRESH_RATE: Duration = Duration::from_millis(100); /// The result of the previous filesystem traversal -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] // TODO remove clone bound pub struct Traversal { /// A tree representing the entire filestem traversal pub tree: Tree,