summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2020-05-22 14:32:56 +0200
committerrabite <rabite@posteo.de>2020-05-22 14:32:56 +0200
commit67fde00a341b3c90afcad2414aaf19f2b0a0cbf1 (patch)
tree981e08fc6cf7341c43426384ec1ab07627c20f9e
parentf434936e622e4cd03551a7ca09db82cb2205d9a9 (diff)
add preview zoom mode
-rw-r--r--config.tar.gzbin3949 -> 3957 bytes
-rw-r--r--src/file_browser.rs87
-rw-r--r--src/hbox.rs4
-rw-r--r--src/keybind.rs2
-rw-r--r--src/preview.rs34
-rw-r--r--src/textview.rs169
-rw-r--r--src/widget.rs2
7 files changed, 243 insertions, 55 deletions
diff --git a/config.tar.gz b/config.tar.gz
index f33e5b9..f95e07d 100644
--- a/config.tar.gz
+++ b/config.tar.gz
Binary files differ
diff --git a/src/file_browser.rs b/src/file_browser.rs
index fcc0c6c..f3cec19 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -72,6 +72,14 @@ impl Widget for FileBrowserWidgets {
FileBrowserWidgets::Blank(widget) => widget.get_drawlist(),
}
}
+
+ fn on_key(&mut self, key: Key) -> HResult<()> {
+ match self {
+ FileBrowserWidgets::FileList(widget) => widget.on_key(key),
+ FileBrowserWidgets::Previewer(widget) => widget.on_key(key),
+ FileBrowserWidgets::Blank(widget) => widget.on_key(key)
+ }
+ }
}
pub struct FileBrowser {
@@ -854,9 +862,45 @@ impl FileBrowser {
}
}
+ fn cancel_preview_animation(&mut self) {
+ self.preview_widget_mut()
+ .map(|preview| preview.cancel_animation())
+ .log();
+ }
+
+ fn activate_main_widget(&mut self) {
+ const MAIN_INDEX: usize = 1;
+ self.columns
+ .set_active(MAIN_INDEX)
+ .log();
+ }
+
+ fn activate_preview_widget(&mut self) {
+ const PREVIEW_INDEX: usize = 2;
+ self.columns
+ .set_active(PREVIEW_INDEX)
+ .log();
+ }
+
pub fn toggle_colums(&mut self) {
- self.preview_widget().map(|preview| preview.cancel_animation()).log();
- self.columns.toggle_zoom().log();
+ self.cancel_preview_animation();
+ self.activate_main_widget();
+ self.columns
+ .toggle_zoom()
+ .log();
+ }
+
+ pub fn zoom_preview(&mut self) {
+ self.cancel_preview_animation();
+ self.activate_preview_widget();
+ self.preview_widget_mut()
+ .map(|preview| {
+ preview.reload_text();
+ }).log();
+
+ self.columns
+ .toggle_zoom()
+ .log();
}
pub fn quit_with_dir(&self) -> HResult<()> {
@@ -1419,13 +1463,25 @@ impl Widget for FileBrowser {
let sized_path = crate::term::sized_string(&pretty_path, xsize);
Ok(sized_path.to_string())
}
+
fn render_footer(&self) -> HResult<String> {
let xsize = term::xsize_u();
- match self.get_core()?.status_bar_content.lock()?.as_mut().take() {
- Some(status) => Ok(term::sized_string_u(&status, xsize)),
- _ => { self.get_footer() },
+ let mut status = self.get_core()?
+ .status_bar_content
+ .lock()?;
+ let status = status.as_mut()
+ .take();
+ let active = self.columns
+ .active
+ .unwrap_or(1);
+
+ match (status, active) {
+ (Some(status), _) => Ok(term::sized_string_u(&status, xsize)),
+ (_, 2) => self.preview_widget()?.render_footer(),
+ _ => self.get_footer(),
}
}
+
fn refresh(&mut self) -> HResult<()> {
self.set_title().log();
self.columns.refresh().log();
@@ -1441,6 +1497,26 @@ impl Widget for FileBrowser {
}
fn on_key(&mut self, key: Key) -> HResult<()> {
+ // Special handling for preview zoom
+ let binds = self.search_in();
+ let action = binds.get(key);
+
+ match (action, self.columns.active) {
+ (Some(FileBrowserAction::ZoomPreview), Some(2)) => {
+ self.toggle_colums();
+ return Ok(());
+ }
+ (Some(FileBrowserAction::ZoomPreview), Some(1)) => {
+ self.zoom_preview();
+ return Ok(());
+ }
+ (_, Some(2)) => {
+ self.columns.active_widget_mut()?.on_key(key)?;
+ return Ok(());
+ }
+ _ => {}
+ }
+
match self.do_key(key) {
Err(HError::WidgetUndefinedKeyError{..}) => {
match self.main_widget_mut()?.on_key(key) {
@@ -1509,6 +1585,7 @@ impl Acting for FileBrowser {
ShowQuickActions => self.quick_action()?,
RunSubshell => self.run_subshell()?,
ToggleColumns => self.toggle_colums(),
+ ZoomPreview => self.zoom_preview(),
// Tab implementation needs to call exec_cmd because ALL files are needed
ExecCmd => Err(HError::FileBrowserNeedTabFiles)?
}
diff --git a/src/hbox.rs b/src/hbox.rs
index 8ad23c6..391aae0 100644
--- a/src/hbox.rs
+++ b/src/hbox.rs
@@ -210,4 +210,8 @@ impl<T> Widget for HBox<T> where T: Widget + PartialEq {
self.active_widget_mut()?.on_event(event)?;
Ok(())
}
+
+ fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
+ self.active_widget_mut()?.on_key(key)
+ }
}
diff --git a/src/keybind.rs b/src/keybind.rs
index e9e0e22..64a725a 100644
--- a/src/keybind.rs
+++ b/src/keybind.rs
@@ -481,6 +481,7 @@ pub enum FileBrowserAction {
ShowQuickActions,
RunSubshell,
ToggleColumns,
+ ZoomPreview,
ExecCmd
}
@@ -686,6 +687,7 @@ impl Default for Bindings<FileBrowserAction> {
ShowQuickActions => Char('a'),
RunSubshell => Char('z'),
ToggleColumns => Char('c'),
+ ZoomPreview => Char('C'),
ExecCmd => Char('!')
};
diff --git a/src/preview.rs b/src/preview.rs
index 7ba2010..95ad8cc 100644
--- a/src/preview.rs
+++ b/src/preview.rs
@@ -19,7 +19,7 @@ pub type AsyncWidgetFn<W> = dyn FnOnce(&Stale, WidgetCore)
-> HResult<W> + Send + Sync;
lazy_static! {
- static ref SUBPROC: Arc<Mutex<Option<u32>>> = { Arc::new(Mutex::new(None)) };
+ static ref SUBPROC: Arc<Mutex<Option<u32>>> = Arc::new(Mutex::new(None));
}
fn kill_proc() -> HResult<()> {
@@ -199,6 +199,10 @@ impl<T: Widget + Send + 'static> Widget for AsyncWidget<T> {
if self.widget().is_err() { return Ok(()) }
self.widget_mut()?.on_key(key)
}
+ fn render_footer(&self) -> HResult<String> {
+ if self.widget().is_err() { return Ok(String::new()) }
+ self.widget()?.render_footer()
+ }
}
@@ -454,6 +458,12 @@ impl Previewer {
}
}
+ pub fn reload_text(&mut self) {
+ match self.widget.widget_mut() {
+ Ok(PreviewWidget::TextView(w)) => w.load_full(),
+ _ => {}
+ }
+ }
fn preview_failed<T>(file: &File) -> HResult<T> {
@@ -564,14 +574,11 @@ impl Previewer {
match previewer {
ExtPreviewer::Text(previewer) => {
if stale.is_stale()? { return Previewer::preview_failed(&file) }
- let lines = Previewer::run_external(previewer, file, stale);
+ let lines = Previewer::run_external(previewer, file, stale)?;
if stale.is_stale()? { return Previewer::preview_failed(&file) }
- let mut textview = TextView {
- lines: lines?,
- core: core.clone(),
- follow: false,
- offset: 0};
+ let mut textview = TextView::new_blank(&core);
+ textview.set_lines(lines)?;
textview.set_coordinates(&core.coordinates).log();
textview.refresh().log();
textview.animate_slide_up(Some(animator)).log();
@@ -633,6 +640,10 @@ impl Widget for Previewer {
self.widget.get_drawlist()
}
+ fn render_footer(&self) -> HResult<String> {
+ self.widget.render_footer()
+ }
+
fn on_key(&mut self, key: Key) -> HResult<()> {
self.widget.on_key(key)
}
@@ -680,6 +691,15 @@ impl Widget for PreviewWidget {
}
}
+ fn render_footer(&self) -> HResult<String> {
+ match self {
+ PreviewWidget::FileList(widget) => widget.render_footer(),
+ PreviewWidget::TextView(widget) => widget.render_footer(),
+ PreviewWidget::ImgView(widget) => widget.render_footer(),
+ PreviewWidget::MediaView(widget) => widget.render_footer()
+ }
+ }
+
fn on_key(&mut self, key: Key) -> HResult<()> {
match self {
PreviewWidget::FileList(widget) => widget.on_key(key),
diff --git a/src/textview.rs b/src/textview.rs
index cd9a815..b7dd0a9 100644
--- a/src/textview.rs
+++ b/src/textview.rs
@@ -1,6 +1,7 @@
-use std::io::{BufRead, BufReader};
+use std::io::BufRead;
use strip_ansi_escapes::strip;
+use termion::event::Key;
use crate::files::File;
use crate::term::sized_string_u;
@@ -15,6 +16,8 @@ pub struct TextView {
pub core: WidgetCore,
pub follow: bool,
pub offset: usize,
+ file: Option<File>,
+ limited: bool,
}
impl TextView {
@@ -24,56 +27,78 @@ impl TextView {
core: core.clone(),
follow: false,
offset: 0,
+ file: None,
+ limited: false
}
}
+
pub fn new_from_file(core: &WidgetCore, file: &File) -> HResult<TextView> {
- let file = std::fs::File::open(&file.path)?;
- let file = std::io::BufReader::new(file);
- let lines = file.lines()
- .map(|line| line
- .and_then(|l| strip(l))
- .map_err(HError::from)
- .and_then(|s| std::str::from_utf8(&s)
- .map(|s| s.to_string())
- .map_err(HError::from)))
- .collect::<HResult<_>>()?;
- Ok(TextView {
- lines: lines,
- core: core.clone(),
- follow: false,
- offset: 0,
- })
+ let mut view = TextView::new_from_file_limit_lines(core, file, 0)?;
+ view.limited = false;
+ Ok(view)
}
pub fn new_from_file_limit_lines(core: &WidgetCore,
file: &File,
num: usize) -> HResult<TextView> {
- let file = std::fs::File::open(&file.path)?;
- let file = BufReader::new(file);
- let lines = file.lines()
- .take(num)
- .map(|line| line
- .and_then(|l| strip(l))
- .map_err(HError::from)
- .and_then(|s| std::str::from_utf8(&s)
- .map(|s| s.to_string())
- .map_err(HError::from)))
- .collect::<HResult<_>>()?;
+ let buf = std::fs::File::open(&file.path)
+ .map(|f| std::io::BufReader::new(f))?;
+
+ let lines = buf.lines()
+ .enumerate()
+ .take_while(|(i, _)| num == 0 || i <= &num)
+ .map(|(_, l)| {
+ l.map_err(HError::from)
+ .and_then(|l| {
+ let l = strip(&l);
+ Ok(String::from_utf8_lossy(&l?).to_string())
+ })
+ .map_err(HError::from)
+
+ })
+ .collect::<HResult<_>>()?;
+
Ok(TextView {
lines: lines,
core: core.clone(),
follow: false,
offset: 0,
+ file: Some(file.clone()),
+ limited: true
})
}
pub fn set_text(&mut self, text: &str) -> HResult<()> {
let lines = text.lines().map(|l| l.to_string()).collect();
self.lines = lines;
+ self.limited = false;
+ self.file = None;
self.core.set_dirty();
self.refresh()
}
+ pub fn set_lines(&mut self, lines: Vec<String>) -> HResult<()> {
+ self.lines = lines;
+ self.limited = false;
+ self.file = None;
+ self.core.set_dirty();
+ self.refresh()
+ }
+
+ pub fn load_full(&mut self) {
+ if self.limited {
+ self.file
+ .as_ref()
+ .and_then(|f| {
+ TextView::new_from_file(&self.core, f).ok()
+ })
+ .map(|v| {
+ *self = v;
+ self.limited = false;
+ });
+ }
+ }
+
pub fn toggle_follow(&mut self) {
self.follow = !self.follow
}
@@ -159,21 +184,79 @@ impl Widget for TextView {
let (xsize, ysize) = self.get_coordinates()?.size().size();
let (xpos, ypos) = self.get_coordinates()?.position().position();
- let output = self.core.get_clearlist()? +
- &self
- .lines
- .iter()
- .skip(self.offset)
- .take(ysize as usize)
- .enumerate()
- .map(|(i, line)| {
- format!(
- "{}{}{}",
- crate::term::goto_xy(xpos, i as u16 + ypos),
- crate::term::reset(),
- sized_string_u(&line, (xsize-1) as usize))
- })
- .collect::<String>();
+ let mut output = crate::term::reset();
+
+ output += &self.lines
+ .iter()
+ .skip(self.offset)
+ .take(ysize as usize)
+ .enumerate()
+ .map(|(i, line)| {
+ format!(
+ "{}{}",
+ crate::term::goto_xy(xpos, i as u16 + ypos),
+ sized_string_u(&line, (xsize-1) as usize))
+ })
+ .collect::<String>();
Ok(output)
}
+
+ fn render_footer(&self) -> HResult<String> {
+ let (xsize, ysize) = self.core.coordinates.size_u();
+ let (_, ypos) = self.core.coordinates.position_u();
+ let lines = self.lines
+ .len()
+ .saturating_sub(1);
+ let current_line_top = self.offset;
+ let current_line_bot = std::cmp::min(current_line_top + ysize + 1,
+ lines);
+ let line_hint = format!("{} - {} / {}",
+ current_line_top,
+ current_line_bot,
+ lines);
+ let hint_xpos = xsize - line_hint.len();
+ let hint_ypos = ysize + ypos + 1;
+
+ let footer = format!("{}{}",
+ crate::term::goto_xy_u(hint_xpos, hint_ypos),
+ line_hint);
+
+ Ok(footer)
+ }
+
+ fn on_key(&mut self, key: Key) -> HResult<()> {
+ self.do_key(key)
+ }
+}
+
+use crate::keybind::{Acting, Bindings, Movement};
+
+impl Acting for TextView {
+ type Action=Movement;
+
+ fn search_in(&self) -> Bindings<Self::Action> {
+ Bindings::default()
+ }
+
+ fn movement(&mut self, movement: &Movement) -> HResult<()> {
+ use Movement::*;
+
+ self.load_full();
+
+ match movement {
+ Up(n) => { for _ in 0..*n { self.scroll_up(); }; self.refresh()?; }
+ Down(n) => { for _ in 0..*n { self.scroll_down(); }; self.refresh()?; }
+ PageUp => self.page_up(),
+ PageDown => self.page_down(),
+ Top => self.scroll_top(),
+ Bottom => self.scroll_bottom(),
+ Left | Right => {}
+ }
+
+ Ok(())
+ }
+
+ fn do_action(&mut self, _action: &Self::Action) -> HResult<()> {
+ Ok(())
+ }
}
diff --git a/src/widget.rs b/src/widget.rs
index 0efcda7..1d27dbe 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -455,6 +455,8 @@ pub trait Widget {
std::thread::sleep(pause);
}
+ self.get_core()?.write_to_screen(&clear).log();
+
Ok(())
}