tui: fix wrapping in trust_directory (#5007)

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"
/>
This commit is contained in:
Jeremy Rose
2025-10-09 10:39:45 -07:00
committed by GitHub
parent bf82353f45
commit 95b41dd7f1
10 changed files with 220 additions and 68 deletions

View File

@@ -6,10 +6,10 @@ pub mod renderable;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Insets {
pub left: u16,
pub top: u16,
pub right: u16,
pub bottom: u16,
left: u16,
top: u16,
right: u16,
bottom: u16,
}
impl Insets {

View File

@@ -3,6 +3,7 @@ use std::sync::Arc;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::text::Line;
use ratatui::text::Span;
use ratatui::widgets::Paragraph;
use ratatui::widgets::WidgetRef;
@@ -45,6 +46,15 @@ impl Renderable for String {
}
}
impl<'a> Renderable for Span<'a> {
fn render(&self, area: Rect, buf: &mut Buffer) {
self.render_ref(area, buf);
}
fn desired_height(&self, _width: u16) -> u16 {
1
}
}
impl<'a> Renderable for Line<'a> {
fn render(&self, area: Rect, buf: &mut Buffer) {
WidgetRef::render_ref(self, area, buf);
@@ -114,11 +124,64 @@ impl Renderable for ColumnRenderable {
}
impl ColumnRenderable {
pub fn new(children: impl IntoIterator<Item = Box<dyn Renderable>>) -> Self {
pub fn new() -> Self {
Self::with(vec![])
}
pub fn with(children: impl IntoIterator<Item = Box<dyn Renderable>>) -> Self {
Self {
children: children.into_iter().collect(),
}
}
pub fn push(&mut self, child: impl Into<Box<dyn Renderable>>) {
self.children.push(child.into());
}
}
pub struct RowRenderable {
children: Vec<(u16, Box<dyn Renderable>)>,
}
impl Renderable for RowRenderable {
fn render(&self, area: Rect, buf: &mut Buffer) {
let mut x = area.x;
for (width, child) in &self.children {
let available_width = area.width.saturating_sub(x - area.x);
let child_area = Rect::new(x, area.y, (*width).min(available_width), area.height);
if child_area.is_empty() {
break;
}
child.render(child_area, buf);
x = x.saturating_add(*width);
}
}
fn desired_height(&self, width: u16) -> u16 {
let mut max_height = 0;
let mut width_remaining = width;
for (child_width, child) in &self.children {
let w = (*child_width).min(width_remaining);
if w == 0 {
break;
}
let height = child.desired_height(w);
if height > max_height {
max_height = height;
}
width_remaining = width_remaining.saturating_sub(w);
}
max_height
}
}
impl RowRenderable {
pub fn new() -> Self {
Self { children: vec![] }
}
pub fn push(&mut self, width: u16, child: impl Into<Box<dyn Renderable>>) {
self.children.push((width, child.into()));
}
}
pub struct InsetRenderable {
@@ -146,3 +209,16 @@ impl InsetRenderable {
}
}
}
pub trait RenderableExt {
fn inset(self, insets: Insets) -> Box<dyn Renderable>;
}
impl<R: Into<Box<dyn Renderable>>> RenderableExt for R {
fn inset(self, insets: Insets) -> Box<dyn Renderable> {
Box::new(InsetRenderable {
child: self.into(),
insets,
})
}
}