Skip to content

Commit

Permalink
fix(console): wrap controls line when the terminal is too narrow (#231)
Browse files Browse the repository at this point in the history
Currently, the controls line for table views is never wrapped, even if
it's too long to fit on a single line in the current terminal width.
This isn't great...especially since PR #223 is adding more controls, and
will make the line even longer.

This branch allows wrapping the controls paragraph if it doesn't fit in
the current area. The code for this was surprisingly complex: while
`tui` supports wrapping text, layout area sizes are determined _before_
text wrapping. So, we can't just say "use as many lines as are necessary
to fit this text in the current terminal width", we have to implement
that logic ourselves, and pass the required number of lines to the
`Layout::constraints` method.

## Screenshots

On the main branch, we don't wrap. Here's a wide enough terminal:
![image](https://user-images.githubusercontent.com/2796466/146655512-59ee64b1-eeb6-418d-b0d0-e84e5b80fb87.png)
...and here's one that's too narrow:
![image](https://user-images.githubusercontent.com/2796466/146655518-760387ab-b1c4-45f2-8f1a-eaf59a16067a.png)

After this change, when the terminal is wide enough, it looks the same:
![image](https://user-images.githubusercontent.com/2796466/146655537-2b1f7f16-0b1f-4a43-a6bb-d40b25569eef.png)
...but if the terminal is too narrow, it wraps:
![image](https://user-images.githubusercontent.com/2796466/146655545-130241ab-e483-40da-ba51-4a5dcf513cc5.png)

It might be nice to change this code to only wrap on commas, so each
`<keys> = <description>` isn't broken...but that seems like a lot more
work than just wrapping on whitespace, and the current thing is better
than it was before!
  • Loading branch information
hawkw committed Dec 18, 2021
1 parent f12429b commit ef41507
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 33 deletions.
9 changes: 5 additions & 4 deletions console/src/view/async_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(),
)
Expand Down Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions console/src/view/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -152,15 +152,17 @@ 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);

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(),
)
Expand Down Expand Up @@ -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
Expand Down
65 changes: 48 additions & 17 deletions console/src/view/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,6 +42,11 @@ pub(crate) struct TableListState<T: TableList> {
pub(crate) table_state: TableState,
}

pub(crate) struct Controls {
pub(crate) paragraph: Paragraph<'static>,
pub(crate) height: u16,
}

impl<T: TableList> TableListState<T> {
pub(in crate::view) fn len(&self) -> usize {
self.sorted_items.len()
Expand Down Expand Up @@ -159,22 +164,6 @@ impl<T: TableList> TableListState<T> {
}
}

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<T> Default for TableListState<T>
where
T: TableList,
Expand All @@ -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,
}
}
}
18 changes: 10 additions & 8 deletions console/src/view/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ef41507

Please sign in to comment.