From a70d6fa9681b2585036a0dae26b6f9e002a44ed9 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 22 Jul 2022 20:38:22 +0000 Subject: [PATCH] refacor: creation & drawing of headers --- src/docker_data/mod.rs | 4 +- src/input_handler/mod.rs | 12 +- src/ui/draw_blocks.rs | 586 +++++++++++++++++---------------------- src/ui/gui_state.rs | 8 +- src/ui/mod.rs | 3 +- 5 files changed, 278 insertions(+), 335 deletions(-) diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index deb5396..dba467d 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -169,8 +169,8 @@ impl DockerData { self.app_data.lock().update_containers(&output); - let current_sort = self.app_data.lock().get_sorted(); - self.app_data.lock().set_sorted(current_sort); + let current_sort = self.app_data.lock().get_sorted(); + self.app_data.lock().set_sorted(current_sort); output .iter() diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index 4bdbaa5..42ce0cf 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -152,7 +152,7 @@ impl InputHandler { } } else { 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('2') => self.sort(Header::Status), 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('h') => self.gui_state.lock().show_help = true, KeyCode::Char('m') => self.m_button(), - KeyCode::Tab => self.gui_state.lock().next_panel(), - KeyCode::BackTab => self.gui_state.lock().previous_panel(), + KeyCode::Tab => { + // 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 => { let mut locked_data = self.app_data.lock(); match self.gui_state.lock().selected_panel { diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index 5357808..d803443 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -125,162 +125,194 @@ pub fn draw_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 - // let sorted_width = if app_data.lock().get_sorted().is_some() { - // 2 - // }else { - // 0 - // }; + // if containers sorted, increase width to match headers + // let sorted_width = if app_data.lock().get_sorted().is_some() { + // 2 + // }else { + // 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 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() + 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$}", + 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::(), + 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::>(); // 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); + // .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 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$}", 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::(), - // 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::>(); - - 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$}", 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::(), - 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::>(); + // let lines = Spans::from(vec![ + // 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::(), + // 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::>(); if items.is_empty() { let debug_text = String::from("no containers running"); @@ -452,152 +484,98 @@ pub fn draw_heading_bar( gui_state: &Arc>, ) { 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); - - /// 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 mut color = Color::Black; - let mut s = ""; - let mut c = 0; + let mut suffix = ""; + let mut suffix_margin = 0; if let Some((a, b)) = sorted_by.as_ref() { if x == a { - match b{ - SortedOrder::Asc => s=" ⌄", - SortedOrder::Desc => s=" ⌃", - } - c = 2; + match b { + SortedOrder::Asc => suffix = " ⌄", + SortedOrder::Desc => suffix = " ⌃", + } + suffix_margin = 2; 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 - let state_block = header_block(&Header::State); - let state_text = format!( - " {}{:>width$}{ic}", - loading_icon, - columns.state.0, - ic=state_block.1, - width = columns.state.1 - state_block.2 - ); - let state_count = state_text.chars().count() as u16; - let state = Paragraph::new(state_text) - .block(state_block.0) - .alignment(Alignment::Left); + // 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 + let gen_header = |header: &Header, width: usize| { + let block = header_block(header); + let text = match header { + Header::State => format!( + " {}{:>width$}{ic}", + loading_icon, + header, + ic = block.1, + width = width - block.2, + ), + 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 status_block = header_block(&x); - let status_text = format!( - "{} {:>width$}{ic}", - MARGIN, - x, - ic=status_block.1, - width = w - status_block.2 - ); - - let count = status_text.chars().count() as u16; - 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 header_data = header_meta + .iter() + .map(|i| { + let header_block = gen_header(&i.0, i.1); + ( + header_block.0, + i.0.to_owned(), + Constraint::Max(header_block.1), + ) + }) + .collect::>(); let suffix = if info_visible { "exit" } else { "show" }; let info_text = format!("( h ) {} help {}", suffix, MARGIN); let info_width = info_text.chars().count() as u16; - let x = area.width - info_width; - let column_width = if x > 0 { x } else { 1 }; + let column_width = area.width - info_width; + let column_width = if column_width > 0 { column_width } else { 1 }; let splits = if has_containers { vec![Constraint::Min(column_width), Constraint::Min(info_width)] } else { @@ -609,60 +587,20 @@ pub fn draw_heading_bar( .constraints(splits.as_ref()) .split(area); if has_containers { - // let a = state_text.chars().count() as u16; - // 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 container_splits = header_data.iter().map(|i| i.2).collect::>(); let headers_section = Layout::default() .direction(Direction::Horizontal) .constraints(container_splits.as_ref()) .split(split_bar[0]); - - - for (index, (header, para)) in v.into_iter().enumerate() { - gui_state - .lock() - .insert_into_header_map(header, 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]); + // draw the actual header blocks + for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() { + gui_state + .lock() + .insert_into_header_map(header, headers_section[index]); + f.render_widget(paragraph, headers_section[index]); + } } let paragraph = Paragraph::new(info_text) @@ -696,8 +634,8 @@ pub fn draw_help_box(f: &mut Frame<'_, B>) { .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 ( h ) to toggle this help information"); - 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 ( 0 ) stop sort"); + help_text.push_str("\n ( 1 - 9 ) sort by header - or click header"); help_text.push_str( "\n ( m ) to toggle mouse capture - if disabled, text on screen can be selected & copied", ); diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 522f03c..ab40371 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -221,16 +221,16 @@ impl GuiState { } /// 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) { - self.panel_map.remove(&panel); + self.panel_map.remove(&panel); self.panel_map.insert(panel, area); } /// 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) { - self.heading_map.remove(&header); + self.heading_map.remove(&header); self.heading_map.insert(header, area); } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index fde9d40..8b7504a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -215,9 +215,8 @@ fn ui( f, has_containers, loading_icon, - sorted_by, + sorted_by, gui_state, - ); // only draw charts if there are containers