1
0
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:
rabite 2019-03-02 19:39:24 +01:00
parent e2acef5ddf
commit eb5a86b7cd
15 changed files with 983 additions and 793 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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