diff --git a/Cargo.toml b/Cargo.toml index 3a94f37..ff594f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/src/bookmarks.rs b/src/bookmarks.rs index a561841..379565e 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -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 { 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 { let ypos = self.get_coordinates()?.ypos(); diff --git a/src/coordinates.rs b/src/coordinates.rs index 3a76030..735286a 100644 --- a/src/coordinates.rs +++ b/src/coordinates.rs @@ -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; } diff --git a/src/fail.rs b/src/fail.rs index edbd0b2..97cbd62 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -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() -> HResult { + Err(HError::TerminalResizedError) + } } diff --git a/src/file_browser.rs b/src/file_browser.rs index 831f96b..51bf72f 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -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 { + 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 { let xsize = self.get_coordinates()?.xsize(); let file = self.selected_file()?; diff --git a/src/foldview.rs b/src/foldview.rs index 32d98e1..74c8f9e 100644 --- a/src/foldview.rs +++ b/src/foldview.rs @@ -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>; @@ -79,6 +79,8 @@ impl From<&HError> for LogEntry { pub trait FoldableWidgetExt { fn on_refresh(&mut self) -> HResult<()> { Ok(()) } + fn render_header(&self) -> HResult { Ok("".to_string()) } + fn render_footer(&self) -> HResult { Ok("".to_string()) } } impl FoldableWidgetExt for ListView> { @@ -88,6 +90,43 @@ impl FoldableWidgetExt for ListView> { } Ok(()) } + + fn render_header(&self) -> HResult { + 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 { + 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>: 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 { 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 { + FoldableWidgetExt::render_header(self) + } + + fn render_footer(&self) -> HResult { + FoldableWidgetExt::render_footer(self) + } + fn on_refresh(&mut self) -> HResult<()> { FoldableWidgetExt::on_refresh(self) } diff --git a/src/hbox.rs b/src/hbox.rs index 7982333..49b9c47 100644 --- a/src/hbox.rs +++ b/src/hbox.rs @@ -118,6 +118,12 @@ impl Widget for HBox 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 { self.active_widget().render_header() } @@ -125,7 +131,7 @@ impl Widget for HBox 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(()) } diff --git a/src/listview.rs b/src/listview.rs index 58e7a3c..1c6f620 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -12,6 +12,8 @@ use crate::dirty::Dirtyable; pub trait Listable { fn len(&self) -> usize; fn render(&self) -> Vec; + fn render_header(&self) -> HResult { Ok("".to_string()) } + fn render_footer(&self) -> HResult { 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 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 Widget for ListView where ListView: Listable { Ok(()) } + fn render_header(&self) -> HResult { + Listable::render_header(self) + } + + fn render_footer(&self) -> HResult { + Listable::render_footer(self) + } fn get_drawlist(&self) -> HResult { let mut output = term::reset(); diff --git a/src/main.rs b/src/main.rs index ddf7d4e..3973908 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ extern crate rayon; extern crate libc; extern crate notify; extern crate parse_ansi; +extern crate signal_notify; use failure::Fail; diff --git a/src/miller_columns.rs b/src/miller_columns.rs index ca621e4..db1daba 100644 --- a/src/miller_columns.rs +++ b/src/miller_columns.rs @@ -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 { diff --git a/src/preview.rs b/src/preview.rs index 17663b2..9965229 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -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 Widget for WillBeWidget { 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() } diff --git a/src/proclist.rs b/src/proclist.rs index f05612f..f36ede4 100644 --- a/src/proclist.rs +++ b/src/proclist.rs @@ -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 { - fn get_listview(&mut self) -> &mut ListView> { + fn get_listview(&self) -> &ListView> { + match &self.widgets[0] { + ProcViewWidgets::List(listview) => listview, + _ => unreachable!() + } + } + fn get_listview_mut(&mut self) -> &mut ListView> { match &mut self.widgets[0] { ProcViewWidgets::List(listview) => listview, _ => unreachable!() @@ -259,22 +266,26 @@ impl ProcView { } } - fn get_listview(&mut self) -> &mut ListView> { + fn get_listview(& self) -> & ListView> { self.hbox.get_listview() } + fn get_listview_mut(&mut self) -> &mut ListView> { + self.hbox.get_listview_mut() + } + fn get_textview(&mut self) -> &mut WillBeWidget { 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 { + 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 { + 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(); }, diff --git a/src/tabview.rs b/src/tabview.rs index e8dc748..524a469 100644 --- a/src/tabview.rs +++ b/src/tabview.rs @@ -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 Widget for TabView where T: Widget, TabView: 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 { let xsize = self.get_coordinates()?.xsize(); let header = self.active_tab_().render_header()?; diff --git a/src/term.rs b/src/term.rs index 94e0c5d..abedbba 100644 --- a/src/term.rs +++ b/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> #[derive(Clone)] pub struct Screen { - screen: Arc>> + screen: Arc>>, + size: Arc>> } 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 { 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()); diff --git a/src/widget.rs b/src/widget.rs index 1d48bdf..1610596 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -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>), InputEnabled(bool), RequestInput, @@ -52,12 +54,14 @@ pub struct WidgetCore { pub event_sender: Sender, event_receiver: Arc>>>, pub status_bar_content: Arc>>, + term_size: (usize, usize), dirty: DirtyBit } impl WidgetCore { pub fn new() -> HResult { 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, rx_global: Receiver) { +fn dispatch_events(tx_internal: Sender, + rx_global: Receiver, + 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> = None; @@ -415,6 +443,11 @@ fn dispatch_events(tx_internal: Sender, rx_global: Receiver) { } 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, rx_input_request: Receiver<()>) { } }); } + +fn signal_thread(tx: Sender) { + std::thread::spawn(move || { + let rx = notify(&[Signal::WINCH]); + for _ in rx.iter() { + tx.send(Events::TerminalResized).unwrap(); + } + }); +}