Automatically hide columns if they do not fit
All checks were successful
Build and deploy / Build container and manifests (push) Successful in 6m4s

This commit is contained in:
Dreaded_X 2025-04-17 01:51:10 +02:00
parent 413d9f2157
commit 61d57d6de1
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
3 changed files with 75 additions and 25 deletions

View File

@ -1,4 +1,5 @@
#![feature(let_chains)] #![feature(let_chains)]
#![feature(iter_intersperse)]
mod helper; mod helper;
mod io; mod io;
pub mod ldap; pub mod ldap;

View File

@ -1,5 +1,6 @@
use std::cmp::{self, max}; use std::cmp::{self, max};
use std::io::Write as _; use std::io::Write as _;
use std::iter::once;
use std::time::Duration; use std::time::Duration;
use futures::StreamExt; use futures::StreamExt;
@ -37,6 +38,42 @@ struct RendererInner {
rx: UnboundedReceiver<Message>, rx: UnboundedReceiver<Message>,
} }
fn compute_widths(rows: &Vec<Vec<Span<'static>>>) -> Vec<u16> {
let table_header = Tunnel::header();
std::iter::once(&table_header)
.chain(rows)
.map(|row| row.iter().map(|cell| cell.width() as u16))
.fold(vec![0; table_header.len()], |acc, row| {
acc.into_iter()
.zip(row)
.map(|v| cmp::max(v.0, v.1))
.collect()
})
}
fn compute_column_skip(
widths: &[u16],
column_spacing: u16,
highlight_symbol: usize,
max_width: u16,
) -> (usize, usize) {
for pattern in [(7, 0), (4, 0), (4, 1), (4, 2)] {
let width: u16 = widths
.iter()
.take(pattern.0)
.skip(pattern.1)
.intersperse(&column_spacing)
.chain(once(&(highlight_symbol as u16)))
.sum();
if width <= max_width {
return pattern;
}
}
(4, 3)
}
impl RendererInner { impl RendererInner {
fn new(rx: UnboundedReceiver<Message>) -> Self { fn new(rx: UnboundedReceiver<Message>) -> Self {
Self { Self {
@ -109,22 +146,6 @@ impl RendererInner {
(height as u16, Paragraph::new(text).centered().block(block)) (height as u16, Paragraph::new(text).centered().block(block))
} }
fn compute_widths(&mut self, rows: &Vec<Vec<Span<'static>>>) -> Vec<Constraint> {
let table_header = Tunnel::header();
std::iter::once(&table_header)
.chain(rows)
.map(|row| row.iter().map(|cell| cell.width() as u16))
.fold(vec![0; table_header.len()], |acc, row| {
acc.into_iter()
.zip(row)
.map(|v| cmp::max(v.0, v.1))
.collect()
})
.into_iter()
.map(|c| Constraint::Length(c + 1))
.collect()
}
fn render(&mut self, frame: &mut Frame) { fn render(&mut self, frame: &mut Frame) {
self.render_title(frame, frame.area()); self.render_title(frame, frame.area());
@ -153,15 +174,41 @@ impl RendererInner {
let highlight_style = Style::default().bold(); let highlight_style = Style::default().bold();
let header_style = Style::default().bold().reversed(); let header_style = Style::default().bold().reversed();
let row_style = Style::default(); let row_style = Style::default();
let highlight_symbol = Line::from("> ");
let column_spacing = 3;
let r = self let rows = self
.rows .rows
.iter() .iter()
.map(From::from) .map(From::from)
.collect::<Vec<Vec<Span<'static>>>>(); .collect::<Vec<Vec<Span<'static>>>>();
let rows = r.iter().map(|row| { let widths = compute_widths(&rows);
let (take, skip) = compute_column_skip(
&widths,
column_spacing,
highlight_symbol.width(),
rect.width,
);
let constraints: Vec<_> = widths
.into_iter()
.take(take)
.enumerate()
.map(|(index, width)| {
if index == 3 {
Constraint::Min(width)
} else {
Constraint::Length(width)
}
})
.skip(skip)
.collect();
let rows = rows.iter().map(|row| {
row.iter() row.iter()
.take(take)
.skip(skip)
.cloned() .cloned()
.map(Cell::from) .map(Cell::from)
.collect::<Row>() .collect::<Row>()
@ -171,6 +218,8 @@ impl RendererInner {
let header = Tunnel::header() let header = Tunnel::header()
.iter() .iter()
.take(take)
.skip(skip)
.cloned() .cloned()
.map(Cell::from) .map(Cell::from)
.collect::<Row>() .collect::<Row>()
@ -181,10 +230,10 @@ impl RendererInner {
.header(header) .header(header)
.rows(rows) .rows(rows)
.flex(Flex::Start) .flex(Flex::Start)
.column_spacing(3) .column_spacing(column_spacing)
.widths(self.compute_widths(&r)) .widths(&constraints)
.row_highlight_style(highlight_style) .row_highlight_style(highlight_style)
.highlight_symbol(Line::from("> ")) .highlight_symbol(highlight_symbol)
.highlight_spacing(HighlightSpacing::Always); .highlight_spacing(HighlightSpacing::Always);
frame.render_stateful_widget(t, rect, &mut self.state); frame.render_stateful_widget(t, rect, &mut self.state);

View File

@ -9,8 +9,8 @@ use crate::io::Stats;
pub struct TunnelRow { pub struct TunnelRow {
name: Span<'static>, name: Span<'static>,
access: Span<'static>,
port: Span<'static>, port: Span<'static>,
access: Span<'static>,
address: Span<'static>, address: Span<'static>,
stats: Arc<Stats>, stats: Arc<Stats>,
} }
@ -19,8 +19,8 @@ impl From<&TunnelRow> for Vec<Span<'static>> {
fn from(row: &TunnelRow) -> Self { fn from(row: &TunnelRow) -> Self {
vec![ vec![
row.name.clone(), row.name.clone(),
row.access.clone(),
row.port.clone(), row.port.clone(),
row.access.clone(),
row.address.clone(), row.address.clone(),
row.stats.connections().to_string().into(), row.stats.connections().to_string().into(),
row.stats.rx().to_string().into(), row.stats.rx().to_string().into(),
@ -33,8 +33,8 @@ impl Tunnel {
pub fn header() -> Vec<Span<'static>> { pub fn header() -> Vec<Span<'static>> {
vec![ vec![
"Name".into(), "Name".into(),
"Access".into(),
"Port".into(), "Port".into(),
"Access".into(),
"Address".into(), "Address".into(),
"Conn".into(), "Conn".into(),
"Rx".into(), "Rx".into(),
@ -56,8 +56,8 @@ impl Tunnel {
TunnelRow { TunnelRow {
name: tunnel.registry_entry.get_name().to_string().into(), name: tunnel.registry_entry.get_name().to_string().into(),
access,
port: tunnel.inner.port.to_string().into(), port: tunnel.inner.port.to_string().into(),
access,
address, address,
stats: tunnel.inner.stats.clone(), stats: tunnel.inner.stats.clone(),
} }