use std::io::{BufRead, BufReader}; use strip_ansi_escapes::strip; use crate::files::File; use crate::term::sized_string_u; use crate::widget::{Widget, WidgetCore}; use crate::fail::{HResult, HError}; use crate::dirty::Dirtyable; #[derive(Debug, PartialEq)] pub struct TextView { pub lines: Vec, pub core: WidgetCore, pub follow: bool, pub offset: usize, } impl TextView { pub fn new_blank(core: &WidgetCore) -> TextView { TextView { lines: vec![], core: core.clone(), follow: false, offset: 0, } } pub fn new_from_file(core: &WidgetCore, file: &File) -> HResult { let file = std::fs::File::open(&file.path)?; let file = std::io::BufReader::new(file); let lines = file.lines() .map(|line| line .and_then(|l| strip(l)) .map_err(HError::from) .and_then(|s| std::str::from_utf8(&s) .map(|s| s.to_string()) .map_err(HError::from))) .collect::>()?; Ok(TextView { lines: lines, core: core.clone(), follow: false, offset: 0, }) } pub fn new_from_file_limit_lines(core: &WidgetCore, file: &File, num: usize) -> HResult { let file = std::fs::File::open(&file.path)?; let file = BufReader::new(file); let lines = file.lines() .take(num) .map(|line| line .and_then(|l| strip(l)) .map_err(HError::from) .and_then(|s| std::str::from_utf8(&s) .map(|s| s.to_string()) .map_err(HError::from))) .collect::>()?; Ok(TextView { lines: lines, core: core.clone(), follow: false, offset: 0, }) } 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() } pub fn toggle_follow(&mut self) { self.follow = !self.follow } pub fn scroll(&mut self, amount: isize) { let ysize = self.get_coordinates().unwrap().ysize() as isize; let offset = self.offset as isize; let len = self.lines.len() as isize; if len <= ysize + offset { return } if amount > 0 { if ysize + amount + offset + 1 >= len { // Too far down self.offset = (len - ysize - 1) as usize; } else { self.offset = (offset as isize + amount) as usize; } } else if amount < 0 { if offset + amount >= 0 { self.offset = (offset + amount) as usize; } else { self.offset = 0; } } if offset != self.offset as isize { self.core.set_dirty(); } } pub fn scroll_up(&mut self) { self.scroll(-1); } pub fn scroll_down(&mut self) { self.scroll(1); } pub fn page_up(&mut self) { let ysize = self.get_coordinates().unwrap().ysize() as isize; self.scroll(0 - ysize + 1); } pub fn page_down(&mut self) { let ysize = self.get_coordinates().unwrap().ysize() as isize; self.scroll(ysize - 1); } pub fn scroll_top(&mut self) { self.offset = 0; } pub fn scroll_bottom(&mut self) { let len = self.lines.len() as isize; self.scroll(len); } } impl Widget for TextView { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { Ok(&mut self.core) } fn refresh(&mut self) -> HResult<()> { // let (xsize, ysize) = self.get_coordinates()?.size().size(); // let (xpos, ypos) = self.get_coordinates()?.position().position(); // let len = self.lines.len(); if self.follow { self.scroll_bottom(); } if self.core.is_dirty() { self.core.set_clean(); } Ok(()) } fn get_drawlist(&self) -> HResult { let (xsize, ysize) = self.get_coordinates()?.size().size(); let (xpos, ypos) = self.get_coordinates()?.position().position(); let output = self.core.get_clearlist()? + &self .lines .iter() .skip(self.offset) .take(ysize as usize) .enumerate() .map(|(i, line)| { format!( "{}{}{}", crate::term::goto_xy(xpos, i as u16 + ypos), crate::term::reset(), sized_string_u(&line, (xsize-1) as usize)) }) .collect::(); Ok(output) } }