Skip to content

Commit

Permalink
feat(FileBrowser): use a thread for io
Browse files Browse the repository at this point in the history
  • Loading branch information
lautarodragan committed Feb 7, 2025
1 parent 989d652 commit b7415d9
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/actions/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl Actions {
pub fn to_file(&self) {}

pub fn action_by_key(&self, key: KeyEvent) -> Vec<Action> {
log::debug!("action_by_key {key:?}");
// log::debug!("action_by_key {key:?}");

if let KeyCode::Char(c) = key.code
&& key.modifiers.is_empty()
Expand Down
59 changes: 49 additions & 10 deletions src/components/file_browser/file_browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use std::{
collections::HashMap,
path::PathBuf,
rc::Rc,
sync::{
mpsc::{channel, RecvTimeoutError},
Arc,
Mutex,
},
thread,
time::Duration,
};

use crate::{
Expand Down Expand Up @@ -38,6 +45,8 @@ pub struct FileBrowser<'a> {
pub(super) help: FileBrowserHelp<'a>,
pub(super) focus_group: FocusGroup<'a>,

pub(super) files_from_io_thread: Arc<Mutex<Vec<FileBrowserSelection>>>,

pub(super) history: Rc<RefCell<HashMap<PathBuf, (usize, usize)>>>,

pub(super) current_directory: Rc<CurrentDirectory>,
Expand All @@ -59,6 +68,40 @@ impl<'a> FileBrowser<'a> {
let on_add_to_playlist_fn: Rc<RefCell<Option<Box<dyn Fn(Vec<Song>) + 'a>>>> = Rc::new(RefCell::new(None));
let add_mode = Rc::new(Cell::new(AddMode::AddToLibrary));

let (io_thread, files_from_io_thread) = {
let (tx, rx) = channel::<PathBuf>();
let files_from_io_thread = Arc::new(Mutex::new(vec![]));
thread::spawn({
let files_from_io_thread = Arc::clone(&files_from_io_thread);
move || loop {
let Ok(mut path) = rx.recv() else {
log::trace!("FileBrowser's IO thread will close now.");
break;
};
log::trace!("FileBrowser's IO thread: received {path:?}");
let path = loop {
match rx.recv_timeout(Duration::from_millis(100)) {
Ok(p) => {
log::trace!("FileBrowser's IO thread: debounced path {p:?}");
path = p;
}
Err(RecvTimeoutError::Timeout) => {
log::trace!("FileBrowser's IO thread: will now process path {path:?}");
break path;
}
_ => {
log::trace!("FileBrowser's IO thread (inner loop) will close now.");
return;
}
}
};
let files = directory_to_songs_and_folders(path.as_path());
*files_from_io_thread.lock().unwrap() = files;
}
});
(tx, files_from_io_thread)
};

children_list.line_style(|i| match i {
FileBrowserSelection::Song(_) | FileBrowserSelection::CueSheet(_) => None,
_ => Some(ratatui::style::Style::new().add_modifier(ratatui::style::Modifier::DIM)),
Expand Down Expand Up @@ -143,18 +186,12 @@ impl<'a> FileBrowser<'a> {
let children_list = Rc::new(children_list);
parents_list.on_select({
let children_list = children_list.clone();
let file_meta = file_meta.clone();
// let file_meta = file_meta.clone();
move |item| {
if let FileBrowserSelection::Directory(path) = item {
let files = directory_to_songs_and_folders(path.as_path());

if let Some(f) = files.first() {
file_meta.set_file(f.clone());
} else {
file_meta.clear();
}

children_list.set_items(files);
if let Err(err) = io_thread.send(path.clone()) {
log::error!("FileBrowser: error sending path to IO thread {err:?}");
};
} else {
children_list.set_items(vec![]);
}
Expand Down Expand Up @@ -272,6 +309,8 @@ impl<'a> FileBrowser<'a> {
file_meta,
focus_group,

files_from_io_thread,

current_directory,
on_enqueue_fn,
on_add_to_lib_fn,
Expand Down
2 changes: 1 addition & 1 deletion src/components/file_browser/keyboard_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::{AddMode, FileBrowser};

impl OnActionMut for FileBrowser<'_> {
fn on_action(&mut self, actions: Vec<Action>) {
log::debug!("FB action {actions:?}");
// log::debug!("FB action {actions:?}");

if self.parents_list.filter().is_empty()
&& let Some(action) = actions.iter().find_map(|action| match action {
Expand Down
22 changes: 22 additions & 0 deletions src/components/file_browser/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ impl Display for AddMode {

impl WidgetRef for FileBrowser<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
if let Ok(mut files_from_io_thread) = self.files_from_io_thread.try_lock() {
// If the IO thread has something for us, and the lock is free, let's grab that stuff.
//
// We use `try_lock` instead of `lock` to avoid blocking the UI while the other thread holds the lock.
// A responsive UI with stale data is better UX than a choppy UI.
//
// Conceptually, this code doesn't belong in a "render" method.
// We should probably have a "component.update()" method or something.
// In practice, it'd be the same — we'll do this in the same thread that
// processes input and does the rendering, between those two.
if !files_from_io_thread.is_empty() {
let files = std::mem::take(&mut *files_from_io_thread);
if let Some(f) = files.first() {
self.file_meta.set_file(f.clone());
} else {
self.file_meta.clear();
}

self.children_list.set_items(files);
}
};

let [area_top, area_main, _, area_help] = Layout::vertical([
Constraint::Length(2),
Constraint::Min(10),
Expand Down

0 comments on commit b7415d9

Please sign in to comment.