refacor: creation & drawing of headers

This commit is contained in:
Jack Wills
2022-07-22 20:38:22 +00:00
parent 96ca208197
commit a70d6fa968
5 changed files with 278 additions and 335 deletions
+2 -2
View File
@@ -169,8 +169,8 @@ impl DockerData {
self.app_data.lock().update_containers(&output); self.app_data.lock().update_containers(&output);
let current_sort = self.app_data.lock().get_sorted(); let current_sort = self.app_data.lock().get_sorted();
self.app_data.lock().set_sorted(current_sort); self.app_data.lock().set_sorted(current_sort);
output output
.iter() .iter()
+9 -3
View File
@@ -152,7 +152,7 @@ impl InputHandler {
} }
} else { } else {
match key_code { match key_code {
KeyCode::Char('0') => self.app_data.lock().set_sorted(None), KeyCode::Char('0') => self.app_data.lock().set_sorted(None),
KeyCode::Char('1') => self.sort(Header::State), KeyCode::Char('1') => self.sort(Header::State),
KeyCode::Char('2') => self.sort(Header::Status), KeyCode::Char('2') => self.sort(Header::Status),
KeyCode::Char('3') => self.sort(Header::Cpu), KeyCode::Char('3') => self.sort(Header::Cpu),
@@ -165,8 +165,14 @@ impl InputHandler {
KeyCode::Char('q') => self.is_running.store(false, Ordering::SeqCst), KeyCode::Char('q') => self.is_running.store(false, Ordering::SeqCst),
KeyCode::Char('h') => self.gui_state.lock().show_help = true, KeyCode::Char('h') => self.gui_state.lock().show_help = true,
KeyCode::Char('m') => self.m_button(), KeyCode::Char('m') => self.m_button(),
KeyCode::Tab => self.gui_state.lock().next_panel(), KeyCode::Tab => {
KeyCode::BackTab => self.gui_state.lock().previous_panel(), // TODO if no containers, skip controls panel
self.gui_state.lock().next_panel();
}
KeyCode::BackTab => {
// TODO if no containers, skip controls panel
self.gui_state.lock().previous_panel();
}
KeyCode::Home => { KeyCode::Home => {
let mut locked_data = self.app_data.lock(); let mut locked_data = self.app_data.lock();
match self.gui_state.lock().selected_panel { match self.gui_state.lock().selected_panel {
+262 -324
View File
@@ -125,162 +125,194 @@ pub fn draw_containers<B: Backend>(
) { ) {
let block = generate_block(app_data, area, gui_state, SelectablePanel::Containers); let block = generate_block(app_data, area, gui_state, SelectablePanel::Containers);
let sorted = app_data.lock().get_sorted(); let sorted = app_data.lock().get_sorted();
// if containers sorted, increase width to match headers // if containers sorted, increase width to match headers
// let sorted_width = if app_data.lock().get_sorted().is_some() { // let sorted_width = if app_data.lock().get_sorted().is_some() {
// 2 // 2
// }else { // }else {
// 0 // 0
// }; // };
// Check if need to alter column width to watch the heading widths
let sorted_width = |(x, s): &(Header, usize)| {
let mut output = 0;
if let Some((h, _)) = &sorted {
if h == x {
output = 2;
}
}
// s + output
s.to_owned()
};
// Check if need to alter column width to watch the heading widths let items = app_data
let sorted_width = |(x,s): &(Header,usize)| { .lock()
let mut output = 0; .containers
if let Some((h,_)) = &sorted { .items
if h == x { .iter()
output = 2; .map(|i| {
} let state_style = Style::default().fg(i.state.get_color());
} let blue = Style::default().fg(Color::Blue);
// s + output
s.to_owned()
}; let mems = format!(
"{:>1} / {:>1}",
i.mem_stats.back().unwrap_or(&ByteStats::new(0)),
i.mem_limit
);
let lines = Spans::from(vec![
Span::styled(
format!(
"{:<width$}",
i.state.to_string(),
width = sorted_width(&widths.state)
),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.status,
width = sorted_width(&widths.status)
),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.cpu_stats.back().unwrap_or(&CpuStats::new(0.0)),
width = sorted_width(&widths.cpu)
),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
mems,
width = sorted_width(&widths.mem)
),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.id.chars().take(8).collect::<String>(),
width = sorted_width(&widths.id),
),
blue,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.name,
width = sorted_width(&widths.name)
),
blue,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.image,
width = sorted_width(&widths.image)
),
blue,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.net_rx,
width = sorted_width(&widths.net_rx)
),
Style::default().fg(Color::Rgb(255, 233, 193)),
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.net_tx,
width = sorted_width(&widths.net_tx)
),
Style::default().fg(Color::Rgb(205, 140, 140)),
),
]);
ListItem::new(lines)
})
.collect::<Vec<_>>();
// let items = app_data // let items = app_data
// .lock() // .lock()
// .containers // .containers
// .items // .items
// .iter() // .iter()
// .map(|i| { // .map(|i| {
// let state_style = Style::default().fg(i.state.get_color()); // let state_style = Style::default().fg(i.state.get_color());
// let blue = Style::default().fg(Color::Blue); // let blue = Style::default().fg(Color::Blue);
// let mems = format!( // let mems = format!(
// "{:>1} / {:>1}", // "{:>1} / {:>1}",
// i.mem_stats.back().unwrap_or(&ByteStats::new(0)), // i.mem_stats.back().unwrap_or(&ByteStats::new(0)),
// i.mem_limit // i.mem_limit
// ); // );
// let lines = Spans::from(vec![ // let lines = Spans::from(vec![
// Span::styled( // Span::styled(
// format!("{:<width$}", i.state.to_string(), width = sorted_width(&widths.state)), // format!("{:<width$}", i.state.to_string(), width = widths.state.1),
// state_style, // state_style,
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, i.status, width = sorted_width(&widths.status)), // format!("{}{:>width$}", MARGIN, i.status, width = widths.status.1),
// state_style, // state_style,
// ), // ),
// Span::styled( // Span::styled(
// format!( // format!(
// "{}{:>width$}", // "{}{:>width$}",
// MARGIN, // MARGIN,
// i.cpu_stats.back().unwrap_or(&CpuStats::new(0.0)), // i.cpu_stats.back().unwrap_or(&CpuStats::new(0.0)),
// width = sorted_width(&widths.cpu) // width = widths.cpu.1
// ), // ),
// state_style, // state_style,
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, mems, width = sorted_width(&widths.mem)), // format!("{}{:>width$}", MARGIN, mems, width = widths.mem.1),
// state_style, // state_style,
// ), // ),
// Span::styled( // Span::styled(
// format!( // format!(
// "{}{:>width$}", // "{}{:>width$}",
// MARGIN, // MARGIN,
// i.id.chars().take(8).collect::<String>(), // i.id.chars().take(8).collect::<String>(),
// width =sorted_width(&widths.id), // width = widths.id.1
// ), // ),
// blue, // blue,
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, i.name, width = sorted_width(&widths.name)), // format!("{}{:>width$}", MARGIN, i.name, width = widths.name.1),
// blue, // blue,
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, i.image, width = sorted_width(&widths.image)), // format!("{}{:>width$}", MARGIN, i.image, width = widths.image.1),
// blue, // blue,
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, i.net_rx, width = sorted_width(&widths.net_rx)), // format!("{}{:>width$}", MARGIN, i.net_rx, width = widths.net_rx.1),
// Style::default().fg(Color::Rgb(255, 233, 193)), // Style::default().fg(Color::Rgb(255, 233, 193)),
// ), // ),
// Span::styled( // Span::styled(
// format!("{}{:>width$}", MARGIN, i.net_tx, width = sorted_width(&widths.net_tx)), // format!("{}{:>width$}", MARGIN, i.net_tx, width = widths.net_tx.1),
// Style::default().fg(Color::Rgb(205, 140, 140)), // Style::default().fg(Color::Rgb(205, 140, 140)),
// ), // ),
// ]); // ]);
// ListItem::new(lines) // ListItem::new(lines)
// }) // })
// .collect::<Vec<_>>(); // .collect::<Vec<_>>();
let items = app_data
.lock()
.containers
.items
.iter()
.map(|i| {
let state_style = Style::default().fg(i.state.get_color());
let blue = Style::default().fg(Color::Blue);
let mems = format!(
"{:>1} / {:>1}",
i.mem_stats.back().unwrap_or(&ByteStats::new(0)),
i.mem_limit
);
let lines = Spans::from(vec![
Span::styled(
format!("{:<width$}", i.state.to_string(), width = widths.state.1),
state_style,
),
Span::styled(
format!("{}{:>width$}", MARGIN, i.status, width = widths.status.1),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.cpu_stats.back().unwrap_or(&CpuStats::new(0.0)),
width = widths.cpu.1
),
state_style,
),
Span::styled(
format!("{}{:>width$}", MARGIN, mems, width = widths.mem.1),
state_style,
),
Span::styled(
format!(
"{}{:>width$}",
MARGIN,
i.id.chars().take(8).collect::<String>(),
width = widths.id.1
),
blue,
),
Span::styled(
format!("{}{:>width$}", MARGIN, i.name, width = widths.name.1),
blue,
),
Span::styled(
format!("{}{:>width$}", MARGIN, i.image, width = widths.image.1),
blue,
),
Span::styled(
format!("{}{:>width$}", MARGIN, i.net_rx, width = widths.net_rx.1),
Style::default().fg(Color::Rgb(255, 233, 193)),
),
Span::styled(
format!("{}{:>width$}", MARGIN, i.net_tx, width = widths.net_tx.1),
Style::default().fg(Color::Rgb(205, 140, 140)),
),
]);
ListItem::new(lines)
})
.collect::<Vec<_>>();
if items.is_empty() { if items.is_empty() {
let debug_text = String::from("no containers running"); let debug_text = String::from("no containers running");
@@ -452,152 +484,98 @@ pub fn draw_heading_bar<B: Backend>(
gui_state: &Arc<Mutex<GuiState>>, gui_state: &Arc<Mutex<GuiState>>,
) { ) {
let block = || Block::default().style(Style::default().bg(Color::Magenta).fg(Color::Black)); let block = || Block::default().style(Style::default().bg(Color::Magenta).fg(Color::Black));
let info_visible = gui_state.lock().show_help; let info_visible = gui_state.lock().show_help;
f.render_widget(block(), area); f.render_widget(block(), area);
/// Generate a bloack for the header, if the header is currently being used to sort a column, then highlight it white
/// Generate a bloack for the header, if the header is currently being used to sort a column, then highlight it white
let header_block = |x: &Header| { let header_block = |x: &Header| {
let mut color = Color::Black; let mut color = Color::Black;
let mut s = ""; let mut suffix = "";
let mut c = 0; let mut suffix_margin = 0;
if let Some((a, b)) = sorted_by.as_ref() { if let Some((a, b)) = sorted_by.as_ref() {
if x == a { if x == a {
match b{ match b {
SortedOrder::Asc => s="", SortedOrder::Asc => suffix = "",
SortedOrder::Desc => s="", SortedOrder::Desc => suffix = "",
} }
c = 2; suffix_margin = 2;
color = Color::White color = Color::White
}; };
}; };
(
(Block::default().style(Style::default().bg(Color::Magenta).fg(color)), s, c) Block::default().style(Style::default().bg(Color::Magenta).fg(color)),
suffix,
suffix_margin,
)
}; };
// Create blocks for each header, count widths // Generate block for the headers, state and status has a specific layout, others all equal
let state_block = header_block(&Header::State); // width is dependant on it that column is selected to sort - or not
let state_text = format!( let gen_header = |header: &Header, width: usize| {
" {}{:>width$}{ic}", let block = header_block(header);
loading_icon, let text = match header {
columns.state.0, Header::State => format!(
ic=state_block.1, " {}{:>width$}{ic}",
width = columns.state.1 - state_block.2 loading_icon,
); header,
let state_count = state_text.chars().count() as u16; ic = block.1,
let state = Paragraph::new(state_text) width = width - block.2,
.block(state_block.0) ),
.alignment(Alignment::Left); Header::Status => format!(
"{} {:>width$}{ic}",
MARGIN,
header,
ic = block.1,
width = width - block.2
),
_ => format!(
"{}{:>width$}{ic}",
MARGIN,
header,
ic = block.1,
width = width - block.2
),
};
let count = text.chars().count() as u16;
let status = Paragraph::new(text)
.block(block.0)
.alignment(Alignment::Left);
(status, count)
};
// Meta data for iterate over to create blocks and correct widths
let header_meta = [
(Header::State, columns.state.1),
(Header::Status, columns.status.1),
(Header::Cpu, columns.cpu.1),
(Header::Memory, columns.mem.1),
(Header::Id, columns.id.1),
(Header::Name, columns.name.1),
(Header::Image, columns.image.1),
(Header::Rx, columns.net_rx.1),
(Header::Tx, columns.net_tx.1),
];
let gen_header = |x: Header, w: usize| { let header_data = header_meta
let status_block = header_block(&x); .iter()
let status_text = format!( .map(|i| {
"{} {:>width$}{ic}", let header_block = gen_header(&i.0, i.1);
MARGIN, (
x, header_block.0,
ic=status_block.1, i.0.to_owned(),
width = w - status_block.2 Constraint::Max(header_block.1),
); )
})
let count = status_text.chars().count() as u16; .collect::<Vec<_>>();
let status = Paragraph::new(status_text)
.block(status_block.0)
.alignment(Alignment::Left);
(status, count)
};
// let (status, status_count) = gen_header(Header::Status,columns.status.1);
// let (cpu, cpu_count) = gen_header(Header::Cpu,columns.cpu.1);
// let (mem, mem_count) = gen_header(Header::Memory,columns.mem.1);
// let (id, id_count) = gen_header(Header::Id,columns.id.1);
// let (name, name_count) = gen_header(Header::Name,columns.name.1);
// let (image, image_count) = gen_header(Header::Image,columns.image.1);
// let (rx, rx_count) = gen_header(Header::Rx,columns.net_rx.1);
// let (tx, tx_count) = gen_header(Header::Tx,columns.net_tx.1);
let status_block = header_block(&Header::Status);
let status_text = format!(
"{} {:>width$}{ic}",
MARGIN,
columns.status.0,
ic=status_block.1,
width = columns.status.1 - status_block.2
);
let status_count = status_text.chars().count() as u16;
let status = Paragraph::new(status_text)
.block(status_block.0)
.alignment(Alignment::Left);
let cpu_text = format!("{}{:>width$}", MARGIN, columns.cpu.0, width = columns.cpu.1);
let cpu_count = cpu_text.chars().count() as u16;
let cpu = Paragraph::new(cpu_text)
.block(header_block(&Header::Cpu).0)
.alignment(Alignment::Left);
// TODO put block into the mouse click hashmap
let mem_text = format!("{}{:>width$}", MARGIN, columns.mem.0, width = columns.mem.1);
let mem_count = mem_text.chars().count() as u16;
let mem = Paragraph::new(mem_text)
.block(header_block(&Header::Memory).0)
.alignment(Alignment::Left);
let id_text = format!("{}{:>width$}", MARGIN, columns.id.0, width = columns.id.1);
let id_count = id_text.chars().count() as u16;
let id = Paragraph::new(id_text)
.block(header_block(&Header::Id).0)
.alignment(Alignment::Left);
let name_text = format!(
"{}{:>width$}",
MARGIN,
columns.name.0,
width = columns.name.1
);
let name_count = name_text.chars().count() as u16;
let name = Paragraph::new(name_text)
.block(header_block(&Header::Name).0)
.alignment(Alignment::Left);
let image_text = format!(
"{}{:>width$}",
MARGIN,
columns.image.0,
width = columns.image.1
);
let image_count = image_text.chars().count() as u16;
let image = Paragraph::new(image_text)
.block(header_block(&Header::Image).0)
.alignment(Alignment::Left);
let rx_text = format!(
"{}{:>width$}",
MARGIN,
columns.net_rx.0,
width = columns.net_rx.1
);
let rx_count = rx_text.chars().count() as u16;
let rx = Paragraph::new(rx_text)
.block(header_block(&Header::Rx).0)
.alignment(Alignment::Left);
let tx_text = format!(
"{}{:>width$}",
MARGIN,
columns.net_tx.0,
width = columns.net_tx.1
);
let tx_count = tx_text.chars().count() as u16;
let tx = Paragraph::new(tx_text)
.block(header_block(&Header::Tx).0)
.alignment(Alignment::Left);
let suffix = if info_visible { "exit" } else { "show" }; let suffix = if info_visible { "exit" } else { "show" };
let info_text = format!("( h ) {} help {}", suffix, MARGIN); let info_text = format!("( h ) {} help {}", suffix, MARGIN);
let info_width = info_text.chars().count() as u16; let info_width = info_text.chars().count() as u16;
let x = area.width - info_width; let column_width = area.width - info_width;
let column_width = if x > 0 { x } else { 1 }; let column_width = if column_width > 0 { column_width } else { 1 };
let splits = if has_containers { let splits = if has_containers {
vec![Constraint::Min(column_width), Constraint::Min(info_width)] vec![Constraint::Min(column_width), Constraint::Min(info_width)]
} else { } else {
@@ -609,60 +587,20 @@ pub fn draw_heading_bar<B: Backend>(
.constraints(splits.as_ref()) .constraints(splits.as_ref())
.split(area); .split(area);
if has_containers { if has_containers {
// let a = state_text.chars().count() as u16; let container_splits = header_data.iter().map(|i| i.2).collect::<Vec<_>>();
// let b = status_text.chars().count() as u16;
let container_splits = vec![
Constraint::Max(state_count),
Constraint::Max(status_count),
Constraint::Max(cpu_count),
Constraint::Max(mem_count),
Constraint::Max(id_count),
Constraint::Max(name_count),
Constraint::Max(image_count),
Constraint::Max(rx_count),
Constraint::Max(tx_count),
// Constraint::Min(1)
];
// insert or update
let v = [
(Header::State,state),
(Header::Status,status),
(Header::Cpu,cpu),
(Header::Memory,mem),
(Header::Id,id),
(Header::Name,name),
(Header::Image,image),
(Header::Rx,rx),
(Header::Tx,tx)
];
let headers_section = Layout::default() let headers_section = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints(container_splits.as_ref()) .constraints(container_splits.as_ref())
.split(split_bar[0]); .split(split_bar[0]);
// draw the actual header blocks
for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() {
for (index, (header, para)) in v.into_iter().enumerate() { gui_state
gui_state .lock()
.lock() .insert_into_header_map(header, headers_section[index]);
.insert_into_header_map(header, headers_section[index]); f.render_widget(paragraph, headers_section[index]);
f.render_widget(para, headers_section[index]); }
}
// gui_state.lock()
// f.render_widget(tx, headers_section[8]);
// f.render_widget(rx, headers_section[7]);
// f.render_widget(image, headers_section[6]);
// f.render_widget(name, headers_section[5]);
// f.render_widget(id, headers_section[4]);
// f.render_widget(mem, headers_section[3]);
// f.render_widget(cpu, headers_section[2]);
// f.render_widget(status, headers_section[1]);
// f.render_widget(state, headers_section[0]);
} }
let paragraph = Paragraph::new(info_text) let paragraph = Paragraph::new(info_text)
@@ -696,8 +634,8 @@ pub fn draw_help_box<B: Backend>(f: &mut Frame<'_, B>) {
.push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line"); .push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line");
help_text.push_str("\n ( enter ) to send docker container commands"); help_text.push_str("\n ( enter ) to send docker container commands");
help_text.push_str("\n ( h ) to toggle this help information"); help_text.push_str("\n ( h ) to toggle this help information");
help_text.push_str("\n ( 0 ) stop sort"); help_text.push_str("\n ( 0 ) stop sort");
help_text.push_str("\n ( 1 - 9 ) sort by matching header - or click header"); help_text.push_str("\n ( 1 - 9 ) sort by header - or click header");
help_text.push_str( help_text.push_str(
"\n ( m ) to toggle mouse capture - if disabled, text on screen can be selected & copied", "\n ( m ) to toggle mouse capture - if disabled, text on screen can be selected & copied",
); );
+4 -4
View File
@@ -221,16 +221,16 @@ impl GuiState {
} }
/// Insert selectable gui panel into area map /// Insert selectable gui panel into area map
/// Remove each time, as terminal may have been resized! /// Remove each time, as terminal may have been resized!
pub fn insert_into_panel_map(&mut self, panel: SelectablePanel, area: Rect) { pub fn insert_into_panel_map(&mut self, panel: SelectablePanel, area: Rect) {
self.panel_map.remove(&panel); self.panel_map.remove(&panel);
self.panel_map.insert(panel, area); self.panel_map.insert(panel, area);
} }
/// Insert selectable gui panel into area map /// Insert selectable gui panel into area map
/// Remove each time, as terminal may have been resized! /// Remove each time, as terminal may have been resized!
pub fn insert_into_header_map(&mut self, header: Header, area: Rect) { pub fn insert_into_header_map(&mut self, header: Header, area: Rect) {
self.heading_map.remove(&header); self.heading_map.remove(&header);
self.heading_map.insert(header, area); self.heading_map.insert(header, area);
} }
+1 -2
View File
@@ -215,9 +215,8 @@ fn ui<B: Backend>(
f, f,
has_containers, has_containers,
loading_icon, loading_icon,
sorted_by, sorted_by,
gui_state, gui_state,
); );
// only draw charts if there are containers // only draw charts if there are containers