refactor: headers::draw()

This commit is contained in:
Jack Wills
2025-02-23 13:14:22 +00:00
parent 6ea4ba86bd
commit bb4eec2b5e
+146 -108
View File
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};
use parking_lot::Mutex;
use ratatui::{
@@ -15,51 +15,14 @@ use crate::{
ui::{FrameData, GuiState, Status, gui_state::Region},
};
// Draw heading bar at top of program, always visible
/// TODO Should separate into loading icon/headers/help functions
#[allow(clippy::too_many_lines)]
pub fn draw(
area: Rect,
/// Generate a header paragrah with it's width
fn gen_header<'a>(
colors: AppColors,
frame: &mut Frame,
fd: &FrameData,
gui_state: &Arc<Mutex<GuiState>>,
keymap: &Keymap,
) {
let gen_style = |bg: Option<Color>, fg: Color| {
bg.map_or_else(
|| Style::default().fg(fg),
|bg| Style::default().bg(bg).fg(fg),
)
};
frame.render_widget(
Block::default().style(gen_style(Some(colors.headers_bar.background), Color::Reset)),
area,
);
// Generate a block for the header, if the header is currently being used to sort a column, then highlight it white
let header_block = |x: &Header, colors: AppColors| {
let mut color = colors.headers_bar.text;
let mut suffix = "";
if let Some((a, b)) = &fd.sorted_by {
if x == a {
match b {
SortedOrder::Asc => suffix = "",
SortedOrder::Desc => suffix = "",
}
color = colors.headers_bar.text_selected;
};
};
(color, suffix)
};
// Generate block for the headers, state and status has a specific layout, others all equal
// width is dependant on it that column is selected to sort - or not
// TODO - yes this is a mess, needs documenting correctly
let gen_header = |header: &Header, width: usize, colors: AppColors| {
let block = header_block(header, colors);
header: Header,
width: usize,
) -> (Paragraph<'a>, u16) {
let block = gen_header_block(colors, fd, header);
let text = format!(
"{x:<width$}{MARGIN}",
@@ -70,7 +33,99 @@ pub fn draw(
.style(gen_style(None, block.0))
.alignment(Alignment::Left);
(status, count)
}
// Generate a block for the header, if the header is currently being used to sort a column, then highlight it white
fn gen_header_block<'a>(colors: AppColors, fd: &FrameData, header: Header) -> (Color, &'a str) {
let mut color = colors.headers_bar.text;
let mut suffix = "";
if let Some((a, b)) = &fd.sorted_by {
if &header == a {
match b {
SortedOrder::Asc => suffix = "",
SortedOrder::Desc => suffix = "",
}
color = colors.headers_bar.text_selected;
};
};
(color, suffix)
}
fn gen_style(bg: Option<Color>, fg: Color) -> Style {
bg.map_or_else(
|| Style::default().fg(fg),
|bg| Style::default().bg(bg).fg(fg),
)
}
/// Generate the text to display on the show help section, as can change with a custom keymap
fn gen_help_text(fd: &FrameData, keymap: &Keymap) -> String {
let suffix = if fd.status.contains(&Status::Help) {
"exit"
} else {
"show"
};
if keymap.toggle_help == Keymap::new().toggle_help {
format!("( h ) {suffix} help{MARGIN}")
} else if let Some(secondary) = keymap.toggle_help.1 {
format!(
" ( {} | {secondary} ) {suffix} help{MARGIN}",
keymap.toggle_help.0
)
} else {
format!(" ( {} ) {suffix} help{MARGIN}", keymap.toggle_help.0)
}
}
/// Draw the show/hide help section
fn draw_help(
colors: AppColors,
f: &mut Frame,
fd: &FrameData,
help_text: String,
gui_state: &Arc<Mutex<GuiState>>,
split_bar: &Rc<[Rect]>,
) {
let help_text_color = if fd.status.contains(&Status::Help) {
colors.headers_bar.text
} else {
colors.headers_bar.text_selected
};
let help_paragraph = Paragraph::new(help_text)
.style(gen_style(None, help_text_color))
.alignment(Alignment::Right);
// If no containers, don't display the headers, could maybe do this first?
let help_index = if fd.has_containers { 2 } else { 0 };
gui_state
.lock()
.update_region_map(Region::HelpPanel, split_bar[help_index]);
f.render_widget(help_paragraph, split_bar[help_index]);
}
// Draw loading icon, or not, and a prefix with a single space
fn draw_loading_spinner(colors: AppColors, f: &mut Frame, fd: &FrameData, rect: Rect) {
let loading_paragraph = Paragraph::new(format!("{:>2}", fd.loading_icon))
.style(gen_style(None, colors.headers_bar.loading_spinner))
.alignment(Alignment::Left);
f.render_widget(loading_paragraph, rect);
}
/// Draw the sortable column headers (name/state/status etc)
fn draw_columns(
colors: AppColors,
f: &mut Frame,
fd: &FrameData,
gui_state: &Arc<Mutex<GuiState>>,
split_bar: &Rc<[Rect]>,
) {
if fd.has_containers {
let header_section_width = split_bar[1].width;
let mut counter = 0;
// Meta data to iterate over to create blocks with correct widths
let header_meta = [
@@ -85,59 +140,14 @@ pub fn draw(
(Header::Tx, fd.columns.net_tx.1),
];
let suffix = if fd.status.contains(&Status::Help) {
"exit"
} else {
"show"
};
let info_text = if keymap.toggle_help == Keymap::new().toggle_help {
format!("( h ) {suffix} help{MARGIN}")
} else if let Some(secondary) = keymap.toggle_help.1 {
format!(
" ( {} | {secondary} ) {suffix} help{MARGIN}",
keymap.toggle_help.0
)
} else {
format!(" ( {} ) {suffix} help{MARGIN}", keymap.toggle_help.0)
};
let info_width = info_text.chars().count();
let column_width = usize::from(area.width).saturating_sub(info_width);
let column_width = if column_width > 0 { column_width } else { 1 };
let splits = if fd.has_containers {
vec![
Constraint::Max(4),
Constraint::Max(column_width.try_into().unwrap_or_default()),
Constraint::Max(info_width.try_into().unwrap_or_default()),
]
} else {
CONSTRAINT_100.to_vec()
};
let split_bar = Layout::default()
.direction(Direction::Horizontal)
.constraints(splits)
.split(area);
// Draw loading icon, or not, and a prefix with a single space
let loading_paragraph = Paragraph::new(format!("{:>2}", fd.loading_icon))
.style(gen_style(None, colors.headers_bar.loading_spinner))
.alignment(Alignment::Left);
frame.render_widget(loading_paragraph, split_bar[0]);
if fd.has_containers {
let header_section_width = split_bar[1].width;
let mut counter = 0;
// Only show a header if the header cumulative header width is less than the header section width
let header_data = header_meta
.iter()
.filter_map(|i| {
let header_block = gen_header(&i.0, i.1.into(), colors);
.into_iter()
.filter_map(|(header, width)| {
let header_block = gen_header(colors, fd, header, usize::from(width));
counter += header_block.1;
if counter <= header_section_width {
Some((header_block.0, i.0, Constraint::Max(header_block.1)))
Some((header_block.0, header, Constraint::Max(header_block.1)))
} else {
None
}
@@ -155,27 +165,55 @@ pub fn draw(
gui_state
.lock()
.update_region_map(Region::Header(header), rect);
frame.render_widget(paragraph, rect);
f.render_widget(paragraph, rect);
}
}
}
// show/hide help
let help_text_color = if fd.status.contains(&Status::Help) {
colors.headers_bar.text
} else {
colors.headers_bar.text_selected
// Draw heading bar at top of program, always visible
pub fn draw(
area: Rect,
colors: AppColors,
f: &mut Frame,
fd: &FrameData,
gui_state: &Arc<Mutex<GuiState>>,
keymap: &Keymap,
) {
let gen_style = |bg: Option<Color>, fg: Color| {
bg.map_or_else(
|| Style::default().fg(fg),
|bg| Style::default().bg(bg).fg(fg),
)
};
let help_paragraph = Paragraph::new(info_text)
.style(gen_style(None, help_text_color))
.alignment(Alignment::Right);
f.render_widget(
Block::default().style(gen_style(Some(colors.headers_bar.background), Color::Reset)),
area,
);
// If no containers, don't display the headers, could maybe do this first?
let help_index = if fd.has_containers { 2 } else { 0 };
gui_state
.lock()
.update_region_map(Region::HelpPanel, split_bar[help_index]);
frame.render_widget(help_paragraph, split_bar[help_index]);
let help_text = gen_help_text(fd, keymap);
let help_width = help_text.chars().count();
let column_width = usize::from(area.width).saturating_sub(help_width);
let column_width = if column_width > 0 { column_width } else { 1 };
let splits = if fd.has_containers {
vec![
Constraint::Max(4),
Constraint::Max(column_width.try_into().unwrap_or_default()),
Constraint::Max(help_width.try_into().unwrap_or_default()),
]
} else {
CONSTRAINT_100.to_vec()
};
let split_bar = Layout::default()
.direction(Direction::Horizontal)
.constraints(splits)
.split(area);
draw_loading_spinner(colors, f, fd, split_bar[0]);
draw_columns(colors, f, fd, gui_state, &split_bar);
draw_help(colors, f, fd, help_text, gui_state, &split_bar);
}
#[cfg(test)]