use crate::{
arragement::{
Position,
Size,
},
frame::Frame,
};
use crossterm::{
cursor::{position, Hide, MoveTo, Show},
queue,
style::Print,
terminal::{size, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io::{Error, Write};
pub struct Terminal<W>
where
W: Write,
{
buffer: W,
frames: [Frame; 2],
index: usize,
}
impl<W> Terminal<W>
where
W: Write,
{
pub fn new(buffer: W) -> Result<Terminal<W>, Error> {
let size = Terminal::<W>::size()?;
Ok(Terminal {
buffer,
frames: [Frame::new(size), Frame::new(size)],
index: 0,
})
}
fn switch(&mut self) -> usize {
if self.index == 1 {
self.index = 0;
} else {
self.index = 1;
};
self.index
}
pub fn draw(&mut self, renderer: impl FnOnce(&mut Frame)) -> Result<(), Error> {
renderer(&mut self.frames[self.index]);
let mut cursor_position = Terminal::<W>::cursor_get()?;
for (cell, position) in self.frames[self.index].diff(&self.frames[1 - self.index]) {
if position.row != cursor_position.row || position.column != cursor_position.column - 1
{
self.cursor_move(position)?;
} else {
cursor_position.column += 1;
}
queue!(self.buffer, Print(cell.symbol))?;
}
self.switch();
Ok(())
}
pub fn size() -> Result<Size, Error> {
match size() {
Ok((columns, rows)) => Ok(Size { rows, columns }),
Err(err) => Err(err),
}
}
pub fn enter_alternate_screen(&mut self) -> Result<(), Error> {
queue!(self.buffer, EnterAlternateScreen)
}
pub fn leave_alternate_screen(&mut self) -> Result<(), Error> {
queue!(self.buffer, LeaveAlternateScreen)
}
pub fn cursor_hide(&mut self) -> Result<(), Error> {
queue!(self.buffer, Hide)
}
pub fn cursor_show(&mut self) -> Result<(), Error> {
queue!(self.buffer, Show)
}
pub fn cursor_get() -> Result<Position, Error> {
let cursor_position = position()?;
Ok(Position {
row: cursor_position.1,
column: cursor_position.0,
})
}
pub fn cursor_move(&mut self, position: Position) -> Result<(), Error> {
queue!(self.buffer, MoveTo(position.column, position.row))
}
}