This commit is contained in:
benstrb 2026-01-26 19:20:06 +01:00
parent 636c02ce59
commit c0c62ee964
2 changed files with 333 additions and 47 deletions

View file

@ -1,61 +1,277 @@
use std::{env, fs, path}; use std::{
env::{self},
use crossterm::event::{self, Event, KeyCode}; fs, io,
use ratatui::{ path::PathBuf,
style::{Color, Style, Stylize},
widgets::{Block, Borders, List, ListState, Padding},
}; };
fn list_dir(path: &str) -> Vec<String> { use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
let paths = fs::read_dir(path).unwrap(); use ratatui::{
let mut array_of_dirs: Vec<String> = Vec::new(); DefaultTerminal, Frame,
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Stylize},
widgets::{Block, Borders, List, ListState, Padding, StatefulWidget},
};
for path in paths { type E = std::io::Error;
array_of_dirs.push(path.unwrap().path().display().to_string());
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<String>) -> List<'a> { fn handle_events(&mut self) -> io::Result<()> {
let block = Block::new() match event::read()? {
.borders(Borders::ALL) Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
.padding(Padding::symmetric(1, 1)) self.handle_key_events(key_event)
.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;
} }
_ => {}
};
Ok(())
}
fn handle_key_events(&mut self, key_event: KeyEvent) {
match key_event.code {
KeyCode::Char('q') | KeyCode::Esc => self.exit(),
KeyCode::Down => { KeyCode::Down => {
state.select_next(); self.window.state.select_next();
self.preview.the_final_stretch();
} }
KeyCode::Up => { 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() { struct Window<'a> {
let mut path = env::current_dir().unwrap(); widget: WindowWidget<'a>,
let mut terminal = ratatui::init(); absolute_path: PathBuf,
let mut state = ListState::default(); state: ListState,
}
impl Window<'_> {
fn new(absolute_path: PathBuf) -> Result<Self, E> {
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<Vec<String>, E> {
let entries = fs::read_dir(absolute_path)?;
let mut prepared_list = Vec::<String>::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<Vec<String>, 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<String>) {
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<String>) -> Self {
let mut window_widget = WindowWidget {
list: List::new(Vec::<String>::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<Vec<String>, E> {
let entries = fs::read_dir(absolute_path)?;
let mut prepared_list = Vec::<String>::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<String>) {
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<String>) -> Self {
let mut preview_widget = PreviewWidget {
list: List::new(Vec::<String>::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
} }

View file

@ -4,7 +4,7 @@ use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{ use ratatui::{
DefaultTerminal, Frame, DefaultTerminal, Frame,
buffer::Buffer, buffer::Buffer,
layout::Rect, layout::{Constraint, Direction, Layout, Rect},
style::{Color, Stylize}, style::{Color, Stylize},
widgets::{Block, Borders, List, ListState, Padding, StatefulWidget}, widgets::{Block, Borders, List, ListState, Padding, StatefulWidget},
}; };
@ -13,6 +13,7 @@ type E = std::io::Error;
struct App<'a> { struct App<'a> {
window: Window<'a>, window: Window<'a>,
preview: Preview<'a>,
exit: bool, exit: bool,
} }
@ -26,7 +27,12 @@ impl App<'_> {
} }
fn draw(&mut self, frame: &mut Frame) { 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<()> { fn handle_events(&mut self) -> io::Result<()> {
@ -43,11 +49,18 @@ impl App<'_> {
fn handle_key_events(&mut self, key_event: KeyEvent) { fn handle_key_events(&mut self, key_event: KeyEvent) {
match key_event.code { match key_event.code {
KeyCode::Char('q') | KeyCode::Esc => self.exit(), KeyCode::Char('q') | KeyCode::Esc => self.exit(),
KeyCode::Down => self.window.state.select_next(), KeyCode::Down => {
KeyCode::Up => self.window.state.select_previous(), self.window.state.select_next();
self.preview.see(&self.window);
}
KeyCode::Up => {
self.window.state.select_previous();
self.preview.see(&self.window);
}
KeyCode::Left => { KeyCode::Left => {
self.window.absolute_path.pop(); self.window.absolute_path.pop();
self.window.update(); self.window.update();
self.preview.see(&self.window);
} }
KeyCode::Right => { KeyCode::Right => {
if let Some(val) = self.window.state.selected() { if let Some(val) = self.window.state.selected() {
@ -61,6 +74,7 @@ impl App<'_> {
if !output { if !output {
self.window.absolute_path.pop(); 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<String>) {
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<String>) -> Self {
let mut preview_widget = PreviewWidget {
list: List::new(Vec::<String>::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<()> { fn main() -> io::Result<()> {
let mut terminal = ratatui::init(); let mut terminal = ratatui::init();
let dir = Window::new(env::current_dir()?)?; let dir = Window::new(env::current_dir()?)?;
let app_result = App { let app_result = App {
window: dir, window: dir,
preview: Preview {
widget: PreviewWidget::new(Window::prepare_list(&env::current_dir().unwrap()).unwrap()),
},
exit: false, exit: false,
} }
.run(&mut terminal); .run(&mut terminal);