use std::io::{self, Write};
use crossterm::{cursor::MoveTo, style::Print, QueueableCommand};
#[cfg(test)]
use ratatui::backend::Backend;
use ratatui::{
backend::CrosstermBackend,
buffer::Buffer,
layout::Rect,
style::Style,
text::Line,
widgets::{Paragraph, Widget},
Terminal,
};
pub(crate) fn edge_scrub_positions(area: Rect) -> Vec<(u16, u16)> {
if area.width == 0 || area.height == 0 {
return Vec::new();
}
let mut positions = Vec::with_capacity(area.height as usize * if area.width > 1 { 2 } else { 1 });
for y in area.y..area.y.saturating_add(area.height) {
positions.push((area.x, y));
if area.width > 1 {
positions.push((area.x + area.width - 1, y));
}
}
positions
}
pub(crate) fn scrub_edge_columns_in_buffer(buf: &mut Buffer, area: Rect, style: Style) {
for (x, y) in edge_scrub_positions(area) {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.reset();
cell.set_style(style);
}
}
}
pub(crate) fn edge_scrub_area(size: Rect, protected_bottom_rows: u16) -> Option<Rect> {
let skip_top = 2u16;
let safe_height = size
.height
.saturating_sub(skip_top.saturating_add(protected_bottom_rows));
(safe_height > 0).then(|| Rect::new(0, skip_top, size.width, safe_height))
}
#[cfg(test)]
pub(crate) fn scrub_terminal_edges<B>(terminal: &mut Terminal<B>, style: Style) -> io::Result<()>
where
B: Backend,
{
let size = terminal.size()?;
let area = Rect::new(0, 0, size.width, size.height);
scrub_edge_columns_in_buffer(terminal.current_buffer_mut(), area, style);
terminal.backend_mut().flush()
}
#[cfg_attr(test, allow(dead_code))]
pub(crate) fn scrub_crossterm_terminal_edges<W>(
terminal: &mut Terminal<CrosstermBackend<W>>,
protected_bottom_rows: u16,
style: Style,
) -> io::Result<()>
where
W: Write,
{
let size = terminal.size()?;
let Some(area) = edge_scrub_area(Rect::new(0, 0, size.width, size.height), protected_bottom_rows) else {
return Ok(());
};
{
let backend = terminal.backend_mut();
for (x, y) in edge_scrub_positions(area) {
backend.queue(MoveTo(x, y))?;
backend.queue(Print(" "))?;
}
std::io::Write::flush(backend)?;
}
scrub_edge_columns_in_buffer(terminal.current_buffer_mut(), area, style);
Ok(())
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn render_scrolled_lines(
buf: &mut Buffer,
area: Rect,
lines: &[Line<'static>],
style: Style,
) {
for y in area.y..area.y.saturating_add(area.height) {
for x in area.x..area.x.saturating_add(area.width) {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.reset();
cell.set_style(style);
}
}
}
Paragraph::new(lines.to_vec()).style(style).render(area, buf);
}