diff --git a/src/uu/top/src/picker.rs b/src/uu/top/src/picker.rs index ff429a2..afe82e8 100644 --- a/src/uu/top/src/picker.rs +++ b/src/uu/top/src/picker.rs @@ -24,13 +24,13 @@ pub fn sysinfo() -> &'static RwLock { } pub trait Column { - fn as_string(&self) -> String; + fn as_string(&self, show_zeros: bool) -> String; fn cmp_dyn(&self, other: &dyn Column) -> Ordering; fn as_any(&self) -> &dyn Any; } impl Column for String { - fn as_string(&self) -> String { + fn as_string(&self, _show_zeros: bool) -> String { self.clone() } @@ -47,7 +47,10 @@ impl Column for String { } impl Column for u32 { - fn as_string(&self) -> String { + fn as_string(&self, show_zeros: bool) -> String { + if !show_zeros && self == &0 { + return String::new(); + } self.to_string() } @@ -64,7 +67,10 @@ impl Column for u32 { } impl Column for Option { - fn as_string(&self) -> String { + fn as_string(&self, show_zeros: bool) -> String { + if !show_zeros && self == &Some(0) { + return String::new(); + } self.map(|v| v.to_string()).unwrap_or_default() } @@ -96,7 +102,10 @@ impl PercentValue { } impl Column for PercentValue { - fn as_string(&self) -> String { + fn as_string(&self, show_zeros: bool) -> String { + if !show_zeros && self.value == 0.0 { + return String::new(); + } format!("{:.1}", self.value) } @@ -123,7 +132,10 @@ impl MemValue { } impl Column for MemValue { - fn as_string(&self) -> String { + fn as_string(&self, show_zeros: bool) -> String { + if !show_zeros && self.value == 0 { + return String::new(); + } let mem_mb = self.value as f64 / bytesize::MIB as f64; if mem_mb >= 10000.0 { format!("{:.1}g", self.value as f64 / bytesize::GIB as f64) @@ -156,7 +168,10 @@ impl TimeMSValue { } impl Column for TimeMSValue { - fn as_string(&self) -> String { + fn as_string(&self, show_zeros: bool) -> String { + if !show_zeros && self.min == 0 && self.sec < 0.01 { + return String::new(); + } format!("{}:{:0>5.2}", self.min, self.sec) } diff --git a/src/uu/top/src/top.rs b/src/uu/top/src/top.rs index 2f49c6a..1ba0118 100644 --- a/src/uu/top/src/top.rs +++ b/src/uu/top/src/top.rs @@ -235,7 +235,11 @@ fn collect(settings: &Settings, fields: &[String], tui_stat: &TuiStat) -> Vec { + let mut stat = tui_stat.write().unwrap(); + stat.show_coordinates = !stat.show_coordinates; + should_update.store(true, Ordering::Relaxed); + } char!('c') => { { // drop the lock as soon as possible @@ -75,6 +81,17 @@ pub fn handle_input( stat.memory_graph_mode = stat.memory_graph_mode.next(); should_update.store(true, Ordering::Relaxed); } + char!('n') => { + let mut stat = tui_stat.write().unwrap(); + stat.input_label = format!( + "Maximum tasks = {}, change to (0 is unlimited)", + stat.max_list_display + ); + stat.input_value.clear(); + stat.input_mode = InputMode::Input(InputEvent::MaxListDisplay); + + should_update.store(true, Ordering::Relaxed); + } char!('R') => { { let mut stat = tui_stat.write().unwrap(); @@ -99,6 +116,16 @@ pub fn handle_input( stat.colorful = !stat.colorful; should_update.store(true, Ordering::Relaxed); } + char!('0') => { + { + // drop the lock as soon as possible + let mut stat = tui_stat.write().unwrap(); + stat.show_zeros = !stat.show_zeros; + } + + data.write().unwrap().1 = ProcList::new(settings, &tui_stat.read().unwrap()); + should_update.store(true, Ordering::Relaxed); + } char!('1') => { let mut stat = tui_stat.write().unwrap(); stat.cpu_value_mode = stat.cpu_value_mode.next(); @@ -132,6 +159,17 @@ pub fn handle_input( stat.cpu_column = stat.cpu_column % 8 + 1; should_update.store(true, Ordering::Relaxed); } + char!('#') => { + let mut stat = tui_stat.write().unwrap(); + stat.input_label = format!( + "Maximum tasks = {}, change to (0 is unlimited)", + stat.max_list_display + ); + stat.input_value.clear(); + stat.input_mode = InputMode::Input(InputEvent::MaxListDisplay); + + should_update.store(true, Ordering::Relaxed); + } char!('<') => { { let mut stat = tui_stat.write().unwrap(); @@ -183,6 +221,24 @@ pub fn handle_input( stat.list_offset += 1; should_update.store(true, Ordering::Relaxed); } + Event::Key(KeyEvent { + code: KeyCode::Left, + .. + }) => { + let mut stat = tui_stat.write().unwrap(); + if stat.horizontal_offset > 0 { + stat.horizontal_offset -= 1; + should_update.store(true, Ordering::Relaxed); + } + } + Event::Key(KeyEvent { + code: KeyCode::Right, + .. + }) => { + let mut stat = tui_stat.write().unwrap(); + stat.horizontal_offset += 1; + should_update.store(true, Ordering::Relaxed); + } Event::Resize(_, _) => should_update.store(true, Ordering::Relaxed), _ => {} }, @@ -222,6 +278,21 @@ fn handle_input_value( should_update: &AtomicBool, ) { match input_event { + InputEvent::MaxListDisplay => { + let input_value = { tui_stat.read().unwrap().input_value.parse::() }; + if input_value.is_err() { + let mut stat = tui_stat.write().unwrap(); + stat.reset_input(); + stat.input_error = Some(" invalid number ".into()); + should_update.store(true, Ordering::Relaxed); + return; + } + let input_value = input_value.unwrap(); + let mut stat = tui_stat.write().unwrap(); + stat.max_list_display = input_value; + stat.reset_input(); + should_update.store(true, Ordering::Relaxed); + } InputEvent::NumaNode => { let input_value = { tui_stat.read().unwrap().input_value.parse::() }; let numa_nodes = get_numa_nodes(); diff --git a/src/uu/top/src/tui/mod.rs b/src/uu/top/src/tui/mod.rs index 99f7a12..0ec10a5 100644 --- a/src/uu/top/src/tui/mod.rs +++ b/src/uu/top/src/tui/mod.rs @@ -65,6 +65,24 @@ impl<'a> Tui<'a> { height } + fn calc_list_coordinates(&self) -> (usize, usize) { + let list_total = self.proc_list.collected.len(); + let list_offset = self.stat.list_offset; + (list_offset, list_total) + } + + fn calc_column_coordinates(&self) -> (usize, usize, usize) { + let total_columns = self.proc_list.fields.len(); + let horizontal_offset = self.stat.horizontal_offset; + let column_coordinate = min(horizontal_offset, total_columns - 1); + let horizontal_offset = if horizontal_offset >= total_columns { + horizontal_offset - (total_columns - 1) + } else { + 0 + }; + (column_coordinate, total_columns, horizontal_offset * 8) + } + fn render_header(&self, area: Rect, buf: &mut Buffer) { let constraints = vec![Constraint::Length(1); self.calc_header_height() as usize]; let colorful = self.stat.colorful; @@ -347,11 +365,32 @@ impl<'a> Tui<'a> { .render(layout[0], buf); return; } - let input = Line::from(vec![ - Span::styled(&self.stat.input_label, Style::default().primary(colorful)), - Span::raw(" "), - Span::raw(&self.stat.input_value), - ]); + let input = if !self.stat.input_label.is_empty() || !self.stat.input_value.is_empty() { + Line::from(vec![ + Span::styled(&self.stat.input_label, Style::default().primary(colorful)), + Span::raw(" "), + Span::raw(&self.stat.input_value), + ]) + } else if self.stat.show_coordinates { + let list_coordinates = self.calc_list_coordinates(); + let column_coordinates = self.calc_column_coordinates(); + Line::from(vec![ + Span::raw(format!( + " scroll coordinates: y = {}/{} (tasks), x = {}/{} (fields)", + list_coordinates.0 + 1, + list_coordinates.1, + column_coordinates.0 + 1, + column_coordinates.1 + )), + Span::raw(if column_coordinates.2 > 0 { + format!(" + {}", column_coordinates.2) + } else { + String::new() + }), + ]) + } else { + Line::from("") + }; input.render(area, buf); } @@ -386,37 +425,57 @@ impl<'a> Tui<'a> { _ => Constraint::Length(0), }; + let list_coordinates = self.calc_list_coordinates(); + let column_coordinates = self.calc_column_coordinates(); + let constraints: Vec = self .proc_list .fields .iter() .map(|field| build_constraint(field)) + .skip(column_coordinates.0) .collect(); - self.stat.list_offset = min(self.stat.list_offset, self.proc_list.collected.len() - 1); - - let header = - Row::new(self.proc_list.fields.clone()).style(Style::default().bg_secondary(colorful)); + let header = Row::new( + self.proc_list + .fields + .clone() + .split_off(column_coordinates.0), + ) + .style(Style::default().bg_secondary(colorful)); let rows = self.proc_list.collected.iter().map(|item| { - let cells = item.iter().enumerate().map(|(n, c)| { - if highlight_sorted && n == highlight_column { - Cell::from(Span::styled( - c, - if highlight_bold { - Style::default().bg_primary(colorful) + let cells = item + .iter() + .enumerate() + .skip(column_coordinates.0) + .map(|(n, c)| { + let c = if column_coordinates.2 > 0 { + if c.len() < column_coordinates.2 { + "" } else { - Style::default().primary(colorful) - }, - )) - } else { - Cell::from(c.as_str()) - } - }); + &c[column_coordinates.2..] + } + } else { + c + }; + if highlight_sorted && n == highlight_column { + Cell::from(Span::styled( + c, + if highlight_bold { + Style::default().bg_primary(colorful) + } else { + Style::default().primary(colorful) + }, + )) + } else { + Cell::from(c) + } + }); Row::new(cells).height(1) }); - let mut state = TableState::default().with_offset(self.stat.list_offset); + let mut state = TableState::default().with_offset(list_coordinates.0); let table = Table::new(rows, constraints).header(header); StatefulWidget::render(table, area, buf, &mut state); @@ -425,6 +484,7 @@ impl<'a> Tui<'a> { impl Widget for Tui<'_> { fn render(mut self, area: Rect, buf: &mut Buffer) { + self.stat.list_offset = min(self.stat.list_offset, self.proc_list.collected.len() - 1); let layout = Layout::new( Direction::Vertical, [ @@ -437,6 +497,11 @@ impl Widget for Tui<'_> { self.render_header(layout[0], buf); self.render_input(layout[1], buf); - self.render_list(layout[2], buf); + let mut list_area = layout[2]; + if self.stat.max_list_display > 0 { + let list_height = min(layout[2].height, self.stat.max_list_display as u16) + 1; // 1 for header + list_area.height = list_height; + } + self.render_list(list_area, buf); } } diff --git a/src/uu/top/src/tui/stat.rs b/src/uu/top/src/tui/stat.rs index 5df47c7..75678dc 100644 --- a/src/uu/top/src/tui/stat.rs +++ b/src/uu/top/src/tui/stat.rs @@ -19,6 +19,8 @@ pub(crate) struct TuiStat { pub memory_graph_mode: MemoryGraphMode, pub cpu_column: u16, pub list_offset: usize, + pub horizontal_offset: usize, + pub max_list_display: usize, pub colorful: bool, pub full_command_line: bool, pub delay: Duration, @@ -26,6 +28,8 @@ pub(crate) struct TuiStat { pub sort_by_pid: bool, pub highlight_sorted: bool, pub highlight_bold: bool, + pub show_coordinates: bool, + pub show_zeros: bool, } impl TuiStat { @@ -49,6 +53,8 @@ impl TuiStat { memory_graph_mode: MemoryGraphMode::default(), cpu_column: 2, list_offset: 0, + horizontal_offset: 0, + max_list_display: 0, // unlimited colorful: true, full_command_line: true, delay: Duration::from_millis(1500), // 1.5s @@ -56,6 +62,8 @@ impl TuiStat { sort_by_pid: false, highlight_sorted: false, highlight_bold: false, + show_coordinates: false, + show_zeros: true, } }