use std::io::{stdout, Write};
use crossterm::{
cursor::MoveTo,
queue,
terminal::{Clear, ClearType},
};
use crate::area::Area;
use crate::displayable_line::DisplayableLine;
use crate::errors::Result;
use crate::text::FmtText;
pub struct TextView<'a, 't> {
area: &'a Area,
text: &'t FmtText<'t, 't>,
pub scroll: i32, pub show_scrollbar: bool,
}
impl<'a, 't> TextView<'a, 't> {
pub fn from(area: &'a Area, text: &'t FmtText<'_, '_>) -> TextView<'a, 't> {
TextView {
area,
text,
scroll: 0,
show_scrollbar: true,
}
}
#[inline(always)]
pub fn content_height(&self) -> i32 {
self.text.lines.len() as i32
}
#[inline(always)]
pub fn scrollbar(&self) -> Option<(u16, u16)> {
if self.show_scrollbar {
self.area.scrollbar(self.scroll, self.content_height())
} else {
None
}
}
pub fn write(&self) -> Result<()> {
let mut stdout = stdout();
self.write_on(&mut stdout)?;
stdout.flush()?;
Ok(())
}
pub fn write_on<W>(&self, w: &mut W) -> Result<()>
where
W: std::io::Write,
{
let scrollbar = self.scrollbar();
let sx = self.area.left + self.area.width;
let mut i = self.scroll as usize;
for y in 0..self.area.height {
queue!(w, MoveTo(self.area.left, self.area.top + y))?;
if i < self.text.lines.len() {
let dl = DisplayableLine::new(self.text.skin, &self.text.lines[i], self.text.width);
write!(w, "{}", &dl)?;
i += 1;
}
self.text.skin.paragraph.compound_style.queue_bg(w)?;
queue!(w, Clear(ClearType::UntilNewLine))?;
if let Some((sctop, scbottom)) = scrollbar {
queue!(w, MoveTo(sx, self.area.top + y))?;
if sctop <= y && y <= scbottom {
write!(w, "{}", self.text.skin.scrollbar.thumb)?;
} else {
write!(w, "{}", self.text.skin.scrollbar.track)?;
}
}
}
Ok(())
}
pub fn set_scroll(&mut self, scroll: i32) -> i32 {
self.scroll = scroll
.min(self.content_height() - i32::from(self.area.height) + 1)
.max(0);
self.scroll
}
pub fn try_scroll_lines(&mut self, lines_count: i32) -> i32{
self.set_scroll(self.scroll + lines_count)
}
pub fn try_scroll_pages(&mut self, pages_count: i32) -> i32{
self.try_scroll_lines(pages_count * i32::from(self.area.height))
}
}