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::Fail;
use failure::Backtrace;
use std::path::PathBuf;
@ -28,21 +29,45 @@ pub enum HError {
#[fail(display = "Was None!")]
NoneError,
#[fail(display = "Not ready yet!")]
WillBeNotReady,
WillBeNotReady(Backtrace),
#[fail(display = "No widget found")]
NoWidgetError,
NoWidgetError(Backtrace),
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
WrongDirectoryError{ path: PathBuf, dir: PathBuf },
#[fail(display = "Widget finnished")]
PopupFinnished,
#[fail(display = "Input finnished")]
InputFinnished,
#[fail(display = "No completions found")]
NoCompletionsError,
#[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 {
fn from(error: std::io::Error) -> Self {
dbg!(&error);

View File

@ -1,22 +1,20 @@
use termion::event::Key;
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
use std::error::Error;
use std::io::Write;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
use std::path::PathBuf;
use crate::coordinates::{Coordinates};
use crate::files::{File, Files};
use crate::listview::ListView;
use crate::miller_columns::MillerColumns;
use crate::widget::Widget;
use crate::tabview::{TabView, Tabbable};
use crate::preview::WillBeWidget;
use crate::fail::{HResult, HError};
use crate::window::{Events, send_event};
use crate::fail::{HResult, HError, ErrorLog};
use crate::widget::{Events, WidgetCore};
use crate::proclist::ProcView;
@ -24,6 +22,7 @@ use crate::proclist::ProcView;
pub struct FileBrowser {
pub columns: MillerColumns<WillBeWidget<ListView<Files>>>,
pub cwd: File,
core: WidgetCore,
watcher: INotifyWatcher,
watches: Vec<PathBuf>,
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
@ -31,22 +30,24 @@ pub struct FileBrowser {
}
impl Tabbable for TabView<FileBrowser> {
fn new_tab(&mut self) {
let mut tab = FileBrowser::new().unwrap();
fn new_tab(&mut self) -> HResult<()> {
let mut tab = FileBrowser::new_cored(&self.active_tab_().core)?;
let proc_view = self.active_tab_().proc_view.clone();
tab.proc_view = proc_view;
self.push_widget(tab);
self.push_widget(tab)?;
self.active += 1;
Ok(())
}
fn close_tab(&mut self) {
self.close_tab_();
fn close_tab(&mut self) -> HResult<()> {
self.close_tab_()
}
fn next_tab(&mut self) {
fn next_tab(&mut self) -> HResult<()> {
self.next_tab_();
Ok(())
}
fn get_tab_names(&self) -> Vec<Option<String>> {
@ -66,18 +67,18 @@ impl Tabbable for TabView<FileBrowser> {
self.active_tab_mut_()
}
fn on_next_tab(&mut self) {
self.active_tab_mut().refresh();
fn on_next_tab(&mut self) -> HResult<()> {
self.active_tab_mut().refresh()
}
fn on_key_sub(&mut self, key: Key) {
fn on_key_sub(&mut self, key: Key) -> HResult<()> {
match key {
Key::Char('!') => {
let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone())
.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 || {
for event in rx.iter() {
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 {
pub fn new() -> Result<FileBrowser, Box<Error>> {
pub fn new_cored(core: &WidgetCore) -> HResult<FileBrowser> {
let cwd = std::env::current_dir().unwrap();
let coords = Coordinates::new_at(crate::term::xsize(),
crate::term::ysize() - 2,
1,
2);
let coords = core.coordinates.clone();
let core_ = core.clone();
let mut miller = MillerColumns::new();
miller.set_coordinates(&coords);
let mut miller = MillerColumns::new(core);
miller.set_coordinates(&coords)?;
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_widget = WillBeWidget::new(Box::new(move |_| {
let mut list = ListView::new(Files::new_from_path(&main_path).unwrap());
list.set_coordinates(&main_coords);
list.animate_slide_up();
let main_widget = WillBeWidget::new(&core, Box::new(move |_| {
let mut list = ListView::new(&core_,
Files::new_from_path(&main_path).unwrap());
list.set_coordinates(&main_coords).log();
list.animate_slide_up().log();
Ok(list)
}));
@ -129,13 +131,14 @@ impl FileBrowser {
let (tx_watch, rx_watch) = channel();
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();
proc_view.set_coordinates(&coords);
let mut proc_view = ProcView::new(core);
proc_view.set_coordinates(&coords).log();
Ok(FileBrowser { columns: miller,
cwd: cwd,
core: core.clone(),
watcher: watcher,
watches: vec![],
dir_events: dir_events,
@ -145,16 +148,17 @@ impl FileBrowser {
pub fn enter_dir(&mut self) -> HResult<()> {
let file = self.selected_file()?;
let (_, coords, _) = self.columns.calculate_coordinates();
let core = self.core.clone();
match file.read_dir() {
Ok(files) => {
std::env::set_current_dir(&file.path).unwrap();
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 mut list = ListView::new(files);
list.set_coordinates(&coords);
list.animate_slide_up();
let mut list = ListView::new(&core, files);
list.set_coordinates(&coords).log();
list.animate_slide_up().log();
Ok(list)
}));
self.columns.push_widget(view);
@ -167,12 +171,12 @@ impl FileBrowser {
match status {
Ok(status) =>
self.show_status(&format!("\"{}\" exited with {}",
"rifle", status)),
"rifle", status)).log(),
Err(err) =>
self.show_status(&format!("Can't run this \"{}\": {}",
"rifle", err))
"rifle", err)).log()
}
};
}
}
Ok(())
@ -185,8 +189,7 @@ impl FileBrowser {
self.cwd = new_cwd;
}
self.refresh();
Ok(())
self.refresh()
}
pub fn update_preview(&mut self) -> HResult<()> {
@ -207,10 +210,12 @@ impl FileBrowser {
let cwd = self.selected_file()?.clone();
if let Ok(grand_parent) = cwd.grand_parent_as_file() {
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
= ListView::new(Files::new_from_path(&grand_parent.path)?);
view.set_coordinates(&coords);
= ListView::new(&core,
Files::new_from_path(&grand_parent.path)?);
view.set_coordinates(&coords).log();
Ok(view)
}));
self.columns.prepend_widget(left_view);
@ -341,17 +346,19 @@ impl FileBrowser {
let dir = std::path::PathBuf::from(&dir);
let left_dir = std::path::PathBuf::from(&dir);
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 mut listview = ListView::new(files);
listview.set_coordinates(&main_coords);
let mut listview = ListView::new(&mcore, files);
listview.set_coordinates(&main_coords).log();
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 mut listview = ListView::new(files);
listview.set_coordinates(&left_coords);
let mut listview = ListView::new(&lcore, files);
listview.set_coordinates(&left_coords).log();
Ok(listview)
}));
self.columns.push_widget(left);
@ -373,7 +380,7 @@ impl FileBrowser {
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 {
cmd.replace("$s", &format!("{}", &filename))
@ -397,35 +404,28 @@ impl FileBrowser {
}
impl Widget for FileBrowser {
fn get_coordinates(&self) -> &Coordinates {
&self.columns.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
self.columns.set_coordinates(coordinates);
self.proc_view.lock().unwrap().set_coordinates(coordinates);
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();
fn render_header(&self) -> HResult<String> {
let xsize = self.get_coordinates()?.xsize();
let file = self.selected_file()?;
let name = &file.name;
let color = if file.is_dir() || file.color.is_none() {
crate::term::highlight_color() } else {
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 sized_path = crate::term::sized_string(&pretty_path, xsize);
sized_path
Ok(sized_path)
}
fn render_footer(&self) -> String {
if self.main_widget().is_err() { return "".to_string() }
let xsize = self.get_coordinates().xsize();
let ypos = self.get_coordinates().position().y();
let file = self.selected_file().unwrap();
fn render_footer(&self) -> HResult<String> {
let xsize = self.get_coordinates()?.xsize();
let ypos = self.get_coordinates()?.position().y();
let file = self.selected_file()?;
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".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 selection = (*self.main_widget().as_ref().unwrap().lock().unwrap()).as_ref().unwrap().get_selection();
let file_count = (*self.main_widget().unwrap().lock().unwrap()).as_ref().unwrap().content.len();
let selection = (*self.main_widget().as_ref().unwrap().lock()?).as_ref()?.get_selection();
let file_count = (*self.main_widget()?.lock()?).as_ref()?.content.len();
let file_count = format!("{}", file_count);
let digits = file_count.len();
let file_count = format!("{:digits$}/{:digits$}",
@ -442,42 +442,44 @@ impl Widget for FileBrowser {
file_count,
digits = digits);
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,
crate::term::goto_xy(count_xpos, count_ypos), file_count)
Ok(format!("{} {}:{} {} {} {}", permissions, user, group, mtime,
crate::term::goto_xy(count_xpos, count_ypos), file_count))
}
fn refresh(&mut self) {
self.handle_dir_events().ok();
self.columns.refresh();
fn refresh(&mut self) -> HResult<()> {
//self.proc_view.lock()?.set_coordinates(self.get_coordinates()?);
self.handle_dir_events()?;
self.columns.refresh().ok();
self.fix_left().ok();
self.fix_selection().ok();
self.set_cwd().ok();
self.update_watches().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() {
self.columns.get_clearlist() + &self.columns.get_drawlist()
Ok(self.columns.get_clearlist()? + &self.columns.get_drawlist()?)
} else {
self.columns.get_drawlist()
Ok(self.columns.get_drawlist()?)
}
}
fn on_key(&mut self, key: Key) -> HResult<()> {
match key {
Key::Char('/') => { self.turbo_cd().ok(); },
Key::Char('Q') => { self.quit_with_dir().ok(); },
Key::Right | Key::Char('f') => { self.enter_dir().ok(); },
Key::Left | Key::Char('b') => { self.go_back().ok(); },
Key::Char('/') => { self.turbo_cd()?; },
Key::Char('Q') => { self.quit_with_dir()?; },
Key::Right | Key::Char('f') => { self.enter_dir()?; },
Key::Left | Key::Char('b') => { self.go_back()?; },
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(())
}
}

View File

@ -2,17 +2,18 @@ use std::cmp::{Ord, Ordering};
use std::ops::Index;
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use lscolors::LsColors;
use mime_detective;
use users;
use chrono::TimeZone;
use failure::Error;
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
use notify::DebouncedEvent;
use crate::fail::{HResult, HError};
use std::sync::{Arc, Mutex};
lazy_static! {
@ -200,7 +201,7 @@ impl Files {
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
self.path_in_here(&path)?;
let file = self.find_file_with_path(&path)?;
file.reload_meta();
file.reload_meta()?;
},
DebouncedEvent::Remove(path) => {
self.path_in_here(&path)?;

View File

@ -1,20 +1,20 @@
use termion::event::{Event};
use crate::widget::Widget;
use crate::widget::{Widget, WidgetCore};
use crate::coordinates::{Coordinates, Size, Position};
use crate::fail::HResult;
use crate::fail::{HResult, ErrorLog};
#[derive(PartialEq)]
pub struct HBox<T: Widget> {
pub coordinates: Coordinates,
pub core: WidgetCore,
pub widgets: Vec<T>,
pub active: Option<usize>,
}
impl<T> HBox<T> where T: Widget + PartialEq {
pub fn new() -> HBox<T> {
HBox { coordinates: Coordinates::new(),
pub fn new(core: &WidgetCore) -> HBox<T> {
HBox { core: core.clone(),
widgets: vec![],
active: None
}
@ -27,34 +27,35 @@ impl<T> HBox<T> where T: Widget + PartialEq {
|w|
self.calculate_coordinates(w)).collect();
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 {
self.widgets.push(widget);
self.resize_children();
self.refresh();
self.refresh().log();
}
pub fn pop_widget(&mut self) -> Option<T> {
let widget = self.widgets.pop();
self.resize_children();
self.refresh();
self.refresh().log();
widget
}
pub fn prepend_widget(&mut self, widget: T) {
self.widgets.insert(0, widget);
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 {
let xsize = self.coordinates.xsize();
let ysize = self.coordinates.ysize();
let top = self.coordinates.top().y();
let coordinates = self.get_coordinates().unwrap();
let xsize = coordinates.xsize();
let ysize = coordinates.ysize();
let top = coordinates.top().y();
let pos = self.widgets.iter().position(|w | w == widget).unwrap();
let num = self.widgets.len();
@ -69,7 +70,7 @@ impl<T> HBox<T> where T: Widget + PartialEq {
top))
}
}
pub fn active_widget(&self) -> &T {
&self.widgets.last().unwrap()
}
@ -80,35 +81,32 @@ impl<T> 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()
}
fn refresh(&mut self) {
fn refresh(&mut self) -> HResult<()> {
self.resize_children();
for child in &mut self.widgets {
child.refresh();
child.refresh()?
}
Ok(())
}
fn get_drawlist(&self) -> String {
self.widgets.iter().map(|child| {
child.get_drawlist()
}).collect()
fn get_drawlist(&self) -> HResult<String> {
Ok(self.widgets.iter().map(|child| {
child.get_drawlist().unwrap()
}).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<()> {
self.widgets.last_mut()?.on_event(event).ok();
self.widgets.last_mut()?.on_event(event)?;
Ok(())
}
}

View File

@ -3,17 +3,16 @@ use unicode_width::UnicodeWidthStr;
use std::path::{Path, PathBuf};
use crate::coordinates::{Coordinates, Position, Size};
use crate::files::{File, Files};
use crate::fail::HResult;
use crate::fail::{HResult, ErrorLog};
use crate::term;
use crate::widget::{Widget};
use crate::widget::{Widget, WidgetCore};
pub trait Listable {
fn len(&self) -> usize;
fn render(&self) -> Vec<String>;
fn on_refresh(&mut self) {}
fn on_key(&mut self, _key: Key) {}
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
fn on_key(&mut self, _key: Key) -> HResult<()> { Ok(()) }
}
impl Listable for ListView<Files> {
@ -25,26 +24,27 @@ impl Listable for ListView<Files> {
self.render()
}
fn on_refresh(&mut self) {
let visible_file_num = self.selection + self.get_coordinates().ysize() as usize;
fn on_refresh(&mut self) -> HResult<()> {
let visible_file_num = self.selection + self.get_coordinates()?.ysize() as usize;
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 {
Key::Up | Key::Char('p') => {
self.move_up();
self.refresh();
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('P') => { for _ in 0..10 { self.move_up() } self.refresh()?; }
Key::Char('N') => { for _ in 0..10 { self.move_down() } self.refresh()?; }
Key::Down | Key::Char('n') => {
self.move_down();
self.refresh();
self.refresh()?;
},
Key::Ctrl('s') => { self.find_file().ok(); }
Key::Left => self.goto_grand_parent(),
Key::Right => self.goto_selected(),
Key::Left => self.goto_grand_parent()?,
Key::Right => self.goto_selected()?,
Key::Char(' ') => self.multi_select_file(),
Key::Char('h') => self.toggle_hidden(),
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_prev_mtime(),
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,
offset: usize,
buffer: Vec<String>,
coordinates: Coordinates,
core: WidgetCore,
seeking: bool,
}
@ -74,17 +75,14 @@ where
ListView<T>: Widget,
ListView<T>: Listable
{
pub fn new(content: T) -> ListView<T> {
pub fn new(core: &WidgetCore, content: T) -> ListView<T> {
let view = ListView::<T> {
content: content,
lines: 0,
selection: 0,
offset: 0,
buffer: Vec::new(),
coordinates: Coordinates {
size: Size((1, 1)),
position: Position((1, 1)),
},
core: core.clone(),
seeking: false
};
view
@ -104,7 +102,7 @@ where
}
pub fn move_down(&mut self) {
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 {
return;
@ -123,7 +121,7 @@ where
}
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;
while position + 2
@ -145,7 +143,7 @@ where
} 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 size_pos = xsize - (size.to_string().len() as u16
+ unit.to_string().len() as u16);
@ -202,30 +200,29 @@ impl ListView<Files>
self.selected_file().grand_parent()
}
pub fn goto_grand_parent(&mut self) {
pub fn goto_grand_parent(&mut self) -> HResult<()> {
match self.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();
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) {
Ok(files) => {
self.content = files;
self.selection = 0;
self.offset = 0;
self.refresh();
self.refresh()
}
Err(err) => {
self.show_status(&format!("Can't open this path: {}", err));
return;
self.show_status(&format!("Can't open this path: {}", err))
}
}
}
@ -245,8 +242,8 @@ impl ListView<Files>
self.content.cycle_sort();
self.content.sort();
self.select_file(&file);
self.refresh();
self.show_status(&format!("Sorting by: {}", self.content.sort));
self.refresh().log();
self.show_status(&format!("Sorting by: {}", self.content.sort)).log();
}
fn reverse_sort(&mut self) {
@ -254,8 +251,8 @@ impl ListView<Files>
self.content.reverse_sort();
self.content.sort();
self.select_file(&file);
self.refresh();
self.show_status(&format!("Reversed sorting by: {}", self.content.sort));
self.refresh().log();
self.show_status(&format!("Reversed sorting by: {}", self.content.sort)).log();
}
fn select_next_mtime(&mut self) {
@ -283,7 +280,7 @@ impl ListView<Files>
self.select_file(&file);
self.seeking = true;
self.refresh();
self.refresh().log();
}
fn select_prev_mtime(&mut self) {
@ -310,7 +307,7 @@ impl ListView<Files>
self.select_file(&file);
self.seeking = true;
self.refresh();
self.refresh().log();
}
fn toggle_hidden(&mut self) {
@ -318,7 +315,7 @@ impl ListView<Files>
self.content.toggle_hidden();
self.content.reload_files();
self.select_file(&file);
self.refresh();
self.refresh().log();
}
fn toggle_dirs_first(&mut self) {
@ -326,15 +323,16 @@ impl ListView<Files>
self.content.dirs_first = !self.content.dirs_first;
self.content.sort();
self.select_file(&file);
self.refresh();
self.show_status(&format!("Direcories first: {}", self.content.dirs_first));
self.refresh().log();
self.show_status(&format!("Direcories first: {}",
self.content.dirs_first)).log();
}
fn multi_select_file(&mut self) {
let file = self.selected_file_mut();
file.toggle_selection();
self.move_down();
self.refresh();
self.refresh().log();
}
fn find_file(&mut self) -> HResult<()> {
@ -352,7 +350,7 @@ impl ListView<Files>
}
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;
self.content
.files
@ -366,29 +364,26 @@ impl ListView<Files>
impl<T> Widget for ListView<T> where ListView<T>: Listable {
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
if self.coordinates == *coordinates {
return;
}
self.coordinates = coordinates.clone();
self.refresh();
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn refresh(&mut self) {
self.on_refresh();
fn refresh(&mut self) -> HResult<()> {
self.on_refresh().log();
self.lines = self.len();
if self.selection >= self.lines && self.selection != 0 {
self.selection -= 1;
}
self.buffer = self.render();
Ok(())
}
fn get_drawlist(&self) -> String {
fn get_drawlist(&self) -> HResult<String> {
let mut output = term::reset();
let (xpos, ypos) = self.coordinates.position().position();
let (xpos, ypos) = self.get_coordinates().unwrap().position().position();
output += &self
.buffer
@ -410,16 +405,15 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
})
.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 {
format!("{} files", self.len())
fn render_header(&self) -> HResult<String> {
Ok(format!("{} files", self.len()))
}
fn on_key(&mut self, key: Key) -> HResult<()> {
Listable::on_key(self, key);
Ok(())
Listable::on_key(self, key)
}
}

View File

@ -18,6 +18,8 @@ extern crate rayon;
extern crate libc;
extern crate notify;
use failure::Fail;
use termion::input::MouseTerminal;
use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
@ -34,7 +36,6 @@ mod term;
mod textview;
mod widget;
mod win_main;
mod window;
mod hbox;
mod tabview;
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());
// Need to do this here to actually turn terminal into raw mode...
let mut _screen = AlternateScreen::from(Box::new(bufout));
let mut _stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
let mut screen = AlternateScreen::from(bufout);
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 mut tabview = crate::tabview::TabView::new();
tabview.push_widget(filebrowser);
let filebrowser = FileBrowser::new_cored(&core)?;
let mut tabview = TabView::new(&core);
tabview.push_widget(filebrowser)?;
let mut win = Window::new(tabview);
win.draw();
win.handle_input();
tabview.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 failure::Backtrace;
use crate::coordinates::{Coordinates, Position, Size};
use crate::preview::Previewer;
use crate::widget::Widget;
use crate::widget::{Widget, WidgetCore};
use crate::hbox::HBox;
use crate::fail::{HError, HResult};
use crate::fail::{HError, HResult, ErrorLog};
#[derive(PartialEq)]
pub struct MillerColumns<T> where T: Widget {
pub widgets: HBox<T>,
pub core: WidgetCore,
// pub left: Option<T>,
// pub main: Option<T>,
//pub preview: AsyncPreviewer,
pub preview: Previewer,
pub ratio: (u16, u16, u16),
pub coordinates: Coordinates,
}
impl<T> MillerColumns<T>
where
T: Widget + PartialEq,
{
pub fn new() -> MillerColumns<T> {
pub fn new(core: &WidgetCore) -> MillerColumns<T> {
MillerColumns {
widgets: HBox::new(),
coordinates: Coordinates::new(),
widgets: HBox::new(core),
core: core.clone(),
ratio: (20, 30, 50),
preview: Previewer::new()
preview: Previewer::new(core)
}
}
pub fn push_widget(&mut self, widget: T) {
self.widgets.push_widget(widget);
self.refresh();
self.refresh().log();
}
pub fn pop_widget(&mut self) -> Option<T> {
let widget = self.widgets.pop_widget();
self.refresh();
self.refresh().log();
widget
}
@ -46,14 +47,15 @@ where
}
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates, Coordinates) {
let xsize = self.coordinates.xsize();
let ysize = self.coordinates.ysize();
let top = self.coordinates.top().y();
let coordinates = self.get_coordinates().unwrap();
let xsize = coordinates.xsize();
let ysize = coordinates.ysize();
let top = coordinates.top().y();
let ratio = self.ratio;
let left_xsize = xsize * ratio.0 / 100;
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_size = Size((main_xsize, ysize));
@ -84,7 +86,7 @@ where
pub fn get_left_widget(&self) -> HResult<&T> {
let len = self.widgets.widgets.len();
if len < 2 {
return Err(HError::NoWidgetError);
return Err(HError::NoWidgetError(Backtrace::new()));
}
let widget = self.widgets.widgets.get(len - 2)?;
Ok(widget)
@ -92,7 +94,7 @@ where
pub fn get_left_widget_mut(&mut self) -> HResult<&mut T> {
let len = self.widgets.widgets.len();
if len < 2 {
return Err(HError::NoWidgetError);
return Err(HError::NoWidgetError(Backtrace::new()));
}
let widget = self.widgets.widgets.get_mut(len - 2)?;
Ok(widget)
@ -112,51 +114,39 @@ where
T: Widget,
T: PartialEq
{
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
if self.coordinates == *coordinates {
return;
}
self.coordinates = coordinates.clone();
self.refresh();
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn render_header(&self) -> String {
"".to_string()
}
fn refresh(&mut self) {
fn refresh(&mut self) -> HResult<()> {
let (left_coords, main_coords, preview_coords) = self.calculate_coordinates();
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() {
main_widget.set_coordinates(&main_coords);
main_widget.set_coordinates(&main_coords).log();
}
let preview_widget = &mut self.preview;
preview_widget.set_coordinates(&preview_coords);
preview_widget.set_coordinates(&preview_coords)?;
Ok(())
}
fn get_drawlist(&self) -> String {
let left_widget = match self.get_left_widget() {
Ok(widget) => widget.get_drawlist(),
Err(_) => "".into(),
};
let main_widget = self.get_main_widget();
match main_widget {
Ok(main_widget) => {
let preview = self.preview.get_drawlist();
format!("{}{}{}", main_widget.get_drawlist(), left_widget, preview)
}
Err(_) => "".to_string()
}
fn get_drawlist(&self) -> HResult<String> {
let left_widget = self.get_left_widget()?;
let main_widget = self.get_main_widget()?;
let preview = self.preview.get_drawlist()?;
Ok(format!("{}{}{}",
main_widget.get_drawlist()?,
left_widget.get_drawlist()?,
preview))
}
fn on_key(&mut self, key: Key) -> HResult<()> {
self.get_main_widget_mut().unwrap().on_key(key);
Ok(())
self.get_main_widget_mut().unwrap().on_key(key)
}
}

View File

@ -2,12 +2,13 @@ use termion::event::Key;
use std::io::{stdout, Write};
use crate::coordinates::{Coordinates};
use crate::widget::Widget;
use crate::widget::{Widget, WidgetCore};
use crate::fail::{HResult, HError};
use crate::term;
#[derive(Debug)]
pub struct MiniBuffer {
coordinates: Coordinates,
core: WidgetCore,
query: String,
input: String,
position: usize,
@ -18,12 +19,14 @@ pub struct MiniBuffer {
}
impl MiniBuffer {
pub fn new() -> MiniBuffer {
pub fn new(core: &WidgetCore) -> MiniBuffer {
let xsize = crate::term::xsize();
let ysize = crate::term::ysize();
let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
let mut core = core.clone();
core.coordinates = coordinates;
MiniBuffer {
coordinates: coordinates,
core: core,
query: String::new(),
input: String::new(),
position: 0,
@ -283,26 +286,23 @@ pub fn find_files(comp_name: String) -> HResult<Vec<String>> {
}
impl Widget for MiniBuffer {
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
self.coordinates = coordinates.clone();
self.refresh();
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn render_header(&self) -> String {
"".to_string()
}
fn refresh(&mut self) {
fn refresh(&mut self) -> HResult<()> {
Ok(())
}
fn get_drawlist(&self) -> String {
let (xpos, ypos) = self.get_coordinates().u16position();
format!("{}{}{}: {}",
fn get_drawlist(&self) -> HResult<String> {
let (xpos, ypos) = self.get_coordinates()?.u16position();
Ok(format!("{}{}{}: {}",
crate::term::goto_xy(xpos, ypos),
termion::clear::CurrentLine,
self.query,
self.input)
self.input))
}
fn on_key(&mut self, key: Key) -> HResult<()> {

View File

@ -1,14 +1,15 @@
use std::sync::{Arc, Mutex};
use crate::coordinates::{Coordinates};
use failure::Backtrace;
use crate::files::{File, Files, Kind};
use crate::listview::ListView;
use crate::textview::TextView;
use crate::widget::Widget;
use crate::fail::HError;
use crate::widget::{Widget, WidgetCore};
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 WidgetO = Box<dyn Widget + Send>;
@ -63,8 +64,8 @@ impl<T: Send + 'static> WillBe<T> where {
let got_thing = closure(stale);
match got_thing {
Ok(got_thing) => {
*thing.try_lock().unwrap() = Some(got_thing);
*state.try_lock().unwrap() = State::Is;
*thing.lock().unwrap() = Some(got_thing);
*state.lock().unwrap() = State::Is;
match *on_ready_fn.lock().unwrap() {
Some(ref on_ready) => { on_ready(thing.clone()).ok(); },
None => {}
@ -76,14 +77,14 @@ impl<T: Send + 'static> WillBe<T> where {
}
pub fn set_stale(&mut self) -> HResult<()> {
*self.stale.try_lock()? = true;
*self.stale.lock()? = true;
Ok(())
}
pub fn check(&self) -> HResult<()> {
match *self.state.try_lock()? {
match *self.state.lock()? {
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() {
fun(self.thing.clone())?;
} else {
*self.on_ready.try_lock()? = Some(fun);
*self.on_ready.lock()? = Some(fun);
}
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 {
if self.coordinates == other.coordinates {
if self.get_coordinates().unwrap() ==
other.get_coordinates().unwrap() {
true
} else {
false
@ -111,19 +113,20 @@ impl<W: Widget + Send> PartialEq for WillBeWidget<W> {
pub struct WillBeWidget<T: Widget + Send> {
willbe: WillBe<T>,
coordinates: Coordinates
core: WidgetCore
}
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)));
willbe.on_ready(Box::new(|_| {
crate::window::send_event(crate::window::Events::WidgetReady)?;
willbe.on_ready(Box::new(move |_| {
sender.send(crate::widget::Events::WidgetReady)?;
Ok(()) })).ok();
WillBeWidget {
willbe: willbe,
coordinates: Coordinates::new()
core: core.clone()
}
}
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> {
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
self.coordinates = coordinates.clone();
{
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 get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn render_header(&self) -> String {
"".to_string()
fn refresh(&mut self) -> HResult<()> {
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) {
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 {
fn get_drawlist(&self) -> HResult<String> {
if self.willbe.check().is_err() {
let clear = self.get_clearlist();
let (xpos, ypos) = self.get_coordinates().u16position();
let clear = self.get_clearlist()?;
let (xpos, ypos) = self.get_coordinates()?.u16position();
let pos = crate::term::goto_xy(xpos, ypos);
return clear + &pos + "..."
return Ok(clear + &pos + "...")
}
let widget = self.widget().unwrap();
let widget = widget.try_lock().unwrap();
let widget = widget.as_ref().unwrap();
let widget = self.widget()?;
let widget = widget.lock()?;
let widget = widget.as_ref()?;
widget.get_drawlist()
}
fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
if self.willbe.check().is_err() { return Ok(()) }
let widget = self.widget().unwrap();
let mut widget = widget.try_lock().unwrap();
let widget = widget.as_mut().unwrap();
let widget = self.widget()?;
let mut widget = widget.lock()?;
let widget = widget.as_mut()?;
widget.on_key(key)
}
}
@ -197,7 +187,8 @@ impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
impl PartialEq for Previewer {
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
} else {
false
@ -207,59 +198,61 @@ impl PartialEq for Previewer {
pub struct Previewer {
widget: WillBeWidget<Box<dyn Widget + Send>>,
core: WidgetCore,
file: Option<File>
}
impl Previewer {
pub fn new() -> Previewer {
let willbe = WillBeWidget::new(Box::new(move |_| {
Ok(Box::new(crate::textview::TextView::new_blank())
pub fn new(core: &WidgetCore) -> Previewer {
let core_ = core.clone();
let willbe = WillBeWidget::new(&core, Box::new(move |_| {
Ok(Box::new(crate::textview::TextView::new_blank(&core_))
as Box<dyn Widget + Send>)
}));
Previewer { widget: willbe,
core: core.clone(),
file: None}
}
fn become_preview(&mut self,
widget: HResult<WillBeWidget<WidgetO>>) {
let coordinates = self.get_coordinates().clone();
let coordinates = self.get_coordinates().unwrap().clone();
self.widget = widget.unwrap();
self.set_coordinates(&coordinates);
self.widget.set_coordinates(&coordinates).ok();
}
pub fn set_file(&mut self, file: &File) {
if Some(file) == self.file.as_ref() { return }
self.file = Some(file.clone());
let coordinates = self.get_coordinates().clone();
let coordinates = self.get_coordinates().unwrap().clone();
let file = file.clone();
let core = self.core.clone();
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();
let file = file.clone();
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;
}
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,
&coordinates,
stale.clone());
let preview = Previewer::preview_external(&file, &core, stale.clone());
if preview.is_ok() { return preview; }
else {
let mut blank = Box::new(TextView::new_blank());
blank.set_coordinates(&coordinates);
blank.refresh();
blank.animate_slide_up();
let mut blank = Box::new(TextView::new_blank(&core));
blank.set_coordinates(&coordinates).log();
blank.refresh().log();
blank.animate_slide_up().log();
return Ok(blank)
}
}))));
@ -276,40 +269,41 @@ impl Previewer {
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> {
let files = Files::new_from_path_cancellable(&file.path,
stale.clone())?;
let len = files.len();
if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) }
let mut file_list = ListView::new(files);
file_list.set_coordinates(&coordinates);
file_list.refresh();
let mut file_list = ListView::new(&core, files);
file_list.set_coordinates(&core.coordinates)?;
file_list.refresh()?;
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>)
}
fn preview_text(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>)
fn preview_text(file: &File, core: &WidgetCore, stale: Arc<Mutex<bool>>)
-> HResult<WidgetO> {
let lines = coordinates.ysize() as usize;
let lines = core.coordinates.ysize() as usize;
let mut textview
= TextView::new_from_file_limit_lines(&file,
lines);
= TextView::new_from_file_limit_lines(&core,
&file,
lines)?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
textview.set_coordinates(&coordinates);
textview.refresh();
textview.set_coordinates(&core.coordinates)?;
textview.refresh()?;
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
textview.animate_slide_up();
textview.animate_slide_up()?;
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> {
let process =
std::process::Command::new("scope.sh")
@ -349,10 +343,10 @@ impl Previewer {
let mut textview = TextView {
lines: output.lines().map(|s| s.to_string()).collect(),
buffer: String::new(),
coordinates: Coordinates::new() };
textview.set_coordinates(&coordinates);
textview.refresh();
textview.animate_slide_up();
core: core.clone()};
textview.set_coordinates(&core.coordinates).log();
textview.refresh().log();
textview.animate_slide_up().log();
return Ok(Box::new(textview))
}
Err(HError::PreviewFailed{file: file.name.clone()})
@ -363,22 +357,16 @@ impl Previewer {
impl Widget for Previewer {
fn get_coordinates(&self) -> &Coordinates {
&self.widget.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
if self.widget.coordinates == *coordinates {
return;
}
self.widget.set_coordinates(coordinates);
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn render_header(&self) -> String {
"".to_string()
fn refresh(&mut self) -> HResult<()> {
self.widget.refresh()
}
fn refresh(&mut self) {
self.widget.refresh();
}
fn get_drawlist(&self) -> String {
fn get_drawlist(&self) -> HResult<String> {
self.widget.get_drawlist()
}
}
@ -538,23 +526,16 @@ impl Widget for Previewer {
impl<T> Widget for Box<T> where T: Widget + ?Sized {
fn get_coordinates(&self) -> &Coordinates {
(**self).get_coordinates()
fn get_core(&self) -> HResult<&WidgetCore> {
Ok((**self).get_core()?)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
if (**self).get_coordinates() == coordinates {
return;
}
(**self).set_coordinates(&coordinates);
(**self).refresh();
}
fn render_header(&self) -> String {
fn render_header(&self) -> HResult<String> {
(**self).render_header()
}
fn refresh(&mut self) {
fn refresh(&mut self) -> HResult<()> {
(**self).refresh()
}
fn get_drawlist(&self) -> String {
fn get_drawlist(&self) -> HResult<String> {
(**self).get_drawlist()
}
}

View File

@ -1,7 +1,6 @@
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use std::process::Child;
use std::process::Stdio;
use std::os::unix::io::FromRawFd;
use std::io::{BufRead, BufReader};
use termion::event::Key;
@ -10,10 +9,9 @@ use unicode_width::UnicodeWidthStr;
use crate::coordinates::{Coordinates, Size, Position};
use crate::listview::{Listable, ListView};
use crate::textview::TextView;
use crate::widget::Widget;
use crate::window::{send_event, Events};
use crate::widget::{Widget, Events, WidgetCore};
use crate::preview::WillBeWidget;
use crate::fail::{HResult, HError};
use crate::fail::{HResult, HError, ErrorLog};
use crate::term;
#[derive(Debug)]
@ -22,7 +20,9 @@ struct Process {
handle: Arc<Mutex<Child>>,
output: Arc<Mutex<String>>,
status: Arc<Mutex<Option<i32>>>,
success: Arc<Mutex<Option<bool>>>
success: Arc<Mutex<Option<bool>>>,
sender: Sender<Events>
}
impl Process {
@ -31,6 +31,7 @@ impl Process {
let output = self.output.clone();
let status = self.status.clone();
let success = self.success.clone();
let sender = self.sender.clone();
std::thread::spawn(move || {
let stdout = handle.lock().unwrap().stdout.take().unwrap();
@ -41,7 +42,7 @@ impl Process {
Ok(0) => break,
Ok(_) => {
output.lock().unwrap().push_str(&line);
send_event(Events::WidgetReady).unwrap();
sender.send(Events::WidgetReady).unwrap();
}
Err(err) => {
dbg!(err);
@ -63,7 +64,7 @@ impl Listable for ListView<Vec<Process>> {
fn len(&self) -> usize { self.content.len() }
fn render(&self) -> Vec<String> {
self.content.iter().map(|proc| {
self.render_proc(proc)
self.render_proc(proc).unwrap()
}).collect()
}
}
@ -75,14 +76,16 @@ impl ListView<Vec<Process>> {
.arg(cmd)
.stdin(std::process::Stdio::null())
.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()?;
let mut proc = Process {
cmd: cmd.to_string(),
handle: Arc::new(Mutex::new(handle)),
output: Arc::new(Mutex::new(String::new())),
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()?;
self.content.push(proc);
@ -107,13 +110,13 @@ impl ListView<Vec<Process>> {
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() {
Some(status) => format!("{}", status),
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 status_pos = xsize - status.len() as u16;
let padding = sized_string.len() - sized_string.width_cjk();
@ -124,7 +127,7 @@ impl ListView<Vec<Process>> {
_ => { status }
};
format!(
Ok(format!(
"{}{}{}{}{}{}",
termion::cursor::Save,
format!("{}{}{:padding$}{}",
@ -136,24 +139,25 @@ impl ListView<Vec<Process>> {
termion::cursor::Restore,
termion::cursor::Right(status_pos),
term::highlight_color(),
color_status
)
color_status))
}
}
pub struct ProcView {
coordinates: Coordinates,
core: WidgetCore,
proc_list: ListView<Vec<Process>>,
textview: Option<WillBeWidget<TextView>>,
textview: WillBeWidget<TextView>,
viewing: Option<usize>
}
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 {
coordinates: Coordinates::new(),
proc_list: ListView::new(vec![]),
textview: None,
core: core.clone(),
proc_list: ListView::new(&core, vec![]),
textview: WillBeWidget::new(&core, textview),
viewing: None
}
}
@ -166,16 +170,17 @@ impl ProcView {
pub fn remove_proc(&mut self) -> HResult<()> {
let (_, coords) = self.calculate_coordinates();
let coords2 = coords.clone();
let mut core = self.core.clone();
core.coordinates = coords;
self.proc_list.remove_proc()?;
self.textview = Some(WillBeWidget::new(Box::new(move |_| {
let mut textview = TextView::new_blank();
textview.set_coordinates(&coords);
textview.refresh();
textview.animate_slide_up();
self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| {
let mut textview = TextView::new_blank(&core);
textview.refresh().log();
textview.animate_slide_up().log();
Ok(textview)
})));
self.textview.as_mut().unwrap().set_coordinates(&coords2);
}));
self.textview.set_coordinates(&coords2).log();
Ok(())
}
@ -185,29 +190,29 @@ impl ProcView {
}
let output = self.proc_list.selected_proc()?.output.lock()?.clone();
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 |_| {
let mut textview = TextView::new_blank();
textview.set_coordinates(&coords);
textview.set_text(&output);
textview.animate_slide_up();
self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| {
let mut textview = TextView::new_blank(&core);
textview.set_text(&output).log();
textview.animate_slide_up().log();
Ok(textview)
})));
self.textview.as_mut().unwrap().set_coordinates(&coords2);
}));
self.viewing = Some(self.proc_list.get_selection());
Ok(())
}
pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates) {
let xsize = self.coordinates.xsize();
let ysize = self.coordinates.ysize();
let top = self.coordinates.top().y();
let coordinates = self.get_coordinates().unwrap();
let xsize = coordinates.xsize();
let ysize = coordinates.ysize();
let top = coordinates.top().y();
let ratio = (33, 66);
let left_xsize = xsize * ratio.0 / 100;
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_size = Size((main_xsize, ysize));
@ -224,7 +229,6 @@ impl ProcView {
size: main_size,
position: main_pos,
};
(left_coords, main_coords)
}
@ -232,36 +236,25 @@ impl ProcView {
}
impl Widget for ProcView {
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
self.coordinates = coordinates.clone();
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn refresh(&mut self) -> HResult<()> {
let (lcoord, rcoord) = self.calculate_coordinates();
self.proc_list.set_coordinates(&lcoord);
if let Some(textview) = &mut self.textview {
textview.set_coordinates(&rcoord);
}
self.proc_list.set_coordinates(&lcoord).log();
self.textview.set_coordinates(&rcoord).log();
self.refresh();
self.show_output().log();
self.proc_list.refresh().log();
self.textview.refresh().log();
Ok(())
}
fn render_header(&self) -> String {
"".to_string()
}
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 get_drawlist(&self) -> HResult<String> {
Ok(self.proc_list.get_drawlist()? + &self.textview.get_drawlist()?)
}
fn on_key(&mut self, key: Key) -> HResult<()> {
match key {
@ -270,18 +263,14 @@ impl Widget for ProcView {
Key::Char('k') => { self.proc_list.kill_proc()? }
Key::Up | Key::Char('p') => {
self.proc_list.move_up();
self.proc_list.refresh();
self.show_output().ok();
}
Key::Down | Key::Char('n') => {
self.proc_list.move_down();
self.proc_list.refresh();
self.show_output().ok();
}
_ => {}
}
self.refresh();
self.draw()?;
self.refresh().log();
self.draw().log();
Ok(())
}
}

View File

@ -1,21 +1,22 @@
use termion::event::Key;
use crate::coordinates::{Coordinates};
use crate::widget::Widget;
use crate::fail::HResult;
use crate::widget::{Widget, WidgetCore};
use crate::fail::{HResult, ErrorLog};
pub trait Tabbable {
fn new_tab(&mut self);
fn close_tab(&mut self);
fn next_tab(&mut self);
fn on_next_tab(&mut self);
fn new_tab(&mut self) -> HResult<()>;
fn close_tab(&mut self) -> HResult<()>;
fn next_tab(&mut self) -> HResult<()>;
fn on_next_tab(&mut self) -> HResult<()> {
Ok(())
}
fn get_tab_names(&self) -> Vec<Option<String>>;
fn active_tab(&self) -> &dyn Widget;
fn active_tab_mut(&mut self) -> &mut dyn Widget;
fn on_key_sub(&mut self, key: Key);
fn on_key(&mut self, key: Key) {
fn on_key_sub(&mut self, key: Key) -> HResult<()>;
fn on_key(&mut self, key: Key) -> HResult<()> {
match key {
Key::Ctrl('t') => { self.new_tab(); },
Key::Ctrl('t') => self.new_tab(),
Key::Ctrl('w') => self.close_tab(),
Key::Char('\t') => self.next_tab(),
_ => self.on_key_sub(key)
@ -28,27 +29,27 @@ pub trait Tabbable {
pub struct TabView<T> where T: Widget, TabView<T>: Tabbable {
pub widgets: Vec<T>,
pub active: usize,
coordinates: Coordinates
core: WidgetCore
}
impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
pub fn new() -> TabView<T> {
pub fn new(core: &WidgetCore) -> TabView<T> {
TabView {
widgets: vec![],
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.refresh();
self.refresh()
}
pub fn pop_widget(&mut self) -> Option<T> {
let widget = self.widgets.pop();
self.refresh();
widget
pub fn pop_widget(&mut self) -> HResult<T> {
let widget = self.widgets.pop()?;
self.refresh()?;
Ok(widget)
}
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]
}
pub fn close_tab_(&mut self) {
if self.active == 0 { return }
if self.active + 1 >= self.widgets.len() { self.active -= 1 }
self.pop_widget();
pub fn close_tab_(&mut self) -> HResult<()> {
self.pop_widget()?;
self.active -= 1;
Ok(())
}
pub fn next_tab_(&mut self) {
@ -72,14 +72,17 @@ impl<T> TabView<T> where T: Widget, TabView<T>: Tabbable {
} else {
self.active += 1
}
self.on_next_tab();
self.on_next_tab().log();
}
}
impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
fn render_header(&self) -> String {
let xsize = self.get_coordinates().xsize();
let header = self.active_tab_().render_header();
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
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 mut nums_length = 0;
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;
format!("{}{}{}{}",
Ok(format!("{}{}{}{}",
header,
crate::term::header_color(),
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()
}
fn refresh(&mut self) {
self.active_tab_mut().refresh();
fn refresh(&mut self) -> HResult<()> {
self.active_tab_mut().refresh()
}
fn get_drawlist(&self) -> String {
fn get_drawlist(&self) -> HResult<String> {
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<()> {
Tabbable::on_key(self, key);
Ok(())
Tabbable::on_key(self, key)?;
self.refresh()
}
}

View File

@ -1,20 +1,36 @@
use std::io::{Stdout, Write};
use std::io::{Stdout, Write, BufWriter};
use termion;
use termion::screen::AlternateScreen;
use crate::fail::HResult;
pub trait ScreenExt: Write {
fn cursor_hide(&mut self) {
write!(self, "{}", termion::cursor::Hide).unwrap();
fn cursor_hide(&mut self) -> HResult<()> {
write!(self, "{}", termion::cursor::Hide)?;
self.flush()?;
Ok(())
}
fn cursor_show(&mut self) {
write!(self, "{}", termion::cursor::Show).unwrap();
fn cursor_show(&mut self) -> HResult<()> {
write!(self, "{}", termion::cursor::Show)?;
self.flush()?;
Ok(())
}
fn reset(&mut self) {
write!(self, "{}", termion::style::Reset).unwrap();
fn reset(&mut self) -> HResult<()> {
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<Stdout> {}
impl ScreenExt for AlternateScreen<BufWriter<Stdout>> {}
pub fn xsize() -> u16 {
let (xsize, _) = termion::terminal_size().unwrap();

View File

@ -1,77 +1,79 @@
use std::io::BufRead;
use crate::coordinates::{Coordinates};
use crate::files::File;
use crate::term::sized_string;
use crate::widget::Widget;
use crate::widget::{Widget, WidgetCore};
use crate::fail::HResult;
#[derive(PartialEq)]
pub struct TextView {
pub lines: Vec<String>,
pub buffer: String,
pub coordinates: Coordinates,
pub core: WidgetCore
}
impl TextView {
pub fn new_blank() -> TextView {
pub fn new_blank(core: &WidgetCore) -> TextView {
TextView {
lines: vec![],
buffer: String::new(),
coordinates: Coordinates::new()
core: core.clone()
}
}
pub fn new_from_file(file: &File) -> TextView {
let file = std::fs::File::open(&file.path).unwrap();
pub fn new_from_file(core: &WidgetCore, file: &File) -> HResult<TextView> {
let file = std::fs::File::open(&file.path)?;
let file = std::io::BufReader::new(file);
let lines = file.lines().map(|line|
line.unwrap()
.replace("\t", " ")).collect();
Ok(line?
.replace("\t", " ")))
.filter_map(|l: HResult<String>| l.ok())
.collect();
TextView {
Ok(TextView {
lines: lines,
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::io::BufReader::new(file);
let lines = file.lines()
.take(num)
.map(|line|
line.unwrap()
.replace("\t", " ")).collect();
Ok(line?
.replace("\t", " ")))
.filter_map(|l: HResult<String>| l.ok())
.collect();
TextView {
Ok(TextView {
lines: lines,
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();
self.lines = lines;
self.refresh();
self.refresh()
}
}
impl Widget for TextView {
fn get_coordinates(&self) -> &Coordinates {
&self.coordinates
fn get_core(&self) -> HResult<&WidgetCore> {
Ok(&self.core)
}
fn set_coordinates(&mut self, coordinates: &Coordinates) {
self.coordinates = coordinates.clone();
self.refresh();
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Ok(&mut self.core)
}
fn render_header(&self) -> String {
"".to_string()
}
fn refresh(&mut self) {
let (xsize, ysize) = self.get_coordinates().size().size();
let (xpos, ypos) = self.get_coordinates().position().position();
fn refresh(&mut self) -> HResult<()> {
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
.lines
.iter()
@ -85,9 +87,10 @@ impl Widget for TextView {
sized_string(&line, xsize))
})
.collect::<String>();
Ok(())
}
fn get_drawlist(&self) -> String {
self.buffer.clone()
fn get_drawlist(&self) -> HResult<String> {
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::input::TermRead;
use termion::screen::AlternateScreen;
use failure::Backtrace;
use crate::coordinates::{Coordinates, Position, Size};
use crate::fail::{HResult, HError};
use crate::window::{send_event, Events};
use crate::fail::{HResult, HError, ErrorLog};
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 {
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 set_coordinates(&mut self, coordinates: &Coordinates);
fn render_header(&self) -> String;
fn render_footer(&self) -> String { "".into() }
fn refresh(&mut self);
fn get_drawlist(&self) -> String;
fn get_core(&self) -> HResult<&WidgetCore> {
Err(HError::NoWidgetCoreError(Backtrace::new()))
}
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
Err(HError::NoWidgetCoreError(Backtrace::new()))
}
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 on_event(&mut self, event: Event) -> HResult<()> {
match event {
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<()> {
match key {
_ => self.bad(Event::Key(key)),
_ => { self.bad(Event::Key(key)).unwrap() },
}
Ok(())
}
fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> {
match event {
_ => self.bad(Event::Mouse(event)),
_ => { self.bad(Event::Mouse(event)).unwrap() },
}
Ok(())
}
fn on_wtf(&mut self, event: Vec<u8>) -> HResult<()> {
match event {
_ => self.bad(Event::Unsupported(event)),
_ => { self.bad(Event::Unsupported(event)).unwrap() },
}
Ok(())
}
fn show_status(&self, status: &str) {
crate::window::show_status(status);
fn bad(&mut self, event: Event) -> HResult<()> {
self.show_status(&format!("Stop the nasty stuff!! {:?} does nothing!", event))
}
fn minibuffer(&self, query: &str) -> HResult<String> {
crate::window::minibuffer(query)
}
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!(
fn get_header_drawlist(&mut self) -> HResult<String> {
Ok(format!(
"{}{}{:xsize$}{}{}",
crate::term::goto_xy(1, 1),
crate::term::header_color(),
" ",
crate::term::goto_xy(1, 1),
self.render_header(),
xsize = self.get_coordinates().xsize() as usize
)
self.render_header()?,
xsize = self.get_coordinates()?.xsize() as usize
))
}
fn get_footer_drawlist(&mut self) -> String {
let xsize = self.get_coordinates().xsize();
fn get_footer_drawlist(&mut self) -> HResult<String> {
let xsize = self.get_coordinates()?.xsize();
let ypos = crate::term::ysize();
format!(
Ok(format!(
"{}{}{:xsize$}{}{}",
crate::term::goto_xy(1, ypos),
crate::term::header_color(),
" ",
crate::term::goto_xy(1, ypos),
self.render_footer(),
xsize = xsize as usize)
self.render_footer()?,
xsize = xsize as usize))
}
fn get_clearlist(&self) -> String {
let (xpos, ypos) = self.get_coordinates().u16position();
let (xsize, ysize) = self.get_coordinates().u16size();
fn get_clearlist(&self) -> HResult<String> {
let (xpos, ypos) = self.get_coordinates()?.u16position();
let (xsize, ysize) = self.get_coordinates()?.u16size();
(ypos..ysize + 2)
Ok((ypos..ysize + 2)
.map(|line| {
format!(
"{}{}{:xsize$}",
@ -104,15 +183,15 @@ pub trait Widget {
xsize = xsize as usize
)
})
.collect()
.collect())
}
fn get_redraw_empty_list(&self, lines: usize) -> String {
let (xpos, ypos) = self.get_coordinates().u16position();
let (xsize, ysize) = self.get_coordinates().u16size();
fn get_redraw_empty_list(&self, lines: usize) -> HResult<String> {
let (xpos, ypos) = self.get_coordinates()?.u16position();
let (xsize, ysize) = self.get_coordinates()?.u16size();
let start_y = lines + ypos as usize;
(start_y..(ysize + 2) as usize)
Ok((start_y..(ysize + 2) as usize)
.map(|i| {
format!(
"{}{:xsize$}",
@ -121,30 +200,21 @@ pub trait Widget {
xsize = xsize as usize
)
})
.collect()
}
fn draw(&self) -> HResult<()> {
let drawlist = self.get_drawlist();
let mut bufout = BufWriter::new(std::io::stdout());
write!(bufout, "{}", drawlist)?;
bufout.flush()?;
Ok(())
.collect())
}
fn popup(&mut self) -> HResult<()> {
self.run_widget();
send_event(Events::ExclusiveEvent(None))?;
self.run_widget().log();
self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?;
Ok(())
}
fn run_widget(&mut self) -> HResult<()> {
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.refresh();
self.refresh().log();
self.draw()?;
for event in rx_event.iter() {
@ -155,32 +225,31 @@ pub trait Widget {
}
}
Events::WidgetReady => {
self.refresh();
self.refresh().log();
}
_ => {}
}
self.draw();
self.after_draw();
self.draw().log();
self.after_draw().log();
}
Ok(())
}
fn clear(&self) -> HResult<()> {
let clearlist = self.get_clearlist();
write!(std::io::stdout(), "{}", clearlist)?;
std::io::stdout().flush()?;
Ok(())
let clearlist = self.get_clearlist()?;
self.write_to_screen(&clearlist)
}
fn animate_slide_up(&mut self) {
let coords = self.get_coordinates().clone();
fn animate_slide_up(&mut self) -> HResult<()> {
let coords = self.get_coordinates()?.clone();
let xpos = coords.position().x();
let ypos = coords.position().y();
let xsize = coords.xsize();
let ysize = coords.ysize();
let clear = self.get_clearlist();
let clear = self.get_clearlist()?;
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() {
let coords = Coordinates { size: Size((xsize,ysize-i)),
@ -188,13 +257,123 @@ pub trait Widget {
((xpos,
ypos+i))
};
self.set_coordinates(&coords);
let buffer = self.get_drawlist();
write!(bufout, "{}{}",
clear, buffer).unwrap();
bufout.flush().ok();
self.set_coordinates(&coords).log();
let buffer = self.get_drawlist()?;
self.write_to_screen(&buffer).log();
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::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, Receiver, channel};
// use std::io::{stdin, stdout, Stdout, Write};
// use std::sync::{Arc, Mutex};
// use std::sync::mpsc::{Sender, Receiver, channel};
use termion::event::Event;
use termion::input::TermRead;
use termion::screen::AlternateScreen;
// use termion::event::Event;
// use termion::input::TermRead;
// use termion::screen::AlternateScreen;
use crate::term;
use crate::term::ScreenExt;
// use crate::term;
// use crate::term::ScreenExt;
use crate::coordinates::{Coordinates, Position, Size};
use crate::widget::Widget;
use crate::minibuffer::MiniBuffer;
use crate::fail::HResult;
// use crate::coordinates::{Coordinates, Position, Size};
// use crate::widget::Widget;
// use crate::minibuffer::MiniBuffer;
// use crate::fail::HResult;
lazy_static! {
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
= Arc::new(Mutex::new(MiniBuffer::new()));
}
// lazy_static! {
// static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
// static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
// = Arc::new(Mutex::new(MiniBuffer::new()));
// }
#[derive(Debug)]
pub enum Events {
InputEvent(Event),
WidgetReady,
ExclusiveEvent(Option<Sender<Events>>),
}
// #[derive(Debug)]
// pub enum Events {
// InputEvent(Event),
// WidgetReady,
// ExclusiveEvent(Option<Sender<Events>>),
// }
pub struct Window<T>
where
T: Widget,
{
pub selection: usize,
pub widget: T,
pub status: Arc<Mutex<Option<String>>>,
pub screen: AlternateScreen<Box<Stdout>>,
pub coordinates: Coordinates,
}
// pub struct Window<T>
// where
// T: Widget,
// {
// pub selection: usize,
// pub widget: T,
// pub status: Arc<Mutex<Option<String>>>,
// pub screen: AlternateScreen<Box<Stdout>>,
// pub coordinates: Coordinates,
// }
impl<T> Window<T>
where
T: Widget,
{
pub fn new(widget: T) -> Window<T> {
let mut screen = AlternateScreen::from(Box::new(stdout()));
screen.cursor_hide();
let (xsize, ysize) = termion::terminal_size().unwrap();
let mut win = Window::<T> {
selection: 0,
widget: widget,
status: STATUS_BAR_CONTENT.clone(),
screen: screen,
coordinates: Coordinates {
size: Size((xsize, ysize)),
position: Position((1, 1)),
},
};
// impl<T> Window<T>
// where
// T: Widget,
// {
// pub fn new(widget: T) -> Window<T> {
// let mut screen = AlternateScreen::from(Box::new(stdout()));
// screen.cursor_hide();
// let (xsize, ysize) = termion::terminal_size().unwrap();
// let mut win = Window::<T> {
// selection: 0,
// widget: widget,
// status: STATUS_BAR_CONTENT.clone(),
// screen: screen,
// coordinates: Coordinates {
// size: Size((xsize, ysize)),
// position: Position((1, 1)),
// },
// };
win.widget.set_coordinates(&Coordinates {
size: Size((xsize, ysize - 2)),
position: Position((1, 2)),
});
win.widget.refresh();
win
}
// win.widget.set_coordinates(&Coordinates {
// size: Size((xsize, ysize - 2)),
// position: Position((1, 2)),
// });
// win.widget.refresh();
// win
// }
pub fn draw(&mut self) {
let output = self.widget.get_drawlist() + &self.widget.get_header_drawlist()
+ &self.widget.get_footer_drawlist();
self.screen.write(output.as_ref()).unwrap();
// pub fn draw(&mut self) {
// let output = self.widget.get_drawlist() + &self.widget.get_header_drawlist()
// + &self.widget.get_footer_drawlist();
// self.screen.write(output.as_ref()).unwrap();
self.screen.flush().unwrap();
}
// self.screen.flush().unwrap();
// }
// pub fn show_status(status: &str) {
// show_status(status);
// }
// // pub fn show_status(status: &str) {
// // show_status(status);
// // }
// pub fn draw_status() {
// draw_status();
// }
// // pub fn draw_status() {
// // draw_status();
// // }
// pub fn clear_status() {
// Self::show_status("");
// }
// // pub fn clear_status() {
// // Self::show_status("");
// // }
pub fn handle_input(&mut self) {
let (tx_event, rx_event) = channel();
let (tx_global_event, rx_global_event) = channel();
*TX_EVENT.try_lock().unwrap() = Some(tx_global_event);
let (tx_internal_event, rx_internal_event) = channel();
// pub fn handle_input(&mut self) {
// let (tx_event, rx_event) = channel();
// let (tx_global_event, rx_global_event) = channel();
// *TX_EVENT.try_lock().unwrap() = Some(tx_global_event);
// let (tx_internal_event, rx_internal_event) = channel();
input_thread(tx_event.clone());
global_event_thread(rx_global_event, tx_event.clone());
dispatch_events(rx_event, tx_internal_event);
// 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.widget.on_event(event);
self.screen.cursor_hide();
self.draw();
},
_ => {
self.widget.refresh();
self.draw();
},
}
}
}
}
// for event in rx_internal_event.iter() {
// match event {
// Events::InputEvent(event) => {
// self.widget.on_event(event);
// self.screen.cursor_hide();
// self.draw();
// },
// _ => {
// self.widget.refresh();
// self.draw();
// },
// }
// }
// }
// }
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 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 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();
}
});
}
// fn input_thread(tx: Sender<Events>) {
// std::thread::spawn(move || {
// for input in stdin().events() {
// let input = input.unwrap();
// tx.send(Events::InputEvent(input)).unwrap();
// }
// });
// }
pub fn send_event(event: Events) -> HResult<()> {
let tx = TX_EVENT.lock()?.clone()?.clone();
tx.send(event)?;
Ok(())
}
// pub fn send_event(event: Events) -> HResult<()> {
// let tx = TX_EVENT.lock()?.clone()?.clone();
// tx.send(event)?;
// Ok(())
// }
impl<T> Drop for Window<T>
where
T: Widget,
{
fn drop(&mut self) {
// When done, restore the defaults to avoid messing with the terminal.
self.screen
.write(
format!(
"{}{}{}{}{}",
termion::screen::ToMainScreen,
termion::clear::All,
termion::style::Reset,
termion::cursor::Show,
termion::cursor::Goto(1, 1)
)
.as_ref(),
)
.unwrap();
}
}
// impl<T> Drop for Window<T>
// where
// T: Widget,
// {
// fn drop(&mut self) {
// // When done, restore the defaults to avoid messing with the terminal.
// self.screen
// .write(
// format!(
// "{}{}{}{}{}",
// termion::screen::ToMainScreen,
// termion::clear::All,
// termion::style::Reset,
// termion::cursor::Show,
// termion::cursor::Goto(1, 1)
// )
// .as_ref(),
// )
// .unwrap();
// }
// }
lazy_static! {
static ref STATUS_BAR_CONTENT: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
}
// lazy_static! {
// static ref STATUS_BAR_CONTENT: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
// }
pub fn draw_status() {
let xsize = term::xsize() as u16;
let status = STATUS_BAR_CONTENT.try_lock().unwrap().clone();
// pub fn draw_status() {
// let xsize = term::xsize() as u16;
// let status = STATUS_BAR_CONTENT.try_lock().unwrap().clone();
status.or(Some("".to_string())).and_then(|status| {
write!(
stdout(),
"{}{}{:xsize$}{}{}",
term::move_bottom(),
term::status_bg(),
" ",
term::move_bottom(),
status,
xsize = xsize as usize
)
.ok()
});
stdout().flush().unwrap();
}
// status.or(Some("".to_string())).and_then(|status| {
// write!(
// stdout(),
// "{}{}{:xsize$}{}{}",
// term::move_bottom(),
// term::status_bg(),
// " ",
// term::move_bottom(),
// status,
// xsize = xsize as usize
// )
// .ok()
// });
// stdout().flush().unwrap();
// }
pub fn show_status(status: &str) {
{
let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
*status_content = Some(status.to_string());
}
draw_status();
}
// pub fn show_status(status: &str) {
// {
// let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
// *status_content = Some(status.to_string());
// }
// draw_status();
// }
pub fn minibuffer(query: &str) -> HResult<String> {
MINIBUFFER.lock()?.query(query)
}
// pub fn minibuffer(query: &str) -> HResult<String> {
// MINIBUFFER.lock()?.query(query)
// }