use std::fmt;
use crossterm::{cursor::MoveTo, terminal::ClearType};
use crate::render::{Clear, ResetAttributes};
#[derive(Default)]
pub struct Renderer {
buffer: String,
selected_item: (usize, usize),
start_line: usize,
}
pub trait Render {
fn render(&self, r: &mut Renderer) -> fmt::Result;
}
impl fmt::Write for Renderer {
fn write_str(&mut self, s: &str) -> fmt::Result {
write!(self.buffer, "{s}")
}
}
fn truncate_ansi(s: &str, length: usize) -> &str {
struct Performer(usize);
impl vte::Perform for Performer {
fn print(&mut self, _c: char) {
self.0 += 1;
}
}
let mut performer = Performer(0);
let mut parser = vte::Parser::new();
let bytes = s.as_bytes().iter().enumerate();
for (i, b) in bytes {
parser.advance(&mut performer, &[*b]);
if performer.0 > length {
return &s[0..i];
}
}
s
}
impl Renderer {
pub fn insert_cursor(&mut self) {
let next_line = self.buffer.lines().count();
self.selected_item = (next_line, next_line);
}
pub fn insert_item_end(&mut self) {
self.selected_item.1 = self.buffer.lines().count() - 1;
}
pub fn show_and_clear(
&mut self,
width: usize,
height: usize,
lookahead: usize,
truncate: bool,
) {
print!("{}", Clear(ClearType::All));
let (cursor_start_idx, cursor_end_idx) = self.selected_item;
let count_lines = self.buffer.lines().count();
if cursor_end_idx + lookahead >= self.start_line + height {
self.start_line = (cursor_end_idx + lookahead)
.min(count_lines - 1)
.saturating_sub(height - 1);
}
else if cursor_start_idx.saturating_sub(lookahead) < self.start_line {
self.start_line = cursor_start_idx.saturating_sub(lookahead);
}
if cursor_end_idx - cursor_start_idx >= height {
self.start_line = cursor_start_idx;
}
else if count_lines - self.start_line < height {
self.start_line = count_lines.saturating_sub(height);
}
if truncate {
for (row, l) in self
.buffer
.lines()
.skip(self.start_line)
.take(height)
.map(|l| truncate_ansi(l, width))
.enumerate()
{
print!("{}{l}{}", MoveTo(0, row as u16), ResetAttributes);
}
} else {
for (row, l) in self
.buffer
.lines()
.skip(self.start_line)
.take(height)
.enumerate()
{
print!("{}{l}", MoveTo(0, row as u16));
}
print!("{ResetAttributes}");
}
self.buffer.clear();
}
}