Refactor trust_directory to use ColumnRenderable & friends, thus correcting wrapping behavior at small widths. Also introduce RowRenderable with fixed-width rows. - fixed wrapping in trust_directory - changed selector cursor to match other list item selections - allow y/n to work as well as 1/2 - fixed key_hint to be standard before: <img width="661" height="550" alt="Screenshot 2025-10-09 at 9 50 36 AM" src="https://github.com/user-attachments/assets/e01627aa-bee4-4e25-8eca-5575c43f05bf" /> after: <img width="661" height="550" alt="Screenshot 2025-10-09 at 9 51 31 AM" src="https://github.com/user-attachments/assets/cb816cbd-7609-4c83-b62f-b4dba392d79a" />
125 lines
3.4 KiB
Rust
125 lines
3.4 KiB
Rust
use std::fmt::{self};
|
|
use std::io::Write;
|
|
use std::io::{self};
|
|
|
|
use ratatui::prelude::CrosstermBackend;
|
|
|
|
use ratatui::backend::Backend;
|
|
use ratatui::backend::ClearType;
|
|
use ratatui::backend::WindowSize;
|
|
use ratatui::buffer::Cell;
|
|
use ratatui::layout::Position;
|
|
use ratatui::layout::Size;
|
|
|
|
/// This wraps a CrosstermBackend and a vt100::Parser to mock
|
|
/// a "real" terminal.
|
|
///
|
|
/// Importantly, this wrapper avoids calling any crossterm methods
|
|
/// which write to stdout regardless of the writer. This includes:
|
|
/// - getting the terminal size
|
|
/// - getting the cursor position
|
|
pub struct VT100Backend {
|
|
crossterm_backend: CrosstermBackend<vt100::Parser>,
|
|
}
|
|
|
|
impl VT100Backend {
|
|
/// Creates a new `TestBackend` with the specified width and height.
|
|
pub fn new(width: u16, height: u16) -> Self {
|
|
Self {
|
|
crossterm_backend: CrosstermBackend::new(vt100::Parser::new(height, width, 0)),
|
|
}
|
|
}
|
|
|
|
pub fn vt100(&self) -> &vt100::Parser {
|
|
self.crossterm_backend.writer()
|
|
}
|
|
}
|
|
|
|
impl Write for VT100Backend {
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
self.crossterm_backend.writer_mut().write(buf)
|
|
}
|
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
self.crossterm_backend.writer_mut().flush()
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VT100Backend {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.crossterm_backend.writer().screen().contents())
|
|
}
|
|
}
|
|
|
|
impl Backend for VT100Backend {
|
|
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
|
|
where
|
|
I: Iterator<Item = (u16, u16, &'a Cell)>,
|
|
{
|
|
self.crossterm_backend.draw(content)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn hide_cursor(&mut self) -> io::Result<()> {
|
|
self.crossterm_backend.hide_cursor()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn show_cursor(&mut self) -> io::Result<()> {
|
|
self.crossterm_backend.show_cursor()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn get_cursor_position(&mut self) -> io::Result<Position> {
|
|
Ok(self.vt100().screen().cursor_position().into())
|
|
}
|
|
|
|
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> io::Result<()> {
|
|
self.crossterm_backend.set_cursor_position(position)
|
|
}
|
|
|
|
fn clear(&mut self) -> io::Result<()> {
|
|
self.crossterm_backend.clear()
|
|
}
|
|
|
|
fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
|
|
self.crossterm_backend.clear_region(clear_type)
|
|
}
|
|
|
|
fn append_lines(&mut self, line_count: u16) -> io::Result<()> {
|
|
self.crossterm_backend.append_lines(line_count)
|
|
}
|
|
|
|
fn size(&self) -> io::Result<Size> {
|
|
let (rows, cols) = self.vt100().screen().size();
|
|
Ok(Size::new(cols, rows))
|
|
}
|
|
|
|
fn window_size(&mut self) -> io::Result<WindowSize> {
|
|
Ok(WindowSize {
|
|
columns_rows: self.vt100().screen().size().into(),
|
|
// Arbitrary size, we don't rely on this in testing.
|
|
pixels: Size {
|
|
width: 640,
|
|
height: 480,
|
|
},
|
|
})
|
|
}
|
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
self.crossterm_backend.writer_mut().flush()
|
|
}
|
|
|
|
fn scroll_region_up(&mut self, region: std::ops::Range<u16>, scroll_by: u16) -> io::Result<()> {
|
|
self.crossterm_backend.scroll_region_up(region, scroll_by)
|
|
}
|
|
|
|
fn scroll_region_down(
|
|
&mut self,
|
|
region: std::ops::Range<u16>,
|
|
scroll_by: u16,
|
|
) -> io::Result<()> {
|
|
self.crossterm_backend.scroll_region_down(region, scroll_by)
|
|
}
|
|
}
|