diff --git a/src/dirty.rs b/src/dirty.rs new file mode 100644 index 0000000..fe6e4d1 --- /dev/null +++ b/src/dirty.rs @@ -0,0 +1,35 @@ +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct DirtyBit(bool); + +pub trait Dirtyable { + fn get_bit(&self) -> &DirtyBit; + fn get_bit_mut(&mut self) -> &mut DirtyBit; + + fn is_dirty(&self) -> bool { + self.get_bit().0 + } + + fn set_dirty(&mut self) { + self.get_bit_mut().0 = true; + } + fn set_clean(&mut self) { + self.get_bit_mut().0 = false; + } +} + + +impl DirtyBit { + pub fn new() -> DirtyBit { + DirtyBit(false) + } +} + + +impl Dirtyable for DirtyBit { + fn get_bit(&self) -> &DirtyBit { + self + } + fn get_bit_mut(&mut self) -> &mut DirtyBit { + self + } +} diff --git a/src/files.rs b/src/files.rs index e2a5aab..5cc7dd8 100644 --- a/src/files.rs +++ b/src/files.rs @@ -13,6 +13,7 @@ use failure::Error; use notify::DebouncedEvent; use crate::fail::{HResult, HError}; +use crate::dirty::{DirtyBit, Dirtyable}; @@ -54,7 +55,8 @@ pub struct Files { pub sort: SortBy, pub dirs_first: bool, pub reverse: bool, - pub show_hidden: bool + pub show_hidden: bool, + pub dirty: DirtyBit } impl Index for Files { @@ -65,7 +67,15 @@ impl Index for Files { } +impl Dirtyable for Files { + fn get_bit(&self) -> &DirtyBit { + &self.dirty + } + fn get_bit_mut(&mut self) -> &mut DirtyBit { + &mut self.dirty + } +} impl Files { @@ -88,7 +98,8 @@ impl Files { sort: SortBy::Name, dirs_first: true, reverse: false, - show_hidden: true + show_hidden: true, + dirty: DirtyBit::new() }; files.sort(); @@ -131,7 +142,8 @@ impl Files { sort: SortBy::Name, dirs_first: true, reverse: false, - show_hidden: true + show_hidden: true, + dirty: DirtyBit::new() }; files.sort(); @@ -188,6 +200,7 @@ impl Files { if self.reverse { self.files.reverse(); } + self.set_dirty(); } pub fn cycle_sort(&mut self) { @@ -216,6 +229,7 @@ impl Files { .collect(); self.files = files; + self.set_dirty(); } pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> { @@ -247,6 +261,7 @@ impl Files { }, _ => {}, } + self.set_dirty(); Ok(()) } diff --git a/src/foldview.rs b/src/foldview.rs index ffe5281..32d98e1 100644 --- a/src/foldview.rs +++ b/src/foldview.rs @@ -1,11 +1,12 @@ use termion::event::Key; use failure::Fail; -use chrono::{DateTime, TimeZone, Local}; +use chrono::{DateTime, Local}; use crate::term; use crate::widget::Widget; use crate::listview::{ListView, Listable}; use crate::fail::{HResult, HError}; +use crate::dirty::Dirtyable; pub type LogView = ListView>; @@ -82,25 +83,30 @@ pub trait FoldableWidgetExt { impl FoldableWidgetExt for ListView> { fn on_refresh(&mut self) -> HResult<()> { - self.content.refresh_logs() + if self.content.refresh_logs()? > 0 { + self.core.set_dirty(); + } + Ok(()) } } trait LogList { - fn refresh_logs(&mut self) -> HResult<()>; + fn refresh_logs(&mut self) -> HResult; } impl LogList for Vec { - fn refresh_logs(&mut self) -> HResult<()> { + fn refresh_logs(&mut self) -> HResult { let logs = crate::fail::get_logs()?; let mut logentries = logs.into_iter().map(|log| { LogEntry::from(log) - }).collect(); + }).collect::>(); + + let n = logentries.len(); self.append(&mut logentries); - //self.truncate(10); - Ok(()) + + Ok(n) } } @@ -154,7 +160,8 @@ where self.set_selection(fold_pos); } - self.refresh() + self.core.set_dirty(); + Ok(()) } fn fold_start_pos(&self, fold: usize) -> usize { @@ -201,11 +208,9 @@ where } fn render(&self) -> Vec { - let (xsize, ysize) = self.core.coordinates.size_u(); - let offset = self.offset; + let (xsize, _) = self.core.coordinates.size_u(); self.content .iter() - //.skip(offset) .map(|foldable| foldable .render() @@ -213,8 +218,6 @@ where .map(|line| term::sized_string_u(line, xsize)) .collect::>()) .flatten() - .skip(offset) - .take(ysize+1) .collect() } fn on_refresh(&mut self) -> HResult<()> { @@ -223,18 +226,12 @@ where fn on_key(&mut self, key: Key) -> HResult<()> { match key { - Key::Up | Key::Char('p') => { - self.move_up(); - self.refresh()?; - } - Key::Char('P') => { for _ in 0..10 { self.move_up() } self.refresh()?; } - Key::Char('N') => { for _ in 0..10 { self.move_down() } self.refresh()?; } - Key::Down | Key::Char('n') => { - self.move_down(); - self.refresh()?; - }, - Key::Char('t') => { self.toggle_fold()?; } - Key::Char('l') => { self.popup_finnished()?; } + Key::Up | Key::Char('p') => self.move_up(), + Key::Char('P') => for _ in 0..10 { self.move_up() }, + Key::Char('N') => for _ in 0..10 { self.move_down() }, + Key::Down | Key::Char('n') => self.move_down(), + Key::Char('t') => self.toggle_fold()?, + Key::Char('l') => self.popup_finnished()?, _ => {} } Ok(()) diff --git a/src/listview.rs b/src/listview.rs index e41eb8e..416191b 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -7,6 +7,7 @@ use crate::files::{File, Files}; use crate::fail::{HResult, ErrorLog}; use crate::term; use crate::widget::{Widget, WidgetCore}; +use crate::dirty::Dirtyable; pub trait Listable { fn len(&self) -> usize; @@ -27,6 +28,11 @@ impl Listable for ListView { 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); + + if self.content.is_dirty() { + self.core.set_dirty(); + self.content.set_clean(); + } Ok(()) } @@ -366,13 +372,9 @@ impl ListView } fn render(&self) -> Vec { - let ysize = self.get_coordinates().unwrap().ysize() as usize; - let offset = self.offset; self.content .files .iter() - .skip(offset) - .take(ysize) .map(|file| self.render_line(&file)) .collect() } @@ -389,10 +391,15 @@ impl Widget for ListView where ListView: Listable { 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(); + + if self.core.is_dirty() || self.buffer.len() != self.len() { + self.buffer = self.render(); + self.core.set_clean(); + } Ok(()) } @@ -400,10 +407,13 @@ impl Widget for ListView where ListView: Listable { fn get_drawlist(&self) -> HResult { let mut output = term::reset(); let (xpos, ypos) = self.get_coordinates().unwrap().position().position(); + let ysize = self.get_coordinates().unwrap().ysize() as usize; output += &self .buffer .iter() + .skip(self.offset) + .take(ysize) .enumerate() .map(|(i, item)| { let mut output = term::normal_color(); @@ -425,9 +435,6 @@ impl Widget for ListView where ListView: Listable { Ok(output) } - fn render_header(&self) -> HResult { - Ok(format!("{} files", self.len())) - } fn on_key(&mut self, key: Key) -> HResult<()> { Listable::on_key(self, key) diff --git a/src/main.rs b/src/main.rs index 80029fd..0659a09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,8 @@ mod proclist; mod bookmarks; mod paths; mod foldview; +mod dirty; + diff --git a/src/preview.rs b/src/preview.rs index 832f136..17663b2 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -28,7 +28,7 @@ fn kill_proc() -> HResult<()> { } pub fn is_stale(stale: &Arc>) -> HResult { - let stale = *(stale.try_lock().unwrap()); + let stale = *(stale.lock().unwrap()); Ok(stale) } @@ -405,7 +405,6 @@ impl Previewer { .to_string(); let mut textview = TextView { lines: output.lines().map(|s| s.to_string()).collect(), - buffer: String::new(), core: core.clone(), follow: false, offset: 0}; diff --git a/src/tabview.rs b/src/tabview.rs index 53bed38..e8dc748 100644 --- a/src/tabview.rs +++ b/src/tabview.rs @@ -150,6 +150,6 @@ impl Widget for TabView where T: Widget, TabView: Tabbable { fn on_key(&mut self, key: Key) -> HResult<()> { Tabbable::on_key(self, key)?; - self.refresh() + Ok(()) } } diff --git a/src/term.rs b/src/term.rs index 2cfd70e..6b19c1b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -51,7 +51,6 @@ pub trait ScreenExt: Write { } fn reset(&mut self) -> HResult<()> { write!(self, "{}", termion::style::Reset)?; - self.flush()?; Ok(()) } fn clear(&mut self) -> HResult<()> { @@ -60,7 +59,6 @@ pub trait ScreenExt: Write { } fn write_str(&mut self, str: &str) -> HResult<()> { write!(self, "{}", str)?; - self.flush()?; Ok(()) } fn goto_xy(&mut self, x: usize, y: usize) -> HResult<()> { @@ -149,6 +147,9 @@ pub fn sized_string_u(string: &str, xsize: usize) -> String { // Do these as constants + + + pub fn highlight_color() -> String { format!( "{}", diff --git a/src/textview.rs b/src/textview.rs index 25dc63e..870bb4b 100644 --- a/src/textview.rs +++ b/src/textview.rs @@ -4,11 +4,11 @@ use crate::files::File; use crate::term::sized_string; use crate::widget::{Widget, WidgetCore}; use crate::fail::HResult; +use crate::dirty::Dirtyable; #[derive(PartialEq)] pub struct TextView { pub lines: Vec, - pub buffer: String, pub core: WidgetCore, pub follow: bool, pub offset: usize, @@ -18,7 +18,6 @@ impl TextView { pub fn new_blank(core: &WidgetCore) -> TextView { TextView { lines: vec![], - buffer: String::new(), core: core.clone(), follow: false, offset: 0, @@ -35,7 +34,6 @@ impl TextView { Ok(TextView { lines: lines, - buffer: String::new(), core: core.clone(), follow: false, offset: 0, @@ -56,7 +54,6 @@ impl TextView { Ok(TextView { lines: lines, - buffer: String::new(), core: core.clone(), follow: false, offset: 0, @@ -66,6 +63,7 @@ impl TextView { pub fn set_text(&mut self, text: &str) -> HResult<()> { let lines = text.lines().map(|l| l.to_string()).collect(); self.lines = lines; + self.core.set_dirty(); self.refresh() } @@ -94,6 +92,10 @@ impl TextView { self.offset = 0; } } + + if offset != self.offset as isize { + self.core.set_dirty(); + } } pub fn scroll_up(&mut self) { @@ -140,8 +142,17 @@ impl Widget for TextView { self.scroll_bottom(); } + if self.core.is_dirty() { + self.core.set_clean(); + } + Ok(()) + } - self.buffer = self.get_clearlist()? + + fn get_drawlist(&self) -> HResult { + let (xsize, ysize) = self.get_coordinates()?.size().size(); + let (xpos, ypos) = self.get_coordinates()?.position().position(); + + let output = self.get_clearlist()? + &self .lines .iter() @@ -156,10 +167,6 @@ impl Widget for TextView { sized_string(&line, xsize)) }) .collect::(); - Ok(()) - } - - fn get_drawlist(&self) -> HResult { - Ok(self.buffer.clone()) + Ok(output) } } diff --git a/src/widget.rs b/src/widget.rs index f4b18b9..8c9942b 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1,5 +1,6 @@ use std::sync::{Arc, Mutex}; use std::sync::mpsc::{Sender, Receiver, channel}; +use std::io::Write; use termion::event::{Event, Key, MouseEvent}; use termion::input::TermRead; @@ -9,6 +10,7 @@ use crate::fail::{HResult, HError, ErrorLog}; use crate::minibuffer::MiniBuffer; use crate::term; use crate::term::{Screen, ScreenExt}; +use crate::dirty::{Dirtyable, DirtyBit}; use std::io::stdin; @@ -47,7 +49,8 @@ pub struct WidgetCore { pub minibuffer: Arc>>, pub event_sender: Sender, event_receiver: Arc>>>, - pub status_bar_content: Arc>> + pub status_bar_content: Arc>>, + dirty: DirtyBit } impl WidgetCore { @@ -66,7 +69,8 @@ impl WidgetCore { minibuffer: Arc::new(Mutex::new(None)), event_sender: sender, event_receiver: Arc::new(Mutex::new(Some(receiver))), - status_bar_content: status_bar_content }; + status_bar_content: status_bar_content, + dirty: DirtyBit::new() }; let minibuffer = MiniBuffer::new(&core); *core.minibuffer.lock().unwrap() = Some(minibuffer); @@ -78,6 +82,17 @@ impl WidgetCore { } } +impl Dirtyable for WidgetCore { + fn get_bit(&self) -> &DirtyBit { + &self.dirty + } + + fn get_bit_mut(&mut self) -> &mut DirtyBit { + &mut self.dirty + } +} + + pub trait Widget { fn get_core(&self) -> HResult<&WidgetCore>; // { // Err(HError::NoWidgetCoreError(Backtrace::new())) @@ -89,8 +104,12 @@ pub trait Widget { Ok(&self.get_core()?.coordinates) } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { - self.get_core_mut()?.coordinates = coordinates.clone(); - self.refresh()?; + 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()?; + } Ok(()) } fn render_header(&self) -> HResult { @@ -235,8 +254,10 @@ pub trait Widget { } _ => {} } + self.refresh().log(); self.draw().log(); self.after_draw().log(); + self.get_core_mut()?.screen.flush().ok(); } Ok(()) }