use crate::{
arrangement::{Position, Size},
style::Style,
window::Window,
};
use crossterm::{
cursor::{position, Hide, MoveTo, Show},
execute, queue,
style::{Print, SetBackgroundColor, SetForegroundColor},
terminal::{size, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io::{Error, Write};
pub struct Terminal<W>
where
W: Write,
{
buffer: W,
frames: [Window; 2],
index: usize,
}
impl<W> Terminal<W>
where
W: Write,
{
pub fn new(buffer: W) -> Result<Terminal<W>, Error> {
let size = match size() {
Ok((columns, rows)) => Ok(Size { rows, columns }),
Err(err) => Err(err),
}
.unwrap();
Ok(Terminal {
buffer,
frames: [Window::new(size), Window::new(size)],
index: 0,
})
}
pub fn size(&self) -> Result<Size, Error> {
match size() {
Ok((columns, rows)) => Ok(Size { rows, columns }),
Err(err) => Err(err),
}
}
fn switch(&mut self) {
if self.index == 1 {
self.index = 0;
} else {
self.index = 1;
};
}
pub fn alternate_screen_enter(&mut self) -> Result<(), Error> {
execute!(self.buffer, EnterAlternateScreen)
}
pub fn alternate_screen_leave(&mut self) -> Result<(), Error> {
execute!(self.buffer, LeaveAlternateScreen)
}
pub fn draw(&mut self, renderer: impl FnOnce(&mut Window)) -> Result<(), Error> {
renderer(&mut self.frames[1 - self.index]);
let mut cursor_position: Position = match position() {
Ok((column, row)) => Position::from(row, column),
Err(err) => return Err(err),
};
let mut style = Style::default();
let diff = self.frames[self.index].diff(&self.frames[1 - self.index]);
for (cell, position) in diff {
if position.row != cursor_position.row
|| position.column as i32 != cursor_position.column as i32 - 1
{
queue!(self.buffer, MoveTo(position.column, position.row))?;
cursor_position.row = position.row;
cursor_position.column = position.column;
} else {
cursor_position.column += 1;
}
if cell.style.foreground != style.foreground {
queue!(self.buffer, SetForegroundColor(cell.style.foreground))?;
style.foreground = cell.style.foreground;
}
if cell.style.background != style.background {
queue!(self.buffer, SetBackgroundColor(cell.style.background))?;
style.background = cell.style.background;
}
queue!(self.buffer, Print(cell.symbol))?;
self.buffer.flush()?;
}
self.buffer.flush()?;
self.switch();
Ok(())
}
pub fn cursor_hide(&mut self) -> Result<(), Error> {
execute!(self.buffer, Hide)
}
pub fn cursor_show(&mut self) -> Result<(), Error> {
execute!(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> {
execute!(self.buffer, MoveTo(position.column, position.row))
}
}