use {
crate::{
area::Area,
displayable_line::DisplayableLine,
errors::Result,
text::FmtText,
SPACE_FILLING,
},
crossterm::{
cursor::MoveTo,
event::{
KeyCode,
KeyEvent,
KeyModifiers,
},
queue,
QueueableCommand,
style::Print,
},
std::io::{stdout, Write},
};
pub struct TextView<'a, 't> {
area: &'a Area,
text: &'t FmtText<'t, 't>,
pub scroll: usize, pub show_scrollbar: bool,
}
impl<'a, 't> TextView<'a, 't> {
pub const fn from(area: &'a Area, text: &'t FmtText<'_, '_>) -> TextView<'a, 't> {
TextView {
area,
text,
scroll: 0,
show_scrollbar: true,
}
}
pub fn content_height(&self) -> usize {
self.text.lines.len()
}
pub fn scrollbar(&self) -> Option<(u16, u16)> {
if self.show_scrollbar {
self.area.scrollbar(
self.scroll as u16,
self.content_height() as u16,
)
} else {
None
}
}
pub fn write(&self) -> Result<()> {
let mut stdout = stdout();
self.write_on(&mut stdout)?;
stdout.flush()?;
Ok(())
}
pub fn write_on<W: Write>(&self, w: &mut W) -> Result<()> {
let scrollbar = self.scrollbar();
let mut lines = self.text.lines.iter().skip(self.scroll as usize);
let mut width = self.area.width as usize;
if scrollbar.is_some() {
width -= 1;
}
for j in 0..self.area.height {
let y = self.area.top + j;
w.queue(MoveTo(self.area.left, y))?;
if let Some(line) = lines.next() {
let dl = DisplayableLine::new(
self.text.skin,
line,
Some(width),
);
queue!(w, Print(&dl))?;
} else {
SPACE_FILLING.queue_styled(w, &self.text.skin.paragraph.compound_style, width)?;
}
if let Some((sctop, scbottom)) = scrollbar {
if sctop <= y && y <= scbottom {
self.text.skin.scrollbar.thumb.queue(w)?;
} else {
self.text.skin.scrollbar.track.queue(w)?;
}
}
}
Ok(())
}
pub fn set_scroll(&mut self, scroll: usize) -> usize {
let area_height = self.area.height as usize;
self.scroll = if self.content_height() > area_height {
scroll.min(self.content_height() - area_height)
} else {
0
};
self.scroll
}
pub fn try_scroll_lines(&mut self, lines_count: i32) {
if lines_count < 0 {
let lines_count = -lines_count as usize;
self.scroll = if lines_count >= self.scroll {
0
} else {
self.scroll - lines_count
};
} else {
self.set_scroll(self.scroll + lines_count as usize);
}
}
pub fn try_scroll_pages(&mut self, pages_count: i32) {
self.try_scroll_lines(pages_count * i32::from(self.area.height))
}
pub fn line_up(&mut self) -> bool {
if self.scroll > 0 {
self.scroll -= 1;
true
} else {
false
}
}
pub fn line_down(&mut self) -> bool {
let content_height = self.content_height();
let page_height = self.area.height as usize;
if self.scroll + page_height < content_height {
self.scroll += 1;
true
} else {
false
}
}
pub fn page_up(&mut self) -> bool {
let page_height = self.area.height as usize;
if self.scroll > page_height {
self.scroll -= page_height;
true
} else if self.scroll > 0 {
self.scroll = 0;
true
} else {
false
}
}
pub fn page_down(&mut self) -> bool {
let content_height = self.content_height();
let page_height = self.area.height as usize;
if self.scroll + 2 * page_height < content_height {
self.scroll += page_height;
true
} else if self.scroll + page_height < content_height {
self.scroll = content_height - page_height;
true
} else {
false
}
}
pub fn apply_key_event(&mut self, key: KeyEvent) -> bool {
if key.modifiers != KeyModifiers::NONE {
return false;
}
match key.code {
KeyCode::Up => self.line_up(),
KeyCode::Down => self.line_down(),
KeyCode::PageUp => self.page_up(),
KeyCode::PageDown => self.page_down(),
_ => false,
}
}
}