use std::io;
use std::io::Write;
use crossterm::{
cursor::{Hide, MoveTo},
queue,
terminal::{Clear, ClearType},
};
use rand::Rng;
use crate::layout_panel::LayoutPanel;
use crate::render_context::RenderContext;
pub struct Renderer<W: Write> {
pub writer: W,
pub rng: Box<dyn Rng>,
ctx: RenderContext,
panel: LayoutPanel,
}
impl<W: Write> Renderer<W> {
pub fn new(writer: W, rng: Box<dyn Rng>, ctx: RenderContext) -> Self {
Self {
writer,
rng,
ctx,
panel: LayoutPanel {
row: 0,
column: 0,
width: 0,
},
}
}
pub fn ctx(&self) -> &RenderContext {
&self.ctx
}
pub fn panel(&self) -> LayoutPanel {
self.panel
}
#[cfg(not(tarpaulin_include))]
pub fn frame<F>(&mut self, ctx: RenderContext, f: F) -> io::Result<()>
where
F: FnOnce(&mut Self) -> io::Result<()>,
{
self.ctx = ctx;
queue!(self.writer, Hide, Clear(ClearType::All))?;
f(self)?;
self.writer.flush()
}
#[cfg(not(tarpaulin_include))]
pub fn draw(&mut self, component: &impl Component, panel: LayoutPanel) -> io::Result<u16> {
self.panel = panel;
component.render(self)
}
#[cfg(not(tarpaulin_include))]
pub fn with_panel<F>(&mut self, f: F) -> io::Result<u16>
where
F: FnOnce(&mut W, LayoutPanel, &mut Box<dyn Rng>) -> io::Result<u16>,
{
let panel = self.panel;
queue!(self.writer, MoveTo(panel.column, panel.row))?;
f(&mut self.writer, panel, &mut self.rng)
}
}
pub trait Component {
fn render<W: Write>(&self, renderer: &mut Renderer<W>) -> io::Result<u16>;
}
#[cfg(test)]
mod tests {
use crate::test_helpers::test_renderer;
#[test]
fn ctx_returns_terminal_size_and_elapsed() {
let renderer = test_renderer();
let ctx = renderer.ctx();
assert_eq!(ctx.terminal_size.width, 80);
assert_eq!(ctx.terminal_size.height, 24);
}
#[test]
fn panel_defaults_to_zero() {
let renderer = test_renderer();
let panel = renderer.panel();
assert_eq!(panel.row, 0);
assert_eq!(panel.column, 0);
assert_eq!(panel.width, 0);
}
}