mirror of https://github.com/bobwen-dev/hunter
handle terminal resizing
This commit is contained in:
parent
c719ec7a3c
commit
161ba5ac3f
|
@ -21,6 +21,7 @@ failure = "0.1.5"
|
|||
failure_derive = "0.1.1"
|
||||
notify = "4.0.9"
|
||||
parse-ansi = "0.1.6"
|
||||
signal-notify = "0.1.3"
|
||||
|
||||
#[profile.release]
|
||||
#debug = true
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
use crate::widget::{Widget, WidgetCore};
|
||||
use crate::coordinates::Coordinates;
|
||||
use crate::files::{Files, File};
|
||||
use crate::term;
|
||||
|
||||
|
@ -68,19 +69,25 @@ pub struct BMPopup {
|
|||
|
||||
impl BMPopup {
|
||||
pub fn new(core: &WidgetCore) -> BMPopup {
|
||||
let bmpopup = BMPopup {
|
||||
let mut bmpopup = BMPopup {
|
||||
core: core.clone(),
|
||||
bookmarks: Bookmarks::new(),
|
||||
bookmark_path: None,
|
||||
add_mode: false
|
||||
};
|
||||
bmpopup.set_coordinates(&core.coordinates).log();
|
||||
bmpopup
|
||||
}
|
||||
|
||||
pub fn pick(&mut self, cwd: String) -> HResult<String> {
|
||||
self.bookmark_path = Some(cwd);
|
||||
self.refresh()?;
|
||||
self.popup()?;
|
||||
match self.popup() {
|
||||
Ok(_) => {},
|
||||
Err(HError::PopupFinnished) => {},
|
||||
err @ Err(HError::TerminalResizedError) => err?,
|
||||
err @ Err(_) => err?,
|
||||
}
|
||||
self.clear()?;
|
||||
|
||||
let bookmark = self.bookmark_path.take();
|
||||
|
@ -120,16 +127,24 @@ impl Widget for BMPopup {
|
|||
Ok(&mut self.core)
|
||||
}
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
let tysize = crate::term::ysize();
|
||||
let txsize = crate::term::xsize();
|
||||
let len = self.bookmarks.mapping.len() as u16;
|
||||
let ysize = tysize - (len + 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
self.core.coordinates.set_position(1, ysize);
|
||||
self.core.coordinates.set_size(txsize, len+1);
|
||||
fn resize(&mut self) -> HResult<()> {
|
||||
HError::terminal_resized()
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
let (xsize, ysize) = coordinates.size_u();
|
||||
let len = self.bookmarks.mapping.len();
|
||||
let ysize = ysize.saturating_sub( len + 1 );
|
||||
|
||||
self.core.coordinates.set_size_u(xsize.saturating_sub(1), len);
|
||||
self.core.coordinates.set_position_u(1, ysize+2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_drawlist(&self) -> HResult<String> {
|
||||
let ypos = self.get_coordinates()?.ypos();
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ impl Coordinates {
|
|||
self.size.0 = (x, y);
|
||||
}
|
||||
|
||||
pub fn set_size_u(&mut self, x: usize, y: usize) {
|
||||
self.size.0 = ((x+1) as u16, (y+1) as u16);
|
||||
}
|
||||
|
||||
pub fn set_xsize(&mut self, x: u16) {
|
||||
(self.size.0).0 = x;
|
||||
}
|
||||
|
@ -44,6 +48,10 @@ impl Coordinates {
|
|||
self.position.0 = (x, y);
|
||||
}
|
||||
|
||||
pub fn set_position_u(&mut self, x: usize, y: usize) {
|
||||
self.position.0 = ((x+1) as u16, (y+1) as u16);
|
||||
}
|
||||
|
||||
pub fn set_xpos(&mut self, x: u16) {
|
||||
(self.position.0).0 = x;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,9 @@ pub enum HError {
|
|||
#[fail(display = "Empty input!")]
|
||||
MiniBufferEmptyInput,
|
||||
#[fail(display = "Undefined key: {:?}", key)]
|
||||
WidgetUndefinedKeyError{key: Key}
|
||||
WidgetUndefinedKeyError{key: Key},
|
||||
#[fail(display = "Terminal has been resized!")]
|
||||
TerminalResizedError,
|
||||
}
|
||||
|
||||
impl HError {
|
||||
|
@ -109,6 +111,10 @@ impl HError {
|
|||
Err(HError::PreviewFailed{ file: name,
|
||||
backtrace: Backtrace::new() })
|
||||
}
|
||||
|
||||
pub fn terminal_resized<T>() -> HResult<T> {
|
||||
Err(HError::TerminalResizedError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::proclist::ProcView;
|
|||
use crate::bookmarks::BMPopup;
|
||||
use crate::term::ScreenExt;
|
||||
use crate::foldview::LogView;
|
||||
use crate::coordinates::Coordinates;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum FileBrowserWidgets {
|
||||
|
@ -40,6 +41,12 @@ impl Widget for FileBrowserWidgets {
|
|||
FileBrowserWidgets::Previewer(widget) => widget.get_core_mut()
|
||||
}
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
match self {
|
||||
FileBrowserWidgets::FileList(widget) => widget.set_coordinates(coordinates),
|
||||
FileBrowserWidgets::Previewer(widget) => widget.set_coordinates(coordinates),
|
||||
}
|
||||
}
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
match self {
|
||||
FileBrowserWidgets::FileList(widget) => widget.refresh(),
|
||||
|
@ -335,13 +342,28 @@ impl FileBrowser {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn goto_bookmark(&mut self) -> HResult<()> {
|
||||
let cwd = match self.prev_cwd.as_ref() {
|
||||
fn get_boomark(&mut self) -> HResult<String> {
|
||||
let cwd = &match self.prev_cwd.as_ref() {
|
||||
Some(cwd) => cwd,
|
||||
None => &self.cwd
|
||||
}.path.to_string_lossy().to_string();
|
||||
|
||||
let path = self.bookmarks.lock()?.pick(cwd)?;
|
||||
loop {
|
||||
let bookmark = self.bookmarks.lock()?.pick(cwd.to_string());
|
||||
|
||||
if let Err(HError::TerminalResizedError) = bookmark {
|
||||
self.core.screen.clear();
|
||||
self.resize();
|
||||
self.refresh();
|
||||
self.draw();
|
||||
continue;
|
||||
}
|
||||
return bookmark;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn goto_bookmark(&mut self) -> HResult<()> {
|
||||
let path = self.get_boomark()?;
|
||||
let path = File::new_from_path(&PathBuf::from(path))?;
|
||||
self.main_widget_goto(&path)?;
|
||||
Ok(())
|
||||
|
@ -727,6 +749,16 @@ impl Widget for FileBrowser {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
self.columns.set_coordinates(&coordinates).log();
|
||||
self.proc_view.lock()?.set_coordinates(&coordinates).log();
|
||||
self.log_view.lock()?.set_coordinates(&coordinates).log();
|
||||
self.bookmarks.lock()?.set_coordinates(&coordinates).log();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
let xsize = self.get_coordinates()?.xsize();
|
||||
let file = self.selected_file()?;
|
||||
|
|
|
@ -5,7 +5,7 @@ use chrono::{DateTime, Local};
|
|||
use crate::term;
|
||||
use crate::widget::Widget;
|
||||
use crate::listview::{ListView, Listable};
|
||||
use crate::fail::{HResult, HError};
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
use crate::dirty::Dirtyable;
|
||||
|
||||
pub type LogView = ListView<Vec<LogEntry>>;
|
||||
|
@ -79,6 +79,8 @@ impl From<&HError> for LogEntry {
|
|||
|
||||
pub trait FoldableWidgetExt {
|
||||
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
|
||||
fn render_header(&self) -> HResult<String> { Ok("".to_string()) }
|
||||
fn render_footer(&self) -> HResult<String> { Ok("".to_string()) }
|
||||
}
|
||||
|
||||
impl FoldableWidgetExt for ListView<Vec<LogEntry>> {
|
||||
|
@ -88,6 +90,43 @@ impl FoldableWidgetExt for ListView<Vec<LogEntry>> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
let (xsize, _) = self.core.coordinates.size_u();
|
||||
let current = self.current_fold().unwrap_or(0);
|
||||
let num = self.content.len();
|
||||
let hint = format!("{} / {}", current, num);
|
||||
let hint_xpos = xsize - hint.len();
|
||||
let header = format!("Logged entries: {}{}{}",
|
||||
num,
|
||||
term::goto_xy_u(hint_xpos, 0),
|
||||
hint);
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn render_footer(&self) -> HResult<String> {
|
||||
let current = self.current_fold()?;
|
||||
if let Some(logentry) = self.content.get(current) {
|
||||
let (xsize, ysize) = self.core.coordinates.size_u();
|
||||
let (_, ypos) = self.core.coordinates.position_u();
|
||||
let description = logentry.description();
|
||||
let lines = logentry.lines();
|
||||
let start_pos = self.fold_start_pos(current);
|
||||
let selection = self.get_selection();
|
||||
let current_line = (selection - start_pos) + 1;
|
||||
let line_hint = format!("{} / {}", current_line, lines);
|
||||
let hint_xpos = xsize - line_hint.len();
|
||||
let hint_ypos = ysize + ypos + 1;
|
||||
|
||||
let footer = format!("LogEntry: {}{}{}{}{}",
|
||||
description,
|
||||
term::reset(),
|
||||
term::status_bg(),
|
||||
term::goto_xy_u(hint_xpos, hint_ypos),
|
||||
line_hint);
|
||||
Ok(footer)
|
||||
} else { Ok("No log entries".to_string()) }
|
||||
}
|
||||
}
|
||||
|
||||
trait LogList {
|
||||
|
@ -151,7 +190,7 @@ where
|
|||
ListView<Vec<F>>: FoldableWidgetExt {
|
||||
|
||||
fn toggle_fold(&mut self) -> HResult<()> {
|
||||
let fold = self.current_fold();
|
||||
let fold = self.current_fold()?;
|
||||
let fold_pos = self.fold_start_pos(fold);
|
||||
|
||||
self.content[fold].toggle_fold();
|
||||
|
@ -173,7 +212,7 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn current_fold(&self) -> usize {
|
||||
fn current_fold(&self) -> Option<usize> {
|
||||
let pos = self.get_selection();
|
||||
|
||||
let fold_lines = self
|
||||
|
@ -194,7 +233,7 @@ where
|
|||
} else {
|
||||
(lines + current_fold_lines, None)
|
||||
}
|
||||
}}).1.unwrap()
|
||||
}}).1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,6 +259,15 @@ where
|
|||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
FoldableWidgetExt::render_header(self)
|
||||
}
|
||||
|
||||
fn render_footer(&self) -> HResult<String> {
|
||||
FoldableWidgetExt::render_footer(self)
|
||||
}
|
||||
|
||||
fn on_refresh(&mut self) -> HResult<()> {
|
||||
FoldableWidgetExt::on_refresh(self)
|
||||
}
|
||||
|
|
|
@ -118,6 +118,12 @@ impl<T> Widget for HBox<T> where T: Widget + PartialEq {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
self.resize_children()
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
self.active_widget().render_header()
|
||||
}
|
||||
|
@ -125,7 +131,7 @@ impl<T> Widget for HBox<T> where T: Widget + PartialEq {
|
|||
fn refresh(&mut self) -> HResult<()> {
|
||||
self.resize_children().log();
|
||||
for child in &mut self.widgets {
|
||||
child.refresh()?
|
||||
child.refresh().log();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ use crate::dirty::Dirtyable;
|
|||
pub trait Listable {
|
||||
fn len(&self) -> usize;
|
||||
fn render(&self) -> Vec<String>;
|
||||
fn render_header(&self) -> HResult<String> { Ok("".to_string()) }
|
||||
fn render_footer(&self) -> HResult<String> { Ok("".to_string()) }
|
||||
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
|
||||
fn on_key(&mut self, _key: Key) -> HResult<()> { Ok(()) }
|
||||
}
|
||||
|
@ -304,6 +306,7 @@ impl ListView<Files>
|
|||
fn toggle_tag(&mut self) -> HResult<()> {
|
||||
self.selected_file_mut().toggle_tag()?;
|
||||
self.move_down();
|
||||
self.core.set_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -412,6 +415,13 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
Listable::render_header(self)
|
||||
}
|
||||
|
||||
fn render_footer(&self) -> HResult<String> {
|
||||
Listable::render_footer(self)
|
||||
}
|
||||
|
||||
fn get_drawlist(&self) -> HResult<String> {
|
||||
let mut output = term::reset();
|
||||
|
|
|
@ -18,6 +18,7 @@ extern crate rayon;
|
|||
extern crate libc;
|
||||
extern crate notify;
|
||||
extern crate parse_ansi;
|
||||
extern crate signal_notify;
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
|
|
|
@ -93,9 +93,14 @@ where
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
self.widgets.set_coordinates(&coordinates)
|
||||
}
|
||||
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
self.widgets.refresh().log();
|
||||
Ok(())
|
||||
self.widgets.refresh()
|
||||
}
|
||||
|
||||
fn get_drawlist(&self) -> HResult<String> {
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::files::{File, Files, Kind};
|
|||
use crate::listview::ListView;
|
||||
use crate::textview::TextView;
|
||||
use crate::widget::{Widget, WidgetCore};
|
||||
use crate::coordinates::Coordinates;
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
|
||||
|
||||
|
@ -189,12 +190,29 @@ impl<T: Widget + Send + 'static> Widget for WillBeWidget<T> {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
if let Ok(widget) = self.widget() {
|
||||
let mut widget = widget.lock()?;
|
||||
let widget = widget.as_mut()?;
|
||||
widget.set_coordinates(self.get_coordinates()?).log();
|
||||
widget.refresh().log();
|
||||
widget.set_coordinates(&coordinates)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
let coords = self.get_coordinates()?;
|
||||
if let Ok(widget) = self.widget() {
|
||||
let mut widget = widget.lock()?;
|
||||
let widget = widget.as_mut()?;
|
||||
|
||||
if widget.get_coordinates()? != coords {
|
||||
widget.set_coordinates(self.get_coordinates()?)?;
|
||||
widget.refresh()?;
|
||||
} else {
|
||||
widget.refresh()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -427,6 +445,12 @@ impl Widget for Previewer {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
self.widget.set_coordinates(&coordinates)
|
||||
}
|
||||
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
self.widget.refresh()
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use crate::listview::{Listable, ListView};
|
||||
use crate::textview::TextView;
|
||||
use crate::widget::{Widget, Events, WidgetCore};
|
||||
use crate::coordinates::Coordinates;
|
||||
use crate::hbox::HBox;
|
||||
use crate::preview::WillBeWidget;
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
|
@ -227,7 +228,13 @@ pub struct ProcView {
|
|||
}
|
||||
|
||||
impl HBox<ProcViewWidgets> {
|
||||
fn get_listview(&mut self) -> &mut ListView<Vec<Process>> {
|
||||
fn get_listview(&self) -> &ListView<Vec<Process>> {
|
||||
match &self.widgets[0] {
|
||||
ProcViewWidgets::List(listview) => listview,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
fn get_listview_mut(&mut self) -> &mut ListView<Vec<Process>> {
|
||||
match &mut self.widgets[0] {
|
||||
ProcViewWidgets::List(listview) => listview,
|
||||
_ => unreachable!()
|
||||
|
@ -259,22 +266,26 @@ impl ProcView {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_listview(&mut self) -> &mut ListView<Vec<Process>> {
|
||||
fn get_listview(& self) -> & ListView<Vec<Process>> {
|
||||
self.hbox.get_listview()
|
||||
}
|
||||
|
||||
fn get_listview_mut(&mut self) -> &mut ListView<Vec<Process>> {
|
||||
self.hbox.get_listview_mut()
|
||||
}
|
||||
|
||||
fn get_textview(&mut self) -> &mut WillBeWidget<TextView> {
|
||||
self.hbox.get_textview()
|
||||
}
|
||||
|
||||
pub fn run_proc(&mut self, cmd: &str) -> HResult<()> {
|
||||
self.get_listview().run_proc(cmd)?;
|
||||
self.get_listview_mut().run_proc(cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_proc(&mut self) -> HResult<()> {
|
||||
if self.get_listview().content.len() == 0 { return Ok(()) }
|
||||
self.get_listview().remove_proc()?;
|
||||
if self.get_listview_mut().content.len() == 0 { return Ok(()) }
|
||||
self.get_listview_mut().remove_proc()?;
|
||||
self.get_textview().change_to(Box::new(move |_, core| {
|
||||
let mut textview = TextView::new_blank(&core);
|
||||
textview.refresh().log();
|
||||
|
@ -285,10 +296,10 @@ impl ProcView {
|
|||
}
|
||||
|
||||
fn show_output(&mut self) -> HResult<()> {
|
||||
if Some(self.get_listview().get_selection()) == self.viewing {
|
||||
if Some(self.get_listview_mut().get_selection()) == self.viewing {
|
||||
return Ok(());
|
||||
}
|
||||
let output = self.get_listview().selected_proc()?.output.lock()?.clone();
|
||||
let output = self.get_listview_mut().selected_proc()?.output.lock()?.clone();
|
||||
|
||||
self.get_textview().change_to(Box::new(move |_, core| {
|
||||
let mut textview = TextView::new_blank(&core);
|
||||
|
@ -296,7 +307,7 @@ impl ProcView {
|
|||
textview.animate_slide_up().log();
|
||||
Ok(textview)
|
||||
})).log();
|
||||
self.viewing = Some(self.get_listview().get_selection());
|
||||
self.viewing = Some(self.get_listview_mut().get_selection());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -343,11 +354,75 @@ impl Widget for ProcView {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
self.hbox.core.coordinates = coordinates.clone();
|
||||
self.hbox.set_coordinates(&coordinates)
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
let listview = self.get_listview();
|
||||
let procs_num = listview.len();
|
||||
let procs_running = listview
|
||||
.content
|
||||
.iter()
|
||||
.filter(|proc| proc.status.lock().unwrap().is_none())
|
||||
.count();
|
||||
|
||||
let header = format!("Running processes: {} / {}",
|
||||
procs_running,
|
||||
procs_num);
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn render_footer(&self) -> HResult<String> {
|
||||
let listview = self.get_listview();
|
||||
let selection = listview.get_selection();
|
||||
|
||||
if let Some(proc) = listview.content.get(selection) {
|
||||
let cmd = &proc.cmd;
|
||||
let pid = proc.handle.lock()?.id();
|
||||
let proc_status = proc.status.lock()?;
|
||||
let proc_success = proc.success.lock()?;
|
||||
|
||||
let procinfo = if proc_status.is_some() {
|
||||
let color_success =
|
||||
if let Some(_) = *proc_success {
|
||||
format!("{}successfully", term::color_green())
|
||||
} else {
|
||||
format!("{}unsuccessfully", term::color_red())
|
||||
};
|
||||
|
||||
let color_status =
|
||||
if let Some(success) = *proc_success {
|
||||
if success {
|
||||
format!("{}{}", term::color_green(), proc_status.unwrap())
|
||||
} else {
|
||||
format!("{}{}", term::color_red(), proc_status.unwrap())
|
||||
}
|
||||
} else { "wtf".to_string() };
|
||||
|
||||
let procinfo = format!("Process: {}:{} exited {}{}{} with status: {}",
|
||||
cmd,
|
||||
pid,
|
||||
color_success,
|
||||
term::reset(),
|
||||
term::status_bg(),
|
||||
color_status);
|
||||
procinfo
|
||||
} else { "still running".to_string() };
|
||||
|
||||
let footer = format!("{}: {}", cmd, procinfo);
|
||||
|
||||
Ok(footer)
|
||||
} else { Ok("No proccesses".to_string()) }
|
||||
}
|
||||
|
||||
fn refresh(&mut self) -> HResult<()> {
|
||||
self.hbox.refresh().log();
|
||||
|
||||
self.show_output().log();
|
||||
self.get_listview().refresh().log();
|
||||
self.get_listview_mut().refresh().log();
|
||||
self.get_textview().refresh().log();
|
||||
|
||||
Ok(())
|
||||
|
@ -359,12 +434,12 @@ impl Widget for ProcView {
|
|||
match key {
|
||||
Key::Char('w') => { return Err(HError::PopupFinnished) }
|
||||
Key::Char('d') => { self.remove_proc()? }
|
||||
Key::Char('k') => { self.get_listview().kill_proc()? }
|
||||
Key::Char('k') => { self.get_listview_mut().kill_proc()? }
|
||||
Key::Up | Key::Char('p') => {
|
||||
self.get_listview().move_up();
|
||||
self.get_listview_mut().move_up();
|
||||
}
|
||||
Key::Down | Key::Char('n') => {
|
||||
self.get_listview().move_down();
|
||||
self.get_listview_mut().move_down();
|
||||
}
|
||||
Key::Char('f') => { self.toggle_follow().log(); }
|
||||
Key::Ctrl('n') => { self.scroll_down().log(); },
|
||||
|
|
|
@ -2,6 +2,8 @@ use termion::event::Key;
|
|||
|
||||
use crate::widget::{Widget, WidgetCore};
|
||||
use crate::fail::{HResult, ErrorLog};
|
||||
use crate::coordinates::Coordinates;
|
||||
use crate::dirty::Dirtyable;
|
||||
|
||||
pub trait Tabbable {
|
||||
fn new_tab(&mut self) -> HResult<()>;
|
||||
|
@ -105,6 +107,15 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
|
|||
fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> {
|
||||
Ok(&mut self.core)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
self.core.coordinates = coordinates.clone();
|
||||
for widget in &mut self.widgets {
|
||||
widget.set_coordinates(coordinates).log();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_header(&self) -> HResult<String> {
|
||||
let xsize = self.get_coordinates()?.xsize();
|
||||
let header = self.active_tab_().render_header()?;
|
||||
|
|
34
src/term.rs
34
src/term.rs
|
@ -1,5 +1,5 @@
|
|||
use std::io::{Stdout, Write, BufWriter};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use termion;
|
||||
use termion::screen::AlternateScreen;
|
||||
|
@ -14,7 +14,8 @@ pub type TermMode = AlternateScreen<MouseTerminal<RawTerminal<BufWriter<Stdout>>
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Screen {
|
||||
screen: Arc<Mutex<Option<TermMode>>>
|
||||
screen: Arc<Mutex<Option<TermMode>>>,
|
||||
size: Arc<RwLock<Option<(usize, usize)>>>
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
|
@ -25,7 +26,8 @@ impl Screen {
|
|||
|
||||
screen.cursor_hide()?;
|
||||
Ok(Screen {
|
||||
screen: Arc::new(Mutex::new(Some(screen)))
|
||||
screen: Arc::new(Mutex::new(Some(screen))),
|
||||
size: Arc::new(RwLock::new(None))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -43,6 +45,19 @@ impl Screen {
|
|||
*self.screen.lock()? = Some(screen);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_size(&self, size: (usize, usize)) -> HResult<()> {
|
||||
*self.size.write()? = Some(size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_resized(&self) -> HResult<(usize, usize)> {
|
||||
Ok(self.size.read()?.clone()?)
|
||||
}
|
||||
|
||||
pub fn take_size(&self) -> HResult<(usize, usize)> {
|
||||
Ok(self.size.write()?.take()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Screen {
|
||||
|
@ -70,7 +85,9 @@ pub trait ScreenExt: Write {
|
|||
Ok(())
|
||||
}
|
||||
fn clear(&mut self) -> HResult<()> {
|
||||
write!(self, "{}", termion::clear::All)?;
|
||||
write!(self, "{}{}",
|
||||
termion::style::Reset,
|
||||
termion::clear::All)?;
|
||||
Ok(())
|
||||
}
|
||||
fn write_str(&mut self, str: &str) -> HResult<()> {
|
||||
|
@ -83,6 +100,10 @@ pub trait ScreenExt: Write {
|
|||
write!(self, "{}", goto_xy(x + 1, y + 1))?;
|
||||
Ok(())
|
||||
}
|
||||
fn size(&self) -> HResult<(usize, usize)> {
|
||||
let (xsize, ysize) = termion::terminal_size()?;
|
||||
Ok(((xsize-1) as usize, (ysize-1) as usize))
|
||||
}
|
||||
fn xsize(&self) -> HResult<usize> {
|
||||
let (xsize, _) = termion::terminal_size()?;
|
||||
Ok((xsize - 1) as usize)
|
||||
|
@ -116,6 +137,11 @@ pub fn ysize() -> u16 {
|
|||
ysize
|
||||
}
|
||||
|
||||
pub fn size() -> HResult<(usize, usize)> {
|
||||
let (xsize, ysize) = termion::terminal_size()?;
|
||||
Ok(((xsize-1) as usize, (ysize-1) as usize))
|
||||
}
|
||||
|
||||
pub fn sized_string(string: &str, xsize: u16) -> String {
|
||||
string.chars().fold("".to_string(), |acc, ch| {
|
||||
let width: usize = unicode_width::UnicodeWidthStr::width_cjk(acc.as_str());
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::minibuffer::MiniBuffer;
|
|||
use crate::term;
|
||||
use crate::term::{Screen, ScreenExt};
|
||||
use crate::dirty::{Dirtyable, DirtyBit};
|
||||
use crate::signal_notify::{notify, Signal};
|
||||
|
||||
use std::io::stdin;
|
||||
|
||||
|
@ -18,6 +19,7 @@ use std::io::stdin;
|
|||
pub enum Events {
|
||||
InputEvent(Event),
|
||||
WidgetReady,
|
||||
TerminalResized,
|
||||
ExclusiveEvent(Option<Sender<Events>>),
|
||||
InputEnabled(bool),
|
||||
RequestInput,
|
||||
|
@ -52,12 +54,14 @@ pub struct WidgetCore {
|
|||
pub event_sender: Sender<Events>,
|
||||
event_receiver: Arc<Mutex<Option<Receiver<Events>>>>,
|
||||
pub status_bar_content: Arc<Mutex<Option<String>>>,
|
||||
term_size: (usize, usize),
|
||||
dirty: DirtyBit
|
||||
}
|
||||
|
||||
impl WidgetCore {
|
||||
pub fn new() -> HResult<WidgetCore> {
|
||||
let screen = Screen::new()?;
|
||||
let (xsize, ysize) = screen.size()?;
|
||||
let coords = Coordinates::new_at(term::xsize(),
|
||||
term::ysize() - 2,
|
||||
1,
|
||||
|
@ -72,6 +76,7 @@ impl WidgetCore {
|
|||
event_sender: sender,
|
||||
event_receiver: Arc::new(Mutex::new(Some(receiver))),
|
||||
status_bar_content: status_bar_content,
|
||||
term_size: (xsize, ysize),
|
||||
dirty: DirtyBit::new() };
|
||||
|
||||
let minibuffer = MiniBuffer::new(&core);
|
||||
|
@ -106,11 +111,10 @@ pub trait Widget {
|
|||
Ok(&self.get_core()?.coordinates)
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> {
|
||||
let widget_coords = &mut self.get_core_mut()?.coordinates;
|
||||
if widget_coords != coordinates {
|
||||
*widget_coords = coordinates.clone();
|
||||
self.get_core_mut()?.set_dirty();
|
||||
self.refresh()?;
|
||||
let core = &mut self.get_core_mut()?;
|
||||
if &core.coordinates != coordinates {
|
||||
core.coordinates = coordinates.clone();
|
||||
core.set_dirty();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -259,6 +263,13 @@ pub trait Widget {
|
|||
Events::Status(status) => {
|
||||
self.show_status(&status).log();
|
||||
}
|
||||
Events::TerminalResized => {
|
||||
self.screen()?.clear().log();
|
||||
match self.resize() {
|
||||
err @ Err(HError::TerminalResizedError) => err?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.refresh().log();
|
||||
|
@ -313,7 +324,7 @@ pub trait Widget {
|
|||
let (tx_internal_event, rx_internal_event) = channel();
|
||||
let rx_global_event = self.get_core()?.event_receiver.lock()?.take()?;
|
||||
|
||||
dispatch_events(tx_internal_event, rx_global_event);
|
||||
dispatch_events(tx_internal_event, rx_global_event, self.screen()?);
|
||||
|
||||
for event in rx_internal_event.iter() {
|
||||
match event {
|
||||
|
@ -327,8 +338,13 @@ pub trait Widget {
|
|||
Events::Status(status) => {
|
||||
self.show_status(&status).log();
|
||||
}
|
||||
Events::TerminalResized => {
|
||||
self.screen()?.clear().log();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.resize().log();
|
||||
self.screen()?.take_size().ok();
|
||||
self.refresh().ok();
|
||||
self.draw().ok();
|
||||
}
|
||||
|
@ -387,14 +403,26 @@ pub trait Widget {
|
|||
let mut screen = self.screen()?;
|
||||
screen.write_str(s)
|
||||
}
|
||||
|
||||
fn resize(&mut self) -> HResult<()> {
|
||||
if let Ok((xsize, ysize)) = self.screen()?.is_resized() {
|
||||
let mut coords = self.get_core()?.coordinates.clone();
|
||||
coords.set_size_u(xsize, ysize-2);
|
||||
self.set_coordinates(&coords)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_events(tx_internal: Sender<Events>, rx_global: Receiver<Events>) {
|
||||
fn dispatch_events(tx_internal: Sender<Events>,
|
||||
rx_global: Receiver<Events>,
|
||||
screen: Screen) {
|
||||
let (tx_event, rx_event) = channel();
|
||||
let (tx_input_req, rx_input_req) = channel();
|
||||
|
||||
input_thread(tx_event.clone(), rx_input_req);
|
||||
event_thread(rx_global, tx_event.clone());
|
||||
signal_thread(tx_event.clone());
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut tx_exclusive_event: Option<Sender<Events>> = None;
|
||||
|
@ -415,6 +443,11 @@ fn dispatch_events(tx_internal: Sender<Events>, rx_global: Receiver<Events>) {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
Events::TerminalResized => {
|
||||
if let Ok(size) = term::size() {
|
||||
screen.set_size(size).log();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(tx_exclusive) = &tx_exclusive_event {
|
||||
|
@ -444,3 +477,12 @@ fn input_thread(tx: Sender<Events>, rx_input_request: Receiver<()>) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn signal_thread(tx: Sender<Events>) {
|
||||
std::thread::spawn(move || {
|
||||
let rx = notify(&[Signal::WINCH]);
|
||||
for _ in rx.iter() {
|
||||
tx.send(Events::TerminalResized).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue