use super::text_utils;
use crossterm::{
cursor, queue,
terminal::{self, BeginSynchronizedUpdate, ClearType, EndSynchronizedUpdate},
};
use std::io::{self, Write};
pub(crate) struct MinimumSize {
pub cols: u16,
pub rows: u16,
}
fn render_size_warning(
stdout: &mut io::Stdout,
cols: u16,
rows: u16,
min: &MinimumSize,
) -> io::Result<()> {
let width = cols as usize;
let height = rows as usize;
let title = "Terminal too small";
let current_line = format!("Current: {}x{}", cols, rows);
let minimum_line = format!("Minimum: {}x{}", min.cols, min.rows);
let messages = [
title,
"",
¤t_line,
&minimum_line,
"",
"Please resize your terminal.",
"",
"Press q or Esc to exit.",
];
queue!(
stdout,
BeginSynchronizedUpdate,
cursor::MoveTo(0, 0),
cursor::Hide
)?;
let start_row = height.saturating_sub(messages.len()) / 2;
for row in 0..height {
queue!(stdout, terminal::Clear(ClearType::CurrentLine))?;
if row >= start_row && row < start_row + messages.len() {
let msg = messages[row - start_row];
let msg_width = text_utils::display_width(msg);
let padding = width.saturating_sub(msg_width) / 2;
if msg == title {
write!(
stdout,
"\r{}{}",
" ".repeat(padding),
crossterm::style::Stylize::bold(crossterm::style::Stylize::yellow(msg))
)?;
} else {
write!(stdout, "\r{}{}", " ".repeat(padding), msg)?;
}
}
if row + 1 < height {
write!(stdout, "\r\n")?;
}
}
queue!(stdout, EndSynchronizedUpdate)?;
stdout.flush()?;
Ok(())
}
pub(crate) fn check_terminal_too_small(min: &MinimumSize) -> Option<(u16, u16)> {
let (cols, rows) = terminal::size().unwrap_or((80, 24));
if is_below_minimum(cols, rows, min) {
Some((cols, rows))
} else {
None
}
}
pub(crate) fn render_size_warning_if_too_small(
stdout: &mut io::Stdout,
min: &MinimumSize,
) -> io::Result<bool> {
match check_terminal_too_small(min) {
Some((cols, rows)) => {
render_size_warning(stdout, cols, rows, min)?;
Ok(true)
}
None => Ok(false),
}
}
const MIN_RESULT_ROWS: usize = 3;
pub(crate) fn visible_result_rows_for(rows: usize, chrome_rows: usize) -> usize {
rows.saturating_sub(chrome_rows).max(MIN_RESULT_ROWS)
}
pub(crate) fn visible_result_rows(chrome_rows: usize) -> usize {
let (_, rows) = terminal::size().unwrap_or((80, 24));
visible_result_rows_for(rows as usize, chrome_rows)
}
fn is_below_minimum(cols: u16, rows: u16, min: &MinimumSize) -> bool {
cols < min.cols || rows < min.rows
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_below_minimum() {
let min = MinimumSize { cols: 70, rows: 10 };
for (cols, rows, expected) in [
(80, 24, false),
(70, 10, false), (69, 24, true), (80, 9, true), (40, 5, true), ] {
assert_eq!(
is_below_minimum(cols, rows, &min),
expected,
"{cols}x{rows}"
);
}
}
}