mirror of https://github.com/bobwen-dev/hunter
view exec'd processes status/output
This commit is contained in:
parent
fd67621dee
commit
06817602a8
|
@ -32,7 +32,9 @@ pub enum HError {
|
||||||
#[fail(display = "No widget found")]
|
#[fail(display = "No widget found")]
|
||||||
NoWidgetError,
|
NoWidgetError,
|
||||||
#[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")]
|
||||||
|
PopupFinnished,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for HError {
|
impl From<std::io::Error> for HError {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use crate::tabview::{TabView, Tabbable};
|
||||||
use crate::preview::WillBeWidget;
|
use crate::preview::WillBeWidget;
|
||||||
use crate::fail::{HResult, HError};
|
use crate::fail::{HResult, HError};
|
||||||
use crate::window::{Events, send_event};
|
use crate::window::{Events, send_event};
|
||||||
|
use crate::proclist::ProcView;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,12 +26,17 @@ pub struct FileBrowser {
|
||||||
pub cwd: File,
|
pub cwd: File,
|
||||||
watcher: INotifyWatcher,
|
watcher: INotifyWatcher,
|
||||||
watches: Vec<PathBuf>,
|
watches: Vec<PathBuf>,
|
||||||
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>
|
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
|
||||||
|
proc_view: Arc<Mutex<ProcView>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabbable for TabView<FileBrowser> {
|
impl Tabbable for TabView<FileBrowser> {
|
||||||
fn new_tab(&mut self) {
|
fn new_tab(&mut self) {
|
||||||
let tab = FileBrowser::new().unwrap();
|
let mut tab = FileBrowser::new().unwrap();
|
||||||
|
|
||||||
|
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;
|
self.active += 1;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +77,7 @@ impl Tabbable for TabView<FileBrowser> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.widgets[self.active].exec_cmd(tab_dirs).ok();
|
self.widgets[self.active].exec_cmd(tab_dirs).ok();
|
||||||
}
|
}
|
||||||
_ => self.active_tab_mut().on_key(key)
|
_ => { self.active_tab_mut().on_key(key).ok(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,11 +131,15 @@ impl FileBrowser {
|
||||||
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());
|
||||||
|
|
||||||
|
let mut proc_view = ProcView::new();
|
||||||
|
proc_view.set_coordinates(&coords);
|
||||||
|
|
||||||
Ok(FileBrowser { columns: miller,
|
Ok(FileBrowser { columns: miller,
|
||||||
cwd: cwd,
|
cwd: cwd,
|
||||||
watcher: watcher,
|
watcher: watcher,
|
||||||
watches: vec![],
|
watches: vec![],
|
||||||
dir_events: dir_events })
|
dir_events: dir_events,
|
||||||
|
proc_view: Arc::new(Mutex::new(proc_view)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter_dir(&mut self) -> HResult<()> {
|
pub fn enter_dir(&mut self) -> HResult<()> {
|
||||||
|
@ -250,7 +260,7 @@ impl FileBrowser {
|
||||||
self.watches.push(left_dir.path);
|
self.watches.push(left_dir.path);
|
||||||
}
|
}
|
||||||
if let Some(preview_dir) = preview_dir {
|
if let Some(preview_dir) = preview_dir {
|
||||||
if !watched_dirs.contains(&preview_dir) {
|
if !watched_dirs.contains(&preview_dir) && preview_dir.is_dir() {
|
||||||
self.watcher.watch(&preview_dir, RecursiveMode::NonRecursive).unwrap();
|
self.watcher.watch(&preview_dir, RecursiveMode::NonRecursive).unwrap();
|
||||||
self.watches.push(preview_dir);
|
self.watches.push(preview_dir);
|
||||||
}
|
}
|
||||||
|
@ -380,21 +390,8 @@ impl FileBrowser {
|
||||||
cmd = cmd.replace(&tab_identifier, &tab_path);
|
cmd = cmd.replace(&tab_identifier, &tab_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = std::process::Command::new("sh")
|
self.proc_view.lock()?.run_proc(&cmd)?;
|
||||||
.arg("-c")
|
|
||||||
.arg(&cmd)
|
|
||||||
.status();
|
|
||||||
let mut bufout = std::io::BufWriter::new(std::io::stdout());
|
|
||||||
write!(bufout, "{}{}",
|
|
||||||
termion::style::Reset,
|
|
||||||
termion::clear::All).unwrap();
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Ok(status) => self.show_status(&format!("\"{}\" exited with {}",
|
|
||||||
cmd, status)),
|
|
||||||
Err(err) => self.show_status(&format!("Can't run this \"{}\": {}",
|
|
||||||
cmd, err)),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,7 +401,8 @@ impl Widget for FileBrowser {
|
||||||
&self.columns.coordinates
|
&self.columns.coordinates
|
||||||
}
|
}
|
||||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||||
self.columns.coordinates = coordinates.clone();
|
self.columns.set_coordinates(coordinates);
|
||||||
|
self.proc_view.lock().unwrap().set_coordinates(coordinates);
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
fn render_header(&self) -> String {
|
fn render_header(&self) -> String {
|
||||||
|
@ -467,15 +465,20 @@ impl Widget for FileBrowser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Char('/') => { self.turbo_cd().ok(); },
|
Key::Char('/') => { self.turbo_cd().ok(); },
|
||||||
Key::Char('Q') => { self.quit_with_dir().ok(); },
|
Key::Char('Q') => { self.quit_with_dir().ok(); },
|
||||||
Key::Right | Key::Char('f') => { self.enter_dir().ok(); },
|
Key::Right | Key::Char('f') => { self.enter_dir().ok(); },
|
||||||
Key::Left | Key::Char('b') => { self.go_back().ok(); },
|
Key::Left | Key::Char('b') => { self.go_back().ok(); },
|
||||||
_ => self.columns.get_main_widget_mut().unwrap().on_key(key),
|
Key::Char('w') => {
|
||||||
|
self.proc_view.lock()?.popup().ok();
|
||||||
|
}
|
||||||
|
,
|
||||||
|
_ => { self.columns.get_main_widget_mut()?.on_key(key).ok(); },
|
||||||
}
|
}
|
||||||
self.update_preview().ok();
|
self.update_preview().ok();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use termion::event::{Event};
|
||||||
|
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
use crate::coordinates::{Coordinates, Size, Position};
|
use crate::coordinates::{Coordinates, Size, Position};
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct HBox<T: Widget> {
|
pub struct HBox<T: Widget> {
|
||||||
|
@ -106,7 +107,8 @@ impl<T> Widget for HBox<T> where T: Widget + PartialEq {
|
||||||
self.coordinates = coordinates.clone();
|
self.coordinates = coordinates.clone();
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
fn on_event(&mut self, event: Event) {
|
fn on_event(&mut self, event: Event) -> HResult<()> {
|
||||||
self.widgets.last_mut().unwrap().on_event(event);
|
self.widgets.last_mut()?.on_event(event).ok();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ where
|
||||||
view
|
view
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_up(&mut self) {
|
pub fn move_up(&mut self) {
|
||||||
if self.selection == 0 {
|
if self.selection == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -102,11 +102,11 @@ where
|
||||||
self.selection -= 1;
|
self.selection -= 1;
|
||||||
self.seeking = false;
|
self.seeking = false;
|
||||||
}
|
}
|
||||||
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.coordinates.ysize() as usize;
|
||||||
|
|
||||||
if self.selection == lines - 1 {
|
if self.lines == 0 || self.selection == lines - 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
self.on_refresh();
|
self.on_refresh();
|
||||||
self.lines = self.len();
|
self.lines = self.len();
|
||||||
if self.selection >= self.lines {
|
if self.selection >= self.lines && self.selection != 0 {
|
||||||
self.selection -= 1;
|
self.selection -= 1;
|
||||||
}
|
}
|
||||||
self.buffer = self.render();
|
self.buffer = self.render();
|
||||||
|
@ -418,7 +418,8 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
||||||
format!("{} files", self.len())
|
format!("{} files", self.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
Listable::on_key(self, key);
|
Listable::on_key(self, key);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,9 @@ mod tabview;
|
||||||
mod async_widget;
|
mod async_widget;
|
||||||
mod fail;
|
mod fail;
|
||||||
mod minibuffer;
|
mod minibuffer;
|
||||||
|
mod proclist;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use window::Window;
|
use window::Window;
|
||||||
|
|
|
@ -155,7 +155,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl Widget for MiniBuffer {
|
||||||
self.input)
|
self.input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
Key::Esc | Key::Ctrl('c') => { self.input.clear(); self.done = true; },
|
Key::Esc | Key::Ctrl('c') => { self.input.clear(); self.done = true; },
|
||||||
Key::Char('\n') => {
|
Key::Char('\n') => {
|
||||||
|
@ -205,7 +205,8 @@ impl Widget for MiniBuffer {
|
||||||
self.input.insert(self.position, key);
|
self.input.insert(self.position, key);
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => { }
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,12 +185,12 @@ impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
||||||
let widget = widget.as_ref().unwrap();
|
let widget = widget.as_ref().unwrap();
|
||||||
widget.get_drawlist()
|
widget.get_drawlist()
|
||||||
}
|
}
|
||||||
fn on_key(&mut self, key: termion::event::Key) {
|
fn on_key(&mut self, key: termion::event::Key) -> HResult<()> {
|
||||||
if self.willbe.check().is_err() { return }
|
if self.willbe.check().is_err() { return Ok(()) }
|
||||||
let widget = self.widget().unwrap();
|
let widget = self.widget().unwrap();
|
||||||
let mut widget = widget.try_lock().unwrap();
|
let mut widget = widget.try_lock().unwrap();
|
||||||
let widget = widget.as_mut().unwrap();
|
let widget = widget.as_mut().unwrap();
|
||||||
widget.on_key(key);
|
widget.on_key(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::process::Child;
|
||||||
|
use std::process::Stdio;
|
||||||
|
use std::os::unix::io::FromRawFd;
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
|
use termion::event::Key;
|
||||||
|
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::fail::{HResult, HError};
|
||||||
|
use crate::term;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Process {
|
||||||
|
cmd: String,
|
||||||
|
handle: Arc<Mutex<Child>>,
|
||||||
|
output: Arc<Mutex<String>>,
|
||||||
|
status: Arc<Mutex<Option<i32>>>,
|
||||||
|
success: Arc<Mutex<Option<bool>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
fn read_proc(&mut self) -> HResult<()> {
|
||||||
|
let handle = self.handle.clone();
|
||||||
|
let output = self.output.clone();
|
||||||
|
let status = self.status.clone();
|
||||||
|
let success = self.success.clone();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let stdout = handle.lock().unwrap().stdout.take().unwrap();
|
||||||
|
let mut stdout = BufReader::new(stdout);
|
||||||
|
loop {
|
||||||
|
let mut line = String::new();
|
||||||
|
match stdout.read_line(&mut line) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(_) => {
|
||||||
|
output.lock().unwrap().push_str(&line);
|
||||||
|
send_event(Events::WidgetReady).unwrap();
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
dbg!(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(proc_status) = handle.lock().unwrap().wait() {
|
||||||
|
*success.lock().unwrap() = Some(proc_status.success());
|
||||||
|
*status.lock().unwrap() = proc_status.code();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListView<Vec<Process>> {
|
||||||
|
fn run_proc(&mut self, cmd: &str) -> HResult<()> {
|
||||||
|
let handle = std::process::Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(cmd)
|
||||||
|
.stdin(std::process::Stdio::null())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(unsafe { Stdio::from_raw_fd(2) })
|
||||||
|
.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))
|
||||||
|
};
|
||||||
|
proc.read_proc()?;
|
||||||
|
self.content.push(proc);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_proc(&mut self) -> HResult<()> {
|
||||||
|
let proc = self.selected_proc()?;
|
||||||
|
proc.handle.lock()?.kill()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_proc(&mut self) -> HResult<()> {
|
||||||
|
self.kill_proc().ok();
|
||||||
|
let selection = self.get_selection();
|
||||||
|
self.content.remove(selection);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_proc(&mut self) -> Option<&mut Process> {
|
||||||
|
let selection = self.get_selection();
|
||||||
|
self.content.get_mut(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_proc(&self, proc: &Process) -> String {
|
||||||
|
let status = match *proc.status.lock().unwrap() {
|
||||||
|
Some(status) => format!("{}", status),
|
||||||
|
None => "<R>".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
let padding = xsize - padding as u16;
|
||||||
|
|
||||||
|
let color_status = match *proc.success.lock().unwrap() {
|
||||||
|
Some(false) => { format!("{}{}", term::color_red(), status) }
|
||||||
|
_ => { status }
|
||||||
|
};
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}{}{}{}{}{}",
|
||||||
|
termion::cursor::Save,
|
||||||
|
format!("{}{}{:padding$}{}",
|
||||||
|
term::normal_color(),
|
||||||
|
&sized_string,
|
||||||
|
" ",
|
||||||
|
term::normal_color(),
|
||||||
|
padding = padding as usize),
|
||||||
|
termion::cursor::Restore,
|
||||||
|
termion::cursor::Right(status_pos),
|
||||||
|
term::highlight_color(),
|
||||||
|
color_status
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcView {
|
||||||
|
coordinates: Coordinates,
|
||||||
|
proc_list: ListView<Vec<Process>>,
|
||||||
|
textview: TextView,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcView {
|
||||||
|
pub fn new() -> ProcView {
|
||||||
|
ProcView {
|
||||||
|
coordinates: Coordinates::new(),
|
||||||
|
proc_list: ListView::new(vec![]),
|
||||||
|
textview: TextView::new_blank(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_proc(&mut self, cmd: &str) -> HResult<()> {
|
||||||
|
self.proc_list.run_proc(cmd)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_proc(&mut self) -> HResult<()> {
|
||||||
|
self.proc_list.remove_proc()?;
|
||||||
|
self.textview.set_text("");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_output(&mut self) -> HResult<()> {
|
||||||
|
let output = self.proc_list.selected_proc()?.output.lock()?;
|
||||||
|
self.textview.set_text(&*output);
|
||||||
|
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 ratio = (33, 66);
|
||||||
|
|
||||||
|
let left_xsize = xsize * ratio.0 / 100;
|
||||||
|
let left_size = Size((left_xsize, ysize));
|
||||||
|
let left_pos = self.coordinates.top();
|
||||||
|
|
||||||
|
let main_xsize = xsize * ratio.1 / 100;
|
||||||
|
let main_size = Size((main_xsize, ysize));
|
||||||
|
let main_pos = Position((left_xsize + 2, top));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let left_coords = Coordinates {
|
||||||
|
size: left_size,
|
||||||
|
position: left_pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
let main_coords = Coordinates {
|
||||||
|
size: main_size,
|
||||||
|
position: main_pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
(left_coords, main_coords)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for ProcView {
|
||||||
|
fn get_coordinates(&self) -> &Coordinates {
|
||||||
|
&self.coordinates
|
||||||
|
}
|
||||||
|
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||||
|
self.coordinates = coordinates.clone();
|
||||||
|
|
||||||
|
let (lcoord, rcoord) = self.calculate_coordinates();
|
||||||
|
self.proc_list.set_coordinates(&lcoord);
|
||||||
|
self.textview.set_coordinates(&rcoord);
|
||||||
|
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
|
fn render_header(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
fn refresh(&mut self) {
|
||||||
|
self.show_output().ok();
|
||||||
|
self.proc_list.refresh();
|
||||||
|
self.textview.refresh();
|
||||||
|
}
|
||||||
|
fn get_drawlist(&self) -> String {
|
||||||
|
self.proc_list.get_drawlist() + &self.textview.get_drawlist()
|
||||||
|
}
|
||||||
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
|
match key {
|
||||||
|
Key::Char('w') => { return Err(HError::PopupFinnished) }
|
||||||
|
Key::Char('d') => { self.remove_proc()? }
|
||||||
|
Key::Char('k') => { self.proc_list.kill_proc()? }
|
||||||
|
Key::Up | Key::Char('p') => {
|
||||||
|
self.proc_list.move_up();
|
||||||
|
self.proc_list.refresh();
|
||||||
|
}
|
||||||
|
Key::Down | Key::Char('n') => {
|
||||||
|
self.proc_list.move_down();
|
||||||
|
self.proc_list.refresh();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
self.refresh();
|
||||||
|
self.draw()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ use termion::event::Key;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates};
|
use crate::coordinates::{Coordinates};
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
pub trait Tabbable {
|
pub trait Tabbable {
|
||||||
fn new_tab(&mut self);
|
fn new_tab(&mut self);
|
||||||
|
@ -129,7 +130,8 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
||||||
self.refresh();
|
self.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
Tabbable::on_key(self, key);
|
Tabbable::on_key(self, key);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,12 @@ impl TextView {
|
||||||
coordinates: Coordinates::new(),
|
coordinates: Coordinates::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_text(&mut self, text: &str) {
|
||||||
|
let lines = text.lines().map(|l| l.to_string()).collect();
|
||||||
|
self.lines = lines;
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for TextView {
|
impl Widget for TextView {
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
use termion::event::{Event, Key, MouseEvent};
|
use termion::event::{Event, Key, MouseEvent};
|
||||||
|
use termion::input::TermRead;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
use crate::fail::HResult;
|
use crate::fail::{HResult, HError};
|
||||||
|
use crate::window::{send_event, Events};
|
||||||
|
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write, stdin};
|
||||||
|
|
||||||
|
|
||||||
pub trait Widget {
|
pub trait Widget {
|
||||||
|
@ -18,7 +22,7 @@ pub trait Widget {
|
||||||
fn get_drawlist(&self) -> String;
|
fn get_drawlist(&self) -> String;
|
||||||
|
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) {
|
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!"),
|
||||||
Event::Key(key) => self.on_key(key),
|
Event::Key(key) => self.on_key(key),
|
||||||
|
@ -27,22 +31,25 @@ pub trait Widget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_key(&mut self, key: Key) {
|
fn on_key(&mut self, key: Key) -> HResult<()> {
|
||||||
match key {
|
match key {
|
||||||
_ => self.bad(Event::Key(key)),
|
_ => self.bad(Event::Key(key)),
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse(&mut self, event: MouseEvent) {
|
fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
_ => self.bad(Event::Mouse(event)),
|
_ => self.bad(Event::Mouse(event)),
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_wtf(&mut self, event: Vec<u8>) {
|
fn on_wtf(&mut self, event: Vec<u8>) -> HResult<()> {
|
||||||
match event {
|
match event {
|
||||||
_ => self.bad(Event::Unsupported(event)),
|
_ => self.bad(Event::Unsupported(event)),
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_status(&self, status: &str) {
|
fn show_status(&self, status: &str) {
|
||||||
|
@ -125,6 +132,49 @@ pub trait Widget {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn popup(&mut self) -> HResult<()> {
|
||||||
|
self.run_widget();
|
||||||
|
send_event(Events::ExclusiveEvent(None));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_widget(&mut self) -> HResult<()> {
|
||||||
|
let (tx_event, rx_event) = channel();
|
||||||
|
send_event(Events::ExclusiveEvent(Some(tx_event)))?;
|
||||||
|
dbg!("sent exclusive request");
|
||||||
|
|
||||||
|
self.clear()?;
|
||||||
|
self.refresh();
|
||||||
|
self.draw()?;
|
||||||
|
|
||||||
|
dbg!("entering loop");
|
||||||
|
|
||||||
|
for event in rx_event.iter() {
|
||||||
|
dbg!(&event);
|
||||||
|
match event {
|
||||||
|
Events::InputEvent(input) => {
|
||||||
|
if let Err(HError::PopupFinnished) = self.on_event(input) {
|
||||||
|
return Err(HError::PopupFinnished)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Events::WidgetReady => {
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.draw()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&self) -> HResult<()> {
|
||||||
|
let clearlist = self.get_clearlist();
|
||||||
|
write!(std::io::stdout(), "{}", clearlist)?;
|
||||||
|
std::io::stdout().flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn animate_slide_up(&mut self) {
|
fn animate_slide_up(&mut self) {
|
||||||
let coords = self.get_coordinates().clone();
|
let coords = self.get_coordinates().clone();
|
||||||
let xpos = coords.position().x();
|
let xpos = coords.position().x();
|
||||||
|
|
|
@ -20,11 +20,12 @@ lazy_static! {
|
||||||
= Arc::new(Mutex::new(MiniBuffer::new()));
|
= Arc::new(Mutex::new(MiniBuffer::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Events {
|
pub enum Events {
|
||||||
InputEvent(Event),
|
InputEvent(Event),
|
||||||
WidgetReady,
|
WidgetReady,
|
||||||
ExclusiveInput(bool),
|
ExclusiveInput(bool),
|
||||||
|
ExclusiveEvent(Option<Sender<Events>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window<T>
|
pub struct Window<T>
|
||||||
|
@ -87,32 +88,22 @@ where
|
||||||
|
|
||||||
|
|
||||||
pub fn handle_input(&mut self) {
|
pub fn handle_input(&mut self) {
|
||||||
let (tx_event_internal, rx_event_internal) = channel();
|
|
||||||
let (tx_event, rx_event) = channel();
|
let (tx_event, rx_event) = channel();
|
||||||
*TX_EVENT.try_lock().unwrap() = Some(tx_event);
|
let (tx_global_event, rx_global_event) = channel();
|
||||||
let (tx_request_input, rx_request_input) = channel();
|
*TX_EVENT.try_lock().unwrap() = Some(tx_global_event);
|
||||||
|
let (tx_internal_event, rx_internal_event) = channel();
|
||||||
|
|
||||||
let mut exclusive_mode = false;
|
input_thread(tx_event.clone());
|
||||||
|
global_event_thread(rx_global_event, tx_event.clone());
|
||||||
|
dispatch_events(rx_event, tx_internal_event);
|
||||||
|
|
||||||
event_thread(rx_event, tx_event_internal.clone());
|
for event in rx_internal_event.iter() {
|
||||||
input_thread(tx_event_internal.clone(), rx_request_input);
|
|
||||||
tx_request_input.send(()).unwrap();
|
|
||||||
|
|
||||||
for event in rx_event_internal.iter() {
|
|
||||||
//Self::clear_status();
|
|
||||||
//let event = event.unwrap();
|
|
||||||
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();
|
||||||
if !exclusive_mode {
|
|
||||||
tx_request_input.send(()).unwrap();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Events::ExclusiveInput(setting) => {
|
|
||||||
exclusive_mode = setting
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
self.widget.refresh();
|
self.widget.refresh();
|
||||||
self.draw();
|
self.draw();
|
||||||
|
@ -122,23 +113,39 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_thread(rx: Receiver<Events>,
|
fn dispatch_events(rx: Receiver<Events>, tx: Sender<Events>) {
|
||||||
tx: Sender<Events>) {
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
let mut tx_exclusive_event: Option<Sender<Events>> = None;
|
||||||
for event in rx.iter() {
|
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();
|
tx.send(event).unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_thread(tx: Sender<Events>, request_input: Receiver<()>) {
|
fn input_thread(tx: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
for _ in request_input.iter() {
|
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();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue