mirror of
https://github.com/bobwen-dev/hunter
synced 2025-04-12 00:55:41 +02:00
added on_ready support
This commit is contained in:
parent
fe542047c2
commit
3b38143f9b
48
src/fail.rs
48
src/fail.rs
@ -1,4 +1,6 @@
|
|||||||
use failure;
|
use failure;
|
||||||
|
use failure::Error;
|
||||||
|
use failure::{Fail, ResultExt};
|
||||||
|
|
||||||
pub type HResult<T> = Result<T, HError>;
|
pub type HResult<T> = Result<T, HError>;
|
||||||
|
|
||||||
@ -8,36 +10,80 @@ pub enum HError {
|
|||||||
IoError{#[cause] error: std::io::Error},
|
IoError{#[cause] error: std::io::Error},
|
||||||
#[fail(display = "Mutex failed")]
|
#[fail(display = "Mutex failed")]
|
||||||
MutexError,
|
MutexError,
|
||||||
|
#[fail(display = "Can't lock!")]
|
||||||
|
TryLockError,
|
||||||
#[fail(display = "Channel failed: {}", error)]
|
#[fail(display = "Channel failed: {}", error)]
|
||||||
ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError},
|
ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError},
|
||||||
|
#[fail(display = "Channel failed: {}", error)]
|
||||||
|
ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError},
|
||||||
|
#[fail(display = "Channel failed")]
|
||||||
|
ChannelSendError,
|
||||||
#[fail(display = "Previewer failed on file: {}", file)]
|
#[fail(display = "Previewer failed on file: {}", file)]
|
||||||
PreviewFailed{file: String},
|
PreviewFailed{file: String},
|
||||||
#[fail(display = "StalePreviewer for file: {}", file)]
|
#[fail(display = "StalePreviewer for file: {}", file)]
|
||||||
StalePreviewError{file: String},
|
StalePreviewError{file: String},
|
||||||
#[fail(display = "Failed: {}", error)]
|
#[fail(display = "Failed: {}", error)]
|
||||||
Error{#[cause] error: failure::Error }
|
Error{#[cause] error: failure::Error },
|
||||||
|
#[fail(display = "Was None!")]
|
||||||
|
NoneError,
|
||||||
|
#[fail(display = "Not ready yet!")]
|
||||||
|
WillBeNotReady,
|
||||||
|
#[fail(display = "No widget found")]
|
||||||
|
NoWidgetError
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for HError {
|
impl From<std::io::Error> for HError {
|
||||||
fn from(error: std::io::Error) -> Self {
|
fn from(error: std::io::Error) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
HError::IoError { error: error }
|
HError::IoError { error: error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<failure::Error> for HError {
|
impl From<failure::Error> for HError {
|
||||||
fn from(error: failure::Error) -> Self {
|
fn from(error: failure::Error) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
HError::Error { error: error }
|
HError::Error { error: error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::sync::mpsc::TryRecvError> for HError {
|
impl From<std::sync::mpsc::TryRecvError> for HError {
|
||||||
fn from(error: std::sync::mpsc::TryRecvError) -> Self {
|
fn from(error: std::sync::mpsc::TryRecvError) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
HError::ChannelTryRecvError { error: error }
|
HError::ChannelTryRecvError { error: error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::sync::mpsc::RecvError> for HError {
|
||||||
|
fn from(error: std::sync::mpsc::RecvError) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
|
HError::ChannelRecvError { error: error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<std::sync::mpsc::SendError<T>> for HError {
|
||||||
|
fn from(error: std::sync::mpsc::SendError<T>) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
|
HError::ChannelSendError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<std::sync::PoisonError<T>> for HError {
|
impl<T> From<std::sync::PoisonError<T>> for HError {
|
||||||
fn from(_: std::sync::PoisonError<T>) -> Self {
|
fn from(_: std::sync::PoisonError<T>) -> Self {
|
||||||
|
dbg!("Poisoned Mutex");
|
||||||
HError::MutexError
|
HError::MutexError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<std::sync::TryLockError<T>> for HError {
|
||||||
|
fn from(error: std::sync::TryLockError<T>) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
|
HError::TryLockError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::option::NoneError> for HError {
|
||||||
|
fn from(error: std::option::NoneError) -> Self {
|
||||||
|
dbg!(&error);
|
||||||
|
HError::NoneError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ use termion::event::Key;
|
|||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
use crate::coordinates::{Coordinates};
|
||||||
use crate::files::{File, Files};
|
use crate::files::{File, Files};
|
||||||
@ -9,10 +10,13 @@ use crate::listview::ListView;
|
|||||||
use crate::miller_columns::MillerColumns;
|
use crate::miller_columns::MillerColumns;
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
use crate::tabview::{TabView, Tabbable};
|
use crate::tabview::{TabView, Tabbable};
|
||||||
|
use crate::preview::WillBeWidget;
|
||||||
|
use crate::fail::{HError, HResult};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct FileBrowser {
|
pub struct FileBrowser {
|
||||||
pub columns: MillerColumns<ListView<Files>>,
|
pub columns: MillerColumns<WillBeWidget<ListView<Files>>>,
|
||||||
|
pub cwd: File
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabbable for TabView<FileBrowser> {
|
impl Tabbable for TabView<FileBrowser> {
|
||||||
@ -55,37 +59,70 @@ impl FileBrowser {
|
|||||||
miller.set_coordinates(&coords);
|
miller.set_coordinates(&coords);
|
||||||
|
|
||||||
|
|
||||||
let lists: Result<Vec<ListView<Files>>, Box<Error>> = cwd
|
// let lists: Result<Vec<ListView<Files>>, Box<Error>> = cwd
|
||||||
.ancestors()
|
// .ancestors()
|
||||||
.map(|path| Ok(ListView::new(Files::new_from_path(path)?)))
|
// .map(|path| Ok(ListView::new(Files::new_from_path(path)?)))
|
||||||
.take(2)
|
// .take(2)
|
||||||
.collect();
|
// .collect();
|
||||||
let mut lists = lists?;
|
// let mut lists = lists?;
|
||||||
lists.reverse();
|
// lists.reverse();
|
||||||
|
let (left_coords, main_coords, _) = miller.calculate_coordinates();
|
||||||
|
|
||||||
for widget in lists {
|
let main_path: std::path::PathBuf = cwd.ancestors().take(1).map(|path| std::path::PathBuf::from(path)).collect();
|
||||||
miller.push_widget(widget);
|
let main_widget = WillBeWidget::new(Box::new(move |_| {
|
||||||
}
|
let mut list = ListView::new(Files::new_from_path(&main_path).unwrap());
|
||||||
|
list.set_coordinates(&main_coords);
|
||||||
|
list.animate_slide_up();
|
||||||
|
Ok(list)
|
||||||
|
}));
|
||||||
|
|
||||||
let mut file_browser = FileBrowser { columns: miller };
|
let left_path: std::path::PathBuf = cwd.ancestors().skip(1).take(1).map(|path| std::path::PathBuf::from(path)).collect();
|
||||||
|
let left_widget = WillBeWidget::new(Box::new(move |_| {
|
||||||
|
let mut list = ListView::new(Files::new_from_path(&left_path).unwrap());
|
||||||
|
list.set_coordinates(&left_coords);
|
||||||
|
list.animate_slide_up();
|
||||||
|
Ok(list)
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
file_browser.fix_selection();
|
|
||||||
file_browser.animate_columns();
|
|
||||||
file_browser.update_preview();
|
miller.push_widget(left_widget);
|
||||||
|
miller.push_widget(main_widget);
|
||||||
|
|
||||||
|
// for widget in lists {
|
||||||
|
// miller.push_widget(widget);
|
||||||
|
// }
|
||||||
|
|
||||||
|
let cwd = File::new_from_path(&cwd).unwrap();
|
||||||
|
|
||||||
|
let mut file_browser = FileBrowser { columns: miller,
|
||||||
|
cwd: cwd };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//file_browser.fix_selection();
|
||||||
|
//file_browser.animate_columns();
|
||||||
|
//file_browser.update_preview();
|
||||||
|
|
||||||
Ok(file_browser)
|
Ok(file_browser)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter_dir(&mut self) {
|
pub fn enter_dir(&mut self) -> HResult<()> {
|
||||||
let file = self.selected_file();
|
let file = self.selected_file()?;
|
||||||
|
let (_, coords, _) = self.columns.calculate_coordinates();
|
||||||
|
|
||||||
match file.read_dir() {
|
match file.read_dir() {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
std::env::set_current_dir(&file.path).unwrap();
|
std::env::set_current_dir(&file.path).unwrap();
|
||||||
let view = ListView::new(files);
|
let view = WillBeWidget::new(Box::new(move |_| {
|
||||||
|
let files = files.clone();
|
||||||
|
let mut list = ListView::new(files);
|
||||||
|
list.set_coordinates(&coords);
|
||||||
|
list.animate_slide_up();
|
||||||
|
Ok(list)
|
||||||
|
}));
|
||||||
self.columns.push_widget(view);
|
self.columns.push_widget(view);
|
||||||
self.update_preview();
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let status = std::process::Command::new("rifle")
|
let status = std::process::Command::new("rifle")
|
||||||
@ -103,102 +140,121 @@ impl FileBrowser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn go_back(&mut self) {
|
pub fn go_back(&mut self) -> HResult<()> {
|
||||||
if self.columns.get_left_widget().is_none() {
|
// if self.left_widget().is_err() {
|
||||||
return;
|
// return None;
|
||||||
}
|
// }
|
||||||
let fileview = self.columns.get_main_widget();
|
// if self.columns.get_main_widget().is_none() {
|
||||||
let path = fileview.selected_file().grand_parent().unwrap();
|
// return None;
|
||||||
std::env::set_current_dir(path).unwrap();
|
// }
|
||||||
|
let fileview = self.main_widget()?;
|
||||||
|
let path = self.selected_file()?.grand_parent()?;
|
||||||
|
std::env::set_current_dir(path)?;
|
||||||
self.columns.pop_widget();
|
self.columns.pop_widget();
|
||||||
|
|
||||||
// Make sure there's a directory on the left unless it's /
|
// Make sure there's a directory on the left unless it's /
|
||||||
if self.columns.get_left_widget().is_none() {
|
if self.left_widget().is_err() {
|
||||||
let file = self.columns.get_main_widget().clone_selected_file();
|
let file = self.selected_file()?.clone();
|
||||||
if let Some(grand_parent) = file.grand_parent() {
|
if let Some(grand_parent) = file.grand_parent() {
|
||||||
let mut left_view = ListView::new(Files::new_from_path(&grand_parent).unwrap());
|
let mut left_view = WillBeWidget::new(Box::new(move |_| {
|
||||||
left_view.select_file(&file);
|
let mut view
|
||||||
|
= ListView::new(Files::new_from_path(&grand_parent)?);
|
||||||
|
Ok(view)
|
||||||
|
}));
|
||||||
self.columns.prepend_widget(left_view);
|
self.columns.prepend_widget(left_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.fix_selection();
|
|
||||||
self.columns.refresh();
|
self.columns.refresh();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_preview(&mut self) {
|
pub fn update_preview(&mut self) -> HResult<()> {
|
||||||
if self.columns.get_main_widget().content.len() == 0 { return }
|
let file = self.selected_file()?.clone();
|
||||||
let file = self.columns.get_main_widget().selected_file().clone();
|
|
||||||
let preview = &mut self.columns.preview;
|
let preview = &mut self.columns.preview;
|
||||||
preview.set_file(&file);
|
preview.set_file(&file);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fix_selection(&mut self) {
|
pub fn fix_selection(&mut self) -> HResult<()> {
|
||||||
let cwd = self.cwd();
|
let cwd = self.cwd()?;
|
||||||
self.columns.get_left_widget_mut()
|
(*self.left_widget()?.lock()?).as_mut()?.select_file(&cwd);
|
||||||
.map(|w|
|
Ok(())
|
||||||
w.select_file(&cwd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cwd(&self) -> File {
|
pub fn cwd(&self) -> HResult<File> {
|
||||||
self.columns.get_main_widget().content.directory.clone()
|
//(self.columns.get_main_widget()?.widget()?.content.directory.clone())
|
||||||
|
let widget = self.columns.get_main_widget()?.widget()?;
|
||||||
|
let cwd = (*widget.lock()?).as_ref()?.content.directory.clone();
|
||||||
|
Ok(cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_file(&self) -> &File {
|
pub fn selected_file(&self) -> HResult<File> {
|
||||||
self.main_column().selected_file()
|
let widget = self.main_widget()?;
|
||||||
|
let file = widget.lock()?.as_ref()?.selected_file().clone();
|
||||||
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_column(&self) -> &ListView<Files> {
|
pub fn main_widget(&self) -> HResult<Arc<Mutex<Option<ListView<Files>>>>> {
|
||||||
self.columns.get_main_widget()
|
let widget = self.columns.get_main_widget()?.widget()?;
|
||||||
|
Ok(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn quit_with_dir(&self) {
|
pub fn left_widget(&self) -> HResult<Arc<Mutex<Option<ListView<Files>>>>> {
|
||||||
let cwd = self.cwd().path;
|
let widget = self.columns.get_left_widget()?.widget()?;
|
||||||
let selected_file = self.selected_file().path.to_string_lossy();
|
Ok(widget)
|
||||||
|
}
|
||||||
|
|
||||||
let mut filepath = dirs_2::home_dir().unwrap();
|
pub fn quit_with_dir(&self) -> HResult<()> {
|
||||||
|
let cwd = self.cwd()?.path;
|
||||||
|
let selected_file = self.selected_file()?;
|
||||||
|
let selected_file = selected_file.path.to_string_lossy();
|
||||||
|
|
||||||
|
let mut filepath = dirs_2::home_dir()?;
|
||||||
filepath.push(".hunter_cwd");
|
filepath.push(".hunter_cwd");
|
||||||
|
|
||||||
let output = format!("HUNTER_CWD=\"{}\"\nF=\"{}\"",
|
let output = format!("HUNTER_CWD=\"{}\"\nF=\"{}\"",
|
||||||
cwd.to_str().unwrap(),
|
cwd.to_str()?,
|
||||||
selected_file);
|
selected_file);
|
||||||
|
|
||||||
let mut file = std::fs::File::create(filepath).unwrap();
|
let mut file = std::fs::File::create(filepath)?;
|
||||||
file.write(output.as_bytes()).unwrap();
|
file.write(output.as_bytes())?;
|
||||||
panic!("Quitting!");
|
panic!("Quitting!");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animate_columns(&mut self) {
|
pub fn animate_columns(&mut self) {
|
||||||
self.columns.get_left_widget_mut().map(|w| w.animate_slide_up());
|
self.columns.get_left_widget_mut().map(|w| w.animate_slide_up());
|
||||||
self.columns.get_main_widget_mut().animate_slide_up();
|
self.columns.get_main_widget_mut().unwrap().animate_slide_up();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn turbo_cd(&mut self) {
|
pub fn turbo_cd(&mut self) {
|
||||||
let dir = self.minibuffer("cd: ");
|
let dir = self.minibuffer("cd: ");
|
||||||
|
|
||||||
match dir {
|
// match dir {
|
||||||
Some(dir) => {
|
// Some(dir) => {
|
||||||
Files::new_from_path(&std::path::PathBuf::from(&dir)).and_then(|files| {
|
// Files::new_from_path(&std::path::PathBuf::from(&dir)).and_then(|files| {
|
||||||
let cwd = files.directory.clone();
|
// let cwd = files.directory.clone();
|
||||||
self.columns.widgets.widgets.clear();
|
// self.columns.widgets.widgets.clear();
|
||||||
self.columns.push_widget(ListView::new(files));
|
// self.columns.push_widget(ListView::new(files));
|
||||||
|
|
||||||
std::env::set_current_dir(&cwd.path).unwrap();
|
// std::env::set_current_dir(&cwd.path).unwrap();
|
||||||
|
|
||||||
if let Some(grand_parent) = cwd.path.parent() {
|
// if let Some(grand_parent) = cwd.path.parent() {
|
||||||
let left_view =
|
// let left_view =
|
||||||
ListView::new(Files::new_from_path(&grand_parent).unwrap());
|
// ListView::new(Files::new_from_path(&grand_parent).unwrap());
|
||||||
self.columns.prepend_widget(left_view);
|
// self.columns.prepend_widget(left_view);
|
||||||
}
|
// }
|
||||||
self.fix_selection();
|
// self.fix_selection();
|
||||||
self.update_preview();
|
// self.update_preview();
|
||||||
self.refresh();
|
// self.refresh();
|
||||||
self.columns.refresh();
|
// self.columns.refresh();
|
||||||
Ok(())
|
// Ok(())
|
||||||
}).ok();
|
// }).ok();
|
||||||
} None => {}
|
// } None => {}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,8 +267,9 @@ impl Widget for FileBrowser {
|
|||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn render_header(&self) -> String {
|
||||||
|
if self.main_widget().is_err() { return "".to_string() }
|
||||||
let xsize = self.get_coordinates().xsize();
|
let xsize = self.get_coordinates().xsize();
|
||||||
let file = self.selected_file();
|
let file = self.selected_file().unwrap();
|
||||||
let name = &file.name;
|
let name = &file.name;
|
||||||
|
|
||||||
let color = if file.is_dir() || file.color.is_none() {
|
let color = if file.is_dir() || file.color.is_none() {
|
||||||
@ -226,9 +283,10 @@ impl Widget for FileBrowser {
|
|||||||
sized_path
|
sized_path
|
||||||
}
|
}
|
||||||
fn render_footer(&self) -> String {
|
fn render_footer(&self) -> String {
|
||||||
|
if self.main_widget().is_err() { return "".to_string() }
|
||||||
let xsize = self.get_coordinates().xsize();
|
let xsize = self.get_coordinates().xsize();
|
||||||
let ypos = self.get_coordinates().position().y();
|
let ypos = self.get_coordinates().position().y();
|
||||||
let file = self.selected_file();
|
let file = self.selected_file().unwrap();
|
||||||
|
|
||||||
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into());
|
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into());
|
||||||
let user = file.pretty_user().unwrap_or("NOUSER".into());
|
let user = file.pretty_user().unwrap_or("NOUSER".into());
|
||||||
@ -236,8 +294,8 @@ impl Widget for FileBrowser {
|
|||||||
let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into());
|
let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into());
|
||||||
|
|
||||||
|
|
||||||
let selection = self.main_column().get_selection();
|
let selection = (*self.main_widget().as_ref().unwrap().lock().unwrap()).as_ref().unwrap().get_selection();
|
||||||
let file_count = self.main_column().content.len();
|
let file_count = (*self.main_widget().unwrap().lock().unwrap()).as_ref().unwrap().content.len();
|
||||||
let file_count = format!("{}", file_count);
|
let file_count = format!("{}", file_count);
|
||||||
let digits = file_count.len();
|
let digits = file_count.len();
|
||||||
let file_count = format!("{:digits$}/{:digits$}",
|
let file_count = format!("{:digits$}/{:digits$}",
|
||||||
@ -251,11 +309,13 @@ impl Widget for FileBrowser {
|
|||||||
crate::term::goto_xy(count_xpos, count_ypos), file_count)
|
crate::term::goto_xy(count_xpos, count_ypos), file_count)
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
|
self.update_preview();
|
||||||
|
self.fix_selection();
|
||||||
self.columns.refresh();
|
self.columns.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> String {
|
||||||
if self.columns.get_left_widget().is_none() {
|
if self.columns.get_left_widget().is_err() {
|
||||||
self.columns.get_clearlist() + &self.columns.get_drawlist()
|
self.columns.get_clearlist() + &self.columns.get_drawlist()
|
||||||
} else {
|
} else {
|
||||||
self.columns.get_drawlist()
|
self.columns.get_drawlist()
|
||||||
@ -265,10 +325,10 @@ impl Widget for FileBrowser {
|
|||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) {
|
||||||
match key {
|
match key {
|
||||||
Key::Char('/') => self.turbo_cd(),
|
Key::Char('/') => self.turbo_cd(),
|
||||||
Key::Char('Q') => self.quit_with_dir(),
|
Key::Char('Q') => { self.quit_with_dir(); },
|
||||||
Key::Right | Key::Char('f') => self.enter_dir(),
|
Key::Right | Key::Char('f') => { self.enter_dir(); },
|
||||||
Key::Left | Key::Char('b') => self.go_back(),
|
Key::Left | Key::Char('b') => { self.go_back(); },
|
||||||
_ => self.columns.get_main_widget_mut().on_key(key),
|
_ => self.columns.get_main_widget_mut().unwrap().on_key(key),
|
||||||
}
|
}
|
||||||
self.update_preview();
|
self.update_preview();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(vec_remove_item)]
|
#![feature(vec_remove_item)]
|
||||||
#![feature(trivial_bounds)]
|
#![feature(trivial_bounds)]
|
||||||
|
#![feature(try_trait)]
|
||||||
|
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
extern crate unicode_width;
|
extern crate unicode_width;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
use crate::preview::Previewer;
|
use crate::preview::Previewer;
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
use crate::hbox::HBox;
|
use crate::hbox::HBox;
|
||||||
|
use crate::fail::{HError, HResult};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct MillerColumns<T> where T: Widget {
|
pub struct MillerColumns<T> where T: Widget {
|
||||||
@ -80,25 +83,27 @@ where
|
|||||||
(left_coords, main_coords, preview_coords)
|
(left_coords, main_coords, preview_coords)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_left_widget(&self) -> Option<&T> {
|
pub fn get_left_widget(&self) -> HResult<&T> {
|
||||||
let len = self.widgets.widgets.len();
|
let len = self.widgets.widgets.len();
|
||||||
if len < 2 {
|
if len < 2 {
|
||||||
return None;
|
return Err(HError::NoWidgetError);
|
||||||
}
|
}
|
||||||
self.widgets.widgets.get(len - 2)
|
Ok(self.widgets.widgets.get(len - 2)?)
|
||||||
}
|
}
|
||||||
pub fn get_left_widget_mut(&mut self) -> Option<&mut T> {
|
pub fn get_left_widget_mut(&mut self) -> HResult<&mut T> {
|
||||||
let len = self.widgets.widgets.len();
|
let len = self.widgets.widgets.len();
|
||||||
if len < 2 {
|
if len < 2 {
|
||||||
return None;
|
return Err(HError::NoWidgetError);
|
||||||
}
|
}
|
||||||
self.widgets.widgets.get_mut(len - 2)
|
Ok(self.widgets.widgets.get_mut(len - 2)?)
|
||||||
}
|
}
|
||||||
pub fn get_main_widget(&self) -> &T {
|
pub fn get_main_widget(&self) -> HResult<&T> {
|
||||||
self.widgets.widgets.last().unwrap()
|
let widget = self.widgets.widgets.last()?;
|
||||||
|
Ok(widget)
|
||||||
}
|
}
|
||||||
pub fn get_main_widget_mut(&mut self) -> &mut T {
|
pub fn get_main_widget_mut(&mut self) -> HResult<&mut T> {
|
||||||
self.widgets.widgets.last_mut().unwrap()
|
let widget = self.widgets.widgets.last_mut()?;
|
||||||
|
Ok(widget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,11 +128,11 @@ where
|
|||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
let (left_coords, main_coords, preview_coords) = self.calculate_coordinates();
|
let (left_coords, main_coords, preview_coords) = self.calculate_coordinates();
|
||||||
|
|
||||||
if let Some(left_widget) = self.get_left_widget_mut() {
|
if let Ok(left_widget) = self.get_left_widget_mut() {
|
||||||
left_widget.set_coordinates(&left_coords);
|
left_widget.set_coordinates(&left_coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(main_widget) = self.widgets.widgets.last_mut() {
|
if let Ok(main_widget) = self.get_main_widget_mut() {
|
||||||
main_widget.set_coordinates(&main_coords);
|
main_widget.set_coordinates(&main_coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,15 +142,20 @@ where
|
|||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> String {
|
||||||
let left_widget = match self.get_left_widget() {
|
let left_widget = match self.get_left_widget() {
|
||||||
Some(widget) => widget.get_drawlist(),
|
Ok(widget) => widget.get_drawlist(),
|
||||||
None => "".into(),
|
Err(_) => "".into(),
|
||||||
};
|
};
|
||||||
let main_widget = self.get_main_widget().get_drawlist();
|
let main_widget = self.get_main_widget();
|
||||||
|
match main_widget {
|
||||||
|
Ok(main_widget) => {
|
||||||
let preview = self.preview.get_drawlist();
|
let preview = self.preview.get_drawlist();
|
||||||
format!("{}{}{}", main_widget, left_widget, preview)
|
format!("{}{}{}", main_widget.get_drawlist(), left_widget, preview)
|
||||||
|
}
|
||||||
|
Err(_) => "".to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) {
|
||||||
self.get_main_widget_mut().on_key(key);
|
self.get_main_widget_mut().unwrap().on_key(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
125
src/preview.rs
125
src/preview.rs
@ -29,19 +29,21 @@ fn kill_proc() -> HResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_stale(stale: &Arc<Mutex<bool>>) -> HResult<bool> {
|
pub fn is_stale(stale: &Arc<Mutex<bool>>) -> HResult<bool> {
|
||||||
Ok(*(stale.lock()?))
|
let stale = *(stale.try_lock().unwrap());
|
||||||
|
Ok(stale)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State<T: Send> {
|
enum State {
|
||||||
Is(T),
|
Is,
|
||||||
Becoming,
|
Becoming,
|
||||||
Taken,
|
|
||||||
Fail
|
Fail
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WillBe<T: Send> {
|
struct WillBe<T: Send> {
|
||||||
pub state: State<T>,
|
pub state: Arc<Mutex<State>>,
|
||||||
rx: std::sync::mpsc::Receiver<T>,
|
pub thing: Arc<Mutex<Option<T>>>,
|
||||||
|
on_ready: Arc<Mutex<Option<Box<Fn(Arc<Mutex<Option<T>>>) -> HResult<()> + Send>>>>,
|
||||||
|
rx: Option<std::sync::mpsc::Receiver<T>>,
|
||||||
stale: Arc<Mutex<bool>>
|
stale: Arc<Mutex<bool>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,42 +51,58 @@ impl<T: Send + 'static> WillBe<T> where {
|
|||||||
pub fn new_become(closure: HClosure<T>)
|
pub fn new_become(closure: HClosure<T>)
|
||||||
-> WillBe<T> {
|
-> WillBe<T> {
|
||||||
let (tx,rx) = std::sync::mpsc::channel();
|
let (tx,rx) = std::sync::mpsc::channel();
|
||||||
let mut willbe = WillBe { state: State::Becoming,
|
let mut willbe = WillBe { state: Arc::new(Mutex::new(State::Becoming)),
|
||||||
rx: rx,
|
thing: Arc::new(Mutex::new(None)),
|
||||||
|
on_ready: Arc::new(Mutex::new(None)),
|
||||||
|
rx: Some(rx),
|
||||||
stale: Arc::new(Mutex::new(false)) };
|
stale: Arc::new(Mutex::new(false)) };
|
||||||
willbe.run(closure, tx);
|
willbe.run(closure, tx);
|
||||||
willbe
|
willbe
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self, closure: HClosure<T>, tx: std::sync::mpsc::Sender<T>) {
|
fn run(&mut self, closure: HClosure<T>, tx: std::sync::mpsc::Sender<T>) {
|
||||||
|
let state = self.state.clone();
|
||||||
let stale = self.stale.clone();
|
let stale = self.stale.clone();
|
||||||
|
let thing = self.thing.clone();
|
||||||
|
let on_ready_fn = self.on_ready.clone();
|
||||||
std::thread::spawn(move|| {
|
std::thread::spawn(move|| {
|
||||||
let thing = closure(stale);
|
let got_thing = closure(stale);
|
||||||
match thing {
|
match got_thing {
|
||||||
Ok(thing) => { tx.send(thing).ok(); },
|
Ok(got_thing) => {
|
||||||
|
*thing.try_lock().unwrap() = Some(got_thing);
|
||||||
|
*state.try_lock().unwrap() = State::Is;
|
||||||
|
match *on_ready_fn.lock().unwrap() {
|
||||||
|
Some(ref on_ready) => { on_ready(thing.clone()); },
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(err) => { dbg!(err); }
|
Err(err) => { dbg!(err); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_stale(&mut self) -> HResult<()> {
|
pub fn set_stale(&mut self) -> HResult<()> {
|
||||||
*self.stale.lock()? = true;
|
*self.stale.try_lock()? = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&mut self) -> Result<(), Error> {
|
pub fn check(&self) -> HResult<()> {
|
||||||
match self.state {
|
match *self.state.try_lock()? {
|
||||||
State::Is(_) => Ok(()),
|
State::Is => Ok(()),
|
||||||
_ => {
|
_ => Err(HError::WillBeNotReady)
|
||||||
let thing = self.rx.try_recv()?;
|
|
||||||
self.state = State::Is(thing);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait(self) -> Result<T, std::sync::mpsc::RecvError> {
|
pub fn on_ready(&mut self,
|
||||||
self.rx.recv()
|
fun: Box<Fn(Arc<Mutex<Option<T>>>) -> HResult<()> + Send>)
|
||||||
|
-> HResult<()> {
|
||||||
|
if self.check().is_ok() {
|
||||||
|
fun(self.thing.clone());
|
||||||
|
//*self.on_ready.try_lock()? = None;
|
||||||
|
} else {
|
||||||
|
*self.on_ready.try_lock()? = Some(fun);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,21 +116,30 @@ impl<W: Widget + Send> PartialEq for WillBeWidget<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WillBeWidget<T: Widget + Send> {
|
pub struct WillBeWidget<T: Widget + Send> {
|
||||||
willbe: WillBe<T>,
|
willbe: WillBe<T>,
|
||||||
coordinates: Coordinates
|
coordinates: Coordinates
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
||||||
fn new(closure: HClosure<T>) -> WillBeWidget<T> {
|
pub fn new(closure: HClosure<T>) -> WillBeWidget<T> {
|
||||||
|
let mut willbe = WillBe::new_become(Box::new(move |stale| closure(stale)));
|
||||||
|
willbe.on_ready(Box::new(|_| {
|
||||||
|
crate::window::send_event(crate::window::Events::WidgetReady);
|
||||||
|
Ok(()) }));
|
||||||
|
|
||||||
WillBeWidget {
|
WillBeWidget {
|
||||||
willbe: WillBe::new_become(Box::new(move |stale| closure(stale))),
|
willbe: willbe,
|
||||||
coordinates: Coordinates::new()
|
coordinates: Coordinates::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_stale(&mut self) -> HResult<()> {
|
pub fn set_stale(&mut self) -> HResult<()> {
|
||||||
self.willbe.set_stale()
|
self.willbe.set_stale()
|
||||||
}
|
}
|
||||||
|
pub fn widget(&self) -> HResult<Arc<Mutex<Option<T>>>> {
|
||||||
|
self.willbe.check()?;
|
||||||
|
Ok(self.willbe.thing.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<T: Widget + Send> WillBeWidget<T> {
|
// impl<T: Widget + Send> WillBeWidget<T> {
|
||||||
@ -126,41 +153,46 @@ impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
impl<T: Widget + Send> Widget for WillBeWidget<T> {
|
impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_coordinates(&self) -> &Coordinates {
|
||||||
&self.coordinates
|
&self.coordinates
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||||
if self.coordinates == *coordinates {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.coordinates = coordinates.clone();
|
self.coordinates = coordinates.clone();
|
||||||
match &mut self.willbe.state {
|
|
||||||
State::Is(widget) => {
|
{
|
||||||
|
if self.willbe.check().is_err() { return }
|
||||||
|
let widget = self.widget().unwrap();
|
||||||
|
let mut widget = widget.try_lock().unwrap();
|
||||||
|
let widget = widget.as_mut().unwrap();
|
||||||
widget.set_coordinates(&coordinates.clone());
|
widget.set_coordinates(&coordinates.clone());
|
||||||
|
}
|
||||||
|
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn render_header(&self) -> String {
|
fn render_header(&self) -> String {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
match &mut self.willbe.state {
|
if self.willbe.check().is_err() { return }
|
||||||
State::Is(widget) => {
|
let widget = self.widget().unwrap();
|
||||||
|
let mut widget = widget.try_lock().unwrap();
|
||||||
|
let widget = widget.as_mut().unwrap();
|
||||||
widget.refresh();
|
widget.refresh();
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> String {
|
||||||
match &self.willbe.state {
|
if self.willbe.check().is_err() { return "".to_string() }
|
||||||
State::Is(widget) => {
|
let widget = self.widget().unwrap();
|
||||||
|
let widget = widget.try_lock().unwrap();
|
||||||
|
let widget = widget.as_ref().unwrap();
|
||||||
widget.get_drawlist()
|
widget.get_drawlist()
|
||||||
},
|
|
||||||
_ => { "".to_string() }
|
|
||||||
}
|
}
|
||||||
|
fn on_key(&mut self, key: termion::event::Key) {
|
||||||
|
if self.willbe.check().is_err() { return }
|
||||||
|
let widget = self.widget().unwrap();
|
||||||
|
let mut widget = widget.try_lock().unwrap();
|
||||||
|
let widget = widget.as_mut().unwrap();
|
||||||
|
widget.on_key(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +209,7 @@ impl PartialEq for Previewer {
|
|||||||
|
|
||||||
pub struct Previewer {
|
pub struct Previewer {
|
||||||
widget: WillBeWidget<Box<dyn Widget + Send>>,
|
widget: WillBeWidget<Box<dyn Widget + Send>>,
|
||||||
|
file: Option<File>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -186,7 +219,8 @@ impl Previewer {
|
|||||||
Ok(Box::new(crate::textview::TextView::new_blank())
|
Ok(Box::new(crate::textview::TextView::new_blank())
|
||||||
as Box<dyn Widget + Send>)
|
as Box<dyn Widget + Send>)
|
||||||
}));
|
}));
|
||||||
Previewer { widget: willbe }
|
Previewer { widget: willbe,
|
||||||
|
file: None}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn become_preview(&mut self,
|
fn become_preview(&mut self,
|
||||||
@ -197,6 +231,9 @@ impl Previewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_file(&mut self, file: &File) {
|
pub fn set_file(&mut self, file: &File) {
|
||||||
|
if Some(file) == self.file.as_ref() { return }
|
||||||
|
self.file = Some(file.clone());
|
||||||
|
|
||||||
let coordinates = self.get_coordinates().clone();
|
let coordinates = self.get_coordinates().clone();
|
||||||
let file = file.clone();
|
let file = file.clone();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::io::{stdin, stdout, Stdout, Write};
|
use std::io::{stdin, stdout, Stdout, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::mpsc::{Sender, Receiver, channel};
|
||||||
|
|
||||||
use termion::event::{Event, Key};
|
use termion::event::{Event, Key};
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
@ -10,6 +11,18 @@ use crate::term::ScreenExt;
|
|||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Events {
|
||||||
|
InputEvent(Event),
|
||||||
|
WidgetReady
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Window<T>
|
pub struct Window<T>
|
||||||
where
|
where
|
||||||
@ -69,15 +82,56 @@ where
|
|||||||
// Self::show_status("");
|
// Self::show_status("");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
pub fn handle_input(&mut self) {
|
pub fn handle_input(&mut self) {
|
||||||
for event in stdin().events() {
|
let (tx_event_internal, rx_event_internal) = channel();
|
||||||
|
let (tx_event, rx_event) = channel();
|
||||||
|
*TX_EVENT.try_lock().unwrap() = Some(tx_event);
|
||||||
|
|
||||||
|
event_thread(rx_event, tx_event_internal.clone());
|
||||||
|
input_thread(tx_event_internal);
|
||||||
|
|
||||||
|
for event in rx_event_internal.iter() {
|
||||||
//Self::clear_status();
|
//Self::clear_status();
|
||||||
let event = event.unwrap();
|
//let event = event.unwrap();
|
||||||
|
dbg!(&event);
|
||||||
|
match event {
|
||||||
|
Events::InputEvent(event) => {
|
||||||
self.widget.on_event(event);
|
self.widget.on_event(event);
|
||||||
self.screen.cursor_hide();
|
self.screen.cursor_hide();
|
||||||
self.draw();
|
self.draw();
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
self.widget.refresh();
|
||||||
|
self.draw();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_thread(rx: Receiver<Events>, tx: Sender<Events>) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for event in rx.iter() {
|
||||||
|
dbg!(&event);
|
||||||
|
tx.send(event).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_thread(tx: Sender<Events>) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for input in stdin().events() {
|
||||||
|
let input = input.unwrap();
|
||||||
|
tx.send(Events::InputEvent(input)).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_event(event: Events) -> HResult<()> {
|
||||||
|
let tx = TX_EVENT.lock()?.clone()?.clone();
|
||||||
|
tx.send(event)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for Window<T>
|
impl<T> Drop for Window<T>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user