mirror of
https://github.com/bobwen-dev/hunter
synced 2025-04-12 00:55:41 +02:00
moved window stuff to widget itself
This commit is contained in:
parent
e2acef5ddf
commit
eb5a86b7cd
35
src/fail.rs
35
src/fail.rs
@ -1,5 +1,6 @@
|
|||||||
use failure;
|
use failure;
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
|
use failure::Backtrace;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -28,21 +29,45 @@ pub enum HError {
|
|||||||
#[fail(display = "Was None!")]
|
#[fail(display = "Was None!")]
|
||||||
NoneError,
|
NoneError,
|
||||||
#[fail(display = "Not ready yet!")]
|
#[fail(display = "Not ready yet!")]
|
||||||
WillBeNotReady,
|
WillBeNotReady(Backtrace),
|
||||||
#[fail(display = "No widget found")]
|
#[fail(display = "No widget found")]
|
||||||
NoWidgetError,
|
NoWidgetError(Backtrace),
|
||||||
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
|
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
|
||||||
WrongDirectoryError{ path: PathBuf, dir: PathBuf },
|
WrongDirectoryError{ path: PathBuf, dir: PathBuf },
|
||||||
#[fail(display = "Widget finnished")]
|
#[fail(display = "Widget finnished")]
|
||||||
PopupFinnished,
|
PopupFinnished,
|
||||||
#[fail(display = "Input finnished")]
|
|
||||||
InputFinnished,
|
|
||||||
#[fail(display = "No completions found")]
|
#[fail(display = "No completions found")]
|
||||||
NoCompletionsError,
|
NoCompletionsError,
|
||||||
#[fail(display = "No more history")]
|
#[fail(display = "No more history")]
|
||||||
NoHistoryError
|
NoHistoryError,
|
||||||
|
#[fail(display = "No core for widget")]
|
||||||
|
NoWidgetCoreError(Backtrace),
|
||||||
|
#[fail(display = "No header for widget")]
|
||||||
|
NoHeaderError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ErrorLog where Self: Sized {
|
||||||
|
fn log(self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ErrorLog for HResult<T> {
|
||||||
|
fn log(self) {
|
||||||
|
if let Err(err) = self {
|
||||||
|
eprintln!("{:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// impl From<&HError> for HError {
|
||||||
|
// fn from(error: &HError) -> Self {
|
||||||
|
// dbg!(&error);
|
||||||
|
// (error.clone())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
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);
|
dbg!(&error);
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
|
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::mpsc::{channel, Receiver};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
|
||||||
use crate::files::{File, Files};
|
use crate::files::{File, Files};
|
||||||
use crate::listview::ListView;
|
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::preview::WillBeWidget;
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError, ErrorLog};
|
||||||
use crate::window::{Events, send_event};
|
use crate::widget::{Events, WidgetCore};
|
||||||
use crate::proclist::ProcView;
|
use crate::proclist::ProcView;
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +22,7 @@ use crate::proclist::ProcView;
|
|||||||
pub struct FileBrowser {
|
pub struct FileBrowser {
|
||||||
pub columns: MillerColumns<WillBeWidget<ListView<Files>>>,
|
pub columns: MillerColumns<WillBeWidget<ListView<Files>>>,
|
||||||
pub cwd: File,
|
pub cwd: File,
|
||||||
|
core: WidgetCore,
|
||||||
watcher: INotifyWatcher,
|
watcher: INotifyWatcher,
|
||||||
watches: Vec<PathBuf>,
|
watches: Vec<PathBuf>,
|
||||||
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
|
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
|
||||||
@ -31,22 +30,24 @@ pub struct FileBrowser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Tabbable for TabView<FileBrowser> {
|
impl Tabbable for TabView<FileBrowser> {
|
||||||
fn new_tab(&mut self) {
|
fn new_tab(&mut self) -> HResult<()> {
|
||||||
let mut tab = FileBrowser::new().unwrap();
|
let mut tab = FileBrowser::new_cored(&self.active_tab_().core)?;
|
||||||
|
|
||||||
let proc_view = self.active_tab_().proc_view.clone();
|
let proc_view = self.active_tab_().proc_view.clone();
|
||||||
tab.proc_view = proc_view;
|
tab.proc_view = proc_view;
|
||||||
|
|
||||||
self.push_widget(tab);
|
self.push_widget(tab)?;
|
||||||
self.active += 1;
|
self.active += 1;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_tab(&mut self) {
|
fn close_tab(&mut self) -> HResult<()> {
|
||||||
self.close_tab_();
|
self.close_tab_()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_tab(&mut self) {
|
fn next_tab(&mut self) -> HResult<()> {
|
||||||
self.next_tab_();
|
self.next_tab_();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tab_names(&self) -> Vec<Option<String>> {
|
fn get_tab_names(&self) -> Vec<Option<String>> {
|
||||||
@ -66,18 +67,18 @@ impl Tabbable for TabView<FileBrowser> {
|
|||||||
self.active_tab_mut_()
|
self.active_tab_mut_()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_next_tab(&mut self) {
|
fn on_next_tab(&mut self) -> HResult<()> {
|
||||||
self.active_tab_mut().refresh();
|
self.active_tab_mut().refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key_sub(&mut self, key: Key) {
|
fn on_key_sub(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Char('!') => {
|
Key::Char('!') => {
|
||||||
let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone())
|
let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.widgets[self.active].exec_cmd(tab_dirs).ok();
|
self.widgets[self.active].exec_cmd(tab_dirs)
|
||||||
}
|
}
|
||||||
_ => { self.active_tab_mut().on_key(key).ok(); }
|
_ => { self.active_tab_mut().on_key(key) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,11 +87,13 @@ impl Tabbable for TabView<FileBrowser> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn watch_dir(rx: Receiver<DebouncedEvent>, dir_events: Arc<Mutex<Vec<DebouncedEvent>>>) {
|
fn watch_dir(rx: Receiver<DebouncedEvent>,
|
||||||
|
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
|
||||||
|
sender: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
for event in rx.iter() {
|
for event in rx.iter() {
|
||||||
dir_events.lock().unwrap().push(event);
|
dir_events.lock().unwrap().push(event);
|
||||||
send_event(Events::WidgetReady).unwrap();
|
sender.send(Events::WidgetReady).unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,24 +103,23 @@ fn watch_dir(rx: Receiver<DebouncedEvent>, dir_events: Arc<Mutex<Vec<DebouncedEv
|
|||||||
|
|
||||||
|
|
||||||
impl FileBrowser {
|
impl FileBrowser {
|
||||||
pub fn new() -> Result<FileBrowser, Box<Error>> {
|
pub fn new_cored(core: &WidgetCore) -> HResult<FileBrowser> {
|
||||||
let cwd = std::env::current_dir().unwrap();
|
let cwd = std::env::current_dir().unwrap();
|
||||||
let coords = Coordinates::new_at(crate::term::xsize(),
|
let coords = core.coordinates.clone();
|
||||||
crate::term::ysize() - 2,
|
let core_ = core.clone();
|
||||||
1,
|
|
||||||
2);
|
|
||||||
|
|
||||||
let mut miller = MillerColumns::new();
|
let mut miller = MillerColumns::new(core);
|
||||||
miller.set_coordinates(&coords);
|
miller.set_coordinates(&coords)?;
|
||||||
|
|
||||||
|
|
||||||
let (_, main_coords, _) = miller.calculate_coordinates();
|
let (_, main_coords, _) = miller.calculate_coordinates();
|
||||||
|
|
||||||
let main_path: std::path::PathBuf = cwd.ancestors().take(1).map(|path| std::path::PathBuf::from(path)).collect();
|
let main_path: std::path::PathBuf = cwd.ancestors().take(1).map(|path| std::path::PathBuf::from(path)).collect();
|
||||||
let main_widget = WillBeWidget::new(Box::new(move |_| {
|
let main_widget = WillBeWidget::new(&core, Box::new(move |_| {
|
||||||
let mut list = ListView::new(Files::new_from_path(&main_path).unwrap());
|
let mut list = ListView::new(&core_,
|
||||||
list.set_coordinates(&main_coords);
|
Files::new_from_path(&main_path).unwrap());
|
||||||
list.animate_slide_up();
|
list.set_coordinates(&main_coords).log();
|
||||||
|
list.animate_slide_up().log();
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -129,13 +131,14 @@ impl FileBrowser {
|
|||||||
|
|
||||||
let (tx_watch, rx_watch) = channel();
|
let (tx_watch, rx_watch) = channel();
|
||||||
let watcher = INotifyWatcher::new(tx_watch, Duration::from_secs(2)).unwrap();
|
let watcher = INotifyWatcher::new(tx_watch, Duration::from_secs(2)).unwrap();
|
||||||
watch_dir(rx_watch, dir_events.clone());
|
watch_dir(rx_watch, dir_events.clone(), core.get_sender());
|
||||||
|
|
||||||
let mut proc_view = ProcView::new();
|
let mut proc_view = ProcView::new(core);
|
||||||
proc_view.set_coordinates(&coords);
|
proc_view.set_coordinates(&coords).log();
|
||||||
|
|
||||||
Ok(FileBrowser { columns: miller,
|
Ok(FileBrowser { columns: miller,
|
||||||
cwd: cwd,
|
cwd: cwd,
|
||||||
|
core: core.clone(),
|
||||||
watcher: watcher,
|
watcher: watcher,
|
||||||
watches: vec![],
|
watches: vec![],
|
||||||
dir_events: dir_events,
|
dir_events: dir_events,
|
||||||
@ -145,16 +148,17 @@ impl FileBrowser {
|
|||||||
pub fn enter_dir(&mut self) -> HResult<()> {
|
pub fn enter_dir(&mut self) -> HResult<()> {
|
||||||
let file = self.selected_file()?;
|
let file = self.selected_file()?;
|
||||||
let (_, coords, _) = self.columns.calculate_coordinates();
|
let (_, coords, _) = self.columns.calculate_coordinates();
|
||||||
|
let core = self.core.clone();
|
||||||
|
|
||||||
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();
|
||||||
self.cwd = file.clone();
|
self.cwd = file.clone();
|
||||||
let view = WillBeWidget::new(Box::new(move |_| {
|
let view = WillBeWidget::new(&core.clone(), Box::new(move |_| {
|
||||||
let files = files.clone();
|
let files = files.clone();
|
||||||
let mut list = ListView::new(files);
|
let mut list = ListView::new(&core, files);
|
||||||
list.set_coordinates(&coords);
|
list.set_coordinates(&coords).log();
|
||||||
list.animate_slide_up();
|
list.animate_slide_up().log();
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}));
|
}));
|
||||||
self.columns.push_widget(view);
|
self.columns.push_widget(view);
|
||||||
@ -167,12 +171,12 @@ impl FileBrowser {
|
|||||||
match status {
|
match status {
|
||||||
Ok(status) =>
|
Ok(status) =>
|
||||||
self.show_status(&format!("\"{}\" exited with {}",
|
self.show_status(&format!("\"{}\" exited with {}",
|
||||||
"rifle", status)),
|
"rifle", status)).log(),
|
||||||
Err(err) =>
|
Err(err) =>
|
||||||
self.show_status(&format!("Can't run this \"{}\": {}",
|
self.show_status(&format!("Can't run this \"{}\": {}",
|
||||||
"rifle", err))
|
"rifle", err)).log()
|
||||||
|
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -185,8 +189,7 @@ impl FileBrowser {
|
|||||||
self.cwd = new_cwd;
|
self.cwd = new_cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.refresh();
|
self.refresh()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_preview(&mut self) -> HResult<()> {
|
pub fn update_preview(&mut self) -> HResult<()> {
|
||||||
@ -207,10 +210,12 @@ impl FileBrowser {
|
|||||||
let cwd = self.selected_file()?.clone();
|
let cwd = self.selected_file()?.clone();
|
||||||
if let Ok(grand_parent) = cwd.grand_parent_as_file() {
|
if let Ok(grand_parent) = cwd.grand_parent_as_file() {
|
||||||
let (coords, _, _) = self.columns.calculate_coordinates();
|
let (coords, _, _) = self.columns.calculate_coordinates();
|
||||||
let left_view = WillBeWidget::new(Box::new(move |_| {
|
let core = self.core.clone();
|
||||||
|
let left_view = WillBeWidget::new(&self.core, Box::new(move |_| {
|
||||||
let mut view
|
let mut view
|
||||||
= ListView::new(Files::new_from_path(&grand_parent.path)?);
|
= ListView::new(&core,
|
||||||
view.set_coordinates(&coords);
|
Files::new_from_path(&grand_parent.path)?);
|
||||||
|
view.set_coordinates(&coords).log();
|
||||||
Ok(view)
|
Ok(view)
|
||||||
}));
|
}));
|
||||||
self.columns.prepend_widget(left_view);
|
self.columns.prepend_widget(left_view);
|
||||||
@ -341,17 +346,19 @@ impl FileBrowser {
|
|||||||
let dir = std::path::PathBuf::from(&dir);
|
let dir = std::path::PathBuf::from(&dir);
|
||||||
let left_dir = std::path::PathBuf::from(&dir);
|
let left_dir = std::path::PathBuf::from(&dir);
|
||||||
let (left_coords, main_coords, _) = self.columns.calculate_coordinates();
|
let (left_coords, main_coords, _) = self.columns.calculate_coordinates();
|
||||||
|
let mcore = self.core.clone();
|
||||||
|
let lcore = self.core.clone();
|
||||||
|
|
||||||
let middle = WillBeWidget::new(Box::new(move |_| {
|
let middle = WillBeWidget::new(&self.core, Box::new(move |_| {
|
||||||
let files = Files::new_from_path(&dir.clone())?;
|
let files = Files::new_from_path(&dir.clone())?;
|
||||||
let mut listview = ListView::new(files);
|
let mut listview = ListView::new(&mcore, files);
|
||||||
listview.set_coordinates(&main_coords);
|
listview.set_coordinates(&main_coords).log();
|
||||||
Ok(listview)
|
Ok(listview)
|
||||||
}));
|
}));
|
||||||
let left = WillBeWidget::new(Box::new(move |_| {
|
let left = WillBeWidget::new(&self.core, Box::new(move |_| {
|
||||||
let files = Files::new_from_path(&left_dir.parent()?)?;
|
let files = Files::new_from_path(&left_dir.parent()?)?;
|
||||||
let mut listview = ListView::new(files);
|
let mut listview = ListView::new(&lcore, files);
|
||||||
listview.set_coordinates(&left_coords);
|
listview.set_coordinates(&left_coords).log();
|
||||||
Ok(listview)
|
Ok(listview)
|
||||||
}));
|
}));
|
||||||
self.columns.push_widget(left);
|
self.columns.push_widget(left);
|
||||||
@ -373,7 +380,7 @@ impl FileBrowser {
|
|||||||
|
|
||||||
let cmd = self.minibuffer("exec:")?;
|
let cmd = self.minibuffer("exec:")?;
|
||||||
|
|
||||||
self.show_status(&format!("Running: \"{}\"", &cmd));
|
self.show_status(&format!("Running: \"{}\"", &cmd)).log();
|
||||||
|
|
||||||
let mut cmd = if file_names.len() == 0 {
|
let mut cmd = if file_names.len() == 0 {
|
||||||
cmd.replace("$s", &format!("{}", &filename))
|
cmd.replace("$s", &format!("{}", &filename))
|
||||||
@ -397,35 +404,28 @@ impl FileBrowser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for FileBrowser {
|
impl Widget for FileBrowser {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.columns.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn render_header(&self) -> HResult<String> {
|
||||||
self.columns.set_coordinates(coordinates);
|
let xsize = self.get_coordinates()?.xsize();
|
||||||
self.proc_view.lock().unwrap().set_coordinates(coordinates);
|
let file = self.selected_file()?;
|
||||||
self.refresh();
|
|
||||||
}
|
|
||||||
fn render_header(&self) -> String {
|
|
||||||
if self.main_widget().is_err() { return "".to_string() }
|
|
||||||
let xsize = self.get_coordinates().xsize();
|
|
||||||
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() {
|
||||||
crate::term::highlight_color() } else {
|
crate::term::highlight_color() } else {
|
||||||
crate::term::from_lscolor(file.color.as_ref().unwrap()) };
|
crate::term::from_lscolor(file.color.as_ref().unwrap()) };
|
||||||
|
|
||||||
let path = file.path.parent().unwrap().to_string_lossy().to_string();
|
let path = file.path.parent()?.to_string_lossy().to_string();
|
||||||
|
|
||||||
let pretty_path = format!("{}/{}{}", path, &color, name );
|
let pretty_path = format!("{}/{}{}", path, &color, name );
|
||||||
let sized_path = crate::term::sized_string(&pretty_path, xsize);
|
let sized_path = crate::term::sized_string(&pretty_path, xsize);
|
||||||
sized_path
|
Ok(sized_path)
|
||||||
}
|
}
|
||||||
fn render_footer(&self) -> String {
|
fn render_footer(&self) -> HResult<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());
|
||||||
@ -433,8 +433,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_widget().as_ref().unwrap().lock().unwrap()).as_ref().unwrap().get_selection();
|
let selection = (*self.main_widget().as_ref().unwrap().lock()?).as_ref()?.get_selection();
|
||||||
let file_count = (*self.main_widget().unwrap().lock().unwrap()).as_ref().unwrap().content.len();
|
let file_count = (*self.main_widget()?.lock()?).as_ref()?.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$}",
|
||||||
@ -442,42 +442,44 @@ impl Widget for FileBrowser {
|
|||||||
file_count,
|
file_count,
|
||||||
digits = digits);
|
digits = digits);
|
||||||
let count_xpos = xsize - file_count.len() as u16;
|
let count_xpos = xsize - file_count.len() as u16;
|
||||||
let count_ypos = ypos + self.get_coordinates().ysize();
|
let count_ypos = ypos + self.get_coordinates()?.ysize();
|
||||||
|
|
||||||
format!("{} {}:{} {} {} {}", permissions, user, group, mtime,
|
Ok(format!("{} {}:{} {} {} {}", permissions, user, group, mtime,
|
||||||
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) -> HResult<()> {
|
||||||
self.handle_dir_events().ok();
|
//self.proc_view.lock()?.set_coordinates(self.get_coordinates()?);
|
||||||
self.columns.refresh();
|
self.handle_dir_events()?;
|
||||||
|
self.columns.refresh().ok();
|
||||||
self.fix_left().ok();
|
self.fix_left().ok();
|
||||||
self.fix_selection().ok();
|
self.fix_selection().ok();
|
||||||
self.set_cwd().ok();
|
self.set_cwd().ok();
|
||||||
self.update_watches().ok();
|
self.update_watches().ok();
|
||||||
self.update_preview().ok();
|
self.update_preview().ok();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
if self.columns.get_left_widget().is_err() {
|
if self.columns.get_left_widget().is_err() {
|
||||||
self.columns.get_clearlist() + &self.columns.get_drawlist()
|
Ok(self.columns.get_clearlist()? + &self.columns.get_drawlist()?)
|
||||||
} else {
|
} else {
|
||||||
self.columns.get_drawlist()
|
Ok(self.columns.get_drawlist()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Char('/') => { self.turbo_cd().ok(); },
|
Key::Char('/') => { self.turbo_cd()?; },
|
||||||
Key::Char('Q') => { self.quit_with_dir().ok(); },
|
Key::Char('Q') => { self.quit_with_dir()?; },
|
||||||
Key::Right | Key::Char('f') => { self.enter_dir().ok(); },
|
Key::Right | Key::Char('f') => { self.enter_dir()?; },
|
||||||
Key::Left | Key::Char('b') => { self.go_back().ok(); },
|
Key::Left | Key::Char('b') => { self.go_back()?; },
|
||||||
Key::Char('w') => {
|
Key::Char('w') => {
|
||||||
self.proc_view.lock()?.popup().ok();
|
self.proc_view.lock()?.popup()?;
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
_ => { self.columns.get_main_widget_mut()?.on_key(key).ok(); },
|
_ => { self.columns.get_main_widget_mut()?.on_key(key)?; },
|
||||||
}
|
}
|
||||||
self.update_preview().ok();
|
self.update_preview()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,18 @@ use std::cmp::{Ord, Ordering};
|
|||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use lscolors::LsColors;
|
use lscolors::LsColors;
|
||||||
use mime_detective;
|
use mime_detective;
|
||||||
use users;
|
use users;
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
|
use notify::DebouncedEvent;
|
||||||
|
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError};
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -200,7 +201,7 @@ impl Files {
|
|||||||
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
|
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
|
||||||
self.path_in_here(&path)?;
|
self.path_in_here(&path)?;
|
||||||
let file = self.find_file_with_path(&path)?;
|
let file = self.find_file_with_path(&path)?;
|
||||||
file.reload_meta();
|
file.reload_meta()?;
|
||||||
},
|
},
|
||||||
DebouncedEvent::Remove(path) => {
|
DebouncedEvent::Remove(path) => {
|
||||||
self.path_in_here(&path)?;
|
self.path_in_here(&path)?;
|
||||||
|
58
src/hbox.rs
58
src/hbox.rs
@ -1,20 +1,20 @@
|
|||||||
use termion::event::{Event};
|
use termion::event::{Event};
|
||||||
|
|
||||||
use crate::widget::Widget;
|
use crate::widget::{Widget, WidgetCore};
|
||||||
use crate::coordinates::{Coordinates, Size, Position};
|
use crate::coordinates::{Coordinates, Size, Position};
|
||||||
use crate::fail::HResult;
|
use crate::fail::{HResult, ErrorLog};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct HBox<T: Widget> {
|
pub struct HBox<T: Widget> {
|
||||||
pub coordinates: Coordinates,
|
pub core: WidgetCore,
|
||||||
pub widgets: Vec<T>,
|
pub widgets: Vec<T>,
|
||||||
pub active: Option<usize>,
|
pub active: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> HBox<T> where T: Widget + PartialEq {
|
impl<T> HBox<T> where T: Widget + PartialEq {
|
||||||
pub fn new() -> HBox<T> {
|
pub fn new(core: &WidgetCore) -> HBox<T> {
|
||||||
HBox { coordinates: Coordinates::new(),
|
HBox { core: core.clone(),
|
||||||
widgets: vec![],
|
widgets: vec![],
|
||||||
active: None
|
active: None
|
||||||
}
|
}
|
||||||
@ -27,34 +27,35 @@ impl<T> HBox<T> where T: Widget + PartialEq {
|
|||||||
|w|
|
|w|
|
||||||
self.calculate_coordinates(w)).collect();
|
self.calculate_coordinates(w)).collect();
|
||||||
for (widget, coord) in self.widgets.iter_mut().zip(coords.iter()) {
|
for (widget, coord) in self.widgets.iter_mut().zip(coords.iter()) {
|
||||||
widget.set_coordinates(coord);
|
widget.set_coordinates(coord).log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_widget(&mut self, widget: T) where T: PartialEq {
|
pub fn push_widget(&mut self, widget: T) where T: PartialEq {
|
||||||
self.widgets.push(widget);
|
self.widgets.push(widget);
|
||||||
self.resize_children();
|
self.resize_children();
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_widget(&mut self) -> Option<T> {
|
pub fn pop_widget(&mut self) -> Option<T> {
|
||||||
let widget = self.widgets.pop();
|
let widget = self.widgets.pop();
|
||||||
self.resize_children();
|
self.resize_children();
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
widget
|
widget
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepend_widget(&mut self, widget: T) {
|
pub fn prepend_widget(&mut self, widget: T) {
|
||||||
self.widgets.insert(0, widget);
|
self.widgets.insert(0, widget);
|
||||||
self.resize_children();
|
self.resize_children();
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_coordinates(&self, widget: &T)
|
pub fn calculate_coordinates(&self, widget: &T)
|
||||||
-> Coordinates where T: PartialEq {
|
-> Coordinates where T: PartialEq {
|
||||||
let xsize = self.coordinates.xsize();
|
let coordinates = self.get_coordinates().unwrap();
|
||||||
let ysize = self.coordinates.ysize();
|
let xsize = coordinates.xsize();
|
||||||
let top = self.coordinates.top().y();
|
let ysize = coordinates.ysize();
|
||||||
|
let top = coordinates.top().y();
|
||||||
|
|
||||||
let pos = self.widgets.iter().position(|w | w == widget).unwrap();
|
let pos = self.widgets.iter().position(|w | w == widget).unwrap();
|
||||||
let num = self.widgets.len();
|
let num = self.widgets.len();
|
||||||
@ -80,35 +81,32 @@ impl<T> HBox<T> where T: Widget + PartialEq {
|
|||||||
|
|
||||||
|
|
||||||
impl<T> Widget for HBox<T> where T: Widget + PartialEq {
|
impl<T> Widget for HBox<T> where T: Widget + PartialEq {
|
||||||
fn render_header(&self) -> String {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
|
Ok(&self.core)
|
||||||
|
}
|
||||||
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
|
Ok(&mut self.core)
|
||||||
|
}
|
||||||
|
fn render_header(&self) -> HResult<String> {
|
||||||
self.active_widget().render_header()
|
self.active_widget().render_header()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
self.resize_children();
|
self.resize_children();
|
||||||
for child in &mut self.widgets {
|
for child in &mut self.widgets {
|
||||||
child.refresh();
|
child.refresh()?
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
self.widgets.iter().map(|child| {
|
Ok(self.widgets.iter().map(|child| {
|
||||||
child.get_drawlist()
|
child.get_drawlist().unwrap()
|
||||||
}).collect()
|
}).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
|
||||||
&self.coordinates
|
|
||||||
}
|
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
|
||||||
if self.coordinates == *coordinates {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.coordinates = coordinates.clone();
|
|
||||||
self.refresh();
|
|
||||||
}
|
|
||||||
fn on_event(&mut self, event: Event) -> HResult<()> {
|
fn on_event(&mut self, event: Event) -> HResult<()> {
|
||||||
self.widgets.last_mut()?.on_event(event).ok();
|
self.widgets.last_mut()?.on_event(event)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
src/listview.rs
116
src/listview.rs
@ -3,17 +3,16 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
|
||||||
use crate::files::{File, Files};
|
use crate::files::{File, Files};
|
||||||
use crate::fail::HResult;
|
use crate::fail::{HResult, ErrorLog};
|
||||||
use crate::term;
|
use crate::term;
|
||||||
use crate::widget::{Widget};
|
use crate::widget::{Widget, WidgetCore};
|
||||||
|
|
||||||
pub trait Listable {
|
pub trait Listable {
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn render(&self) -> Vec<String>;
|
fn render(&self) -> Vec<String>;
|
||||||
fn on_refresh(&mut self) {}
|
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
|
||||||
fn on_key(&mut self, _key: Key) {}
|
fn on_key(&mut self, _key: Key) -> HResult<()> { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Listable for ListView<Files> {
|
impl Listable for ListView<Files> {
|
||||||
@ -25,26 +24,27 @@ impl Listable for ListView<Files> {
|
|||||||
self.render()
|
self.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_refresh(&mut self) {
|
fn on_refresh(&mut self) -> HResult<()> {
|
||||||
let visible_file_num = self.selection + self.get_coordinates().ysize() as usize;
|
let visible_file_num = self.selection + self.get_coordinates()?.ysize() as usize;
|
||||||
self.content.meta_upto(visible_file_num);
|
self.content.meta_upto(visible_file_num);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Up | Key::Char('p') => {
|
Key::Up | Key::Char('p') => {
|
||||||
self.move_up();
|
self.move_up();
|
||||||
self.refresh();
|
self.refresh()?;
|
||||||
}
|
}
|
||||||
Key::Char('P') => { for _ in 0..10 { self.move_up() } self.refresh(); }
|
Key::Char('P') => { for _ in 0..10 { self.move_up() } self.refresh()?; }
|
||||||
Key::Char('N') => { for _ in 0..10 { self.move_down() } self.refresh(); }
|
Key::Char('N') => { for _ in 0..10 { self.move_down() } self.refresh()?; }
|
||||||
Key::Down | Key::Char('n') => {
|
Key::Down | Key::Char('n') => {
|
||||||
self.move_down();
|
self.move_down();
|
||||||
self.refresh();
|
self.refresh()?;
|
||||||
},
|
},
|
||||||
Key::Ctrl('s') => { self.find_file().ok(); }
|
Key::Ctrl('s') => { self.find_file().ok(); }
|
||||||
Key::Left => self.goto_grand_parent(),
|
Key::Left => self.goto_grand_parent()?,
|
||||||
Key::Right => self.goto_selected(),
|
Key::Right => self.goto_selected()?,
|
||||||
Key::Char(' ') => self.multi_select_file(),
|
Key::Char(' ') => self.multi_select_file(),
|
||||||
Key::Char('h') => self.toggle_hidden(),
|
Key::Char('h') => self.toggle_hidden(),
|
||||||
Key::Char('r') => self.reverse_sort(),
|
Key::Char('r') => self.reverse_sort(),
|
||||||
@ -52,8 +52,9 @@ impl Listable for ListView<Files> {
|
|||||||
Key::Char('K') => self.select_next_mtime(),
|
Key::Char('K') => self.select_next_mtime(),
|
||||||
Key::Char('k') => self.select_prev_mtime(),
|
Key::Char('k') => self.select_prev_mtime(),
|
||||||
Key::Char('d') => self.toggle_dirs_first(),
|
Key::Char('d') => self.toggle_dirs_first(),
|
||||||
_ => self.bad(Event::Key(key))
|
_ => { self.bad(Event::Key(key))?; }
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ pub struct ListView<T> where ListView<T>: Listable
|
|||||||
selection: usize,
|
selection: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
buffer: Vec<String>,
|
buffer: Vec<String>,
|
||||||
coordinates: Coordinates,
|
core: WidgetCore,
|
||||||
seeking: bool,
|
seeking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,17 +75,14 @@ where
|
|||||||
ListView<T>: Widget,
|
ListView<T>: Widget,
|
||||||
ListView<T>: Listable
|
ListView<T>: Listable
|
||||||
{
|
{
|
||||||
pub fn new(content: T) -> ListView<T> {
|
pub fn new(core: &WidgetCore, content: T) -> ListView<T> {
|
||||||
let view = ListView::<T> {
|
let view = ListView::<T> {
|
||||||
content: content,
|
content: content,
|
||||||
lines: 0,
|
lines: 0,
|
||||||
selection: 0,
|
selection: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
buffer: Vec::new(),
|
buffer: Vec::new(),
|
||||||
coordinates: Coordinates {
|
core: core.clone(),
|
||||||
size: Size((1, 1)),
|
|
||||||
position: Position((1, 1)),
|
|
||||||
},
|
|
||||||
seeking: false
|
seeking: false
|
||||||
};
|
};
|
||||||
view
|
view
|
||||||
@ -104,7 +102,7 @@ where
|
|||||||
}
|
}
|
||||||
pub fn move_down(&mut self) {
|
pub fn move_down(&mut self) {
|
||||||
let lines = self.lines;
|
let lines = self.lines;
|
||||||
let y_size = self.coordinates.ysize() as usize;
|
let y_size = self.get_coordinates().unwrap().ysize() as usize;
|
||||||
|
|
||||||
if self.lines == 0 || self.selection == lines - 1 {
|
if self.lines == 0 || self.selection == lines - 1 {
|
||||||
return;
|
return;
|
||||||
@ -123,7 +121,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_selection(&mut self, position: usize) {
|
fn set_selection(&mut self, position: usize) {
|
||||||
let ysize = self.coordinates.ysize() as usize;
|
let ysize = self.get_coordinates().unwrap().ysize() as usize;
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
while position + 2
|
while position + 2
|
||||||
@ -145,7 +143,7 @@ where
|
|||||||
} else { (name.clone(), "".to_string()) };
|
} else { (name.clone(), "".to_string()) };
|
||||||
|
|
||||||
|
|
||||||
let xsize = self.get_coordinates().xsize();
|
let xsize = self.get_coordinates().unwrap().xsize();
|
||||||
let sized_string = term::sized_string(&name, xsize);
|
let sized_string = term::sized_string(&name, xsize);
|
||||||
let size_pos = xsize - (size.to_string().len() as u16
|
let size_pos = xsize - (size.to_string().len() as u16
|
||||||
+ unit.to_string().len() as u16);
|
+ unit.to_string().len() as u16);
|
||||||
@ -202,30 +200,29 @@ impl ListView<Files>
|
|||||||
self.selected_file().grand_parent()
|
self.selected_file().grand_parent()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn goto_grand_parent(&mut self) {
|
pub fn goto_grand_parent(&mut self) -> HResult<()> {
|
||||||
match self.grand_parent() {
|
match self.grand_parent() {
|
||||||
Some(grand_parent) => self.goto_path(&grand_parent),
|
Some(grand_parent) => self.goto_path(&grand_parent),
|
||||||
None => self.show_status("Can't go further!"),
|
None => { self.show_status("Can't go further!") },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn goto_selected(&mut self) {
|
fn goto_selected(&mut self) -> HResult<()> {
|
||||||
let path = self.selected_file().path();
|
let path = self.selected_file().path();
|
||||||
|
|
||||||
self.goto_path(&path);
|
self.goto_path(&path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn goto_path(&mut self, path: &Path) {
|
pub fn goto_path(&mut self, path: &Path) -> HResult<()> {
|
||||||
match crate::files::Files::new_from_path(path) {
|
match crate::files::Files::new_from_path(path) {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
self.content = files;
|
self.content = files;
|
||||||
self.selection = 0;
|
self.selection = 0;
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.refresh();
|
self.refresh()
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.show_status(&format!("Can't open this path: {}", err));
|
self.show_status(&format!("Can't open this path: {}", err))
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,8 +242,8 @@ impl ListView<Files>
|
|||||||
self.content.cycle_sort();
|
self.content.cycle_sort();
|
||||||
self.content.sort();
|
self.content.sort();
|
||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
self.show_status(&format!("Sorting by: {}", self.content.sort));
|
self.show_status(&format!("Sorting by: {}", self.content.sort)).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reverse_sort(&mut self) {
|
fn reverse_sort(&mut self) {
|
||||||
@ -254,8 +251,8 @@ impl ListView<Files>
|
|||||||
self.content.reverse_sort();
|
self.content.reverse_sort();
|
||||||
self.content.sort();
|
self.content.sort();
|
||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
self.show_status(&format!("Reversed sorting by: {}", self.content.sort));
|
self.show_status(&format!("Reversed sorting by: {}", self.content.sort)).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_next_mtime(&mut self) {
|
fn select_next_mtime(&mut self) {
|
||||||
@ -283,7 +280,7 @@ impl ListView<Files>
|
|||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.seeking = true;
|
self.seeking = true;
|
||||||
|
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_prev_mtime(&mut self) {
|
fn select_prev_mtime(&mut self) {
|
||||||
@ -310,7 +307,7 @@ impl ListView<Files>
|
|||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.seeking = true;
|
self.seeking = true;
|
||||||
|
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_hidden(&mut self) {
|
fn toggle_hidden(&mut self) {
|
||||||
@ -318,7 +315,7 @@ impl ListView<Files>
|
|||||||
self.content.toggle_hidden();
|
self.content.toggle_hidden();
|
||||||
self.content.reload_files();
|
self.content.reload_files();
|
||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_dirs_first(&mut self) {
|
fn toggle_dirs_first(&mut self) {
|
||||||
@ -326,15 +323,16 @@ impl ListView<Files>
|
|||||||
self.content.dirs_first = !self.content.dirs_first;
|
self.content.dirs_first = !self.content.dirs_first;
|
||||||
self.content.sort();
|
self.content.sort();
|
||||||
self.select_file(&file);
|
self.select_file(&file);
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
self.show_status(&format!("Direcories first: {}", self.content.dirs_first));
|
self.show_status(&format!("Direcories first: {}",
|
||||||
|
self.content.dirs_first)).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multi_select_file(&mut self) {
|
fn multi_select_file(&mut self) {
|
||||||
let file = self.selected_file_mut();
|
let file = self.selected_file_mut();
|
||||||
file.toggle_selection();
|
file.toggle_selection();
|
||||||
self.move_down();
|
self.move_down();
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_file(&mut self) -> HResult<()> {
|
fn find_file(&mut self) -> HResult<()> {
|
||||||
@ -352,7 +350,7 @@ impl ListView<Files>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self) -> Vec<String> {
|
fn render(&self) -> Vec<String> {
|
||||||
let ysize = self.get_coordinates().ysize() as usize;
|
let ysize = self.get_coordinates().unwrap().ysize() as usize;
|
||||||
let offset = self.offset;
|
let offset = self.offset;
|
||||||
self.content
|
self.content
|
||||||
.files
|
.files
|
||||||
@ -366,29 +364,26 @@ impl ListView<Files>
|
|||||||
|
|
||||||
|
|
||||||
impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
if self.coordinates == *coordinates {
|
Ok(&mut self.core)
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.coordinates = coordinates.clone();
|
|
||||||
self.refresh();
|
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
self.on_refresh();
|
self.on_refresh().log();
|
||||||
self.lines = self.len();
|
self.lines = self.len();
|
||||||
if self.selection >= self.lines && self.selection != 0 {
|
if self.selection >= self.lines && self.selection != 0 {
|
||||||
self.selection -= 1;
|
self.selection -= 1;
|
||||||
}
|
}
|
||||||
self.buffer = self.render();
|
self.buffer = self.render();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
let mut output = term::reset();
|
let mut output = term::reset();
|
||||||
let (xpos, ypos) = self.coordinates.position().position();
|
let (xpos, ypos) = self.get_coordinates().unwrap().position().position();
|
||||||
|
|
||||||
output += &self
|
output += &self
|
||||||
.buffer
|
.buffer
|
||||||
@ -410,16 +405,15 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
|||||||
})
|
})
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
output += &self.get_redraw_empty_list(self.buffer.len());
|
output += &self.get_redraw_empty_list(self.buffer.len())?;
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn render_header(&self) -> HResult<String> {
|
||||||
format!("{} files", self.len())
|
Ok(format!("{} files", self.len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
Listable::on_key(self, key);
|
Listable::on_key(self, key)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
src/main.rs
43
src/main.rs
@ -18,6 +18,8 @@ extern crate rayon;
|
|||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
|
||||||
|
use failure::Fail;
|
||||||
|
|
||||||
use termion::input::MouseTerminal;
|
use termion::input::MouseTerminal;
|
||||||
use termion::raw::IntoRawMode;
|
use termion::raw::IntoRawMode;
|
||||||
use termion::screen::AlternateScreen;
|
use termion::screen::AlternateScreen;
|
||||||
@ -34,7 +36,6 @@ mod term;
|
|||||||
mod textview;
|
mod textview;
|
||||||
mod widget;
|
mod widget;
|
||||||
mod win_main;
|
mod win_main;
|
||||||
mod window;
|
|
||||||
mod hbox;
|
mod hbox;
|
||||||
mod tabview;
|
mod tabview;
|
||||||
mod async_widget;
|
mod async_widget;
|
||||||
@ -45,23 +46,41 @@ mod proclist;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
use window::Window;
|
|
||||||
|
|
||||||
|
use widget::{Widget, WidgetCore};
|
||||||
|
use term::ScreenExt;
|
||||||
|
use fail::HResult;
|
||||||
|
use file_browser::FileBrowser;
|
||||||
|
use tabview::TabView;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> HResult<()> {
|
||||||
|
match run() {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{:?}\n{:?}", err, err.cause());
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run() -> HResult<()> {
|
||||||
let bufout = std::io::BufWriter::new(std::io::stdout());
|
let bufout = std::io::BufWriter::new(std::io::stdout());
|
||||||
// Need to do this here to actually turn terminal into raw mode...
|
// Need to do this here to actually turn terminal into raw mode...
|
||||||
let mut _screen = AlternateScreen::from(Box::new(bufout));
|
let mut screen = AlternateScreen::from(bufout);
|
||||||
let mut _stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
let mut _stdout = MouseTerminal::from(stdout().into_raw_mode()?);
|
||||||
|
screen.cursor_hide()?;
|
||||||
|
screen.flush()?;
|
||||||
|
|
||||||
|
let core = WidgetCore::new()?;
|
||||||
|
|
||||||
let filebrowser = crate::file_browser::FileBrowser::new().unwrap();
|
let filebrowser = FileBrowser::new_cored(&core)?;
|
||||||
let mut tabview = crate::tabview::TabView::new();
|
let mut tabview = TabView::new(&core);
|
||||||
tabview.push_widget(filebrowser);
|
tabview.push_widget(filebrowser)?;
|
||||||
|
|
||||||
let mut win = Window::new(tabview);
|
tabview.handle_input()?;
|
||||||
win.draw();
|
|
||||||
win.handle_input();
|
|
||||||
|
|
||||||
write!(_stdout, "{}", termion::cursor::Show).unwrap();
|
screen.cursor_show()?;
|
||||||
|
screen.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,44 @@
|
|||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
|
use failure::Backtrace;
|
||||||
|
|
||||||
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, WidgetCore};
|
||||||
use crate::hbox::HBox;
|
use crate::hbox::HBox;
|
||||||
use crate::fail::{HError, HResult};
|
use crate::fail::{HError, HResult, ErrorLog};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct MillerColumns<T> where T: Widget {
|
pub struct MillerColumns<T> where T: Widget {
|
||||||
pub widgets: HBox<T>,
|
pub widgets: HBox<T>,
|
||||||
|
pub core: WidgetCore,
|
||||||
// pub left: Option<T>,
|
// pub left: Option<T>,
|
||||||
// pub main: Option<T>,
|
// pub main: Option<T>,
|
||||||
//pub preview: AsyncPreviewer,
|
//pub preview: AsyncPreviewer,
|
||||||
pub preview: Previewer,
|
pub preview: Previewer,
|
||||||
pub ratio: (u16, u16, u16),
|
pub ratio: (u16, u16, u16),
|
||||||
pub coordinates: Coordinates,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MillerColumns<T>
|
impl<T> MillerColumns<T>
|
||||||
where
|
where
|
||||||
T: Widget + PartialEq,
|
T: Widget + PartialEq,
|
||||||
{
|
{
|
||||||
pub fn new() -> MillerColumns<T> {
|
pub fn new(core: &WidgetCore) -> MillerColumns<T> {
|
||||||
MillerColumns {
|
MillerColumns {
|
||||||
widgets: HBox::new(),
|
widgets: HBox::new(core),
|
||||||
coordinates: Coordinates::new(),
|
core: core.clone(),
|
||||||
ratio: (20, 30, 50),
|
ratio: (20, 30, 50),
|
||||||
preview: Previewer::new()
|
preview: Previewer::new(core)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_widget(&mut self, widget: T) {
|
pub fn push_widget(&mut self, widget: T) {
|
||||||
self.widgets.push_widget(widget);
|
self.widgets.push_widget(widget);
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_widget(&mut self) -> Option<T> {
|
pub fn pop_widget(&mut self) -> Option<T> {
|
||||||
let widget = self.widgets.pop_widget();
|
let widget = self.widgets.pop_widget();
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
widget
|
widget
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,14 +47,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates, Coordinates) {
|
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates, Coordinates) {
|
||||||
let xsize = self.coordinates.xsize();
|
let coordinates = self.get_coordinates().unwrap();
|
||||||
let ysize = self.coordinates.ysize();
|
let xsize = coordinates.xsize();
|
||||||
let top = self.coordinates.top().y();
|
let ysize = coordinates.ysize();
|
||||||
|
let top = coordinates.top().y();
|
||||||
let ratio = self.ratio;
|
let ratio = self.ratio;
|
||||||
|
|
||||||
let left_xsize = xsize * ratio.0 / 100;
|
let left_xsize = xsize * ratio.0 / 100;
|
||||||
let left_size = Size((left_xsize, ysize));
|
let left_size = Size((left_xsize, ysize));
|
||||||
let left_pos = self.coordinates.top();
|
let left_pos = coordinates.top();
|
||||||
|
|
||||||
let main_xsize = xsize * ratio.1 / 100;
|
let main_xsize = xsize * ratio.1 / 100;
|
||||||
let main_size = Size((main_xsize, ysize));
|
let main_size = Size((main_xsize, ysize));
|
||||||
@ -84,7 +86,7 @@ where
|
|||||||
pub fn get_left_widget(&self) -> HResult<&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 Err(HError::NoWidgetError);
|
return Err(HError::NoWidgetError(Backtrace::new()));
|
||||||
}
|
}
|
||||||
let widget = self.widgets.widgets.get(len - 2)?;
|
let widget = self.widgets.widgets.get(len - 2)?;
|
||||||
Ok(widget)
|
Ok(widget)
|
||||||
@ -92,7 +94,7 @@ where
|
|||||||
pub fn get_left_widget_mut(&mut self) -> HResult<&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 Err(HError::NoWidgetError);
|
return Err(HError::NoWidgetError(Backtrace::new()));
|
||||||
}
|
}
|
||||||
let widget = self.widgets.widgets.get_mut(len - 2)?;
|
let widget = self.widgets.widgets.get_mut(len - 2)?;
|
||||||
Ok(widget)
|
Ok(widget)
|
||||||
@ -112,51 +114,39 @@ where
|
|||||||
T: Widget,
|
T: Widget,
|
||||||
T: PartialEq
|
T: PartialEq
|
||||||
{
|
{
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
if self.coordinates == *coordinates {
|
Ok(&mut self.core)
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.coordinates = coordinates.clone();
|
|
||||||
self.refresh();
|
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
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 Ok(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).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(main_widget) = self.get_main_widget_mut() {
|
if let Ok(main_widget) = self.get_main_widget_mut() {
|
||||||
main_widget.set_coordinates(&main_coords);
|
main_widget.set_coordinates(&main_coords).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
let preview_widget = &mut self.preview;
|
let preview_widget = &mut self.preview;
|
||||||
preview_widget.set_coordinates(&preview_coords);
|
preview_widget.set_coordinates(&preview_coords)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
let left_widget = match self.get_left_widget() {
|
let left_widget = self.get_left_widget()?;
|
||||||
Ok(widget) => widget.get_drawlist(),
|
let main_widget = self.get_main_widget()?;
|
||||||
Err(_) => "".into(),
|
let preview = self.preview.get_drawlist()?;
|
||||||
};
|
Ok(format!("{}{}{}",
|
||||||
let main_widget = self.get_main_widget();
|
main_widget.get_drawlist()?,
|
||||||
match main_widget {
|
left_widget.get_drawlist()?,
|
||||||
Ok(main_widget) => {
|
preview))
|
||||||
let preview = self.preview.get_drawlist();
|
|
||||||
format!("{}{}{}", main_widget.get_drawlist(), left_widget, preview)
|
|
||||||
}
|
|
||||||
Err(_) => "".to_string()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
self.get_main_widget_mut().unwrap().on_key(key);
|
self.get_main_widget_mut().unwrap().on_key(key)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@ use termion::event::Key;
|
|||||||
use std::io::{stdout, Write};
|
use std::io::{stdout, Write};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
use crate::coordinates::{Coordinates};
|
||||||
use crate::widget::Widget;
|
use crate::widget::{Widget, WidgetCore};
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError};
|
||||||
use crate::term;
|
use crate::term;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MiniBuffer {
|
pub struct MiniBuffer {
|
||||||
coordinates: Coordinates,
|
core: WidgetCore,
|
||||||
query: String,
|
query: String,
|
||||||
input: String,
|
input: String,
|
||||||
position: usize,
|
position: usize,
|
||||||
@ -18,12 +19,14 @@ pub struct MiniBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MiniBuffer {
|
impl MiniBuffer {
|
||||||
pub fn new() -> MiniBuffer {
|
pub fn new(core: &WidgetCore) -> MiniBuffer {
|
||||||
let xsize = crate::term::xsize();
|
let xsize = crate::term::xsize();
|
||||||
let ysize = crate::term::ysize();
|
let ysize = crate::term::ysize();
|
||||||
let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
|
let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
|
||||||
|
let mut core = core.clone();
|
||||||
|
core.coordinates = coordinates;
|
||||||
MiniBuffer {
|
MiniBuffer {
|
||||||
coordinates: coordinates,
|
core: core,
|
||||||
query: String::new(),
|
query: String::new(),
|
||||||
input: String::new(),
|
input: String::new(),
|
||||||
position: 0,
|
position: 0,
|
||||||
@ -283,26 +286,23 @@ pub fn find_files(comp_name: String) -> HResult<Vec<String>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for MiniBuffer {
|
impl Widget for MiniBuffer {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
self.coordinates = coordinates.clone();
|
Ok(&mut self.core)
|
||||||
self.refresh();
|
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
"".to_string()
|
Ok(())
|
||||||
}
|
|
||||||
fn refresh(&mut self) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
let (xpos, ypos) = self.get_coordinates().u16position();
|
let (xpos, ypos) = self.get_coordinates()?.u16position();
|
||||||
format!("{}{}{}: {}",
|
Ok(format!("{}{}{}: {}",
|
||||||
crate::term::goto_xy(xpos, ypos),
|
crate::term::goto_xy(xpos, ypos),
|
||||||
termion::clear::CurrentLine,
|
termion::clear::CurrentLine,
|
||||||
self.query,
|
self.query,
|
||||||
self.input)
|
self.input))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
|
199
src/preview.rs
199
src/preview.rs
@ -1,14 +1,15 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
use failure::Backtrace;
|
||||||
|
|
||||||
use crate::files::{File, Files, Kind};
|
use crate::files::{File, Files, Kind};
|
||||||
use crate::listview::ListView;
|
use crate::listview::ListView;
|
||||||
use crate::textview::TextView;
|
use crate::textview::TextView;
|
||||||
use crate::widget::Widget;
|
use crate::widget::{Widget, WidgetCore};
|
||||||
use crate::fail::HError;
|
use crate::fail::{HResult, HError, ErrorLog};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type HResult<T> = Result<T, HError>;
|
|
||||||
type HClosure<T> = Box<Fn(Arc<Mutex<bool>>) -> Result<T, HError> + Send>;
|
type HClosure<T> = Box<Fn(Arc<Mutex<bool>>) -> Result<T, HError> + Send>;
|
||||||
type WidgetO = Box<dyn Widget + Send>;
|
type WidgetO = Box<dyn Widget + Send>;
|
||||||
|
|
||||||
@ -63,8 +64,8 @@ impl<T: Send + 'static> WillBe<T> where {
|
|||||||
let got_thing = closure(stale);
|
let got_thing = closure(stale);
|
||||||
match got_thing {
|
match got_thing {
|
||||||
Ok(got_thing) => {
|
Ok(got_thing) => {
|
||||||
*thing.try_lock().unwrap() = Some(got_thing);
|
*thing.lock().unwrap() = Some(got_thing);
|
||||||
*state.try_lock().unwrap() = State::Is;
|
*state.lock().unwrap() = State::Is;
|
||||||
match *on_ready_fn.lock().unwrap() {
|
match *on_ready_fn.lock().unwrap() {
|
||||||
Some(ref on_ready) => { on_ready(thing.clone()).ok(); },
|
Some(ref on_ready) => { on_ready(thing.clone()).ok(); },
|
||||||
None => {}
|
None => {}
|
||||||
@ -76,14 +77,14 @@ impl<T: Send + 'static> WillBe<T> where {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_stale(&mut self) -> HResult<()> {
|
pub fn set_stale(&mut self) -> HResult<()> {
|
||||||
*self.stale.try_lock()? = true;
|
*self.stale.lock()? = true;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self) -> HResult<()> {
|
pub fn check(&self) -> HResult<()> {
|
||||||
match *self.state.try_lock()? {
|
match *self.state.lock()? {
|
||||||
State::Is => Ok(()),
|
State::Is => Ok(()),
|
||||||
_ => Err(HError::WillBeNotReady)
|
_ => Err(HError::WillBeNotReady(Backtrace::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,15 +94,16 @@ impl<T: Send + 'static> WillBe<T> where {
|
|||||||
if self.check().is_ok() {
|
if self.check().is_ok() {
|
||||||
fun(self.thing.clone())?;
|
fun(self.thing.clone())?;
|
||||||
} else {
|
} else {
|
||||||
*self.on_ready.try_lock()? = Some(fun);
|
*self.on_ready.lock()? = Some(fun);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Widget + Send> PartialEq for WillBeWidget<W> {
|
impl<W: Widget + Send + 'static> PartialEq for WillBeWidget<W> {
|
||||||
fn eq(&self, other: &WillBeWidget<W>) -> bool {
|
fn eq(&self, other: &WillBeWidget<W>) -> bool {
|
||||||
if self.coordinates == other.coordinates {
|
if self.get_coordinates().unwrap() ==
|
||||||
|
other.get_coordinates().unwrap() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -111,19 +113,20 @@ impl<W: Widget + Send> PartialEq for WillBeWidget<W> {
|
|||||||
|
|
||||||
pub struct WillBeWidget<T: Widget + Send> {
|
pub struct WillBeWidget<T: Widget + Send> {
|
||||||
willbe: WillBe<T>,
|
willbe: WillBe<T>,
|
||||||
coordinates: Coordinates
|
core: WidgetCore
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
||||||
pub fn new(closure: HClosure<T>) -> WillBeWidget<T> {
|
pub fn new(core: &WidgetCore, closure: HClosure<T>) -> WillBeWidget<T> {
|
||||||
|
let sender = core.get_sender();
|
||||||
let mut willbe = WillBe::new_become(Box::new(move |stale| closure(stale)));
|
let mut willbe = WillBe::new_become(Box::new(move |stale| closure(stale)));
|
||||||
willbe.on_ready(Box::new(|_| {
|
willbe.on_ready(Box::new(move |_| {
|
||||||
crate::window::send_event(crate::window::Events::WidgetReady)?;
|
sender.send(crate::widget::Events::WidgetReady)?;
|
||||||
Ok(()) })).ok();
|
Ok(()) })).ok();
|
||||||
|
|
||||||
WillBeWidget {
|
WillBeWidget {
|
||||||
willbe: willbe,
|
willbe: willbe,
|
||||||
coordinates: Coordinates::new()
|
core: core.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_stale(&mut self) -> HResult<()> {
|
pub fn set_stale(&mut self) -> HResult<()> {
|
||||||
@ -147,49 +150,36 @@ impl<T: Widget + Send + 'static> WillBeWidget<T> {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
self.coordinates = coordinates.clone();
|
Ok(&mut self.core)
|
||||||
|
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.refresh();
|
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
"".to_string()
|
let widget = self.widget()?;
|
||||||
|
let mut widget = widget.lock()?;
|
||||||
|
let widget = widget.as_mut()?;
|
||||||
|
widget.set_coordinates(self.get_coordinates()?).log();
|
||||||
|
widget.refresh()
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
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.refresh();
|
|
||||||
}
|
|
||||||
fn get_drawlist(&self) -> String {
|
|
||||||
if self.willbe.check().is_err() {
|
if self.willbe.check().is_err() {
|
||||||
let clear = self.get_clearlist();
|
let clear = self.get_clearlist()?;
|
||||||
let (xpos, ypos) = self.get_coordinates().u16position();
|
let (xpos, ypos) = self.get_coordinates()?.u16position();
|
||||||
let pos = crate::term::goto_xy(xpos, ypos);
|
let pos = crate::term::goto_xy(xpos, ypos);
|
||||||
return clear + &pos + "..."
|
return Ok(clear + &pos + "...")
|
||||||
}
|
}
|
||||||
let widget = self.widget().unwrap();
|
let widget = self.widget()?;
|
||||||
let widget = widget.try_lock().unwrap();
|
let widget = widget.lock()?;
|
||||||
let widget = widget.as_ref().unwrap();
|
let widget = widget.as_ref()?;
|
||||||
widget.get_drawlist()
|
widget.get_drawlist()
|
||||||
}
|
}
|
||||||
fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
|
fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
|
||||||
if self.willbe.check().is_err() { return Ok(()) }
|
if self.willbe.check().is_err() { return Ok(()) }
|
||||||
let widget = self.widget().unwrap();
|
let widget = self.widget()?;
|
||||||
let mut widget = widget.try_lock().unwrap();
|
let mut widget = widget.lock()?;
|
||||||
let widget = widget.as_mut().unwrap();
|
let widget = widget.as_mut()?;
|
||||||
widget.on_key(key)
|
widget.on_key(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,7 +187,8 @@ impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
|||||||
|
|
||||||
impl PartialEq for Previewer {
|
impl PartialEq for Previewer {
|
||||||
fn eq(&self, other: &Previewer) -> bool {
|
fn eq(&self, other: &Previewer) -> bool {
|
||||||
if self.widget.coordinates == other.widget.coordinates {
|
if self.widget.get_coordinates().unwrap() ==
|
||||||
|
other.widget.get_coordinates().unwrap() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -207,59 +198,61 @@ impl PartialEq for Previewer {
|
|||||||
|
|
||||||
pub struct Previewer {
|
pub struct Previewer {
|
||||||
widget: WillBeWidget<Box<dyn Widget + Send>>,
|
widget: WillBeWidget<Box<dyn Widget + Send>>,
|
||||||
|
core: WidgetCore,
|
||||||
file: Option<File>
|
file: Option<File>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Previewer {
|
impl Previewer {
|
||||||
pub fn new() -> Previewer {
|
pub fn new(core: &WidgetCore) -> Previewer {
|
||||||
let willbe = WillBeWidget::new(Box::new(move |_| {
|
let core_ = core.clone();
|
||||||
Ok(Box::new(crate::textview::TextView::new_blank())
|
let willbe = WillBeWidget::new(&core, Box::new(move |_| {
|
||||||
|
Ok(Box::new(crate::textview::TextView::new_blank(&core_))
|
||||||
as Box<dyn Widget + Send>)
|
as Box<dyn Widget + Send>)
|
||||||
}));
|
}));
|
||||||
Previewer { widget: willbe,
|
Previewer { widget: willbe,
|
||||||
|
core: core.clone(),
|
||||||
file: None}
|
file: None}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn become_preview(&mut self,
|
fn become_preview(&mut self,
|
||||||
widget: HResult<WillBeWidget<WidgetO>>) {
|
widget: HResult<WillBeWidget<WidgetO>>) {
|
||||||
let coordinates = self.get_coordinates().clone();
|
let coordinates = self.get_coordinates().unwrap().clone();
|
||||||
self.widget = widget.unwrap();
|
self.widget = widget.unwrap();
|
||||||
self.set_coordinates(&coordinates);
|
self.widget.set_coordinates(&coordinates).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_file(&mut self, file: &File) {
|
pub fn set_file(&mut self, file: &File) {
|
||||||
if Some(file) == self.file.as_ref() { return }
|
if Some(file) == self.file.as_ref() { return }
|
||||||
self.file = Some(file.clone());
|
self.file = Some(file.clone());
|
||||||
|
|
||||||
let coordinates = self.get_coordinates().clone();
|
let coordinates = self.get_coordinates().unwrap().clone();
|
||||||
let file = file.clone();
|
let file = file.clone();
|
||||||
|
let core = self.core.clone();
|
||||||
|
|
||||||
self.widget.set_stale().ok();
|
self.widget.set_stale().ok();
|
||||||
|
|
||||||
self.become_preview(Ok(WillBeWidget::new(Box::new(move |stale| {
|
self.become_preview(Ok(WillBeWidget::new(&self.core, Box::new(move |stale| {
|
||||||
kill_proc().unwrap();
|
kill_proc().unwrap();
|
||||||
|
|
||||||
let file = file.clone();
|
let file = file.clone();
|
||||||
|
|
||||||
if file.kind == Kind::Directory {
|
if file.kind == Kind::Directory {
|
||||||
let preview = Previewer::preview_dir(&file, &coordinates, stale.clone());
|
let preview = Previewer::preview_dir(&file, &core, stale.clone());
|
||||||
return preview;
|
return preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.get_mime() == Some("text".to_string()) {
|
if file.get_mime() == Some("text".to_string()) {
|
||||||
return Previewer::preview_text(&file, &coordinates, stale.clone())
|
return Previewer::preview_text(&file, &core, stale.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
let preview = Previewer::preview_external(&file,
|
let preview = Previewer::preview_external(&file, &core, stale.clone());
|
||||||
&coordinates,
|
|
||||||
stale.clone());
|
|
||||||
if preview.is_ok() { return preview; }
|
if preview.is_ok() { return preview; }
|
||||||
else {
|
else {
|
||||||
let mut blank = Box::new(TextView::new_blank());
|
let mut blank = Box::new(TextView::new_blank(&core));
|
||||||
blank.set_coordinates(&coordinates);
|
blank.set_coordinates(&coordinates).log();
|
||||||
blank.refresh();
|
blank.refresh().log();
|
||||||
blank.animate_slide_up();
|
blank.animate_slide_up().log();
|
||||||
return Ok(blank)
|
return Ok(blank)
|
||||||
}
|
}
|
||||||
}))));
|
}))));
|
||||||
@ -276,7 +269,7 @@ impl Previewer {
|
|||||||
Err(HError::PreviewFailed { file: file.name.clone() })
|
Err(HError::PreviewFailed { file: file.name.clone() })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preview_dir(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>)
|
fn preview_dir(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
|
||||||
-> Result<WidgetO, HError> {
|
-> Result<WidgetO, HError> {
|
||||||
let files = Files::new_from_path_cancellable(&file.path,
|
let files = Files::new_from_path_cancellable(&file.path,
|
||||||
stale.clone())?;
|
stale.clone())?;
|
||||||
@ -284,32 +277,33 @@ impl Previewer {
|
|||||||
|
|
||||||
if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||||
|
|
||||||
let mut file_list = ListView::new(files);
|
let mut file_list = ListView::new(&core, files);
|
||||||
file_list.set_coordinates(&coordinates);
|
file_list.set_coordinates(&core.coordinates)?;
|
||||||
file_list.refresh();
|
file_list.refresh()?;
|
||||||
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||||
file_list.animate_slide_up();
|
file_list.animate_slide_up()?;
|
||||||
Ok(Box::new(file_list) as Box<dyn Widget + Send>)
|
Ok(Box::new(file_list) as Box<dyn Widget + Send>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preview_text(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>)
|
fn preview_text(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
|
||||||
-> HResult<WidgetO> {
|
-> HResult<WidgetO> {
|
||||||
let lines = coordinates.ysize() as usize;
|
let lines = core.coordinates.ysize() as usize;
|
||||||
let mut textview
|
let mut textview
|
||||||
= TextView::new_from_file_limit_lines(&file,
|
= TextView::new_from_file_limit_lines(&core,
|
||||||
lines);
|
&file,
|
||||||
|
lines)?;
|
||||||
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||||
|
|
||||||
textview.set_coordinates(&coordinates);
|
textview.set_coordinates(&core.coordinates)?;
|
||||||
textview.refresh();
|
textview.refresh()?;
|
||||||
|
|
||||||
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||||
|
|
||||||
textview.animate_slide_up();
|
textview.animate_slide_up()?;
|
||||||
Ok(Box::new(textview))
|
Ok(Box::new(textview))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preview_external(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>)
|
fn preview_external(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
|
||||||
-> Result<Box<dyn Widget + Send>, HError> {
|
-> Result<Box<dyn Widget + Send>, HError> {
|
||||||
let process =
|
let process =
|
||||||
std::process::Command::new("scope.sh")
|
std::process::Command::new("scope.sh")
|
||||||
@ -349,10 +343,10 @@ impl Previewer {
|
|||||||
let mut textview = TextView {
|
let mut textview = TextView {
|
||||||
lines: output.lines().map(|s| s.to_string()).collect(),
|
lines: output.lines().map(|s| s.to_string()).collect(),
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
coordinates: Coordinates::new() };
|
core: core.clone()};
|
||||||
textview.set_coordinates(&coordinates);
|
textview.set_coordinates(&core.coordinates).log();
|
||||||
textview.refresh();
|
textview.refresh().log();
|
||||||
textview.animate_slide_up();
|
textview.animate_slide_up().log();
|
||||||
return Ok(Box::new(textview))
|
return Ok(Box::new(textview))
|
||||||
}
|
}
|
||||||
Err(HError::PreviewFailed{file: file.name.clone()})
|
Err(HError::PreviewFailed{file: file.name.clone()})
|
||||||
@ -363,22 +357,16 @@ impl Previewer {
|
|||||||
|
|
||||||
|
|
||||||
impl Widget for Previewer {
|
impl Widget for Previewer {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.widget.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
if self.widget.coordinates == *coordinates {
|
Ok(&mut self.core)
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.widget.set_coordinates(coordinates);
|
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
"".to_string()
|
self.widget.refresh()
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
self.widget.refresh();
|
|
||||||
}
|
|
||||||
fn get_drawlist(&self) -> String {
|
|
||||||
self.widget.get_drawlist()
|
self.widget.get_drawlist()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,23 +526,16 @@ impl Widget for Previewer {
|
|||||||
|
|
||||||
|
|
||||||
impl<T> Widget for Box<T> where T: Widget + ?Sized {
|
impl<T> Widget for Box<T> where T: Widget + ?Sized {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
(**self).get_coordinates()
|
Ok((**self).get_core()?)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn render_header(&self) -> HResult<String> {
|
||||||
if (**self).get_coordinates() == coordinates {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(**self).set_coordinates(&coordinates);
|
|
||||||
(**self).refresh();
|
|
||||||
}
|
|
||||||
fn render_header(&self) -> String {
|
|
||||||
(**self).render_header()
|
(**self).render_header()
|
||||||
}
|
}
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
(**self).refresh()
|
(**self).refresh()
|
||||||
}
|
}
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
(**self).get_drawlist()
|
(**self).get_drawlist()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
135
src/proclist.rs
135
src/proclist.rs
@ -1,7 +1,6 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use std::process::Child;
|
use std::process::Child;
|
||||||
use std::process::Stdio;
|
|
||||||
use std::os::unix::io::FromRawFd;
|
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
@ -10,10 +9,9 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
use crate::coordinates::{Coordinates, Size, Position};
|
use crate::coordinates::{Coordinates, Size, Position};
|
||||||
use crate::listview::{Listable, ListView};
|
use crate::listview::{Listable, ListView};
|
||||||
use crate::textview::TextView;
|
use crate::textview::TextView;
|
||||||
use crate::widget::Widget;
|
use crate::widget::{Widget, Events, WidgetCore};
|
||||||
use crate::window::{send_event, Events};
|
|
||||||
use crate::preview::WillBeWidget;
|
use crate::preview::WillBeWidget;
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError, ErrorLog};
|
||||||
use crate::term;
|
use crate::term;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -22,7 +20,9 @@ struct Process {
|
|||||||
handle: Arc<Mutex<Child>>,
|
handle: Arc<Mutex<Child>>,
|
||||||
output: Arc<Mutex<String>>,
|
output: Arc<Mutex<String>>,
|
||||||
status: Arc<Mutex<Option<i32>>>,
|
status: Arc<Mutex<Option<i32>>>,
|
||||||
success: Arc<Mutex<Option<bool>>>
|
success: Arc<Mutex<Option<bool>>>,
|
||||||
|
sender: Sender<Events>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
@ -31,6 +31,7 @@ impl Process {
|
|||||||
let output = self.output.clone();
|
let output = self.output.clone();
|
||||||
let status = self.status.clone();
|
let status = self.status.clone();
|
||||||
let success = self.success.clone();
|
let success = self.success.clone();
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let stdout = handle.lock().unwrap().stdout.take().unwrap();
|
let stdout = handle.lock().unwrap().stdout.take().unwrap();
|
||||||
@ -41,7 +42,7 @@ impl Process {
|
|||||||
Ok(0) => break,
|
Ok(0) => break,
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
output.lock().unwrap().push_str(&line);
|
output.lock().unwrap().push_str(&line);
|
||||||
send_event(Events::WidgetReady).unwrap();
|
sender.send(Events::WidgetReady).unwrap();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
dbg!(err);
|
dbg!(err);
|
||||||
@ -63,7 +64,7 @@ impl Listable for ListView<Vec<Process>> {
|
|||||||
fn len(&self) -> usize { self.content.len() }
|
fn len(&self) -> usize { self.content.len() }
|
||||||
fn render(&self) -> Vec<String> {
|
fn render(&self) -> Vec<String> {
|
||||||
self.content.iter().map(|proc| {
|
self.content.iter().map(|proc| {
|
||||||
self.render_proc(proc)
|
self.render_proc(proc).unwrap()
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,14 +76,16 @@ impl ListView<Vec<Process>> {
|
|||||||
.arg(cmd)
|
.arg(cmd)
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
.stdout(std::process::Stdio::piped())
|
.stdout(std::process::Stdio::piped())
|
||||||
.stderr(unsafe { Stdio::from_raw_fd(2) })
|
//.stderr(unsafe { Stdio::from_raw_fd(2) })
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
let mut proc = Process {
|
let mut proc = Process {
|
||||||
cmd: cmd.to_string(),
|
cmd: cmd.to_string(),
|
||||||
handle: Arc::new(Mutex::new(handle)),
|
handle: Arc::new(Mutex::new(handle)),
|
||||||
output: Arc::new(Mutex::new(String::new())),
|
output: Arc::new(Mutex::new(String::new())),
|
||||||
status: Arc::new(Mutex::new(None)),
|
status: Arc::new(Mutex::new(None)),
|
||||||
success: Arc::new(Mutex::new(None))
|
success: Arc::new(Mutex::new(None)),
|
||||||
|
sender: self.get_core()?.get_sender()
|
||||||
};
|
};
|
||||||
proc.read_proc()?;
|
proc.read_proc()?;
|
||||||
self.content.push(proc);
|
self.content.push(proc);
|
||||||
@ -107,13 +110,13 @@ impl ListView<Vec<Process>> {
|
|||||||
self.content.get_mut(selection)
|
self.content.get_mut(selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_proc(&self, proc: &Process) -> String {
|
pub fn render_proc(&self, proc: &Process) -> HResult<String> {
|
||||||
let status = match *proc.status.lock().unwrap() {
|
let status = match *proc.status.lock().unwrap() {
|
||||||
Some(status) => format!("{}", status),
|
Some(status) => format!("{}", status),
|
||||||
None => "<R>".to_string()
|
None => "<R>".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let xsize = self.get_coordinates().xsize();
|
let xsize = self.get_coordinates()?.xsize();
|
||||||
let sized_string = term::sized_string(&proc.cmd, xsize);
|
let sized_string = term::sized_string(&proc.cmd, xsize);
|
||||||
let status_pos = xsize - status.len() as u16;
|
let status_pos = xsize - status.len() as u16;
|
||||||
let padding = sized_string.len() - sized_string.width_cjk();
|
let padding = sized_string.len() - sized_string.width_cjk();
|
||||||
@ -124,7 +127,7 @@ impl ListView<Vec<Process>> {
|
|||||||
_ => { status }
|
_ => { status }
|
||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
Ok(format!(
|
||||||
"{}{}{}{}{}{}",
|
"{}{}{}{}{}{}",
|
||||||
termion::cursor::Save,
|
termion::cursor::Save,
|
||||||
format!("{}{}{:padding$}{}",
|
format!("{}{}{:padding$}{}",
|
||||||
@ -136,24 +139,25 @@ impl ListView<Vec<Process>> {
|
|||||||
termion::cursor::Restore,
|
termion::cursor::Restore,
|
||||||
termion::cursor::Right(status_pos),
|
termion::cursor::Right(status_pos),
|
||||||
term::highlight_color(),
|
term::highlight_color(),
|
||||||
color_status
|
color_status))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProcView {
|
pub struct ProcView {
|
||||||
coordinates: Coordinates,
|
core: WidgetCore,
|
||||||
proc_list: ListView<Vec<Process>>,
|
proc_list: ListView<Vec<Process>>,
|
||||||
textview: Option<WillBeWidget<TextView>>,
|
textview: WillBeWidget<TextView>,
|
||||||
viewing: Option<usize>
|
viewing: Option<usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcView {
|
impl ProcView {
|
||||||
pub fn new() -> ProcView {
|
pub fn new(core: &WidgetCore) -> ProcView {
|
||||||
|
let tcore = core.clone();
|
||||||
|
let textview = Box::new(move |_| Ok(TextView::new_blank(&tcore)));
|
||||||
ProcView {
|
ProcView {
|
||||||
coordinates: Coordinates::new(),
|
core: core.clone(),
|
||||||
proc_list: ListView::new(vec![]),
|
proc_list: ListView::new(&core, vec![]),
|
||||||
textview: None,
|
textview: WillBeWidget::new(&core, textview),
|
||||||
viewing: None
|
viewing: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,16 +170,17 @@ impl ProcView {
|
|||||||
pub fn remove_proc(&mut self) -> HResult<()> {
|
pub fn remove_proc(&mut self) -> HResult<()> {
|
||||||
let (_, coords) = self.calculate_coordinates();
|
let (_, coords) = self.calculate_coordinates();
|
||||||
let coords2 = coords.clone();
|
let coords2 = coords.clone();
|
||||||
|
let mut core = self.core.clone();
|
||||||
|
core.coordinates = coords;
|
||||||
|
|
||||||
self.proc_list.remove_proc()?;
|
self.proc_list.remove_proc()?;
|
||||||
self.textview = Some(WillBeWidget::new(Box::new(move |_| {
|
self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| {
|
||||||
let mut textview = TextView::new_blank();
|
let mut textview = TextView::new_blank(&core);
|
||||||
textview.set_coordinates(&coords);
|
textview.refresh().log();
|
||||||
textview.refresh();
|
textview.animate_slide_up().log();
|
||||||
textview.animate_slide_up();
|
|
||||||
Ok(textview)
|
Ok(textview)
|
||||||
})));
|
}));
|
||||||
self.textview.as_mut().unwrap().set_coordinates(&coords2);
|
self.textview.set_coordinates(&coords2).log();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,29 +190,29 @@ impl ProcView {
|
|||||||
}
|
}
|
||||||
let output = self.proc_list.selected_proc()?.output.lock()?.clone();
|
let output = self.proc_list.selected_proc()?.output.lock()?.clone();
|
||||||
let (_, coords) = self.calculate_coordinates();
|
let (_, coords) = self.calculate_coordinates();
|
||||||
let coords2 = coords.clone();
|
let mut core = self.core.clone();
|
||||||
|
core.coordinates = coords;
|
||||||
|
|
||||||
self.textview = Some(WillBeWidget::new(Box::new(move |_| {
|
self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| {
|
||||||
let mut textview = TextView::new_blank();
|
let mut textview = TextView::new_blank(&core);
|
||||||
textview.set_coordinates(&coords);
|
textview.set_text(&output).log();
|
||||||
textview.set_text(&output);
|
textview.animate_slide_up().log();
|
||||||
textview.animate_slide_up();
|
|
||||||
Ok(textview)
|
Ok(textview)
|
||||||
})));
|
}));
|
||||||
self.textview.as_mut().unwrap().set_coordinates(&coords2);
|
|
||||||
self.viewing = Some(self.proc_list.get_selection());
|
self.viewing = Some(self.proc_list.get_selection());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates) {
|
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates) {
|
||||||
let xsize = self.coordinates.xsize();
|
let coordinates = self.get_coordinates().unwrap();
|
||||||
let ysize = self.coordinates.ysize();
|
let xsize = coordinates.xsize();
|
||||||
let top = self.coordinates.top().y();
|
let ysize = coordinates.ysize();
|
||||||
|
let top = coordinates.top().y();
|
||||||
let ratio = (33, 66);
|
let ratio = (33, 66);
|
||||||
|
|
||||||
let left_xsize = xsize * ratio.0 / 100;
|
let left_xsize = xsize * ratio.0 / 100;
|
||||||
let left_size = Size((left_xsize, ysize));
|
let left_size = Size((left_xsize, ysize));
|
||||||
let left_pos = self.coordinates.top();
|
let left_pos = coordinates.top();
|
||||||
|
|
||||||
let main_xsize = xsize * ratio.1 / 100;
|
let main_xsize = xsize * ratio.1 / 100;
|
||||||
let main_size = Size((main_xsize, ysize));
|
let main_size = Size((main_xsize, ysize));
|
||||||
@ -224,7 +229,6 @@ impl ProcView {
|
|||||||
size: main_size,
|
size: main_size,
|
||||||
position: main_pos,
|
position: main_pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
(left_coords, main_coords)
|
(left_coords, main_coords)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,36 +236,25 @@ impl ProcView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for ProcView {
|
impl Widget for ProcView {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
self.coordinates = coordinates.clone();
|
Ok(&mut self.core)
|
||||||
|
}
|
||||||
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
let (lcoord, rcoord) = self.calculate_coordinates();
|
let (lcoord, rcoord) = self.calculate_coordinates();
|
||||||
self.proc_list.set_coordinates(&lcoord);
|
self.proc_list.set_coordinates(&lcoord).log();
|
||||||
if let Some(textview) = &mut self.textview {
|
self.textview.set_coordinates(&rcoord).log();
|
||||||
textview.set_coordinates(&rcoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.refresh();
|
self.show_output().log();
|
||||||
|
self.proc_list.refresh().log();
|
||||||
|
self.textview.refresh().log();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
"".to_string()
|
Ok(self.proc_list.get_drawlist()? + &self.textview.get_drawlist()?)
|
||||||
}
|
|
||||||
fn refresh(&mut self) {
|
|
||||||
self.show_output();
|
|
||||||
self.proc_list.refresh();
|
|
||||||
if let Some(textview) = &mut self.textview {
|
|
||||||
textview.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn get_drawlist(&self) -> String {
|
|
||||||
if let Some(textview) = &self.textview {
|
|
||||||
self.proc_list.get_drawlist() + &textview.get_drawlist()
|
|
||||||
} else {
|
|
||||||
self.proc_list.get_drawlist()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
@ -270,18 +263,14 @@ impl Widget for ProcView {
|
|||||||
Key::Char('k') => { self.proc_list.kill_proc()? }
|
Key::Char('k') => { self.proc_list.kill_proc()? }
|
||||||
Key::Up | Key::Char('p') => {
|
Key::Up | Key::Char('p') => {
|
||||||
self.proc_list.move_up();
|
self.proc_list.move_up();
|
||||||
self.proc_list.refresh();
|
|
||||||
self.show_output().ok();
|
|
||||||
}
|
}
|
||||||
Key::Down | Key::Char('n') => {
|
Key::Down | Key::Char('n') => {
|
||||||
self.proc_list.move_down();
|
self.proc_list.move_down();
|
||||||
self.proc_list.refresh();
|
|
||||||
self.show_output().ok();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
self.draw()?;
|
self.draw().log();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
use crate::widget::{Widget, WidgetCore};
|
||||||
use crate::widget::Widget;
|
use crate::fail::{HResult, ErrorLog};
|
||||||
use crate::fail::HResult;
|
|
||||||
|
|
||||||
pub trait Tabbable {
|
pub trait Tabbable {
|
||||||
fn new_tab(&mut self);
|
fn new_tab(&mut self) -> HResult<()>;
|
||||||
fn close_tab(&mut self);
|
fn close_tab(&mut self) -> HResult<()>;
|
||||||
fn next_tab(&mut self);
|
fn next_tab(&mut self) -> HResult<()>;
|
||||||
fn on_next_tab(&mut self);
|
fn on_next_tab(&mut self) -> HResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
fn get_tab_names(&self) -> Vec<Option<String>>;
|
fn get_tab_names(&self) -> Vec<Option<String>>;
|
||||||
fn active_tab(&self) -> &dyn Widget;
|
fn active_tab(&self) -> &dyn Widget;
|
||||||
fn active_tab_mut(&mut self) -> &mut dyn Widget;
|
fn active_tab_mut(&mut self) -> &mut dyn Widget;
|
||||||
fn on_key_sub(&mut self, key: Key);
|
fn on_key_sub(&mut self, key: Key) -> HResult<()>;
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Ctrl('t') => { self.new_tab(); },
|
Key::Ctrl('t') => self.new_tab(),
|
||||||
Key::Ctrl('w') => self.close_tab(),
|
Key::Ctrl('w') => self.close_tab(),
|
||||||
Key::Char('\t') => self.next_tab(),
|
Key::Char('\t') => self.next_tab(),
|
||||||
_ => self.on_key_sub(key)
|
_ => self.on_key_sub(key)
|
||||||
@ -28,27 +29,27 @@ pub trait Tabbable {
|
|||||||
pub struct TabView<T> where T: Widget, TabView<T>: Tabbable {
|
pub struct TabView<T> where T: Widget, TabView<T>: Tabbable {
|
||||||
pub widgets: Vec<T>,
|
pub widgets: Vec<T>,
|
||||||
pub active: usize,
|
pub active: usize,
|
||||||
coordinates: Coordinates
|
core: WidgetCore
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
|
impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
|
||||||
pub fn new() -> TabView<T> {
|
pub fn new(core: &WidgetCore) -> TabView<T> {
|
||||||
TabView {
|
TabView {
|
||||||
widgets: vec![],
|
widgets: vec![],
|
||||||
active: 0,
|
active: 0,
|
||||||
coordinates: Coordinates::new()
|
core: core.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_widget(&mut self, widget: T) {
|
pub fn push_widget(&mut self, widget: T) -> HResult<()> {
|
||||||
self.widgets.push(widget);
|
self.widgets.push(widget);
|
||||||
self.refresh();
|
self.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_widget(&mut self) -> Option<T> {
|
pub fn pop_widget(&mut self) -> HResult<T> {
|
||||||
let widget = self.widgets.pop();
|
let widget = self.widgets.pop()?;
|
||||||
self.refresh();
|
self.refresh()?;
|
||||||
widget
|
Ok(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_tab_(&self) -> &T {
|
pub fn active_tab_(&self) -> &T {
|
||||||
@ -59,11 +60,10 @@ impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||||||
&mut self.widgets[self.active]
|
&mut self.widgets[self.active]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_tab_(&mut self) {
|
pub fn close_tab_(&mut self) -> HResult<()> {
|
||||||
if self.active == 0 { return }
|
self.pop_widget()?;
|
||||||
if self.active + 1 >= self.widgets.len() { self.active -= 1 }
|
self.active -= 1;
|
||||||
|
Ok(())
|
||||||
self.pop_widget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_tab_(&mut self) {
|
pub fn next_tab_(&mut self) {
|
||||||
@ -72,14 +72,17 @@ impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||||||
} else {
|
} else {
|
||||||
self.active += 1
|
self.active += 1
|
||||||
}
|
}
|
||||||
self.on_next_tab();
|
self.on_next_tab().log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
||||||
fn render_header(&self) -> String {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
let xsize = self.get_coordinates().xsize();
|
Ok(&self.core)
|
||||||
let header = self.active_tab_().render_header();
|
}
|
||||||
|
fn render_header(&self) -> HResult<String> {
|
||||||
|
let xsize = self.get_coordinates()?.xsize();
|
||||||
|
let header = self.active_tab_().render_header()?;
|
||||||
let tab_names = self.get_tab_names();
|
let tab_names = self.get_tab_names();
|
||||||
let mut nums_length = 0;
|
let mut nums_length = 0;
|
||||||
let tabnums = (0..self.widgets.len()).map(|num| {
|
let tabnums = (0..self.widgets.len()).map(|num| {
|
||||||
@ -100,38 +103,28 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||||||
|
|
||||||
let nums_pos = xsize - nums_length as u16;
|
let nums_pos = xsize - nums_length as u16;
|
||||||
|
|
||||||
format!("{}{}{}{}",
|
Ok(format!("{}{}{}{}",
|
||||||
header,
|
header,
|
||||||
crate::term::header_color(),
|
crate::term::header_color(),
|
||||||
crate::term::goto_xy(nums_pos, 1),
|
crate::term::goto_xy(nums_pos, 1),
|
||||||
tabnums)
|
tabnums))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_footer(&self) -> String {
|
fn render_footer(&self) -> HResult<String>
|
||||||
|
{
|
||||||
self.active_tab_().render_footer()
|
self.active_tab_().render_footer()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
self.active_tab_mut().refresh();
|
self.active_tab_mut().refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
self.active_tab_().get_drawlist()
|
self.active_tab_().get_drawlist()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
|
||||||
&self.coordinates
|
|
||||||
}
|
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
|
||||||
if self.coordinates == *coordinates {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.coordinates = coordinates.clone();
|
|
||||||
self.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
Tabbable::on_key(self, key);
|
Tabbable::on_key(self, key)?;
|
||||||
Ok(())
|
self.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/term.rs
30
src/term.rs
@ -1,20 +1,36 @@
|
|||||||
use std::io::{Stdout, Write};
|
use std::io::{Stdout, Write, BufWriter};
|
||||||
|
|
||||||
use termion;
|
use termion;
|
||||||
use termion::screen::AlternateScreen;
|
use termion::screen::AlternateScreen;
|
||||||
|
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
pub trait ScreenExt: Write {
|
pub trait ScreenExt: Write {
|
||||||
fn cursor_hide(&mut self) {
|
fn cursor_hide(&mut self) -> HResult<()> {
|
||||||
write!(self, "{}", termion::cursor::Hide).unwrap();
|
write!(self, "{}", termion::cursor::Hide)?;
|
||||||
|
self.flush()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn cursor_show(&mut self) {
|
fn cursor_show(&mut self) -> HResult<()> {
|
||||||
write!(self, "{}", termion::cursor::Show).unwrap();
|
write!(self, "{}", termion::cursor::Show)?;
|
||||||
|
self.flush()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) -> HResult<()> {
|
||||||
write!(self, "{}", termion::style::Reset).unwrap();
|
write!(self, "{}", termion::style::Reset)?;
|
||||||
|
self.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn write_str(&mut self, str: &str) -> HResult<()> {
|
||||||
|
write!(self, "{}", str)?;
|
||||||
|
self.flush()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenExt for AlternateScreen<Box<Stdout>> {}
|
impl ScreenExt for AlternateScreen<Box<Stdout>> {}
|
||||||
|
impl ScreenExt for AlternateScreen<Stdout> {}
|
||||||
|
impl ScreenExt for AlternateScreen<BufWriter<Stdout>> {}
|
||||||
|
|
||||||
pub fn xsize() -> u16 {
|
pub fn xsize() -> u16 {
|
||||||
let (xsize, _) = termion::terminal_size().unwrap();
|
let (xsize, _) = termion::terminal_size().unwrap();
|
||||||
|
@ -1,77 +1,79 @@
|
|||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
|
||||||
use crate::files::File;
|
use crate::files::File;
|
||||||
use crate::term::sized_string;
|
use crate::term::sized_string;
|
||||||
use crate::widget::Widget;
|
use crate::widget::{Widget, WidgetCore};
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct TextView {
|
pub struct TextView {
|
||||||
pub lines: Vec<String>,
|
pub lines: Vec<String>,
|
||||||
pub buffer: String,
|
pub buffer: String,
|
||||||
pub coordinates: Coordinates,
|
pub core: WidgetCore
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextView {
|
impl TextView {
|
||||||
pub fn new_blank() -> TextView {
|
pub fn new_blank(core: &WidgetCore) -> TextView {
|
||||||
TextView {
|
TextView {
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
coordinates: Coordinates::new()
|
core: core.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_from_file(file: &File) -> TextView {
|
pub fn new_from_file(core: &WidgetCore, file: &File) -> HResult<TextView> {
|
||||||
let file = std::fs::File::open(&file.path).unwrap();
|
let file = std::fs::File::open(&file.path)?;
|
||||||
let file = std::io::BufReader::new(file);
|
let file = std::io::BufReader::new(file);
|
||||||
let lines = file.lines().map(|line|
|
let lines = file.lines().map(|line|
|
||||||
line.unwrap()
|
Ok(line?
|
||||||
.replace("\t", " ")).collect();
|
.replace("\t", " ")))
|
||||||
|
.filter_map(|l: HResult<String>| l.ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
TextView {
|
Ok(TextView {
|
||||||
lines: lines,
|
lines: lines,
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
coordinates: Coordinates::new(),
|
core: core.clone()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
pub fn new_from_file_limit_lines(file: &File, num: usize) -> TextView {
|
pub fn new_from_file_limit_lines(core: &WidgetCore,
|
||||||
|
file: &File,
|
||||||
|
num: usize) -> HResult<TextView> {
|
||||||
let file = std::fs::File::open(&file.path).unwrap();
|
let file = std::fs::File::open(&file.path).unwrap();
|
||||||
let file = std::io::BufReader::new(file);
|
let file = std::io::BufReader::new(file);
|
||||||
let lines = file.lines()
|
let lines = file.lines()
|
||||||
.take(num)
|
.take(num)
|
||||||
.map(|line|
|
.map(|line|
|
||||||
line.unwrap()
|
Ok(line?
|
||||||
.replace("\t", " ")).collect();
|
.replace("\t", " ")))
|
||||||
|
.filter_map(|l: HResult<String>| l.ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
TextView {
|
Ok(TextView {
|
||||||
lines: lines,
|
lines: lines,
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
coordinates: Coordinates::new(),
|
core: core.clone()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text(&mut self, text: &str) {
|
pub fn set_text(&mut self, text: &str) -> HResult<()> {
|
||||||
let lines = text.lines().map(|l| l.to_string()).collect();
|
let lines = text.lines().map(|l| l.to_string()).collect();
|
||||||
self.lines = lines;
|
self.lines = lines;
|
||||||
self.refresh();
|
self.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for TextView {
|
impl Widget for TextView {
|
||||||
fn get_coordinates(&self) -> &Coordinates {
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
&self.coordinates
|
Ok(&self.core)
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
self.coordinates = coordinates.clone();
|
Ok(&mut self.core)
|
||||||
self.refresh();
|
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn refresh(&mut self) -> HResult<()> {
|
||||||
"".to_string()
|
let (xsize, ysize) = self.get_coordinates()?.size().size();
|
||||||
}
|
let (xpos, ypos) = self.get_coordinates()?.position().position();
|
||||||
fn refresh(&mut self) {
|
|
||||||
let (xsize, ysize) = self.get_coordinates().size().size();
|
|
||||||
let (xpos, ypos) = self.get_coordinates().position().position();
|
|
||||||
|
|
||||||
self.buffer = self.get_clearlist() +
|
self.buffer = self.get_clearlist()? +
|
||||||
&self
|
&self
|
||||||
.lines
|
.lines
|
||||||
.iter()
|
.iter()
|
||||||
@ -85,9 +87,10 @@ impl Widget for TextView {
|
|||||||
sized_string(&line, xsize))
|
sized_string(&line, xsize))
|
||||||
})
|
})
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_drawlist(&self) -> String {
|
fn get_drawlist(&self) -> HResult<String> {
|
||||||
self.buffer.clone()
|
Ok(self.buffer.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
325
src/widget.rs
325
src/widget.rs
@ -1,28 +1,115 @@
|
|||||||
use std::sync::mpsc::channel;
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::mpsc::{Sender, Receiver, channel};
|
||||||
|
|
||||||
use termion::event::{Event, Key, MouseEvent};
|
use termion::event::{Event, Key, MouseEvent};
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
|
use termion::screen::AlternateScreen;
|
||||||
|
use failure::Backtrace;
|
||||||
|
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError, ErrorLog};
|
||||||
use crate::window::{send_event, Events};
|
use crate::minibuffer::MiniBuffer;
|
||||||
|
use crate::term;
|
||||||
|
use crate::term::ScreenExt;
|
||||||
|
|
||||||
use std::io::{BufWriter, Write, stdin};
|
use std::io::{BufWriter, stdin, stdout, Stdout};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Events {
|
||||||
|
InputEvent(Event),
|
||||||
|
WidgetReady,
|
||||||
|
ExclusiveEvent(Option<Sender<Events>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for WidgetCore {
|
||||||
|
fn eq(&self, other: &WidgetCore) -> bool {
|
||||||
|
if self.coordinates == other.coordinates {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for WidgetCore {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
|
let output = format!("{:?}{:?}{:?}",
|
||||||
|
self.coordinates,
|
||||||
|
self.minibuffer,
|
||||||
|
self.status_bar_content);
|
||||||
|
formatter.write_str(&output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WidgetCore {
|
||||||
|
pub screen: Arc<Mutex<AlternateScreen<BufWriter<Stdout>>>>,
|
||||||
|
pub coordinates: Coordinates,
|
||||||
|
pub minibuffer: Arc<Mutex<Option<MiniBuffer>>>,
|
||||||
|
pub event_sender: Sender<Events>,
|
||||||
|
event_receiver: Arc<Mutex<Option<Receiver<Events>>>>,
|
||||||
|
pub status_bar_content: Arc<Mutex<Option<String>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetCore {
|
||||||
|
pub fn new() -> HResult<WidgetCore> {
|
||||||
|
let screen = AlternateScreen::from(BufWriter::new(stdout()));
|
||||||
|
let coords = Coordinates::new_at(term::xsize(),
|
||||||
|
term::ysize() - 2,
|
||||||
|
1,
|
||||||
|
2);
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
let status_bar_content = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
let core = WidgetCore {
|
||||||
|
screen: Arc::new(Mutex::new(screen)),
|
||||||
|
coordinates: coords,
|
||||||
|
minibuffer: Arc::new(Mutex::new(None)),
|
||||||
|
event_sender: sender,
|
||||||
|
event_receiver: Arc::new(Mutex::new(Some(receiver))),
|
||||||
|
status_bar_content: status_bar_content };
|
||||||
|
|
||||||
|
let minibuffer = MiniBuffer::new(&core);
|
||||||
|
*core.minibuffer.lock().unwrap() = Some(minibuffer);
|
||||||
|
Ok(core)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sender(&self) -> Sender<Events> {
|
||||||
|
self.event_sender.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Widget {
|
pub trait Widget {
|
||||||
fn get_widget(&self) -> Box<dyn Widget> {
|
fn get_widget(&self) -> Box<dyn Widget> {
|
||||||
Box::new(crate::textview::TextView::new_blank())
|
Box::new(crate::textview::TextView::new_blank(self.get_core().unwrap()))
|
||||||
}
|
}
|
||||||
fn get_coordinates(&self) -> &Coordinates;
|
fn get_core(&self) -> HResult<&WidgetCore> {
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates);
|
Err(HError::NoWidgetCoreError(Backtrace::new()))
|
||||||
fn render_header(&self) -> String;
|
}
|
||||||
fn render_footer(&self) -> String { "".into() }
|
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||||
fn refresh(&mut self);
|
Err(HError::NoWidgetCoreError(Backtrace::new()))
|
||||||
fn get_drawlist(&self) -> String;
|
}
|
||||||
|
fn get_coordinates(&self) -> HResult<&Coordinates> {
|
||||||
|
Ok(&self.get_core()?.coordinates)
|
||||||
|
}
|
||||||
|
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||||
|
self.get_core_mut()?.coordinates = coordinates.clone();
|
||||||
|
self.refresh()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn render_header(&self) -> HResult<String> {
|
||||||
|
Err(HError::NoHeaderError)
|
||||||
|
}
|
||||||
|
fn render_footer(&self) -> HResult<String> {
|
||||||
|
Err(HError::NoHeaderError)
|
||||||
|
}
|
||||||
|
fn refresh(&mut self) -> HResult<()>;
|
||||||
|
fn get_drawlist(&self) -> HResult<String>;
|
||||||
fn after_draw(&self) -> HResult<()> { Ok(()) }
|
fn after_draw(&self) -> HResult<()> { Ok(()) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> HResult<()> {
|
fn on_event(&mut self, event: Event) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
Event::Key(Key::Char('q')) => panic!("It's your fault!"),
|
Event::Key(Key::Char('q')) => panic!("It's your fault!"),
|
||||||
@ -34,67 +121,59 @@ pub trait Widget {
|
|||||||
|
|
||||||
fn on_key(&mut self, key: Key) -> HResult<()> {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
_ => self.bad(Event::Key(key)),
|
_ => { self.bad(Event::Key(key)).unwrap() },
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> {
|
fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
_ => self.bad(Event::Mouse(event)),
|
_ => { self.bad(Event::Mouse(event)).unwrap() },
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_wtf(&mut self, event: Vec<u8>) -> HResult<()> {
|
fn on_wtf(&mut self, event: Vec<u8>) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
_ => self.bad(Event::Unsupported(event)),
|
_ => { self.bad(Event::Unsupported(event)).unwrap() },
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_status(&self, status: &str) {
|
fn bad(&mut self, event: Event) -> HResult<()> {
|
||||||
crate::window::show_status(status);
|
self.show_status(&format!("Stop the nasty stuff!! {:?} does nothing!", event))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minibuffer(&self, query: &str) -> HResult<String> {
|
fn get_header_drawlist(&mut self) -> HResult<String> {
|
||||||
crate::window::minibuffer(query)
|
Ok(format!(
|
||||||
}
|
|
||||||
|
|
||||||
fn bad(&mut self, event: Event) {
|
|
||||||
self.show_status(&format!("Stop the nasty stuff!! {:?} does nothing!", event));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_header_drawlist(&mut self) -> String {
|
|
||||||
format!(
|
|
||||||
"{}{}{:xsize$}{}{}",
|
"{}{}{:xsize$}{}{}",
|
||||||
crate::term::goto_xy(1, 1),
|
crate::term::goto_xy(1, 1),
|
||||||
crate::term::header_color(),
|
crate::term::header_color(),
|
||||||
" ",
|
" ",
|
||||||
crate::term::goto_xy(1, 1),
|
crate::term::goto_xy(1, 1),
|
||||||
self.render_header(),
|
self.render_header()?,
|
||||||
xsize = self.get_coordinates().xsize() as usize
|
xsize = self.get_coordinates()?.xsize() as usize
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_footer_drawlist(&mut self) -> String {
|
fn get_footer_drawlist(&mut self) -> HResult<String> {
|
||||||
let xsize = self.get_coordinates().xsize();
|
let xsize = self.get_coordinates()?.xsize();
|
||||||
let ypos = crate::term::ysize();
|
let ypos = crate::term::ysize();
|
||||||
format!(
|
Ok(format!(
|
||||||
"{}{}{:xsize$}{}{}",
|
"{}{}{:xsize$}{}{}",
|
||||||
crate::term::goto_xy(1, ypos),
|
crate::term::goto_xy(1, ypos),
|
||||||
crate::term::header_color(),
|
crate::term::header_color(),
|
||||||
" ",
|
" ",
|
||||||
crate::term::goto_xy(1, ypos),
|
crate::term::goto_xy(1, ypos),
|
||||||
self.render_footer(),
|
self.render_footer()?,
|
||||||
xsize = xsize as usize)
|
xsize = xsize as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_clearlist(&self) -> String {
|
fn get_clearlist(&self) -> HResult<String> {
|
||||||
let (xpos, ypos) = self.get_coordinates().u16position();
|
let (xpos, ypos) = self.get_coordinates()?.u16position();
|
||||||
let (xsize, ysize) = self.get_coordinates().u16size();
|
let (xsize, ysize) = self.get_coordinates()?.u16size();
|
||||||
|
|
||||||
(ypos..ysize + 2)
|
Ok((ypos..ysize + 2)
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
format!(
|
format!(
|
||||||
"{}{}{:xsize$}",
|
"{}{}{:xsize$}",
|
||||||
@ -104,15 +183,15 @@ pub trait Widget {
|
|||||||
xsize = xsize as usize
|
xsize = xsize as usize
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_redraw_empty_list(&self, lines: usize) -> String {
|
fn get_redraw_empty_list(&self, lines: usize) -> HResult<String> {
|
||||||
let (xpos, ypos) = self.get_coordinates().u16position();
|
let (xpos, ypos) = self.get_coordinates()?.u16position();
|
||||||
let (xsize, ysize) = self.get_coordinates().u16size();
|
let (xsize, ysize) = self.get_coordinates()?.u16size();
|
||||||
|
|
||||||
let start_y = lines + ypos as usize;
|
let start_y = lines + ypos as usize;
|
||||||
(start_y..(ysize + 2) as usize)
|
Ok((start_y..(ysize + 2) as usize)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
format!(
|
format!(
|
||||||
"{}{:xsize$}",
|
"{}{:xsize$}",
|
||||||
@ -121,30 +200,21 @@ pub trait Widget {
|
|||||||
xsize = xsize as usize
|
xsize = xsize as usize
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect())
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(&self) -> HResult<()> {
|
|
||||||
let drawlist = self.get_drawlist();
|
|
||||||
let mut bufout = BufWriter::new(std::io::stdout());
|
|
||||||
|
|
||||||
write!(bufout, "{}", drawlist)?;
|
|
||||||
bufout.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popup(&mut self) -> HResult<()> {
|
fn popup(&mut self) -> HResult<()> {
|
||||||
self.run_widget();
|
self.run_widget().log();
|
||||||
send_event(Events::ExclusiveEvent(None))?;
|
self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_widget(&mut self) -> HResult<()> {
|
fn run_widget(&mut self) -> HResult<()> {
|
||||||
let (tx_event, rx_event) = channel();
|
let (tx_event, rx_event) = channel();
|
||||||
send_event(Events::ExclusiveEvent(Some(tx_event)))?;
|
self.get_core()?.get_sender().send(Events::ExclusiveEvent(Some(tx_event)))?;
|
||||||
|
|
||||||
self.clear()?;
|
self.clear()?;
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
self.draw()?;
|
self.draw()?;
|
||||||
|
|
||||||
for event in rx_event.iter() {
|
for event in rx_event.iter() {
|
||||||
@ -155,32 +225,31 @@ pub trait Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Events::WidgetReady => {
|
Events::WidgetReady => {
|
||||||
self.refresh();
|
self.refresh().log();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.draw();
|
self.draw().log();
|
||||||
self.after_draw();
|
self.after_draw().log();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self) -> HResult<()> {
|
fn clear(&self) -> HResult<()> {
|
||||||
let clearlist = self.get_clearlist();
|
let clearlist = self.get_clearlist()?;
|
||||||
write!(std::io::stdout(), "{}", clearlist)?;
|
self.write_to_screen(&clearlist)
|
||||||
std::io::stdout().flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn animate_slide_up(&mut self) {
|
fn animate_slide_up(&mut self) -> HResult<()> {
|
||||||
let coords = self.get_coordinates().clone();
|
let coords = self.get_coordinates()?.clone();
|
||||||
let xpos = coords.position().x();
|
let xpos = coords.position().x();
|
||||||
let ypos = coords.position().y();
|
let ypos = coords.position().y();
|
||||||
let xsize = coords.xsize();
|
let xsize = coords.xsize();
|
||||||
let ysize = coords.ysize();
|
let ysize = coords.ysize();
|
||||||
let clear = self.get_clearlist();
|
let clear = self.get_clearlist()?;
|
||||||
let pause = std::time::Duration::from_millis(5);
|
let pause = std::time::Duration::from_millis(5);
|
||||||
let mut bufout = BufWriter::new(std::io::stdout());
|
|
||||||
|
self.write_to_screen(&clear).log();
|
||||||
|
|
||||||
for i in (0..10).rev() {
|
for i in (0..10).rev() {
|
||||||
let coords = Coordinates { size: Size((xsize,ysize-i)),
|
let coords = Coordinates { size: Size((xsize,ysize-i)),
|
||||||
@ -188,13 +257,123 @@ pub trait Widget {
|
|||||||
((xpos,
|
((xpos,
|
||||||
ypos+i))
|
ypos+i))
|
||||||
};
|
};
|
||||||
self.set_coordinates(&coords);
|
self.set_coordinates(&coords).log();
|
||||||
let buffer = self.get_drawlist();
|
let buffer = self.get_drawlist()?;
|
||||||
write!(bufout, "{}{}",
|
self.write_to_screen(&buffer).log();
|
||||||
clear, buffer).unwrap();
|
|
||||||
bufout.flush().ok();
|
|
||||||
|
|
||||||
std::thread::sleep(pause);
|
std::thread::sleep(pause);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self) -> HResult<()> {
|
||||||
|
let output =
|
||||||
|
self.get_drawlist().unwrap_or("".to_string()) +
|
||||||
|
&self.get_header_drawlist().unwrap_or("".to_string()) +
|
||||||
|
&self.get_footer_drawlist().unwrap_or("".to_string());
|
||||||
|
self.write_to_screen(&output).log();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_input(&mut self) -> HResult<()> {
|
||||||
|
let (tx_event, rx_event) = channel();
|
||||||
|
let (tx_internal_event, rx_internal_event) = channel();
|
||||||
|
let rx_global_event = self.get_core()?.event_receiver.lock()?.take()?;
|
||||||
|
|
||||||
|
input_thread(tx_event.clone());
|
||||||
|
global_event_thread(rx_global_event, tx_event.clone());
|
||||||
|
dispatch_events(rx_event, tx_internal_event);
|
||||||
|
|
||||||
|
for event in rx_internal_event.iter() {
|
||||||
|
match event {
|
||||||
|
Events::InputEvent(event) => {
|
||||||
|
self.on_event(event).ok();
|
||||||
|
self.draw().ok();
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.refresh().ok();
|
||||||
|
self.draw().ok();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_status(&self) -> HResult<()> {
|
||||||
|
let xsize = term::xsize() as u16;
|
||||||
|
let status = &self.get_core()?.status_bar_content;
|
||||||
|
|
||||||
|
let status = status.lock()?;
|
||||||
|
|
||||||
|
self.write_to_screen(
|
||||||
|
&format!(
|
||||||
|
"{}{}{:xsize$}{}{}",
|
||||||
|
term::move_bottom(),
|
||||||
|
term::status_bg(),
|
||||||
|
" ",
|
||||||
|
term::move_bottom(),
|
||||||
|
status.as_ref()?,
|
||||||
|
xsize = xsize as usize
|
||||||
|
)).log();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_status(&self, status: &str) -> HResult<()> {
|
||||||
|
{
|
||||||
|
let mut status_content = self.get_core()?.status_bar_content.lock()?;
|
||||||
|
*status_content = Some(status.to_string());
|
||||||
|
}
|
||||||
|
self.draw_status()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minibuffer(&self, query: &str) -> HResult<String> {
|
||||||
|
let answer = self.get_core()?.minibuffer.lock()?.as_mut()?.query(query);
|
||||||
|
let mut screen = self.get_core()?.screen.lock()?;
|
||||||
|
screen.cursor_hide().log();
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_screen(&self, s: &str) -> HResult<()> {
|
||||||
|
let mut screen = self.get_core()?.screen.lock()?;
|
||||||
|
screen.write_str(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispatch_events(rx: Receiver<Events>, tx: Sender<Events>) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let mut tx_exclusive_event: Option<Sender<Events>> = None;
|
||||||
|
for event in rx.iter() {
|
||||||
|
match &event {
|
||||||
|
Events::ExclusiveEvent(tx_event) => {
|
||||||
|
tx_exclusive_event = tx_event.clone();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if let Some(tx_event) = &tx_exclusive_event {
|
||||||
|
tx_event.send(event).unwrap();
|
||||||
|
} else {
|
||||||
|
tx.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_event_thread(rx_global: Receiver<Events>,
|
||||||
|
tx: Sender<Events>) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for event in rx_global.iter() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
374
src/window.rs
374
src/window.rs
@ -1,214 +1,214 @@
|
|||||||
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 std::sync::mpsc::{Sender, Receiver, channel};
|
||||||
|
|
||||||
use termion::event::Event;
|
// use termion::event::Event;
|
||||||
use termion::input::TermRead;
|
// use termion::input::TermRead;
|
||||||
use termion::screen::AlternateScreen;
|
// use termion::screen::AlternateScreen;
|
||||||
|
|
||||||
use crate::term;
|
// use crate::term;
|
||||||
use crate::term::ScreenExt;
|
// 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::minibuffer::MiniBuffer;
|
// use crate::minibuffer::MiniBuffer;
|
||||||
use crate::fail::HResult;
|
// use crate::fail::HResult;
|
||||||
|
|
||||||
lazy_static! {
|
// lazy_static! {
|
||||||
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
|
// static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
|
||||||
static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
|
// static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
|
||||||
= Arc::new(Mutex::new(MiniBuffer::new()));
|
// = Arc::new(Mutex::new(MiniBuffer::new()));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub enum Events {
|
// pub enum Events {
|
||||||
InputEvent(Event),
|
// InputEvent(Event),
|
||||||
WidgetReady,
|
// WidgetReady,
|
||||||
ExclusiveEvent(Option<Sender<Events>>),
|
// ExclusiveEvent(Option<Sender<Events>>),
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub struct Window<T>
|
// pub struct Window<T>
|
||||||
where
|
// where
|
||||||
T: Widget,
|
// T: Widget,
|
||||||
{
|
// {
|
||||||
pub selection: usize,
|
// pub selection: usize,
|
||||||
pub widget: T,
|
// pub widget: T,
|
||||||
pub status: Arc<Mutex<Option<String>>>,
|
// pub status: Arc<Mutex<Option<String>>>,
|
||||||
pub screen: AlternateScreen<Box<Stdout>>,
|
// pub screen: AlternateScreen<Box<Stdout>>,
|
||||||
pub coordinates: Coordinates,
|
// pub coordinates: Coordinates,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<T> Window<T>
|
// impl<T> Window<T>
|
||||||
where
|
// where
|
||||||
T: Widget,
|
// T: Widget,
|
||||||
{
|
// {
|
||||||
pub fn new(widget: T) -> Window<T> {
|
// pub fn new(widget: T) -> Window<T> {
|
||||||
let mut screen = AlternateScreen::from(Box::new(stdout()));
|
// let mut screen = AlternateScreen::from(Box::new(stdout()));
|
||||||
screen.cursor_hide();
|
// screen.cursor_hide();
|
||||||
let (xsize, ysize) = termion::terminal_size().unwrap();
|
// let (xsize, ysize) = termion::terminal_size().unwrap();
|
||||||
let mut win = Window::<T> {
|
// let mut win = Window::<T> {
|
||||||
selection: 0,
|
// selection: 0,
|
||||||
widget: widget,
|
// widget: widget,
|
||||||
status: STATUS_BAR_CONTENT.clone(),
|
// status: STATUS_BAR_CONTENT.clone(),
|
||||||
screen: screen,
|
// screen: screen,
|
||||||
coordinates: Coordinates {
|
// coordinates: Coordinates {
|
||||||
size: Size((xsize, ysize)),
|
// size: Size((xsize, ysize)),
|
||||||
position: Position((1, 1)),
|
// position: Position((1, 1)),
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|
||||||
win.widget.set_coordinates(&Coordinates {
|
// win.widget.set_coordinates(&Coordinates {
|
||||||
size: Size((xsize, ysize - 2)),
|
// size: Size((xsize, ysize - 2)),
|
||||||
position: Position((1, 2)),
|
// position: Position((1, 2)),
|
||||||
});
|
// });
|
||||||
win.widget.refresh();
|
// win.widget.refresh();
|
||||||
win
|
// win
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
// pub fn draw(&mut self) {
|
||||||
let output = self.widget.get_drawlist() + &self.widget.get_header_drawlist()
|
// let output = self.widget.get_drawlist() + &self.widget.get_header_drawlist()
|
||||||
+ &self.widget.get_footer_drawlist();
|
// + &self.widget.get_footer_drawlist();
|
||||||
self.screen.write(output.as_ref()).unwrap();
|
// self.screen.write(output.as_ref()).unwrap();
|
||||||
|
|
||||||
self.screen.flush().unwrap();
|
// self.screen.flush().unwrap();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// pub fn show_status(status: &str) {
|
// // pub fn show_status(status: &str) {
|
||||||
// show_status(status);
|
// // show_status(status);
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
// pub fn draw_status() {
|
// // pub fn draw_status() {
|
||||||
// draw_status();
|
// // draw_status();
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
// pub fn clear_status() {
|
// // pub fn clear_status() {
|
||||||
// Self::show_status("");
|
// // Self::show_status("");
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
|
|
||||||
pub fn handle_input(&mut self) {
|
// pub fn handle_input(&mut self) {
|
||||||
let (tx_event, rx_event) = channel();
|
// let (tx_event, rx_event) = channel();
|
||||||
let (tx_global_event, rx_global_event) = channel();
|
// let (tx_global_event, rx_global_event) = channel();
|
||||||
*TX_EVENT.try_lock().unwrap() = Some(tx_global_event);
|
// *TX_EVENT.try_lock().unwrap() = Some(tx_global_event);
|
||||||
let (tx_internal_event, rx_internal_event) = channel();
|
// let (tx_internal_event, rx_internal_event) = channel();
|
||||||
|
|
||||||
input_thread(tx_event.clone());
|
// input_thread(tx_event.clone());
|
||||||
global_event_thread(rx_global_event, tx_event.clone());
|
// global_event_thread(rx_global_event, tx_event.clone());
|
||||||
dispatch_events(rx_event, tx_internal_event);
|
// dispatch_events(rx_event, tx_internal_event);
|
||||||
|
|
||||||
for event in rx_internal_event.iter() {
|
// for event in rx_internal_event.iter() {
|
||||||
match event {
|
// match event {
|
||||||
Events::InputEvent(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.widget.refresh();
|
||||||
self.draw();
|
// self.draw();
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn dispatch_events(rx: Receiver<Events>, tx: Sender<Events>) {
|
// fn dispatch_events(rx: Receiver<Events>, tx: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
// std::thread::spawn(move || {
|
||||||
let mut tx_exclusive_event: Option<Sender<Events>> = None;
|
// let mut tx_exclusive_event: Option<Sender<Events>> = None;
|
||||||
for event in rx.iter() {
|
// for event in rx.iter() {
|
||||||
match &event {
|
// match &event {
|
||||||
Events::ExclusiveEvent(tx_event) => {
|
// Events::ExclusiveEvent(tx_event) => {
|
||||||
tx_exclusive_event = tx_event.clone();
|
// tx_exclusive_event = tx_event.clone();
|
||||||
}
|
// }
|
||||||
_ => {}
|
// _ => {}
|
||||||
}
|
// }
|
||||||
if let Some(tx_event) = &tx_exclusive_event {
|
// if let Some(tx_event) = &tx_exclusive_event {
|
||||||
tx_event.send(event).unwrap();
|
// tx_event.send(event).unwrap();
|
||||||
} else {
|
// } else {
|
||||||
tx.send(event).unwrap();
|
// tx.send(event).unwrap();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn global_event_thread(rx_global: Receiver<Events>,
|
// fn global_event_thread(rx_global: Receiver<Events>,
|
||||||
tx: Sender<Events>) {
|
// tx: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
// std::thread::spawn(move || {
|
||||||
for event in rx_global.iter() {
|
// for event in rx_global.iter() {
|
||||||
tx.send(event).unwrap();
|
// tx.send(event).unwrap();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn input_thread(tx: Sender<Events>) {
|
// fn input_thread(tx: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
// std::thread::spawn(move || {
|
||||||
for input in stdin().events() {
|
// for input in stdin().events() {
|
||||||
let input = input.unwrap();
|
// let input = input.unwrap();
|
||||||
tx.send(Events::InputEvent(input)).unwrap();
|
// tx.send(Events::InputEvent(input)).unwrap();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn send_event(event: Events) -> HResult<()> {
|
// pub fn send_event(event: Events) -> HResult<()> {
|
||||||
let tx = TX_EVENT.lock()?.clone()?.clone();
|
// let tx = TX_EVENT.lock()?.clone()?.clone();
|
||||||
tx.send(event)?;
|
// tx.send(event)?;
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<T> Drop for Window<T>
|
// impl<T> Drop for Window<T>
|
||||||
where
|
// where
|
||||||
T: Widget,
|
// T: Widget,
|
||||||
{
|
// {
|
||||||
fn drop(&mut self) {
|
// fn drop(&mut self) {
|
||||||
// When done, restore the defaults to avoid messing with the terminal.
|
// // When done, restore the defaults to avoid messing with the terminal.
|
||||||
self.screen
|
// self.screen
|
||||||
.write(
|
// .write(
|
||||||
format!(
|
// format!(
|
||||||
"{}{}{}{}{}",
|
// "{}{}{}{}{}",
|
||||||
termion::screen::ToMainScreen,
|
// termion::screen::ToMainScreen,
|
||||||
termion::clear::All,
|
// termion::clear::All,
|
||||||
termion::style::Reset,
|
// termion::style::Reset,
|
||||||
termion::cursor::Show,
|
// termion::cursor::Show,
|
||||||
termion::cursor::Goto(1, 1)
|
// termion::cursor::Goto(1, 1)
|
||||||
)
|
// )
|
||||||
.as_ref(),
|
// .as_ref(),
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
lazy_static! {
|
// lazy_static! {
|
||||||
static ref STATUS_BAR_CONTENT: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
|
// static ref STATUS_BAR_CONTENT: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn draw_status() {
|
// pub fn draw_status() {
|
||||||
let xsize = term::xsize() as u16;
|
// let xsize = term::xsize() as u16;
|
||||||
let status = STATUS_BAR_CONTENT.try_lock().unwrap().clone();
|
// let status = STATUS_BAR_CONTENT.try_lock().unwrap().clone();
|
||||||
|
|
||||||
status.or(Some("".to_string())).and_then(|status| {
|
// status.or(Some("".to_string())).and_then(|status| {
|
||||||
write!(
|
// write!(
|
||||||
stdout(),
|
// stdout(),
|
||||||
"{}{}{:xsize$}{}{}",
|
// "{}{}{:xsize$}{}{}",
|
||||||
term::move_bottom(),
|
// term::move_bottom(),
|
||||||
term::status_bg(),
|
// term::status_bg(),
|
||||||
" ",
|
// " ",
|
||||||
term::move_bottom(),
|
// term::move_bottom(),
|
||||||
status,
|
// status,
|
||||||
xsize = xsize as usize
|
// xsize = xsize as usize
|
||||||
)
|
// )
|
||||||
.ok()
|
// .ok()
|
||||||
});
|
// });
|
||||||
stdout().flush().unwrap();
|
// stdout().flush().unwrap();
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn show_status(status: &str) {
|
// pub fn show_status(status: &str) {
|
||||||
{
|
// {
|
||||||
let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
|
// let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
|
||||||
*status_content = Some(status.to_string());
|
// *status_content = Some(status.to_string());
|
||||||
}
|
// }
|
||||||
draw_status();
|
// draw_status();
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn minibuffer(query: &str) -> HResult<String> {
|
// pub fn minibuffer(query: &str) -> HResult<String> {
|
||||||
MINIBUFFER.lock()?.query(query)
|
// MINIBUFFER.lock()?.query(query)
|
||||||
}
|
// }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user