add preview zoom mode

This commit is contained in:
rabite 2020-05-22 14:32:56 +02:00
parent f434936e62
commit 67fde00a34
7 changed files with 243 additions and 55 deletions

Binary file not shown.

View File

@ -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)?
}

View File

@ -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)
}
}

View File

@ -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('!')
};

View File

@ -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),

View File

@ -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(())
}
}

View File

@ -455,6 +455,8 @@ pub trait Widget {
std::thread::sleep(pause);
}
self.get_core()?.write_to_screen(&clear).log();
Ok(())
}