summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Zhao <jeff.no.zhao@gmail.com>2021-06-22 22:34:42 -0400
committerJeff Zhao <jeff.no.zhao@gmail.com>2021-06-22 22:34:42 -0400
commita11ed197df4fc3830387931c684346975333baf3 (patch)
treec11b81c58ff0ac52415684e1e383ebac92b94696
parent72aaf0c5d10db0004d48e27c58d18d8f2c568f8f (diff)
rudimentary file preview support
- this currently has only been tested with text files - no line formatting is done yet - only prints the preview as a single line - folder previews can now be pushed onto a separate thread if needed
-rw-r--r--src/context/app_context.rs19
-rw-r--r--src/context/mod.rs2
-rw-r--r--src/context/preview_context.rs27
-rw-r--r--src/context/worker_context.rs10
-rw-r--r--src/event.rs6
-rw-r--r--src/fs/dirlist.rs2
-rw-r--r--src/preview/preview_default.rs2
-rw-r--r--src/preview/preview_dir.rs60
-rw-r--r--src/preview/preview_file.rs190
-rw-r--r--src/ui/tui_backend.rs52
-rw-r--r--src/ui/views/tui_folder_view.rs44
-rw-r--r--src/ui/widgets/mod.rs2
-rw-r--r--src/ui/widgets/tui_file_preview.rs35
-rw-r--r--src/util/input.rs125
14 files changed, 406 insertions, 170 deletions
diff --git a/src/context/app_context.rs b/src/context/app_context.rs
index e03079a..5c206e5 100644
--- a/src/context/app_context.rs
+++ b/src/context/app_context.rs
@@ -2,16 +2,16 @@ use std::collections::VecDeque;
use std::sync::mpsc;
use crate::config;
-use crate::context::{LocalStateContext, TabContext, WorkerContext};
+use crate::context::{LocalStateContext, PreviewContext, TabContext, WorkerContext};
use crate::event::{AppEvent, Events};
use crate::util::search::SearchPattern;
pub struct AppContext {
pub exit: bool,
- // app config
- config: config::AppConfig,
// event loop querying
pub events: Events,
+ // app config
+ config: config::AppConfig,
// context related to tabs
tab_context: TabContext,
// context related to local file state
@@ -22,6 +22,8 @@ pub struct AppContext {
message_queue: VecDeque<String>,
// context related to io workers
worker_context: WorkerContext,
+ // context related to previews
+ preview_context: PreviewContext,
}
impl AppContext {
@@ -36,6 +38,7 @@ impl AppContext {
search_context: None,
message_queue: VecDeque::with_capacity(4),
worker_context: WorkerContext::new(event_tx),
+ preview_context: PreviewContext::new(),
config,
}
}
@@ -47,6 +50,9 @@ impl AppContext {
pub fn flush_event(&self) {
self.events.flush();
}
+ pub fn clone_event_tx(&self) -> mpsc::Sender<AppEvent> {
+ self.events.event_tx.clone()
+ }
pub fn config_ref(&self) -> &config::AppConfig {
&self.config
@@ -87,6 +93,13 @@ impl AppContext {
self.search_context = Some(pattern);
}
+ pub fn preview_context_ref(&self) -> &PreviewContext {
+ &self.preview_context
+ }
+ pub fn preview_context_mut(&mut self) -> &mut PreviewContext {
+ &mut self.preview_context
+ }
+
pub fn worker_context_ref(&self) -> &WorkerContext {
&self.worker_context
}
diff --git a/src/context/mod.rs b/src/context/mod.rs
index a1b8ed9..0a5f6c2 100644
--- a/src/context/mod.rs
+++ b/src/context/mod.rs
@@ -1,9 +1,11 @@
mod app_context;
mod local_state;
+mod preview_context;
mod tab_context;
mod worker_context;
pub use self::app_context::AppContext;
pub use self::local_state::LocalStateContext;
+pub use self::preview_context::PreviewContext;
pub use self::tab_context::TabContext;
pub use self::worker_context::WorkerContext;
diff --git a/src/context/preview_context.rs b/src/context/preview_context.rs
new file mode 100644
index 0000000..d344323
--- /dev/null
+++ b/src/context/preview_context.rs
@@ -0,0 +1,27 @@
+use std::collections::HashMap;
+use std::io;
+use std::path;
+use std::process;
+
+use crate::event::AppEvent;
+use crate::preview::preview_file::FilePreview;
+
+pub struct PreviewContext {
+ pub previews: HashMap<path::PathBuf, FilePreview>,
+}
+
+impl PreviewContext {
+ pub fn new() -> Self {
+ Self {
+ previews: HashMap::new(),
+ }
+ }
+
+ pub fn get_preview(&self, p: &path::Path) -> Option<&FilePreview> {
+ self.previews.get(p)
+ }
+
+ pub fn insert_preview(&mut self, p: path::PathBuf, file_preview: FilePreview) {
+ self.previews.insert(p, file_preview);
+ }
+}
diff --git a/src/context/worker_context.rs b/src/context/worker_context.rs
index bd0802c..10b464b 100644
--- a/src/context/worker_context.rs
+++ b/src/context/worker_context.rs
@@ -7,23 +7,23 @@ use crate::event::AppEvent;
use crate::io::{IoWorkerObserver, IoWorkerProgress, IoWorkerThread};
pub struct WorkerContext {
+ // to send info
+ event_tx: mpsc::Sender<AppEvent>,
// queue of IO workers
worker_queue: VecDeque<IoWorkerThread>,
// current worker
worker: Option<IoWorkerObserver>,
- // to send info
- event_tx: mpsc::Sender<AppEvent>,
}
impl WorkerContext {
pub fn new(event_tx: mpsc::Sender<AppEvent>) -> Self {
Self {
+ event_tx,
worker_queue: VecDeque::new(),
worker: None,
- event_tx,
}
}
- pub fn get_event_tx(&self) -> mpsc::Sender<AppEvent> {
+ pub fn clone_event_tx(&self) -> mpsc::Sender<AppEvent> {
self.event_tx.clone()
}
// worker related
@@ -61,7 +61,7 @@ impl WorkerContext {
}
pub fn start_next_job(&mut self) {
- let tx = self.get_event_tx();
+ let tx = self.clone_event_tx();
if let Some(worker) = self.worker_queue.pop_front() {
let src = worker.paths[0].parent().unwrap().to_path_buf();
diff --git a/src/event.rs b/src/event.rs
index da9330e..f8c8692 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -11,15 +11,17 @@ use signal_hook::iterator::SignalsInfo;
use termion::event::Event;
use termion::input::TermRead;
+use crate::fs::JoshutoDirList;
use crate::io::IoWorkerProgress;
+use crate::preview::preview_file::FilePreview;
#[derive(Debug)]
pub enum AppEvent {
Termion(Event),
IoWorkerProgress(IoWorkerProgress),
IoWorkerResult(io::Result<IoWorkerProgress>),
- PreviewDir(io::Result<path::PathBuf>),
- PreviewFile(process::Output),
+ PreviewDir(io::Result<JoshutoDirList>),
+ PreviewFile(FilePreview),
Signal(i32),
// Filesystem(notify::Result),
}
diff --git a/src/fs/dirlist.rs b/src/fs/dirlist.rs
index e8dc90b..1414531 100644
--- a/src/fs/dirlist.rs
+++ b/src/fs/dirlist.rs
@@ -4,7 +4,7 @@ use std::{fs, path};
use crate::fs::{JoshutoDirEntry, JoshutoMetadata};
use crate::util::display::DisplayOption;
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct JoshutoDirList {
pub index: Option<usize>,
path: path::PathBuf,
diff --git a/src/preview/preview_default.rs b/src/preview/preview_default.rs
index 9601800..691cf72 100644
--- a/src/preview/preview_default.rs
+++ b/src/preview/preview_default.rs
@@ -8,7 +8,7 @@ pub fn load_preview_path(context: &mut AppContext, backend: &mut TuiBackend, p:
if p.is_dir() {
preview_dir::load_preview(context, p);
} else if p.is_file() {
- preview_file::preview_path_with_script(context, backend, p);
+ preview_file::Background::preview_path_with_script(context, backend, p);
}
}
diff --git a/src/preview/preview_dir.rs b/src/preview/preview_dir.rs
index bf1d151..03ae189 100644
--- a/src/preview/preview_dir.rs
+++ b/src/preview/preview_dir.rs
@@ -4,45 +4,47 @@ use std::thread;
use crate::context::AppContext;
use crate::event::AppEvent;
+use crate::fs::JoshutoDirList;
use crate::history::DirectoryHistory;
-pub fn load_preview(context: &mut AppContext, p: path::PathBuf) -> io::Result<path::PathBuf> {
- // get preview
+pub fn load_preview(context: &mut AppContext, p: path::PathBuf) -> io::Result<()> {
let options = context.config_ref().display_options_ref().clone();
context
.tab_context_mut()
.curr_tab_mut()
.history_mut()
.create_or_soft_update(p.as_path(), &options)?;
- Ok(p)
+ Ok(())
}
-pub struct CursorDir {}
-
-impl CursorDir {
- pub fn load(context: &mut AppContext) -> io::Result<()> {
- let mut p: Option<path::PathBuf> = None;
- if let Some(curr_list) = context.tab_context_ref().curr_tab_ref().curr_list_ref() {
- if let Some(index) = curr_list.index {
- let entry = &curr_list.contents[index];
- p = Some(entry.file_path().to_path_buf())
+pub fn background_load_preview(
+ context: &mut AppContext,
+ p: path::PathBuf,
+) -> thread::JoinHandle<()> {
+ let need_to_load = match context
+ .tab_context_mut()
+ .curr_tab_mut()
+ .history_mut()
+ .get(p.as_path())
+ {
+ Some(entry) => entry.need_update(),
+ None => true,
+ };
+ if need_to_load {
+ let event_tx = context.events.event_tx.clone();
+ let options = context.config_ref().display_options_ref().clone();
+ let handle = thread::spawn(move || {
+ match JoshutoDirList::new(p, &options) {
+ Ok(dirlist) => {
+ event_tx.send(AppEvent::PreviewDir(Ok(dirlist)));
+ }
+ Err(_) => {}
}
- }
-
- let res = match p {
- Some(p) if p.is_dir() => load_preview(context, p),
- Some(p) => Err(io::Error::new(
- io::ErrorKind::InvalidData,
- "Not a directory".to_string(),
- )),
- None => Err(io::Error::new(
- io::ErrorKind::NotFound,
- "No such file or directory".to_string(),
- )),
- };
- match res {
- Ok(_) => Ok(()),
- Err(e) => Err(e),
- }
+ ()
+ });
+ handle
+ } else {
+ let handle = thread::spawn(|| ());
+ handle
}
}
diff --git a/src/preview/preview_file.rs b/src/preview/preview_file.rs
index 5aabb13..1a2e5f9 100644
--- a/src/preview/preview_file.rs
+++ b/src/preview/preview_file.rs
@@ -1,86 +1,142 @@
use std::io;
use std::path;
use std::process::{Command, Output};
+use std::thread;
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::widgets::{Block, Borders};
use crate::context::AppContext;
-use crate::ui::TuiBackend;
-
-pub fn preview_path_with_script(
- context: &AppContext,
- backend: &mut TuiBackend,
- p: path::PathBuf,
-) -> io::Result<Output> {
- let preview_options = context.config_ref().preview_options_ref();
- let config = context.config_ref();
-
- match preview_options.preview_script.as_ref() {
- None => Err(io::Error::new(
- io::ErrorKind::Other,
- "No preview script specified",
- )),
- Some(script) => {
- let area = backend.terminal.as_ref().unwrap().size().unwrap();
+use crate::event::AppEvent;
+use crate::ui::{self, TuiBackend};
- let constraints: &[Constraint; 3] = &config.display_options_ref().default_layout;
-
- let layout_rect = if config.display_options_ref().show_borders() {
- let area = Rect {
- y: area.top() + 1,
- height: area.height - 2,
- ..area
- };
-
- let block = Block::default().borders(Borders::ALL);
- let inner = block.inner(area);
-
- let layout_rect = Layout::default()
- .direction(Direction::Horizontal)
- .constraints(constraints.as_ref())
- .split(inner);
-
- let block = Block::default().borders(Borders::LEFT);
- let inner3 = block.inner(layout_rect[2]);
- inner3
- } else {
- let layout_rect = Layout::default()
- .direction(Direction::Horizontal)
- .vertical_margin(1)
- .constraints(constraints.as_ref())
- .split(area);
- layout_rect[2]
- };
-
- let file_full_path = p.as_path();
- let preview_width = layout_rect.width;
- let preview_height = layout_rect.height;
- let image_cache = 0;
- let preview_image = if preview_options.preview_images { 1 } else { 0 };
+#[derive(Clone, Debug)]
+pub struct FilePreview {
+ pub _path: path::PathBuf,
+ pub status: std::process::ExitStatus,
+ pub output: String,
+}
+
+impl std::convert::From<(path::PathBuf, Output)> for FilePreview {
+ fn from((p, output): (path::PathBuf, Output)) -> Self {
+ let s = String::from_utf8_lossy(&output.stdout).to_string();
+ let status = output.status;
+ Self {
+ _path: p,
+ status,
+ output: s,
+ }
+ }
+}
+
+pub struct Foreground {}
+
+impl Foreground {
+ pub fn preview_path_with_script(
+ context: &AppContext,
+ backend: &mut TuiBackend,
+ p: path::PathBuf,
+ ) -> io::Result<Output> {
+ let preview_options = context.config_ref().preview_options_ref();
+ let config = context.config_ref();
+
+ match preview_options.preview_script.as_ref() {
+ None => Err(io::Error::new(
+ io::ErrorKind::Other,
+ "No preview script specified",
+ )),
+ Some(script) => {
+ let area = backend.terminal.as_ref().unwrap().size().unwrap();
+ let display_options = config.display_options_ref();
+ let constraints: &[Constraint; 3] = &display_options.default_layout;
+
+ let layout_rect = ui::build_layout(area, constraints, display_options)[2];
+
+ let file_full_path = p.as_path();
+ let preview_width = layout_rect.width;
+ let preview_height = layout_rect.height;
+ let image_cache = 0;
+ let preview_image = if preview_options.preview_images { 1 } else { 0 };
+
+ // spawn preview process
+ Command::new(script)
+ .arg(file_full_path)
+ .arg(preview_width.to_string())
+ .arg(preview_height.to_string())
+ .arg(image_cache.to_string())
+ .arg(preview_image.to_string())
+ .output()
+ }
+ }
+ }
+
+ pub fn preview_with_script(
+ context: &AppContext,
+ backend: &mut TuiBackend,
+ ) -> io::Result<Output> {
+ let curr_tab = context.tab_context_ref().curr_tab_ref();
+ let child_list = curr_tab.child_list_ref();
+
+ let preview_options = context.config_ref().preview_options_ref();
- // spawn preview process
- Command::new(script)
- .arg(file_full_path)
- .arg(preview_width.to_string())
- .arg(preview_height.to_string())
- .arg(image_cache.to_string())
- .arg(preview_image.to_string())
- .output()
+ let config = context.config_ref();
+
+ match child_list.and_then(|list| list.curr_entry_ref()) {
+ None => Err(io::Error::new(io::ErrorKind::Other, "No file to preview")),
+ Some(entry) => {
+ Self::preview_path_with_script(context, backend, entry.file_path().to_path_buf())
+ }
}
}
}
-pub fn preview_with_script(context: &AppContext, backend: &mut TuiBackend) -> io::Result<Output> {
- let curr_tab = context.tab_context_ref().curr_tab_ref();
- let child_list = curr_tab.child_list_ref();
+pub struct Background {}
+
+impl Background {
+ pub fn preview_path_with_script(
+ context: &AppContext,
+ backend: &mut TuiBackend,
+ p: path::PathBuf,
+ ) {
+ if let Some(preview) = context.preview_context_ref().get_preview(p.as_path()) {
+ return;
+ }
+
+ let preview_options = context.config_ref().preview_options_ref();
+ let config = context.config_ref();
- let preview_options = context.config_ref().preview_options_ref();
+ if let Some(script) = preview_options.preview_script.as_ref() {
+ let area = backend.terminal.as_ref().unwrap().size().unwrap();
+ let display_options = config.display_options_ref();
+ let constraints: &[Constraint; 3] = &display_options.default_layout;
+
+ let layout_rect = ui::build_layout(area, constraints, display_options)[2];
+
+ let preview_width = layout_rect.width;
+ let preview_height = layout_rect.height;
+ let image_cache = 0;
+ let preview_image = if preview_options.preview_images { 1 } else { 0 };
- let config = context.config_ref();
+ let script = script.clone();
+ let event_tx = context.clone_event_tx();
+ let thread = thread::spawn(move || {
+ let file_full_path = p.as_path();
- match child_list.and_then(|list| list.curr_entry_ref()) {
- None => Err(io::Error::new(io::ErrorKind::Other, "No file to preview")),
- Some(entry) => preview_path_with_script(context, backend, entry.file_path().to_path_buf()),
+ let res = Command::new(script)
+ .arg(file_full_path)
+ .arg(preview_width.to_string())
+ .arg(preview_height.to_string())
+ .arg(image_cache.to_string())
+ .arg(preview_image.to_string())
+ .output();
+ match res {
+ Ok(output) => {
+ let preview = FilePreview::from((p, output));
+ event_tx.send(AppEvent::PreviewFile(preview));
+ }
+ Err(_) => {}
+ }
+ });
+ }
}
}
diff --git a/src/ui/tui_backend.rs b/src/ui/tui_backend.rs
index f04450d..cf32c01 100644
--- a/src/ui/tui_backend.rs
+++ b/src/ui/tui_backend.rs
@@ -4,11 +4,14 @@ use std::io::Write;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::screen::AlternateScreen;
use tui::backend::TermionBackend;
-use tui::widgets::Widget;
+use tui::layout::{Constraint, Direction, Layout, Rect};
+use tui::widgets::{Block, Borders, Widget};
#[cfg(feature = "mouse")]
use termion::input::MouseTerminal;
+use crate::util::display::DisplayOption;
+
trait New {
fn new() -> std::io::Result<Self>
where
@@ -81,3 +84,50 @@ impl TuiBackend {
Ok(())
}
}
+
+pub fn build_layout(
+ area: Rect,
+ constraints: &[Constraint; 3],
+ display_options: &DisplayOption,
+) -> Vec<Rect> {
+ let layout_rect = if display_options.show_borders() {
+ let area = Rect {
+ y: area.top() + 1,
+ height: area.height - 2,
+ ..area
+ };
+
+ let block = Block::default().borders(Borders::ALL);
+ let inner = block.inner(area);
+
+ let layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .constraints(constraints.as_ref())
+ .split(inner);
+
+ let block = Block::default().borders(Borders::RIGHT);
+ let inner1 = block.inner(layout_rect[0]);
+
+ let block = Block::default().borders(Borders::LEFT);
+ let inner3 = block.inner(layout_rect[2]);
+
+ vec![inner1, layout_rect[1], inner3]
+ } else {
+ let mut layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .vertical_margin(1)
+ .constraints(constraints.as_ref())
+ .split(area);
+
+ layout_rect[0] = Rect {
+ width: layout_rect[0].width - 1,
+ ..layout_rect[0]
+ };
+ layout_rect[1] = Rect {
+ width: layout_rect[1].width - 1,
+ ..layout_rect[1]
+ };
+ layout_rect
+ };
+ layout_rect
+}
diff --git a/src/ui/views/tui_folder_view.rs b/src/ui/views/tui_folder_view.rs
index be3909a..e391473 100644
--- a/src/ui/views/tui_folder_view.rs
+++ b/src/ui/views/tui_folder_view.rs
@@ -6,7 +6,10 @@ use tui::text::Span;
use tui::widgets::{Block, Borders, Paragraph, Widget, Wrap};
use crate::context::AppContext;
-use crate::ui::widgets::{TuiDirList, TuiDirListDetailed, TuiFooter, TuiTabBar, TuiTopBar};
+use crate::ui;
+use crate::ui::widgets::{
+ TuiDirList, TuiDirListDetailed, TuiFilePreview, TuiFooter, TuiTabBar, TuiTopBar,
+};
const TAB_VIEW_WIDTH: u16 = 15;
@@ -26,22 +29,33 @@ impl<'a> TuiFolderView<'a> {
impl<'a> Widget for TuiFolderView<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
+ let preview_context = self.context.preview_context_ref();
let curr_tab = self.context.tab_context_ref().curr_tab_ref();
let curr_list = curr_tab.curr_list_ref();
let parent_list = curr_tab.parent_list_ref();
let child_list = curr_tab.child_list_ref();
+ let curr_entry = curr_list.and_then(|c| c.curr_entry_ref());
+
let config = self.context.config_ref();
+ let display_options = config.display_options_ref();
- let constraints: &[Constraint; 3] = if !config.display_options_ref().collapse_preview() {
- &config.display_options_ref().default_layout
- } else {
- match child_list {
- Some(_) => &config.display_options_ref().default_layout,
- None => &config.display_options_ref().no_preview_layout,
- }
- };
+ let (default_layout, constraints): (bool, &[Constraint; 3]) =
+ if !display_options.collapse_preview() {
+ (true, &display_options.default_layout)
+ } else {
+ match child_list {
+ Some(_) => (true, &display_options.default_layout),
+ None => match curr_entry {
+ None => (false, &display_options.no_preview_layout),
+ Some(e) => match preview_context.get_preview(e.file_path()) {
+ Some(_) => (true, &display_options.default_layout),
+ None => (false, &display_options.no_preview_layout),
+ },
+ },
+ }
+ };
let layout_rect = if config.display_options_ref().show_borders() {
let area = Rect {
@@ -73,7 +87,7 @@ impl<'a> Widget for TuiFolderView<'a> {
};
intersections.render_left(buf);
- if child_list.as_ref().is_some() {
+ if default_layout {
intersections.render_right(buf);
}
}
@@ -108,7 +122,7 @@ impl<'a> Widget for TuiFolderView<'a> {
// render parent view
if let Some(list) = parent_list.as_ref() {
TuiDirList::new(&list).render(layout_rect[0], buf);
- };
+ }
// render current view
if let Some(list) = curr_list.as_ref() {
@@ -137,12 +151,16 @@ impl<'a> Widget for TuiFolderView<'a> {
TuiFooter::new(list).render(rect, buf);
}
}
- };
+ }
// render preview
if let Some(list) = child_list.as_ref() {
TuiDirList::new(&list).render(layout_rect[2], buf);
- };
+ } else if let Some(entry) = curr_entry {
+ if let Some(preview) = preview_context.get_preview(entry.file_path()) {
+ TuiFilePreview::new(entry, preview).render(layout_rect[2], buf);
+ }
+ }
let topbar_width = area.width;
let rect = Rect {
diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs
index 451ccdb..6319d91 100644
--- a/src/ui/widgets/mod.rs
+++ b/src/ui/widgets/mod.rs
@@ -1,5 +1,6 @@
mod tui_dirlist;
mod tui_dirlist_detailed;
+mod tui_file_preview;
mod tui_footer;
mod tui_menu;
mod tui_prompt;
@@ -10,6 +11,7 @@ mod tui_worker;
pub use self::tui_dirlist::TuiDirList;
pub use self::tui_dirlist_detailed::{trim_file_label, TuiDirListDetailed};
+pub use self::tui_file_preview::TuiFilePreview;
pub use self::tui_footer::TuiFooter;
pub use self::tui_menu::TuiMenu;
pub use self::tui_prompt::TuiPrompt;
diff --git a/src/ui/widgets/tui_file_preview.rs b/src/ui/widgets/tui_file_preview.rs
new file mode 100644
index 0000000..f731a0e
--- /dev/null
+++ b/src/ui/widgets/tui_file_preview.rs
@@ -0,0 +1,35 @@
+use std::process;
+
+use tui::buffer::Buffer;
+use tui::layout::Rect;
+use tui::style::{Color, Modifier, Style};
+use tui::widgets::Widget;
+
+use crate::fs::JoshutoDirEntry;
+use crate::preview::preview_file::FilePreview;
+use crate::util::format;
+use crate::util::string::UnicodeTruncate;
+use crate::util::style;
+use unicode_width::UnicodeWidthStr;
+
+const MIN_LEFT_LABEL_WIDTH: i32 = 15;
+
+const ELLIPSIS: &str = "…";
+
+pub struct TuiFilePreview<'a> {
+ entry: &'a JoshutoDirEntry,
+ preview: &'a FilePreview,
+}
+
+impl<'a> TuiFilePreview<'a> {
+ pub fn new(entry: &'a JoshutoDirEntry, preview: &'a FilePreview) -> Self {
+ Self { entry, preview }
+ }
+}
+
+impl<'a> Widget for TuiFilePreview<'a> {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ let style = Style::default();
+ buf.set_string(area.x, area.y, self.preview.output.as_str(), style);
+ }
+}
diff --git a/src/util/input.rs b/src/util/input.rs
index 24aff5e..0896292 100644
--- a/src/util/input.rs
+++ b/src/util/input.rs
@@ -1,3 +1,7 @@
+use std::collections::{hash_map::Entry, HashMap};
+use std::path;
+use std::process;
+
use signal_hook::consts::signal;
use termion::event::{MouseButton, MouseEvent};
use tui::layout::{Constraint, Direction, Layout};
@@ -5,11 +9,84 @@ use tui::layout::{Constraint, Direction, Layout};
use crate::commands::{cursor_move, parent_cursor_move, AppExecute, KeyCommand};
use crate::context::AppContext;
use crate::event::AppEvent;
+use crate::fs::JoshutoDirList;
use crate::history::DirectoryHistory;
use crate::io::{FileOp, IoWorkerProgress};
+use crate::preview::preview_file::FilePreview;
use crate::ui;
use crate::util::format;
+pub fn process_noninteractive(event: AppEvent, context: &mut AppContext) {
+ match event {
+ AppEvent::IoWorkerProgress(res) => process_worker_progress(context, res),
+ AppEvent::IoWorkerResult(res) => process_finished_worker(context, res),
+ AppEvent::PreviewDir(Ok(dirlist)) => process_dir_preview(context, dirlist),
+ AppEvent::PreviewFile(file_preview) => process_file_preview(context, file_preview),
+ AppEvent::Signal(signal::SIGWINCH) => {}
+ _ => {}
+ }
+}
+
+pub fn process_worker_progress(context: &mut AppContext, res: IoWorkerProgress) {
+ let worker_context = context.worker_context_mut();
+ worker_context.set_progress(res);
+ worker_context.update_msg();
+}
+
+pub fn process_finished_worker(context: &mut AppContext, res: std::io::Result<IoWorkerProgress>) {
+ let worker_context = context.worker_context_mut();
+ let observer = worker_context.remove_worker().unwrap();
+ let options = context.config_ref().display_options_ref().clone();
+ for tab in context.tab_context_mut().iter_mut() {
+ let _ = tab.history_mut().reload(observer.dest_path(), &options);
+ let _ = tab.history_mut().reload(observer.src_path(), &options);
+ }
+ observer.join();
+ match res {
+ Ok(progress) => {
+ let op = match progress.kind() {
+ FileOp::Copy => "copied",
+ FileOp::Cut => "moved",
+ };
+ let processed_size = format::file_size_to_string(progress.bytes_processed());
+ let total_size = format::file_size_to_string(progress.total_bytes());
+ let msg = format!(
+ "successfully {} {} items ({}/{})",
+ op,
+ progress.total_files(),
+ processed_size,
+ total_size,
+ );
+ context.push_msg(msg);
+ }
+ Err(e) => {
+ let msg = format!("{}", e);
+ context.push_msg(msg);
+ }
+ }
+}
+
+pub fn process_dir_preview(context: &mut AppContext, dirlist: JoshutoDirList) {
+ let history = context.tab_context_mut().curr_tab_mut().history_mut();
+ match history.entry(dirlist.file_path().to_path_buf()) {
+ Entry::Occupied(mut entry) => {
+ let old_dirlist = entry.get();
+ if old_dirlist.need_update() {
+ entry.insert(dirlist);
+ }
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(dirlist);
+ }
+ }
+}
+
+pub fn process_file_preview(context: &mut AppContext, file_preview: FilePreview) {
+ context
+ .preview_context_mut()
+ .insert_preview(file_preview._path.clone(), file_preview);
+}
+
pub fn process_mouse(event: MouseEvent, context: &mut AppContext, backend: &mut ui::TuiBackend) {
let f_size = backend.terminal.as_ref().unwrap().size().unwrap();
@@ -93,51 +170,3 @@ pub fn process_mouse(event: MouseEvent, context: &mut AppContext, backend: &mut
}
context.flush_event();
}
-
-pub fn process_noninteractive(event: AppEvent, context: &mut AppContext) {
- match event {
- AppEvent::IoWorkerProgress(res) => process_worker_progress(context, res),
- AppEvent::IoWorkerResult(res) => process_finished_worker(context, res),
- AppEvent::Signal(signal::SIGWINCH) => {}
- _ => {}
- }
-}
-
-pub fn process_worker_progress(context: &mut AppContext, res: IoWorkerProgress) {
- let worker_context = context.worker_context_mut();
- worker_context.set_progress(res);
- worker_context.update_msg();
-}
-
-pub fn process_finished_worker(context: &mut AppContext, res: std::io::Result<IoWorkerProgress>) {
- let worker_context = context.worker_context_mut();
- let observer = worker_context.remove_worker().unwrap();
- let options = context.config_ref().display_options_ref().clone();
- for tab in context.tab_context_mut().iter_mut() {
- let _ = tab.history_mut().reload(observer.dest_path(), &options);
- let _ = tab.history_mut().reload(observer.src_path(), &options);
- }
- observer.join();
- match res {
- Ok(progress) => {
- let op = match progress.kind() {
- FileOp::Copy => "copied",
- FileOp::Cut => "moved",
- };
- let processed_size = format::file_size_to_string(progress.bytes_processed());
- let total_size = format::file_size_to_string(progress.total_bytes());
- let msg = format!(
- "successfully {} {} items ({}/{})",
- op,
- progress.total_files(),
- processed_size,
- total_size,
- );
- context.push_msg(msg);
- }
- Err(e) => {
- let msg = format!("{}", e);
- context.push_msg(msg);
- }
- }
-}