summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Zhao <jeff.no.zhao@gmail.com>2021-12-27 22:04:24 -0500
committerGitHub <noreply@github.com>2021-12-27 22:04:24 -0500
commit5ba0b0ffb28fecf0c38c25d35ea7a78b25395d99 (patch)
tree5585a4e47eac328a7bdaa49aaf1f1f9cc81cfc3f
parent0537e599d30c1a4385c3e7118f1a032c77266724 (diff)
refactor image preview via ueberzug (#115)
* refactor image preview via ueberzug * fix area not correct and calculating with borders * remove area recalculation * only process previews if preview shown and remove scripts are present in config
-rw-r--r--src/commands/numbered_command.rs4
-rw-r--r--src/event/app_event.rs2
-rw-r--r--src/run.rs120
-rw-r--r--src/ui/mod.rs17
-rw-r--r--src/ui/tui_backend.rs4
-rw-r--r--src/ui/views/mod.rs2
-rw-r--r--src/ui/views/tui_command_menu.rs4
-rw-r--r--src/ui/views/tui_folder_view.rs199
-rw-r--r--src/ui/views/tui_textfield.rs4
-rw-r--r--src/ui/views/tui_view.rs7
-rw-r--r--src/ui/widgets/tui_file_preview.rs6
-rw-r--r--src/ui/widgets/tui_prompt.rs4
12 files changed, 231 insertions, 142 deletions
diff --git a/src/commands/numbered_command.rs b/src/commands/numbered_command.rs
index a98eb59..39775e6 100644
--- a/src/commands/numbered_command.rs
+++ b/src/commands/numbered_command.rs
@@ -7,7 +7,6 @@ use crate::error::{JoshutoError, JoshutoErrorKind, JoshutoResult};
use crate::event::AppEvent;
use crate::key_command::{CommandKeybind, NumberedExecute};
use crate::ui::views::TuiView;
-use crate::ui::RenderResult;
use crate::ui::TuiBackend;
use crate::util::input;
@@ -22,8 +21,7 @@ pub fn numbered_command(
loop {
context.message_queue_mut().push_info(prefix.clone());
- let mut render_result = RenderResult::new();
- backend.render(TuiView::new(context, &mut render_result));
+ backend.render(TuiView::new(context));
context.message_queue_mut().pop_front();
let event = match context.poll_event() {
diff --git a/src/event/app_event.rs b/src/event/app_event.rs
index d6b77cb..d840daa 100644
--- a/src/event/app_event.rs
+++ b/src/event/app_event.rs
@@ -3,8 +3,6 @@ use std::path;
use std::sync::mpsc;
use std::thread;
-use notify;
-
use signal_hook::consts::signal;
use signal_hook::iterator::exfiltrator::SignalOnly;
use signal_hook::iterator::SignalsInfo;
diff --git a/src/run.rs b/src/run.rs
index dd0eb25..7f4cc9f 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -2,18 +2,21 @@ use crate::commands::numbered_command;
use crate::config::AppKeyMapping;
use crate::context::{AppContext, QuitType};
use crate::event::AppEvent;
-use crate::key_command::{AppExecute, Command, CommandKeybind};
+use crate::key_command::{AppExecute, CommandKeybind};
use crate::preview::preview_default;
use crate::tab::JoshutoTab;
use crate::ui;
+use crate::ui::views;
use crate::ui::views::TuiView;
-use crate::ui::RenderResult;
+use crate::ui::PreviewArea;
use crate::util::input;
use crate::util::to_string::ToString;
-use std::path;
+
+use std::path::{Path, PathBuf};
use std::process;
use std::thread;
use termion::event::{Event, Key};
+use tui::layout::Rect;
pub fn run(
backend: &mut ui::TuiBackend,
@@ -29,47 +32,24 @@ pub fn run(
// trigger a preview of child
preview_default::load_preview(context, backend);
}
- let mut last_preview_file_path: Option<path::PathBuf> = None;
+
+ let mut preview_area: Option<PreviewArea> = None;
while context.quit == QuitType::DoNot {
- let mut render_result = RenderResult::new();
- backend.render(TuiView::new(context, &mut render_result));
- if render_result.file_preview_path != last_preview_file_path {
- match render_result.file_preview_path.clone() {
- Some(path_buf) => {
- if let Some(preview_shown_hook_script) = context
- .config_ref()
- .preview_options_ref()
- .preview_shown_hook_script
- .clone()
- {
- if let Some(preview_area) = render_result.preview_area {
- let _ = thread::spawn(move || {
- let _ = process::Command::new(preview_shown_hook_script.as_path())
- .arg(path_buf)
- .arg(preview_area.x.to_string())
- .arg(preview_area.y.to_string())
- .arg(preview_area.width.to_string())
- .arg(preview_area.height.to_string())
- .status();
- });
- }
- }
- }
- None => {
- if let Some(preview_removed_hook_script) = context
- .config_ref()
- .preview_options_ref()
- .preview_removed_hook_script
- .clone()
- {
- let _ = thread::spawn(|| {
- let _ = process::Command::new(preview_removed_hook_script).status();
- });
- }
- }
+ backend.render(TuiView::new(context));
+
+ {
+ let config = context.config_ref();
+ let preview_options = config.preview_options_ref();
+ if let Ok(area) = backend.terminal_ref().size() {
+ preview_area = process_preview_on_change(
+ &context,
+ area,
+ preview_area,
+ preview_options.preview_shown_hook_script.as_ref(),
+ preview_options.preview_removed_hook_script.as_ref(),
+ );
}
- last_preview_file_path = render_result.file_preview_path;
}
let event = match context.poll_event() {
@@ -130,3 +110,61 @@ pub fn run(
}
Ok(())
}
+
+fn process_preview_on_change(
+ context: &AppContext,
+ area: Rect,
+ old_preview_area: Option<PreviewArea>,
+ preview_shown_hook_script: Option<&PathBuf>,
+ preview_removed_hook_script: Option<&PathBuf>,
+) -> Option<PreviewArea> {
+ let area = Rect {
+ y: area.top() + 1,
+ height: area.height - 2,
+ ..area
+ };
+
+ let constraints = views::get_constraints(&context);
+ let config = context.config_ref();
+ let display_options = config.display_options_ref();
+ let layout = if display_options.show_borders() {
+ views::calculate_layout_with_borders(area, constraints)
+ } else {
+ views::calculate_layout(area, constraints)
+ };
+ let new_preview_area = views::calculate_preview(&context, layout[2]);
+
+ match new_preview_area.as_ref() {
+ Some(new) => {
+ let should_preview = if let Some(old) = old_preview_area {
+ new.file_preview_path != old.file_preview_path
+ } else {
+ true
+ };
+ if should_preview {
+ if let Some(hook_script) = preview_shown_hook_script {
+ let hook_script = hook_script.to_path_buf();
+ let new2 = new.clone();
+ let _ = thread::spawn(move || {
+ let _ = process::Command::new(hook_script.as_path())
+ .arg(new2.file_preview_path.as_path())
+ .arg(new2.preview_area.x.to_string())
+ .arg(new2.preview_area.y.to_string())
+ .arg(new2.preview_area.width.to_string())
+ .arg(new2.preview_area.height.to_string())
+ .status();
+ });
+ }
+ }
+ }
+ None => {
+ if let Some(hook_script) = preview_shown_hook_script {
+ let hook_script = hook_script.to_path_buf();
+ let _ = thread::spawn(|| {
+ let _ = process::Command::new(hook_script).status();
+ });
+ }
+ }
+ }
+ new_preview_area
+}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index b782b1f..c71feb6 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -14,16 +14,17 @@ pub struct Rect {
pub height: u16,
}
-pub struct RenderResult {
- pub file_preview_path: Option<path::PathBuf>,
- pub preview_area: Option<Rect>,
+#[derive(Debug, Clone)]
+pub struct PreviewArea {
+ pub file_preview_path: path::PathBuf,
+ pub preview_area: Rect,
}
-impl RenderResult {
- pub fn new() -> RenderResult {
- RenderResult {
- file_preview_path: None,
- preview_area: None,
+impl PreviewArea {
+ pub fn new(file_preview_path: path::PathBuf, preview_area: Rect) -> Self {
+ Self {
+ file_preview_path,
+ preview_area,
}
}
}
diff --git a/src/ui/tui_backend.rs b/src/ui/tui_backend.rs
index dc15f03..52afba4 100644
--- a/src/ui/tui_backend.rs
+++ b/src/ui/tui_backend.rs
@@ -69,6 +69,10 @@ impl TuiBackend {
});
}
+ pub fn terminal_ref(&self) -> &JoshutoTerminal {
+ self.terminal.as_ref().unwrap()
+ }
+
pub fn terminal_mut(&mut self) -> &mut JoshutoTerminal {
self.terminal.as_mut().unwrap()
}
diff --git a/src/ui/views/mod.rs b/src/ui/views/mod.rs
index 70517cf..6af230c 100644
--- a/src/ui/views/mod.rs
+++ b/src/ui/views/mod.rs
@@ -5,7 +5,7 @@ mod tui_view;
mod tui_worker_view;
pub use self::tui_command_menu::TuiCommandMenu;
-pub use self::tui_folder_view::TuiFolderView;
+pub use self::tui_folder_view::*;
pub use self::tui_textfield::TuiTextField;
pub use self::tui_view::TuiView;
pub use self::tui_worker_view::TuiWorkerView;
diff --git a/src/ui/views/tui_command_menu.rs b/src/ui/views/tui_command_menu.rs
index d62fa09..bc94752 100644
--- a/src/ui/views/tui_command_menu.rs
+++ b/src/ui/views/tui_command_menu.rs
@@ -8,7 +8,6 @@ use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::ui::views::TuiView;
use crate::ui::widgets::TuiMenu;
-use crate::ui::RenderResult;
use crate::util::to_string::ToString;
const BORDER_HEIGHT: usize = 1;
@@ -27,8 +26,7 @@ impl<'a> TuiCommandMenu<'a> {
impl<'a> Widget for TuiCommandMenu<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- let mut render_result = RenderResult::new();
- TuiView::new(self.context, &mut render_result).render(area, buf);
+ TuiView::new(self.context).render(area, buf);
// draw menu
let mut display_vec: Vec<String> = self
diff --git a/src/ui/views/tui_folder_view.rs b/src/ui/views/tui_folder_view.rs
index 1a07ce0..38d5f4e 100644
--- a/src/ui/views/tui_folder_view.rs
+++ b/src/ui/views/tui_folder_view.rs
@@ -1,5 +1,3 @@
-use std::path;
-
use tui::buffer::Buffer;
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Style};
@@ -12,22 +10,20 @@ use crate::ui;
use crate::ui::widgets::{
TuiDirList, TuiDirListDetailed, TuiFilePreview, TuiFooter, TuiTabBar, TuiTopBar,
};
-use crate::ui::RenderResult;
+use crate::ui::PreviewArea;
const TAB_VIEW_WIDTH: u16 = 15;
pub struct TuiFolderView<'a> {
pub context: &'a AppContext,
pub show_bottom_status: bool,
- pub render_result: &'a mut RenderResult,
}
impl<'a> TuiFolderView<'a> {
- pub fn new(context: &'a AppContext, render_result: &'a mut RenderResult) -> Self {
+ pub fn new(context: &'a AppContext) -> Self {
Self {
context,
show_bottom_status: true,
- render_result,
}
}
}
@@ -45,31 +41,18 @@ impl<'a> Widget for TuiFolderView<'a> {
let config = self.context.config_ref();
let display_options = config.display_options_ref();
- 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_ref(e.file_path()) {
- Some(Some(p)) if p.status.code() != Some(1) => {
- (true, &display_options.default_layout)
- }
- _ => (false, &display_options.no_preview_layout),
- },
- },
- }
- };
+ let constraints = get_constraints(self.context);
+ let is_default_layout = constraints == &display_options.default_layout;
- let layout_rect = if config.display_options_ref().show_borders() {
+ let layout_rect = if display_options.show_borders() {
let area = Rect {
y: area.top() + 1,
height: area.height - 2,
..area
};
+ let layout = calculate_layout_with_borders(area, constraints);
+
let block = Block::default().borders(Borders::ALL);
let inner = block.inner(area);
block.render(area, buf);
@@ -79,6 +62,12 @@ impl<'a> Widget for TuiFolderView<'a> {
.constraints(constraints.as_ref())
.split(inner);
+ let block = Block::default().borders(Borders::RIGHT);
+ block.render(layout_rect[0], buf);
+
+ let block = Block::default().borders(Borders::LEFT);
+ block.render(layout_rect[2], buf);
+
// Render inner borders properly.
{
let top = area.top();
@@ -97,36 +86,18 @@ impl<'a> Widget for TuiFolderView<'a> {
Constraint::Ratio(0, _) => (),
_ => intersections.render_left(buf),
}
- if default_layout {
+ if is_default_layout {
intersections.render_right(buf);
}
}
-
- let block = Block::default().borders(Borders::RIGHT);
- let inner1 = block.inner(layout_rect[0]);
- block.render(layout_rect[0], buf);
-
- let block = Block::default().borders(Borders::LEFT);
- let inner3 = block.inner(layout_rect[2]);
- block.render(layout_rect[2], buf);
-
- vec![inner1, layout_rect[1], inner3]
+ layout
} 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]
+ let area = Rect {
+ y: area.top() + 1,
+ height: area.height - 2,
+ ..area
};
- layout_rect
+ calculate_layout(area, constraints)
};
// render parent view
@@ -168,31 +139,24 @@ impl<'a> Widget for TuiFolderView<'a> {
}
}
- // render preview
- let mut file_preview_path: Option<path::PathBuf> = None;
- let mut preview_area: Option<ui::Rect> = None;
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(Some(preview)) = preview_context.get_preview_ref(entry.file_path()) {
- match preview.status.code() {
- Some(1) | None => {}
- _ => {
- let rect = layout_rect[2];
- TuiFilePreview::new(entry, preview).render(rect, buf);
- file_preview_path = Some(entry.file_path_buf());
- preview_area = Some(ui::Rect {
- x: rect.x,
- y: rect.y,
- width: rect.width,
- height: rect.height,
- })
- }
+ } else if curr_entry.is_some() {
+ let preview_area = calculate_preview(self.context, layout_rect[2]);
+ if let Some(preview_area) = preview_area {
+ let area = Rect {
+ x: preview_area.preview_area.x,
+ y: preview_area.preview_area.y,
+ width: preview_area.preview_area.width,
+ height: preview_area.preview_area.height,
+ };
+ if let Some(Some(preview)) =
+ preview_context.get_preview_ref(&preview_area.file_preview_path)
+ {
+ TuiFilePreview::new(preview).render(area, buf);
}
}
}
- self.render_result.file_preview_path = file_preview_path;
- self.render_result.preview_area = preview_area;
let topbar_width = area.width;
let rect = Rect {
@@ -252,3 +216,100 @@ impl Intersections {
.set_symbol(HORIZONTAL_UP);
}
}
+
+pub fn get_constraints(context: &AppContext) -> &[Constraint; 3] {
+ let preview_context = context.preview_context_ref();
+ let curr_tab = context.tab_context_ref().curr_tab_ref();
+
+ let curr_list = curr_tab.curr_list_ref();
+ let curr_entry = curr_list.and_then(|c| c.curr_entry_ref());
+
+ let child_list = curr_tab.child_list_ref();
+
+ let config = context.config_ref();
+ let display_options = config.display_options_ref();
+
+ if !display_options.collapse_preview() {
+ &display_options.default_layout
+ } else {
+ match child_list {
+ Some(_) => &display_options.default_layout,
+ None => match curr_entry {
+ None => &display_options.no_preview_layout,
+ Some(e) => match preview_context.get_preview_ref(e.file_path()) {
+ Some(Some(p)) if p.status.code() != Some(1) => &display_options.default_layout,
+ _ => &display_options.no_preview_layout,
+ },
+ },
+ }
+ }
+}
+
+pub fn calculate_layout(area: Rect, constraints: &[Constraint; 3]) -> Vec<Rect> {
+ let mut layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .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
+}
+
+pub fn calculate_layout_with_borders(area: Rect, constraints: &[Constraint; 3]) -> Vec<Rect> {
+ 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]
+}
+
+pub fn calculate_preview(context: &AppContext, rect: Rect) -> Option<PreviewArea> {
+ let preview_context = context.preview_context_ref();
+ let curr_tab = context.tab_context_ref().curr_tab_ref();
+
+ let child_list = curr_tab.child_list_ref();
+
+ let curr_list = curr_tab.curr_list_ref();
+ let curr_entry = curr_list.and_then(|c| c.curr_entry_ref());
+
+ if child_list.as_ref().is_some() {
+ None
+ } else if let Some(entry) = curr_entry {
+ if let Some(Some(preview)) = preview_context.get_preview_ref(entry.file_path()) {
+ match preview.status.code() {
+ Some(1) | None => None,
+ _ => {
+ let file_preview_path = entry.file_path_buf();
+ let preview_area = ui::Rect {
+ x: rect.x,
+ y: rect.y,
+ width: rect.width,
+ height: rect.height,
+ };
+ Some(PreviewArea::new(file_preview_path, preview_area))
+ }
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
diff --git a/src/ui/views/tui_textfield.rs b/src/ui/views/tui_textfield.rs
index b9426db..d829852 100644
--- a/src/ui/views/tui_textfield.rs
+++ b/src/ui/views/tui_textfield.rs
@@ -10,7 +10,6 @@ use crate::context::AppContext;
use crate::event::AppEvent;
use crate::ui::views::TuiView;
use crate::ui::widgets::{TuiMenu, TuiMultilineText};
-use crate::ui::RenderResult;
use crate::ui::TuiBackend;
use crate::util::input;
@@ -97,8 +96,7 @@ impl<'a> TuiTextField<'a> {
return;
}
{
- let mut render_result = RenderResult::new();
- let mut view = TuiView::new(context, &mut render_result);
+ let mut view = TuiView::new(context);
view.show_bottom_status = false;
frame.render_widget(view, area);
}
diff --git a/src/ui/views/tui_view.rs b/src/ui/views/tui_view.rs
index a5f093c..2178475 100644
--- a/src/ui/views/tui_view.rs
+++ b/src/ui/views/tui_view.rs
@@ -4,26 +4,23 @@ use tui::widgets::Widget;
use super::TuiFolderView;
use crate::context::AppContext;
-use crate::ui::RenderResult;
pub struct TuiView<'a> {
pub context: &'a AppContext,
pub show_bottom_status: bool,
- pub render_result: &'a mut RenderResult,
}
impl<'a> TuiView<'a> {
- pub fn new(context: &'a AppContext, render_result: &'a mut RenderResult) -> Self {
+ pub fn new(context: &'a AppContext) -> Self {
Self {
context,
show_bottom_status: true,
- render_result,
}
}
}
impl<'a> Widget for TuiView<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- TuiFolderView::new(self.context, self.render_result).render(area, buf);
+ TuiFolderView::new(self.context).render(area, buf);
}
}
diff --git a/src/ui/widgets/tui_file_preview.rs b/src/ui/widgets/tui_file_preview.rs
index cfd6812..de0d3ec 100644
--- a/src/ui/widgets/tui_file_preview.rs
+++ b/src/ui/widgets/tui_file_preview.rs
@@ -4,17 +4,15 @@ use tui::layout::Rect;
use tui::text::Text;
use tui::widgets::Widget;
-use crate::fs::JoshutoDirEntry;
use crate::preview::preview_file::FilePreview;
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 }
+ pub fn new(preview: &'a FilePreview) -> Self {
+ Self { preview }
}
}
diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs
index f1bd0d4..bc1b210 100644
--- a/src/ui/widgets/tui_prompt.rs
+++ b/src/ui/widgets/tui_prompt.rs
@@ -7,7 +7,6 @@ use tui::widgets::{Clear, Paragraph, Wrap};
use crate::context::AppContext;
use crate::event::AppEvent;
use crate::ui::views::TuiView;
-use crate::ui::RenderResult;
use crate::ui::TuiBackend;
use crate::util::input;
@@ -32,8 +31,7 @@ impl<'a> TuiPrompt<'a> {
}
{
- let mut render_result = RenderResult::new();
- let mut view = TuiView::new(context, &mut render_result);
+ let mut view = TuiView::new(context);
view.show_bottom_status = false;
frame.render_widget(view, f_size);
}