Seems to show things
This commit is contained in:
parent
d4f42ec6aa
commit
66c3ef49ac
13 changed files with 4239 additions and 2 deletions
189
src/file_select.rs
Normal file
189
src/file_select.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Style},
|
||||
text::Line,
|
||||
widgets::{StatefulWidget, Widget},
|
||||
};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
env, fs, io, mem,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::{config::Config, directory_operations};
|
||||
|
||||
pub enum ScrollingDirection {
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
pub struct FilesWidget;
|
||||
pub struct FilesState {
|
||||
selected: Option<usize>,
|
||||
top: usize,
|
||||
files: Vec<PathBuf>,
|
||||
current_dir: PathBuf,
|
||||
config: Rc<RefCell<Config>>,
|
||||
}
|
||||
|
||||
impl FilesState {
|
||||
pub fn get_current(&self) -> Option<&Path> {
|
||||
Some(self.files[self.selected?].as_path())
|
||||
}
|
||||
|
||||
pub fn scroll(&mut self, direction: ScrollingDirection) {
|
||||
if self.files.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match direction {
|
||||
ScrollingDirection::Down => self.scroll_down(),
|
||||
ScrollingDirection::Up => self.scroll_up(),
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_down(&mut self) {
|
||||
match self.selected {
|
||||
Some(v) => self.selected = Some((v + 1).min(self.files.len() - 1)),
|
||||
None => self.selected = Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_up(&mut self) {
|
||||
self.selected = Some(self.selected.unwrap_or(0).saturating_sub(1));
|
||||
}
|
||||
|
||||
pub fn go_up(&mut self) -> io::Result<()> {
|
||||
let new_path = self.current_dir.parent().unwrap_or(Path::new("/"));
|
||||
if !directory_operations::can_read(new_path) {
|
||||
return Ok(());
|
||||
}
|
||||
self.current_dir.pop();
|
||||
self.update_contents()
|
||||
}
|
||||
|
||||
fn update_contents(&mut self) -> io::Result<()> {
|
||||
self.files = fs::read_dir(&self.current_dir)?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.map(|entry| entry.path())
|
||||
.collect();
|
||||
|
||||
directory_operations::sort_dir(&self.config.borrow(), &mut self.files);
|
||||
|
||||
self.selected = if self.files.is_empty() { None } else { Some(0) };
|
||||
self.top = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn go_inner(&mut self) -> io::Result<()> {
|
||||
let Some(selected) = self.selected else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if !self.files[selected].is_dir() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !directory_operations::can_read(self.files[selected].as_path()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.current_dir = self.files.swap_remove(selected);
|
||||
self.update_contents()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn toggle_hidden(&mut self) -> io::Result<()> {
|
||||
let current_state = self.config.borrow().show_hidden;
|
||||
self.set_hidden(!current_state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_hidden(&mut self, state: bool) -> io::Result<()> {
|
||||
{
|
||||
let mut cfg = self.config.borrow_mut();
|
||||
cfg.show_hidden = state;
|
||||
}
|
||||
|
||||
self.update_contents()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new(config: Rc<RefCell<Config>>) -> io::Result<Self> {
|
||||
let current_dir = env::current_dir()?;
|
||||
|
||||
let mut output = FilesState {
|
||||
selected: None,
|
||||
files: vec![],
|
||||
top: 0,
|
||||
current_dir,
|
||||
config,
|
||||
};
|
||||
|
||||
output.update_contents()?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidget for FilesWidget {
|
||||
type State = FilesState;
|
||||
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(area);
|
||||
|
||||
let visible_rows = layout[0].height as usize;
|
||||
|
||||
if let Some(selected) = state.selected {
|
||||
if selected < state.top {
|
||||
state.top = selected;
|
||||
} else if selected >= state.top + visible_rows {
|
||||
state.top = selected - visible_rows + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut items: Vec<Line> = state
|
||||
.files
|
||||
.iter()
|
||||
.skip(state.top)
|
||||
.take(visible_rows)
|
||||
.map(|file| {
|
||||
Line::default().spans(vec![
|
||||
" ",
|
||||
file.file_name()
|
||||
.expect("Files should have a valid name")
|
||||
.to_str()
|
||||
.expect("Files should have a valid name"),
|
||||
])
|
||||
})
|
||||
.collect();
|
||||
|
||||
let relative_selected = state.selected.map(|s| s - state.top);
|
||||
|
||||
if let Some(index) = relative_selected
|
||||
&& let Some(item) = items.get_mut(index)
|
||||
{
|
||||
let mut old = mem::take(item);
|
||||
|
||||
old = old.patch_style(Style::default().fg(Color::Blue));
|
||||
old.spans[0] = "> ".into();
|
||||
|
||||
*item = old;
|
||||
}
|
||||
|
||||
let rows_layout =
|
||||
Layout::vertical(vec![Constraint::Length(1); visible_rows]).split(layout[0]);
|
||||
for (item, area) in items.into_iter().zip(rows_layout.iter()) {
|
||||
item.render(*area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue