Skip to content

Commit 102dae1

Browse files
authored
Merge pull request #527 from Bluemangoo/feature/top-tui-disp
top: tui display control
2 parents 383fd8f + 7bd8e6d commit 102dae1

File tree

5 files changed

+195
-32
lines changed

5 files changed

+195
-32
lines changed

src/uu/top/src/picker.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ pub fn sysinfo() -> &'static RwLock<System> {
2424
}
2525

2626
pub trait Column {
27-
fn as_string(&self) -> String;
27+
fn as_string(&self, show_zeros: bool) -> String;
2828
fn cmp_dyn(&self, other: &dyn Column) -> Ordering;
2929
fn as_any(&self) -> &dyn Any;
3030
}
3131

3232
impl Column for String {
33-
fn as_string(&self) -> String {
33+
fn as_string(&self, _show_zeros: bool) -> String {
3434
self.clone()
3535
}
3636

@@ -47,7 +47,10 @@ impl Column for String {
4747
}
4848

4949
impl Column for u32 {
50-
fn as_string(&self) -> String {
50+
fn as_string(&self, show_zeros: bool) -> String {
51+
if !show_zeros && self == &0 {
52+
return String::new();
53+
}
5154
self.to_string()
5255
}
5356

@@ -64,7 +67,10 @@ impl Column for u32 {
6467
}
6568

6669
impl Column for Option<i32> {
67-
fn as_string(&self) -> String {
70+
fn as_string(&self, show_zeros: bool) -> String {
71+
if !show_zeros && self == &Some(0) {
72+
return String::new();
73+
}
6874
self.map(|v| v.to_string()).unwrap_or_default()
6975
}
7076

@@ -96,7 +102,10 @@ impl PercentValue {
96102
}
97103

98104
impl Column for PercentValue {
99-
fn as_string(&self) -> String {
105+
fn as_string(&self, show_zeros: bool) -> String {
106+
if !show_zeros && self.value == 0.0 {
107+
return String::new();
108+
}
100109
format!("{:.1}", self.value)
101110
}
102111

@@ -123,7 +132,10 @@ impl MemValue {
123132
}
124133

125134
impl Column for MemValue {
126-
fn as_string(&self) -> String {
135+
fn as_string(&self, show_zeros: bool) -> String {
136+
if !show_zeros && self.value == 0 {
137+
return String::new();
138+
}
127139
let mem_mb = self.value as f64 / bytesize::MIB as f64;
128140
if mem_mb >= 10000.0 {
129141
format!("{:.1}g", self.value as f64 / bytesize::GIB as f64)
@@ -156,7 +168,10 @@ impl TimeMSValue {
156168
}
157169

158170
impl Column for TimeMSValue {
159-
fn as_string(&self) -> String {
171+
fn as_string(&self, show_zeros: bool) -> String {
172+
if !show_zeros && self.min == 0 && self.sec < 0.01 {
173+
return String::new();
174+
}
160175
format!("{}:{:0>5.2}", self.min, self.sec)
161176
}
162177

src/uu/top/src/top.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ fn collect(settings: &Settings, fields: &[String], tui_stat: &TuiStat) -> Vec<Ve
235235
}
236236
collected
237237
.into_iter()
238-
.map(|it| it.into_iter().map(|c| c.as_string()).collect())
238+
.map(|it| {
239+
it.into_iter()
240+
.map(|c| c.as_string(tui_stat.show_zeros))
241+
.collect()
242+
})
239243
.collect()
240244
}
241245

src/uu/top/src/tui/input.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(crate) enum InputMode {
1818
}
1919
#[derive(Eq, PartialEq, Copy, Clone)]
2020
pub(crate) enum InputEvent {
21+
MaxListDisplay,
2122
NumaNode,
2223
}
2324

@@ -55,6 +56,11 @@ pub fn handle_input(
5556
stat.highlight_bold = !stat.highlight_bold;
5657
should_update.store(true, Ordering::Relaxed);
5758
}
59+
char!('C') => {
60+
let mut stat = tui_stat.write().unwrap();
61+
stat.show_coordinates = !stat.show_coordinates;
62+
should_update.store(true, Ordering::Relaxed);
63+
}
5864
char!('c') => {
5965
{
6066
// drop the lock as soon as possible
@@ -75,6 +81,17 @@ pub fn handle_input(
7581
stat.memory_graph_mode = stat.memory_graph_mode.next();
7682
should_update.store(true, Ordering::Relaxed);
7783
}
84+
char!('n') => {
85+
let mut stat = tui_stat.write().unwrap();
86+
stat.input_label = format!(
87+
"Maximum tasks = {}, change to (0 is unlimited)",
88+
stat.max_list_display
89+
);
90+
stat.input_value.clear();
91+
stat.input_mode = InputMode::Input(InputEvent::MaxListDisplay);
92+
93+
should_update.store(true, Ordering::Relaxed);
94+
}
7895
char!('R') => {
7996
{
8097
let mut stat = tui_stat.write().unwrap();
@@ -99,6 +116,16 @@ pub fn handle_input(
99116
stat.colorful = !stat.colorful;
100117
should_update.store(true, Ordering::Relaxed);
101118
}
119+
char!('0') => {
120+
{
121+
// drop the lock as soon as possible
122+
let mut stat = tui_stat.write().unwrap();
123+
stat.show_zeros = !stat.show_zeros;
124+
}
125+
126+
data.write().unwrap().1 = ProcList::new(settings, &tui_stat.read().unwrap());
127+
should_update.store(true, Ordering::Relaxed);
128+
}
102129
char!('1') => {
103130
let mut stat = tui_stat.write().unwrap();
104131
stat.cpu_value_mode = stat.cpu_value_mode.next();
@@ -132,6 +159,17 @@ pub fn handle_input(
132159
stat.cpu_column = stat.cpu_column % 8 + 1;
133160
should_update.store(true, Ordering::Relaxed);
134161
}
162+
char!('#') => {
163+
let mut stat = tui_stat.write().unwrap();
164+
stat.input_label = format!(
165+
"Maximum tasks = {}, change to (0 is unlimited)",
166+
stat.max_list_display
167+
);
168+
stat.input_value.clear();
169+
stat.input_mode = InputMode::Input(InputEvent::MaxListDisplay);
170+
171+
should_update.store(true, Ordering::Relaxed);
172+
}
135173
char!('<') => {
136174
{
137175
let mut stat = tui_stat.write().unwrap();
@@ -183,6 +221,24 @@ pub fn handle_input(
183221
stat.list_offset += 1;
184222
should_update.store(true, Ordering::Relaxed);
185223
}
224+
Event::Key(KeyEvent {
225+
code: KeyCode::Left,
226+
..
227+
}) => {
228+
let mut stat = tui_stat.write().unwrap();
229+
if stat.horizontal_offset > 0 {
230+
stat.horizontal_offset -= 1;
231+
should_update.store(true, Ordering::Relaxed);
232+
}
233+
}
234+
Event::Key(KeyEvent {
235+
code: KeyCode::Right,
236+
..
237+
}) => {
238+
let mut stat = tui_stat.write().unwrap();
239+
stat.horizontal_offset += 1;
240+
should_update.store(true, Ordering::Relaxed);
241+
}
186242
Event::Resize(_, _) => should_update.store(true, Ordering::Relaxed),
187243
_ => {}
188244
},
@@ -222,6 +278,21 @@ fn handle_input_value(
222278
should_update: &AtomicBool,
223279
) {
224280
match input_event {
281+
InputEvent::MaxListDisplay => {
282+
let input_value = { tui_stat.read().unwrap().input_value.parse::<usize>() };
283+
if input_value.is_err() {
284+
let mut stat = tui_stat.write().unwrap();
285+
stat.reset_input();
286+
stat.input_error = Some(" invalid number ".into());
287+
should_update.store(true, Ordering::Relaxed);
288+
return;
289+
}
290+
let input_value = input_value.unwrap();
291+
let mut stat = tui_stat.write().unwrap();
292+
stat.max_list_display = input_value;
293+
stat.reset_input();
294+
should_update.store(true, Ordering::Relaxed);
295+
}
225296
InputEvent::NumaNode => {
226297
let input_value = { tui_stat.read().unwrap().input_value.parse::<usize>() };
227298
let numa_nodes = get_numa_nodes();

src/uu/top/src/tui/mod.rs

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,24 @@ impl<'a> Tui<'a> {
6565
height
6666
}
6767

68+
fn calc_list_coordinates(&self) -> (usize, usize) {
69+
let list_total = self.proc_list.collected.len();
70+
let list_offset = self.stat.list_offset;
71+
(list_offset, list_total)
72+
}
73+
74+
fn calc_column_coordinates(&self) -> (usize, usize, usize) {
75+
let total_columns = self.proc_list.fields.len();
76+
let horizontal_offset = self.stat.horizontal_offset;
77+
let column_coordinate = min(horizontal_offset, total_columns - 1);
78+
let horizontal_offset = if horizontal_offset >= total_columns {
79+
horizontal_offset - (total_columns - 1)
80+
} else {
81+
0
82+
};
83+
(column_coordinate, total_columns, horizontal_offset * 8)
84+
}
85+
6886
fn render_header(&self, area: Rect, buf: &mut Buffer) {
6987
let constraints = vec![Constraint::Length(1); self.calc_header_height() as usize];
7088
let colorful = self.stat.colorful;
@@ -347,11 +365,32 @@ impl<'a> Tui<'a> {
347365
.render(layout[0], buf);
348366
return;
349367
}
350-
let input = Line::from(vec![
351-
Span::styled(&self.stat.input_label, Style::default().primary(colorful)),
352-
Span::raw(" "),
353-
Span::raw(&self.stat.input_value),
354-
]);
368+
let input = if !self.stat.input_label.is_empty() || !self.stat.input_value.is_empty() {
369+
Line::from(vec![
370+
Span::styled(&self.stat.input_label, Style::default().primary(colorful)),
371+
Span::raw(" "),
372+
Span::raw(&self.stat.input_value),
373+
])
374+
} else if self.stat.show_coordinates {
375+
let list_coordinates = self.calc_list_coordinates();
376+
let column_coordinates = self.calc_column_coordinates();
377+
Line::from(vec![
378+
Span::raw(format!(
379+
" scroll coordinates: y = {}/{} (tasks), x = {}/{} (fields)",
380+
list_coordinates.0 + 1,
381+
list_coordinates.1,
382+
column_coordinates.0 + 1,
383+
column_coordinates.1
384+
)),
385+
Span::raw(if column_coordinates.2 > 0 {
386+
format!(" + {}", column_coordinates.2)
387+
} else {
388+
String::new()
389+
}),
390+
])
391+
} else {
392+
Line::from("")
393+
};
355394
input.render(area, buf);
356395
}
357396

@@ -386,37 +425,57 @@ impl<'a> Tui<'a> {
386425
_ => Constraint::Length(0),
387426
};
388427

428+
let list_coordinates = self.calc_list_coordinates();
429+
let column_coordinates = self.calc_column_coordinates();
430+
389431
let constraints: Vec<Constraint> = self
390432
.proc_list
391433
.fields
392434
.iter()
393435
.map(|field| build_constraint(field))
436+
.skip(column_coordinates.0)
394437
.collect();
395438

396-
self.stat.list_offset = min(self.stat.list_offset, self.proc_list.collected.len() - 1);
397-
398-
let header =
399-
Row::new(self.proc_list.fields.clone()).style(Style::default().bg_secondary(colorful));
439+
let header = Row::new(
440+
self.proc_list
441+
.fields
442+
.clone()
443+
.split_off(column_coordinates.0),
444+
)
445+
.style(Style::default().bg_secondary(colorful));
400446

401447
let rows = self.proc_list.collected.iter().map(|item| {
402-
let cells = item.iter().enumerate().map(|(n, c)| {
403-
if highlight_sorted && n == highlight_column {
404-
Cell::from(Span::styled(
405-
c,
406-
if highlight_bold {
407-
Style::default().bg_primary(colorful)
448+
let cells = item
449+
.iter()
450+
.enumerate()
451+
.skip(column_coordinates.0)
452+
.map(|(n, c)| {
453+
let c = if column_coordinates.2 > 0 {
454+
if c.len() < column_coordinates.2 {
455+
""
408456
} else {
409-
Style::default().primary(colorful)
410-
},
411-
))
412-
} else {
413-
Cell::from(c.as_str())
414-
}
415-
});
457+
&c[column_coordinates.2..]
458+
}
459+
} else {
460+
c
461+
};
462+
if highlight_sorted && n == highlight_column {
463+
Cell::from(Span::styled(
464+
c,
465+
if highlight_bold {
466+
Style::default().bg_primary(colorful)
467+
} else {
468+
Style::default().primary(colorful)
469+
},
470+
))
471+
} else {
472+
Cell::from(c)
473+
}
474+
});
416475
Row::new(cells).height(1)
417476
});
418477

419-
let mut state = TableState::default().with_offset(self.stat.list_offset);
478+
let mut state = TableState::default().with_offset(list_coordinates.0);
420479

421480
let table = Table::new(rows, constraints).header(header);
422481
StatefulWidget::render(table, area, buf, &mut state);
@@ -425,6 +484,7 @@ impl<'a> Tui<'a> {
425484

426485
impl Widget for Tui<'_> {
427486
fn render(mut self, area: Rect, buf: &mut Buffer) {
487+
self.stat.list_offset = min(self.stat.list_offset, self.proc_list.collected.len() - 1);
428488
let layout = Layout::new(
429489
Direction::Vertical,
430490
[
@@ -437,6 +497,11 @@ impl Widget for Tui<'_> {
437497

438498
self.render_header(layout[0], buf);
439499
self.render_input(layout[1], buf);
440-
self.render_list(layout[2], buf);
500+
let mut list_area = layout[2];
501+
if self.stat.max_list_display > 0 {
502+
let list_height = min(layout[2].height, self.stat.max_list_display as u16) + 1; // 1 for header
503+
list_area.height = list_height;
504+
}
505+
self.render_list(list_area, buf);
441506
}
442507
}

0 commit comments

Comments
 (0)