feat: style help info box
Style each button comman in the help information window, instead of just one giant string. Now uses a HelpInfo struct, which contains the content, as well as widths + height
This commit is contained in:
+221
-53
@@ -481,93 +481,249 @@ pub fn heading_bar<B: Backend>(
|
|||||||
|
|
||||||
/// From a given &str, return the maximum number of chars on a single line
|
/// From a given &str, return the maximum number of chars on a single line
|
||||||
fn max_line_width(text: &str) -> usize {
|
fn max_line_width(text: &str) -> usize {
|
||||||
let mut max_line_width = 0;
|
text.lines()
|
||||||
text.lines().into_iter().for_each(|line| {
|
.into_iter()
|
||||||
let width = line.chars().count();
|
.map(|i| i.chars().count())
|
||||||
if width > max_line_width {
|
.max()
|
||||||
max_line_width = width;
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Help popup box needs these three pieces of information
|
||||||
|
struct HelpInfo {
|
||||||
|
spans: Vec<Spans<'static>>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HelpInfo {
|
||||||
|
/// Find the max width of a Span in &[Spans], although it isn't calulating it correctly
|
||||||
|
fn calc_width(spans: &[Spans]) -> usize {
|
||||||
|
spans
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.0.iter())
|
||||||
|
.map(tui::text::Span::width)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Just an empty span, i.e. a new line
|
||||||
|
fn empty_span<'a>() -> Spans<'a> {
|
||||||
|
Spans::from(String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generate a span, of given &str and given color
|
||||||
|
fn span<'a>(input: &str, color: Color) -> Span<'a> {
|
||||||
|
Span::styled(input.to_owned(), Style::default().fg(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Span to black text span
|
||||||
|
fn black_span<'a>(input: &str) -> Span<'a> {
|
||||||
|
Self::span(input, Color::Black)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Span to white text span
|
||||||
|
fn white_span<'a>(input: &str) -> Span<'a> {
|
||||||
|
Self::span(input, Color::White)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_name() -> Self {
|
||||||
|
let mut spans = NAME_TEXT
|
||||||
|
.lines()
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| Spans::from(Self::white_span(i)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
spans.insert(0, Self::empty_span());
|
||||||
|
let width = Self::calc_width(&spans);
|
||||||
|
let height = spans.len();
|
||||||
|
Self {
|
||||||
|
spans,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
max_line_width
|
|
||||||
|
fn gen_description() -> Self {
|
||||||
|
let spans = [
|
||||||
|
Self::empty_span(),
|
||||||
|
Spans::from(Self::white_span(DESCRIPTION)),
|
||||||
|
Self::empty_span(),
|
||||||
|
];
|
||||||
|
let width = Self::calc_width(&spans);
|
||||||
|
let height = spans.len();
|
||||||
|
Self {
|
||||||
|
spans: spans.to_vec(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_button() -> Self {
|
||||||
|
let button_item = |x: &str| Self::white_span(&format!(" {x} "));
|
||||||
|
let button_desc = |x: &str| Self::black_span(x);
|
||||||
|
let or = || button_desc("or");
|
||||||
|
let space = || button_desc(" ");
|
||||||
|
|
||||||
|
let spans = [
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( tab )"),
|
||||||
|
or(),
|
||||||
|
button_item("( shift+tab )"),
|
||||||
|
button_desc("to change panels"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( ↑ ↓ )"),
|
||||||
|
or(),
|
||||||
|
button_item("( j k )"),
|
||||||
|
or(),
|
||||||
|
button_item("( PgUp PgDown )"),
|
||||||
|
or(),
|
||||||
|
button_item("( Home End )"),
|
||||||
|
button_desc("to change selected line"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( enter )"),
|
||||||
|
button_desc("to send docker container command"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( h )"),
|
||||||
|
button_desc("to toggle this help information"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( 0 )"),
|
||||||
|
button_desc("to stop sort"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( 1 - 9 )"),
|
||||||
|
button_desc("sort by header - or click header"),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( m )"),
|
||||||
|
button_desc(
|
||||||
|
"to toggle mouse capture - if disabled, text on screen can be selected & copied",
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
space(),
|
||||||
|
button_item("( q )"),
|
||||||
|
button_desc("to quit at any time"),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let height = spans.len();
|
||||||
|
let width = Self::calc_width(&spans);
|
||||||
|
Self {
|
||||||
|
spans: spans.to_vec(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_final() -> Self {
|
||||||
|
let spans = [
|
||||||
|
Self::empty_span(),
|
||||||
|
Spans::from(vec![Self::black_span(
|
||||||
|
"currently an early work in progress, all and any input appreciated",
|
||||||
|
)]),
|
||||||
|
Spans::from(vec![Span::styled(
|
||||||
|
REPO.to_owned(),
|
||||||
|
Style::default()
|
||||||
|
.bg(Color::Magenta)
|
||||||
|
.fg(Color::Black)
|
||||||
|
.add_modifier(Modifier::UNDERLINED),
|
||||||
|
)]),
|
||||||
|
];
|
||||||
|
let height = spans.len();
|
||||||
|
let width = Self::calc_width(&spans);
|
||||||
|
Self {
|
||||||
|
spans: spans.to_vec(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the help box in the centre of the screen
|
/// Draw the help box in the centre of the screen
|
||||||
/// TODO should make every line it's own renderable span
|
|
||||||
pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
||||||
let title = format!(" {VERSION} ");
|
let title = format!(" {VERSION} ");
|
||||||
|
|
||||||
let description_text = format!("\n{DESCRIPTION}");
|
let name_info = HelpInfo::gen_name();
|
||||||
|
let description_info = HelpInfo::gen_description();
|
||||||
|
let button_info = HelpInfo::gen_button();
|
||||||
|
let final_info = HelpInfo::gen_final();
|
||||||
|
|
||||||
let mut help_text = String::from("\n ( tab ) or ( shift+tab ) to change panels");
|
// have to add 10, but shouldn't need to, is an error somewhere
|
||||||
help_text
|
let max_line_width = [
|
||||||
.push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line");
|
name_info.width,
|
||||||
help_text.push_str("\n ( enter ) to send docker container commands");
|
description_info.width,
|
||||||
help_text.push_str("\n ( h ) to toggle this help information");
|
button_info.width,
|
||||||
help_text.push_str("\n ( 0 ) stop sort");
|
final_info.width,
|
||||||
help_text.push_str("\n ( 1 - 9 ) sort by header - or click header");
|
]
|
||||||
help_text.push_str(
|
.into_iter()
|
||||||
"\n ( m ) to toggle mouse capture - if disabled, text on screen can be selected & copied",
|
.max()
|
||||||
|
.unwrap_or_default()
|
||||||
|
+ 10;
|
||||||
|
let max_height =
|
||||||
|
name_info.height + description_info.height + button_info.height + final_info.height + 2;
|
||||||
|
|
||||||
|
let area = popup(
|
||||||
|
max_height,
|
||||||
|
max_line_width,
|
||||||
|
f.size(),
|
||||||
|
BoxLocation::MiddleCentre,
|
||||||
);
|
);
|
||||||
help_text.push_str("\n ( q ) to quit at any time");
|
|
||||||
help_text.push_str("\n mouse scrolling & clicking also available");
|
|
||||||
help_text.push_str("\n\n currently an early work in progress, all and any input appreciated");
|
|
||||||
help_text.push_str(format!("\n {}", REPO.trim()).as_str());
|
|
||||||
|
|
||||||
// Find the maximum line widths & height
|
let split_popup = Layout::default()
|
||||||
let all_text = format!("{NAME_TEXT}{description_text}{help_text}");
|
.direction(Direction::Vertical)
|
||||||
let mut max_line_width = max_line_width(&all_text);
|
.constraints(
|
||||||
let mut lines = all_text.lines().count();
|
[
|
||||||
|
Constraint::Max(name_info.height.try_into().unwrap_or_default()),
|
||||||
|
Constraint::Max(description_info.height.try_into().unwrap_or_default()),
|
||||||
|
Constraint::Max(button_info.height.try_into().unwrap_or_default()),
|
||||||
|
Constraint::Max(final_info.height.try_into().unwrap_or_default()),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(area);
|
||||||
|
|
||||||
// Add some vertical and horizontal padding to the info box
|
let name_paragraph = Paragraph::new(name_info.spans)
|
||||||
lines += 3;
|
|
||||||
max_line_width += 4;
|
|
||||||
|
|
||||||
let name_paragraph = Paragraph::new(NAME_TEXT)
|
|
||||||
.style(Style::default().bg(Color::Magenta).fg(Color::White))
|
.style(Style::default().bg(Color::Magenta).fg(Color::White))
|
||||||
.block(Block::default())
|
.block(Block::default())
|
||||||
.alignment(Alignment::Center);
|
.alignment(Alignment::Center);
|
||||||
|
|
||||||
let description_paragraph = Paragraph::new(description_text.as_str())
|
let description_paragraph = Paragraph::new(description_info.spans)
|
||||||
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
|
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
|
||||||
.block(Block::default())
|
.block(Block::default())
|
||||||
.alignment(Alignment::Center);
|
.alignment(Alignment::Center);
|
||||||
|
|
||||||
let help_paragraph = Paragraph::new(help_text.as_str())
|
let help_paragraph = Paragraph::new(button_info.spans)
|
||||||
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
|
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
|
||||||
.block(Block::default())
|
.block(Block::default())
|
||||||
.alignment(Alignment::Left);
|
.alignment(Alignment::Left);
|
||||||
|
|
||||||
|
let final_paragraph = Paragraph::new(final_info.spans)
|
||||||
|
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
|
||||||
|
.block(Block::default())
|
||||||
|
.alignment(Alignment::Center);
|
||||||
|
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(title)
|
.title(title)
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Rounded)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(Color::Black));
|
.border_style(Style::default().fg(Color::Black));
|
||||||
|
|
||||||
let area = popup(lines, max_line_width, f.size(), BoxLocation::MiddleCentre);
|
|
||||||
|
|
||||||
let split_popup = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints(
|
|
||||||
[
|
|
||||||
Constraint::Max(NAME_TEXT.lines().count().try_into().unwrap_or_default()),
|
|
||||||
Constraint::Max(
|
|
||||||
description_text
|
|
||||||
.lines()
|
|
||||||
.count()
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
),
|
|
||||||
Constraint::Max(help_text.lines().count().try_into().unwrap_or_default()),
|
|
||||||
]
|
|
||||||
.as_ref(),
|
|
||||||
)
|
|
||||||
.split(area);
|
|
||||||
|
|
||||||
// Order is important here
|
// Order is important here
|
||||||
f.render_widget(Clear, area);
|
f.render_widget(Clear, area);
|
||||||
f.render_widget(name_paragraph, split_popup[0]);
|
f.render_widget(name_paragraph, split_popup[0]);
|
||||||
f.render_widget(description_paragraph, split_popup[1]);
|
f.render_widget(description_paragraph, split_popup[1]);
|
||||||
f.render_widget(help_paragraph, split_popup[2]);
|
f.render_widget(help_paragraph, split_popup[2]);
|
||||||
|
f.render_widget(final_paragraph, split_popup[3]);
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,3 +824,15 @@ fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocatio
|
|||||||
.constraints(h_constraints)
|
.constraints(h_constraints)
|
||||||
.split(popup_layout[indexes.0])[indexes.1]
|
.split(popup_layout[indexes.0])[indexes.1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw nothing, as in a blank screen
|
||||||
|
// pub fn nothing<B: Backend>(f: &mut Frame<'_, B>) {
|
||||||
|
// let whole_layout = Layout::default()
|
||||||
|
// .direction(Direction::Vertical)
|
||||||
|
// .constraints([Constraint::Min(100)].as_ref())
|
||||||
|
// .split(f.size());
|
||||||
|
|
||||||
|
// let block = Block::default()
|
||||||
|
// .borders(Borders::NONE);
|
||||||
|
// f.render_widget(block, whole_layout[0]);
|
||||||
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user