use std::io::{self, Write};
use scrin::{
Frame, Rect, Terminal, TerminalOptions,
widgets::{Block, List, ListState, Widget},
};
use crate::error::Result;
use crate::{normalize_bg, theme_surface, theme_white};
pub(crate) struct TerminalSession {
pub(crate) terminal: Terminal,
}
impl TerminalSession {
pub(crate) fn new() -> Result<Self> {
let mut terminal = Terminal::init_with(TerminalOptions {
mouse_capture: true,
..TerminalOptions::default()
})?;
terminal.clear();
Ok(Self { terminal })
}
}
impl Drop for TerminalSession {
fn drop(&mut self) {
let _ = self.terminal.restore();
}
}
pub(crate) fn draw_full_frame<F>(terminal: &mut Terminal, f: F) -> io::Result<()>
where
F: FnOnce(&mut Frame<'_>),
{
terminal.draw(f)?;
let frame = terminal.back_buffer().to_ansi_string();
let mut stdout = io::stdout();
write!(stdout, "\x1b[H{frame}")?;
stdout.flush()?;
Ok(())
}
pub(crate) trait FrameRenderExt {
fn render_block_widget<'item, W: Widget>(&mut self, area: Rect, widget: W, block: Block<'item>);
fn render_block_stateful_list<'item>(
&mut self,
area: Rect,
list: &List<'item>,
state: &mut ListState,
block: Block<'item>,
);
}
impl FrameRenderExt for Frame<'_> {
fn render_block_widget<'item, W: Widget>(
&mut self,
area: Rect,
widget: W,
block: Block<'item>,
) {
block.render(self.buffer(), area);
let inner = block.inner(area);
if inner.width > 0 && inner.height > 0 {
self.buffer()
.fill(inner, ' ', theme_white(), Some(theme_surface()));
widget.render(self.buffer(), inner);
normalize_bg(self.buffer(), inner, theme_surface());
}
}
fn render_block_stateful_list<'b>(
&mut self,
area: Rect,
list: &List<'b>,
state: &mut ListState,
block: Block<'b>,
) {
block.render(self.buffer(), area);
let inner = block.inner(area);
if inner.width > 0 && inner.height > 0 {
self.buffer()
.fill(inner, ' ', theme_white(), Some(theme_surface()));
list.render_stateful(self.buffer(), inner, state);
normalize_bg(self.buffer(), inner, theme_surface());
}
}
}