diff --git a/console/src/view/async_ops.rs b/console/src/view/async_ops.rs index 3dce3b53c..6b18e5575 100644 --- a/console/src/view/async_ops.rs +++ b/console/src/view/async_ops.rs @@ -14,7 +14,7 @@ use tui::{ layout, style::{self, Color, Style}, text::Spans, - widgets::{Cell, Paragraph, Row, Table}, + widgets::{Cell, Row, Table}, }; #[derive(Debug, Default)] @@ -189,11 +189,12 @@ impl TableList for AsyncOpsTable { .direction(layout::Direction::Vertical) .margin(0); + let controls = table::Controls::for_area(&area, styles); let chunks = layout .constraints( [ - layout::Constraint::Length(1), - layout::Constraint::Min(area.height - 1), + layout::Constraint::Length(controls.height), + layout::Constraint::Max(area.height), ] .as_ref(), ) @@ -223,7 +224,7 @@ impl TableList for AsyncOpsTable { .highlight_style(Style::default().add_modifier(style::Modifier::BOLD)); frame.render_stateful_widget(table, async_ops_area, &mut table_list_state.table_state); - frame.render_widget(Paragraph::new(table::controls(styles)), controls_area); + frame.render_widget(controls.paragraph, controls_area); table_list_state .sorted_items diff --git a/console/src/view/resources.rs b/console/src/view/resources.rs index fde61cf25..670b26d08 100644 --- a/console/src/view/resources.rs +++ b/console/src/view/resources.rs @@ -14,7 +14,7 @@ use tui::{ layout, style::{self, Color, Style}, text::Spans, - widgets::{Cell, Paragraph, Row, Table}, + widgets::{Cell, Row, Table}, }; #[derive(Debug, Default)] @@ -152,6 +152,8 @@ impl TableList for ResourcesTable { table_list_state.len() ))]); + let controls = table::Controls::for_area(&area, styles); + let layout = layout::Layout::default() .direction(layout::Direction::Vertical) .margin(0); @@ -159,8 +161,8 @@ impl TableList for ResourcesTable { let chunks = layout .constraints( [ - layout::Constraint::Length(1), - layout::Constraint::Min(area.height - 1), + layout::Constraint::Length(controls.height), + layout::Constraint::Max(area.height), ] .as_ref(), ) @@ -189,7 +191,7 @@ impl TableList for ResourcesTable { .highlight_style(Style::default().add_modifier(style::Modifier::BOLD)); frame.render_stateful_widget(table, tasks_area, &mut table_list_state.table_state); - frame.render_widget(Paragraph::new(table::controls(styles)), controls_area); + frame.render_widget(controls.paragraph, controls_area); table_list_state .sorted_items diff --git a/console/src/view/table.rs b/console/src/view/table.rs index 157d05369..8b6d177b1 100644 --- a/console/src/view/table.rs +++ b/console/src/view/table.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; use tui::{ layout, text::{self, Span, Spans, Text}, - widgets::TableState, + widgets::{Paragraph, TableState, Wrap}, }; use std::cell::RefCell; @@ -42,6 +42,11 @@ pub(crate) struct TableListState { pub(crate) table_state: TableState, } +pub(crate) struct Controls { + pub(crate) paragraph: Paragraph<'static>, + pub(crate) height: u16, +} + impl TableListState { pub(in crate::view) fn len(&self) -> usize { self.sorted_items.len() @@ -159,22 +164,6 @@ impl TableListState { } } -pub(in crate::view) fn controls(styles: &view::Styles) -> Text { - tui::text::Text::from(Spans::from(vec![ - Span::raw("controls: "), - bold(styles.if_utf8("\u{2190}\u{2192}", "left, right")), - text::Span::raw(" = select column (sort), "), - bold(styles.if_utf8("\u{2191}\u{2193}", "up, down")), - text::Span::raw(" = scroll, "), - bold(styles.if_utf8("\u{21B5}", "enter")), - text::Span::raw(" = view details, "), - bold("i"), - text::Span::raw(" = invert sort (highest/lowest), "), - bold("q"), - text::Span::raw(" = quit"), - ])) -} - impl Default for TableListState where T: TableList, @@ -192,3 +181,45 @@ where } } } + +impl Controls { + pub(in crate::view) fn for_area(area: &layout::Rect, styles: &view::Styles) -> Self { + let text = Text::from(Spans::from(vec![ + Span::raw("controls: "), + bold(styles.if_utf8("\u{2190}\u{2192}", "left, right")), + text::Span::raw(" = select column (sort), "), + bold(styles.if_utf8("\u{2191}\u{2193}", "up, down")), + text::Span::raw(" = scroll, "), + bold(styles.if_utf8("\u{21B5}", "enter")), + text::Span::raw(" = view details, "), + bold("i"), + text::Span::raw(" = invert sort (highest/lowest), "), + bold("q"), + text::Span::raw(" = quit"), + ])); + + // how many lines do we need to display the controls? + let mut height = 1; + + // if the area is narrower than the width of the controls text, we need + // to wrap the text across multiple lines. + let width = text.width() as u16; + if area.width < width { + height = width / area.width; + + // if the text's width is not neatly divisible by the area's width + // (and it almost never will be), round up for the remaining text. + if width % area.width > 0 { + height += 1 + }; + } + + Self { + // TODO(eliza): it would be nice if we could wrap this on commas, + // specifically, rather than whitespace...but that seems like a + // bunch of additional work... + paragraph: Paragraph::new(text).wrap(Wrap { trim: true }), + height, + } + } +} diff --git a/console/src/view/tasks.rs b/console/src/view/tasks.rs index efe21adcf..909a213eb 100644 --- a/console/src/view/tasks.rs +++ b/console/src/view/tasks.rs @@ -13,7 +13,7 @@ use tui::{ layout, style::{self, Color, Style}, text::{Span, Spans, Text}, - widgets::{self, Cell, ListItem, Paragraph, Row, Table}, + widgets::{self, Cell, ListItem, Row, Table}, }; #[derive(Debug, Default)] @@ -200,31 +200,33 @@ impl TableList for TasksTable { .direction(layout::Direction::Vertical) .margin(0); + let controls = table::Controls::for_area(&area, styles); + let (controls_area, tasks_area, warnings_area) = if warnings.is_empty() { let chunks = layout .constraints( [ - layout::Constraint::Length(1), - layout::Constraint::Min(area.height - 1), + layout::Constraint::Length(controls.height), + layout::Constraint::Max(area.height), ] .as_ref(), ) .split(area); (chunks[0], chunks[1], None) } else { + let warnings_height = warnings.len() as u16 + 2; let chunks = layout .constraints( [ - layout::Constraint::Length(1), - layout::Constraint::Length(warnings.len() as u16 + 2), - layout::Constraint::Min(area.height - 1), + layout::Constraint::Length(controls.height), + layout::Constraint::Length(warnings_height), + layout::Constraint::Max(area.height), ] .as_ref(), ) .split(area); (chunks[0], chunks[2], Some(chunks[1])) }; - // Fill all remaining characters in the frame with the task's fields. // // Ideally we'd use Min(0), and it would fill the rest of the space. But that is broken @@ -254,7 +256,7 @@ impl TableList for TasksTable { .highlight_style(Style::default().add_modifier(style::Modifier::BOLD)); frame.render_stateful_widget(table, tasks_area, &mut table_list_state.table_state); - frame.render_widget(Paragraph::new(table::controls(styles)), controls_area); + frame.render_widget(controls.paragraph, controls_area); if let Some(area) = warnings_area { let block = styles