use anyhow::Result;
use ratatui::Terminal;
use ratatui::backend::Backend;
pub(crate) fn blank_lines_after_inline_viewport(
viewport_top: u16,
viewport_height: u16,
terminal_height: u16,
) -> u16 {
terminal_height.saturating_sub(viewport_top.saturating_add(viewport_height))
}
pub(crate) fn maybe_reanchor_inline_viewport<B>(terminal: &mut Terminal<B>) -> Result<()>
where
B: Backend,
B::Error: std::error::Error + Send + Sync + 'static,
{
let terminal_height = terminal.size()?.height;
let viewport_area = terminal.get_frame().area();
let blank_lines =
blank_lines_after_inline_viewport(viewport_area.y, viewport_area.height, terminal_height);
if blank_lines == 0 {
return Ok(());
}
terminal.insert_before(blank_lines, |_| {})?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui::TerminalOptions;
use ratatui::Viewport;
use ratatui::backend::TestBackend;
use ratatui::layout::Position;
fn viewport_bottom<B: Backend>(terminal: &mut Terminal<B>) -> u16 {
let area = terminal.get_frame().area();
area.y.saturating_add(area.height)
}
#[test]
fn reanchor_after_terminal_grow_moves_viewport_to_bottom() {
let mut backend = TestBackend::new(80, 24);
backend
.set_cursor_position(Position { x: 0, y: 1 })
.unwrap();
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Inline(18),
},
)
.unwrap();
maybe_reanchor_inline_viewport(&mut terminal).expect("initial anchor");
assert_eq!(viewport_bottom(&mut terminal), 24);
terminal.backend_mut().resize(80, 40);
terminal.autoresize().expect("autoresize grow");
assert!(
viewport_bottom(&mut terminal) < 40,
"ratatui keeps the viewport row fixed across a grow"
);
maybe_reanchor_inline_viewport(&mut terminal).expect("re-anchor after grow");
assert_eq!(viewport_bottom(&mut terminal), 40);
}
#[test]
fn reanchor_after_horizontal_shrink_moves_viewport_back_to_bottom() {
let mut backend = TestBackend::new(80, 24);
backend
.set_cursor_position(Position { x: 0, y: 1 })
.unwrap();
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Inline(18),
},
)
.unwrap();
maybe_reanchor_inline_viewport(&mut terminal).expect("initial anchor");
assert_eq!(viewport_bottom(&mut terminal), 24);
terminal.backend_mut().resize(60, 24);
terminal.autoresize().expect("autoresize shrink");
assert_eq!(
terminal.get_frame().area().y,
0,
"ratatui resets inline origin to the top on horizontal shrink"
);
maybe_reanchor_inline_viewport(&mut terminal).expect("re-anchor after shrink");
assert_eq!(viewport_bottom(&mut terminal), 24);
}
}