From 96ca2081972f91cd6238c6121c2fa017cc888c1a Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:38:19 +0000 Subject: [PATCH] wip: header+column widths --- src/app_data/mod.rs | 37 ++- src/docker_data/mod.rs | 3 +- src/input_handler/mod.rs | 36 ++- src/ui/draw_blocks.rs | 479 +++++++++++++++++++++++++++------------ src/ui/gui_state.rs | 37 ++- src/ui/mod.rs | 8 +- 6 files changed, 430 insertions(+), 170 deletions(-) diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 52de37c..ec3fad4 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -1,7 +1,10 @@ use bollard::models::ContainerSummary; use core::fmt; -use std::time::{SystemTime, UNIX_EPOCH}; -use tui::widgets::ListItem; +use std::{ + collections::HashMap, + time::{SystemTime, UNIX_EPOCH}, +}; +use tui::{layout::Rect, widgets::ListItem}; mod container_state; @@ -18,6 +21,7 @@ pub struct AppData { pub init: bool, pub show_error: bool, sorted_by: Option<(Header, SortedOrder)>, + // heading_map: HashMap } #[derive(Debug, Clone)] @@ -26,7 +30,7 @@ pub enum SortedOrder { Desc, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Hash, Eq)] pub enum Header { State, Status, @@ -62,12 +66,17 @@ impl AppData { self.sorted_by.clone() } - /// Change the sorted order, also set the selected pointer! + /// Change the sorted order, also set the selected container state to match new order pub fn set_sorted(&mut self, x: Option<(Header, SortedOrder)>) { self.sorted_by = x; - let id = self.get_selected_container_id(); + let id = self.get_selected_container_id(); self.sort_containers(); - self.containers.state.select(self.containers.items.iter().position(|i| Some(i.id.to_owned()) == id)); + self.containers.state.select( + self.containers + .items + .iter() + .position(|i| Some(i.id.to_owned()) == id), + ); } /// Generate a default app_state pub fn default(args: CliArgs) -> Self { @@ -78,10 +87,24 @@ impl AppData { init: false, logs_parsed: false, show_error: false, - sorted_by: Some((Header::Memory, SortedOrder::Asc)), + sorted_by: None, } } + // fn heading_click(&self) { + // if let Some(data) = self + // .heading_map + // .iter() + // .filter(|i| i.1.intersects(rect)) + // .collect::>() + // .get(0) + // { + // // self.selected_panel = *data.0; + + // } + + // } + // Current time as unix timestamp fn get_systemtime(&self) -> u64 { SystemTime::now() diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 5bfde67..deb5396 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -169,7 +169,8 @@ impl DockerData { self.app_data.lock().update_containers(&output); - // self.app_data.lock().sort_containers(SortedOrder::Asc, Header::State); + 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 d7e3260..4bdbaa5 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -117,11 +117,9 @@ impl InputHandler { fn sort(&self, header: Header) { let mut locked_data = self.app_data.lock(); - if let Some((s, h)) = locked_data.get_sorted().as_ref() { - match (s, h) { - (header, SortedOrder::Asc) => { - locked_data.set_sorted(Some((header.to_owned(), SortedOrder::Desc))) - } + if let Some((_, order)) = locked_data.get_sorted().as_ref() { + match order { + SortedOrder::Asc => locked_data.set_sorted(Some((header, SortedOrder::Desc))), _ => locked_data.set_sorted(Some((header, SortedOrder::Asc))), } } else { @@ -154,13 +152,14 @@ impl InputHandler { } } else { match key_code { + 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), KeyCode::Char('4') => self.sort(Header::Memory), KeyCode::Char('5') => self.sort(Header::Id), - KeyCode::Char('6') => self.sort(Header::Image), - KeyCode::Char('7') => self.sort(Header::Name), + KeyCode::Char('6') => self.sort(Header::Name), + KeyCode::Char('7') => self.sort(Header::Image), KeyCode::Char('8') => self.sort(Header::Rx), KeyCode::Char('9') => self.sort(Header::Tx), KeyCode::Char('q') => self.is_running.store(false, Ordering::SeqCst), @@ -247,7 +246,28 @@ impl InputHandler { MouseEventKind::ScrollUp => self.previous(), MouseEventKind::ScrollDown => self.next(), MouseEventKind::Down(MouseButton::Left) => { - self.gui_state.lock().rect_insersects(Rect::new( + let header_int = self.gui_state.lock().header_intersect(Rect::new( + mouse_event.column, + mouse_event.row, + 1, + 1, + )); + + /// Don't like this + let order = if let Some((_, or)) = self.app_data.lock().get_sorted() { + match or { + SortedOrder::Asc => SortedOrder::Desc, + SortedOrder::Desc => SortedOrder::Asc, + } + } else { + SortedOrder::Asc + }; + + if let Some(header) = header_int { + self.app_data.lock().set_sorted(Some((header, order))) + } + + self.gui_state.lock().panel_intersect(Rect::new( mouse_event.column, mouse_event.row, 1, diff --git a/src/ui/draw_blocks.rs b/src/ui/draw_blocks.rs index bb6d263..5357808 100644 --- a/src/ui/draw_blocks.rs +++ b/src/ui/draw_blocks.rs @@ -48,7 +48,7 @@ fn generate_block<'a>( gui_state: &Arc>, panel: SelectablePanel, ) -> Block<'a> { - gui_state.lock().insert_into_area_map(panel, area); + gui_state.lock().insert_into_panel_map(panel, area); let mut block = Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded); @@ -124,72 +124,163 @@ pub fn draw_containers( widths: &Columns, ) { let block = generate_block(app_data, area, gui_state, SelectablePanel::Containers); - 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 sorted = app_data.lock().get_sorted(); - 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 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() + + }; + + // 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); + + 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::>(); if items.is_empty() { let debug_text = String::from("no containers running"); @@ -357,99 +448,149 @@ pub fn draw_heading_bar( f: &mut Frame<'_, B>, has_containers: bool, loading_icon: String, - info_visible: bool, sorted_by: Option<(Header, SortedOrder)>, + gui_state: &Arc>, ) { let block = || Block::default().style(Style::default().bg(Color::Magenta).fg(Color::Black)); + let info_visible = gui_state.lock().show_help; f.render_widget(block(), area); - let aaa = |x: &Header| { - let mut output = ""; + + /// 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; if let Some((a, b)) = sorted_by.as_ref() { if x == a { - output = match b { - SortedOrder::Asc => "A", - SortedOrder::Desc => "B", - }; + match b{ + SortedOrder::Asc => s=" ⌄", + SortedOrder::Desc => s=" ⌃", + } + c = 2; + color = Color::White }; }; - output + + (Block::default().style(Style::default().bg(Color::Magenta).fg(color)), s, c) }; - // need to split this into blocks, and put each block in the split block, and set color to white if is selected - // then just put in the split in a horizontal fashion, with a width equal to widtrh, or char count? - // let white = "\x1b[37m"; - // let reset = "\x1b[0m"; - - // let mut column_headings = format!( - // " {}{:>width$}{}", - // loading_icon, - // columns.state.0, - // width = columns.state.1, - // ); - - // Each - let mut column_headings = format!( - " {}{:>width$}", + // 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, - width = columns.state.1 - ); - column_headings.push_str( - format!( - "{} {:>width$}", - MARGIN, - columns.status.0, - width = columns.status.1 - ) - .as_str(), + 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); - // Get selected and sorted - // Maybe each heading needs to be its own boock - column_headings - .push_str(format!("{}{:>width$}", MARGIN, columns.cpu.0, width = columns.cpu.1).as_str()); - column_headings - .push_str(format!("{}{:>width$}", MARGIN, columns.mem.0, width = columns.mem.1).as_str()); - column_headings - .push_str(format!("{}{:>width$}", MARGIN, columns.id.0, width = columns.id.1).as_str()); - column_headings.push_str( - format!( - "{}{:>width$}", - MARGIN, - columns.name.0, - width = columns.name.1 - ) - .as_str(), + + + 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 ); - column_headings.push_str( - format!( - "{}{:>width$}", - MARGIN, - columns.image.0, - width = columns.image.1 - ) - .as_str(), + 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 ); - column_headings.push_str( - format!( - "{}{:>width$}", - MARGIN, - columns.net_rx.0, - width = columns.net_rx.1 - ) - .as_str(), + 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 ); - column_headings.push_str( - format!( - "{}{:>width$}", - MARGIN, - columns.net_tx.0, - width = columns.net_tx.1 - ) - .as_str(), + 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 info_text = format!("( h ) {} help {}", suffix, MARGIN); @@ -467,12 +608,61 @@ pub fn draw_heading_bar( .direction(Direction::Horizontal) .constraints(splits.as_ref()) .split(area); - if has_containers { - let paragraph = Paragraph::new(column_headings) - .block(block()) - .alignment(Alignment::Left); - f.render_widget(paragraph, split_bar[0]); + // 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 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]); } let paragraph = Paragraph::new(info_text) @@ -506,7 +696,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 ( 1 - 9 ) order headers"); + 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 ( 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 97983da..522f03c 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -1,6 +1,8 @@ use std::{collections::HashMap, fmt}; use tui::layout::{Constraint, Rect}; +use crate::app_data::Header; + #[derive(Debug, PartialEq, std::hash::Hash, std::cmp::Eq, Clone, Copy)] pub enum SelectablePanel { Containers, @@ -165,7 +167,8 @@ pub struct GuiState { // Think this should be a BMapTree, so can define order when iterating over potential intersects // Is an issue if two panels are in the same space, sush as a smaller panel embedded, yet infront of, a larger panel // If a BMapTree think it would mean have to implement ordering for SelectablePanel - area_map: HashMap, + panel_map: HashMap, + heading_map: HashMap, loading_icon: Loading, // Should be a vec, each time loading add a new to the vec, and reset remove from vec // for for if is_loading just check if vec is empty or not @@ -179,7 +182,8 @@ impl GuiState { /// Generate a default gui_state pub fn default() -> Self { Self { - area_map: HashMap::new(), + panel_map: HashMap::new(), + heading_map: HashMap::new(), loading_icon: Loading::One, selected_panel: SelectablePanel::Containers, show_help: false, @@ -190,13 +194,13 @@ impl GuiState { /// clear panels hash map, so on resize can fix the sizes for mouse clicks pub fn clear_area_map(&mut self) { - self.area_map.clear(); + self.panel_map.clear(); } /// Check if a given Rect (a clicked area of 1x1), interacts with any known panels - pub fn rect_insersects(&mut self, rect: Rect) { + pub fn panel_intersect(&mut self, rect: Rect) { if let Some(data) = self - .area_map + .panel_map .iter() .filter(|i| i.1.intersects(rect)) .collect::>() @@ -206,9 +210,28 @@ impl GuiState { } } + /// Check if a given Rect (a clicked area of 1x1), interacts with any known panels + pub fn header_intersect(&mut self, rect: Rect) -> Option
{ + self.heading_map + .iter() + .filter(|i| i.1.intersects(rect)) + .collect::>() + .get(0) + .map(|data| data.0.to_owned()) + } + /// Insert selectable gui panel into area map - pub fn insert_into_area_map(&mut self, panel: SelectablePanel, area: Rect) { - self.area_map.entry(panel).or_insert(area); + /// 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.insert(panel, area); + } + + /// Insert selectable gui panel into area map + /// 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.insert(header, area); } /// Change to next selectable panel diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b937dcf..fde9d40 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -154,10 +154,11 @@ fn ui( let has_containers = !app_data.lock().containers.items.is_empty(); let has_error = app_data.lock().get_error(); let log_index = app_data.lock().get_selected_log_index(); + let sorted_by = app_data.lock().get_sorted(); + let show_help = gui_state.lock().show_help; let info_text = gui_state.lock().info_box_text.clone(); let loading_icon = gui_state.lock().get_loading(); - let sorted_by = app_data.lock().get_sorted(); let whole_layout = Layout::default() .direction(Direction::Vertical) @@ -214,8 +215,9 @@ fn ui( f, has_containers, loading_icon, - show_help, - sorted_by, + sorted_by, + gui_state, + ); // only draw charts if there are containers