diff --git a/src/im_gonna_try_better.rs b/src/im_gonna_try_better.rs index e383453..500c956 100644 --- a/src/im_gonna_try_better.rs +++ b/src/im_gonna_try_better.rs @@ -1,61 +1,277 @@ -use std::{env, fs, path}; - -use crossterm::event::{self, Event, KeyCode}; -use ratatui::{ - style::{Color, Style, Stylize}, - widgets::{Block, Borders, List, ListState, Padding}, +use std::{ + env::{self}, + fs, io, + path::PathBuf, }; -fn list_dir(path: &str) -> Vec { - let paths = fs::read_dir(path).unwrap(); - let mut array_of_dirs: Vec = Vec::new(); +use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{ + DefaultTerminal, Frame, + buffer::Buffer, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Stylize}, + widgets::{Block, Borders, List, ListState, Padding, StatefulWidget}, +}; - for path in paths { - array_of_dirs.push(path.unwrap().path().display().to_string()); +type E = std::io::Error; + +struct App<'a> { + window: Window<'a>, + preview: Preview<'a>, + exit: bool, +} + +impl App<'_> { + fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> { + while !self.exit { + terminal.draw(|frame| self.draw(frame))?; + self.handle_events()?; + } + Ok(()) } - array_of_dirs -} + fn draw(&mut self, frame: &mut Frame) { + let main_area = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(frame.area()); + self.window.render(main_area[0], frame.buffer_mut()); + self.preview.render(main_area[1], frame.buffer_mut()); + } -fn create_list<'a>(ls_result: Vec) -> List<'a> { - let block = Block::new() - .borders(Borders::ALL) - .padding(Padding::symmetric(1, 1)) - .fg(Color::Blue); - - List::new(ls_result) - .block(block) - .highlight_style(Style::new().reversed()) -} - -fn get_events(state: ListState) { - match event::read().expect("failed to read event") { - Event::Key(key_event) => match key_event.code { - KeyCode::Esc => { - break; + fn handle_events(&mut self) -> io::Result<()> { + match event::read()? { + Event::Key(key_event) if key_event.kind == KeyEventKind::Press => { + self.handle_key_events(key_event) } + _ => {} + }; + Ok(()) + } + + fn handle_key_events(&mut self, key_event: KeyEvent) { + match key_event.code { + KeyCode::Char('q') | KeyCode::Esc => self.exit(), KeyCode::Down => { - state.select_next(); + self.window.state.select_next(); + self.preview.the_final_stretch(); } - KeyCode::Up => { - state.select_previous(); + self.window.state.select_previous(); + self.preview.the_final_stretch(); } + KeyCode::Left => { + if &self.window.absolute_path != "/" { + self.preview.absolute_path = self.window.absolute_path.clone(); + } + self.window.absolute_path.pop(); + self.window.update(); + self.preview.state = self.window.state; + self.preview.muhehe(); + } + KeyCode::Right => { + if let Some(val) = self.window.state.selected() { + let current_dir = self + .window + .prepare_updated_list() + .expect("You should always be in a valid directory"); - KeyCode::Left => (), + self.window.absolute_path.push(current_dir[val].clone()); + let output = self.window.update(); + if !output { + self.window.absolute_path.pop(); + } + } + } + _ => {} + } + } - KeyCode::Right => (), - - _ => (), - }, - - _ => (), + fn exit(&mut self) { + self.exit = true; } } -fn main() { - let mut path = env::current_dir().unwrap(); - let mut terminal = ratatui::init(); - let mut state = ListState::default(); +struct Window<'a> { + widget: WindowWidget<'a>, + absolute_path: PathBuf, + state: ListState, +} + +impl Window<'_> { + fn new(absolute_path: PathBuf) -> Result { + Ok(Window { + widget: WindowWidget::new(Self::prepare_list(&absolute_path)?), + absolute_path, + state: ListState::default().with_selected(Some(0)), + }) + } + + fn render(&mut self, area: Rect, buf: &mut Buffer) { + self.widget.render(area, buf, &mut self.state); + } + + fn prepare_list(absolute_path: &PathBuf) -> Result, E> { + let entries = fs::read_dir(absolute_path)?; + let mut prepared_list = Vec::::new(); + + for item in entries { + match item { + Ok(entry) => { + let path_string = entry.path().display().to_string(); + prepared_list.push(path_string); + } + Err(err) => return Err(err), + } + } + + Ok(prepared_list) + } + + fn prepare_updated_list(&self) -> Result, E> { + Self::prepare_list(&self.absolute_path) + } + + fn update(&mut self) -> bool { + let values = self.prepare_updated_list(); + + if let Ok(val) = values { + self.widget.update(val); + self.state.select_first(); + return true; + } + + false + } +} + +struct WindowWidget<'a> { + list: List<'a>, +} + +impl WindowWidget<'_> { + fn update(&mut self, prepared_list: Vec) { + let block = Block::new() + .borders(Borders::ALL) + .padding(Padding::symmetric(1, 1)) + .fg(Color::Blue); + + self.list = List::new(prepared_list) + .block(block) + .highlight_style(Color::Cyan); + } + + fn new(prepared_list: Vec) -> Self { + let mut window_widget = WindowWidget { + list: List::new(Vec::::new()), + }; + + window_widget.update(prepared_list); + window_widget + } +} + +impl StatefulWidget for &mut WindowWidget<'_> { + type State = ListState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut ListState) { + StatefulWidget::render(&self.list, area, buf, state); + } +} + +struct Preview<'a> { + widget: PreviewWidget<'a>, + absolute_path: PathBuf, + state: ListState, +} + +impl Preview<'_> { + fn the_final_stretch(&mut self) { + self.absolute_path.push( + Self::prepare_list(&self.absolute_path).unwrap()[self.state.selected().unwrap()] + .clone(), + ); + + self.widget + .update(Self::prepare_list(&self.absolute_path).unwrap()); + } + + fn muhehe(&mut self) { + self.widget + .update(Self::prepare_list(&self.absolute_path).unwrap()); + } + + fn prepare_list(absolute_path: &PathBuf) -> Result, E> { + let entries = fs::read_dir(absolute_path)?; + let mut prepared_list = Vec::::new(); + + for item in entries { + match item { + Ok(entry) => { + let path_string = entry.path().display().to_string(); + prepared_list.push(path_string); + } + Err(err) => return Err(err), + } + } + + Ok(prepared_list) + } + + fn render(&mut self, area: Rect, buf: &mut Buffer) { + self.widget.render(area, buf, &mut ListState::default()); + } +} + +struct PreviewWidget<'a> { + list: List<'a>, +} + +impl PreviewWidget<'_> { + fn update(&mut self, prepared_list: Vec) { + let block = Block::new() + .borders(Borders::ALL) + .padding(Padding::symmetric(1, 1)) + .fg(Color::Blue); + + self.list = List::new(prepared_list) + .block(block) + .highlight_style(Color::Cyan); + } + + fn new(prepared_list: Vec) -> Self { + let mut preview_widget = PreviewWidget { + list: List::new(Vec::::new()), + }; + + preview_widget.update(prepared_list); + preview_widget + } +} + +impl StatefulWidget for &mut PreviewWidget<'_> { + type State = ListState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut ListState) { + StatefulWidget::render(&self.list, area, buf, state); + } +} + +fn main() -> io::Result<()> { + let mut terminal = ratatui::init(); + let dir = Window::new(env::current_dir()?)?; + let view = env::current_dir()?; + let app_result = App { + window: dir, + preview: Preview { + widget: PreviewWidget::new(Preview::prepare_list(&view).unwrap()), + absolute_path: view, + state: ListState::default(), + }, + exit: false, + } + .run(&mut terminal); + ratatui::restore(); + app_result } diff --git a/src/main.rs b/src/main.rs index ea7ead2..e3cd70a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, buffer::Buffer, - layout::Rect, + layout::{Constraint, Direction, Layout, Rect}, style::{Color, Stylize}, widgets::{Block, Borders, List, ListState, Padding, StatefulWidget}, }; @@ -13,6 +13,7 @@ type E = std::io::Error; struct App<'a> { window: Window<'a>, + preview: Preview<'a>, exit: bool, } @@ -26,7 +27,12 @@ impl App<'_> { } fn draw(&mut self, frame: &mut Frame) { - self.window.render(frame.area(), frame.buffer_mut()); + let main_area = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(frame.area()); + self.window.render(main_area[0], frame.buffer_mut()); + self.preview.render(main_area[1], frame.buffer_mut()); } fn handle_events(&mut self) -> io::Result<()> { @@ -43,11 +49,18 @@ impl App<'_> { fn handle_key_events(&mut self, key_event: KeyEvent) { match key_event.code { KeyCode::Char('q') | KeyCode::Esc => self.exit(), - KeyCode::Down => self.window.state.select_next(), - KeyCode::Up => self.window.state.select_previous(), + KeyCode::Down => { + self.window.state.select_next(); + self.preview.see(&self.window); + } + KeyCode::Up => { + self.window.state.select_previous(); + self.preview.see(&self.window); + } KeyCode::Left => { self.window.absolute_path.pop(); self.window.update(); + self.preview.see(&self.window); } KeyCode::Right => { if let Some(val) = self.window.state.selected() { @@ -61,6 +74,7 @@ impl App<'_> { if !output { self.window.absolute_path.pop(); } + self.preview.see(&self.window); } } _ => {} @@ -159,11 +173,67 @@ impl StatefulWidget for &mut WindowWidget<'_> { } } +struct Preview<'a> { + widget: PreviewWidget<'a>, +} + +impl Preview<'_> { + fn render(&mut self, area: Rect, buf: &mut Buffer) { + self.widget.render(area, buf, &mut ListState::default()); + } + + fn see(&mut self, window: &Window) { + let a = Window::prepare_list(&window.absolute_path).unwrap(); + + self.widget.update( + Window::prepare_list(&PathBuf::from(a[window.state.selected().unwrap()].clone())) + .unwrap(), + ); + } +} + +struct PreviewWidget<'a> { + list: List<'a>, +} + +impl PreviewWidget<'_> { + fn update(&mut self, prepared_list: Vec) { + let block = Block::new() + .borders(Borders::ALL) + .padding(Padding::symmetric(1, 1)) + .fg(Color::Blue); + + self.list = List::new(prepared_list) + .block(block) + .highlight_style(Color::Cyan); + } + + fn new(prepared_list: Vec) -> Self { + let mut preview_widget = PreviewWidget { + list: List::new(Vec::::new()), + }; + + preview_widget.update(prepared_list); + preview_widget + } +} + +impl StatefulWidget for &mut PreviewWidget<'_> { + type State = ListState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut ListState) { + StatefulWidget::render(&self.list, area, buf, state); + } +} + fn main() -> io::Result<()> { let mut terminal = ratatui::init(); let dir = Window::new(env::current_dir()?)?; let app_result = App { window: dir, + preview: Preview { + widget: PreviewWidget::new(Window::prepare_list(&env::current_dir().unwrap()).unwrap()), + }, exit: false, } .run(&mut terminal);