mirror of
https://github.com/danog/ytop.git
synced 2024-11-30 04:29:10 +01:00
Implement basic scrolling, pausing, and tabbing
This commit is contained in:
parent
0e4e9ff00e
commit
d88e202098
@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add sensor label to temperature identifier
|
||||
- Process cpu percents are now working
|
||||
- Draw the proc cursor
|
||||
- Add basic scrolling for `g`, `G`, `j`, and `k`
|
||||
- Add process grouping toggling with `Tab`
|
||||
- Add pausing with `Space`
|
||||
|
||||
### Changed
|
||||
|
||||
|
41
src/draw.rs
41
src/draw.rs
@ -108,3 +108,44 @@ pub fn draw_help_menu<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) ->
|
||||
app.help_menu.render(&mut frame, rect);
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: figure out how to draw the proc widget without clearing rest of the screen
|
||||
pub fn draw_proc<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
|
||||
draw(terminal, app)
|
||||
// terminal.draw(|mut frame| {
|
||||
// let chunks = if app.statusbar.is_some() {
|
||||
// Layout::default()
|
||||
// .constraints([Constraint::Min(0), Constraint::Length(1)].as_ref())
|
||||
// .split(frame.size())
|
||||
// } else {
|
||||
// Layout::default()
|
||||
// .constraints(vec![Constraint::Percentage(100)])
|
||||
// .split(frame.size())
|
||||
// };
|
||||
|
||||
// let vertical_chunks = if app.widgets.temp.is_some() {
|
||||
// Layout::default()
|
||||
// .direction(Direction::Vertical)
|
||||
// .constraints(
|
||||
// [
|
||||
// Constraint::Ratio(1, 3),
|
||||
// Constraint::Ratio(1, 3),
|
||||
// Constraint::Ratio(1, 3),
|
||||
// ]
|
||||
// .as_ref(),
|
||||
// )
|
||||
// .split(chunks[0])
|
||||
// } else {
|
||||
// Layout::default()
|
||||
// .direction(Direction::Vertical)
|
||||
// .constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)].as_ref())
|
||||
// .split(chunks[0])
|
||||
// };
|
||||
|
||||
// let horizontal_chunks = Layout::default()
|
||||
// .direction(Direction::Horizontal)
|
||||
// .constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)].as_ref())
|
||||
// .split(*vertical_chunks.last().unwrap());
|
||||
// app.widgets.proc.render(&mut frame, horizontal_chunks[1]);
|
||||
// })
|
||||
}
|
||||
|
62
src/main.rs
62
src/main.rs
@ -15,7 +15,7 @@ use std::time::{Duration, Instant};
|
||||
use anyhow::Result;
|
||||
use backtrace::Backtrace;
|
||||
use crossbeam_channel::{select, tick, unbounded, Receiver};
|
||||
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
||||
use crossterm::event::{self, Event, KeyCode, KeyModifiers, MouseEvent};
|
||||
use crossterm::execute;
|
||||
use crossterm::terminal;
|
||||
use num_rational::Ratio;
|
||||
@ -108,6 +108,7 @@ fn main() {
|
||||
let args = Args::from_args();
|
||||
let update_ratio = Ratio::new(1, args.rate);
|
||||
let mut show_help_menu = false;
|
||||
let mut paused = false;
|
||||
|
||||
let program_name = env!("CARGO_PKG_NAME");
|
||||
let app_dirs = AppDirs::new(Some(program_name), AppUI::CommandLine).unwrap();
|
||||
@ -135,10 +136,12 @@ fn main() {
|
||||
break;
|
||||
}
|
||||
recv(ticker) -> _ => {
|
||||
update_seconds = (update_seconds + update_ratio) % Ratio::from_integer(60);
|
||||
update_widgets(&mut app.widgets, update_seconds);
|
||||
if !show_help_menu {
|
||||
draw(&mut terminal, &mut app).unwrap();
|
||||
if !paused {
|
||||
update_seconds = (update_seconds + update_ratio) % Ratio::from_integer(60);
|
||||
update_widgets(&mut app.widgets, update_seconds);
|
||||
if !show_help_menu {
|
||||
draw(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
recv(ui_events_receiver) -> message => {
|
||||
@ -160,6 +163,33 @@ fn main() {
|
||||
draw(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
' ' => {
|
||||
paused = !paused;
|
||||
},
|
||||
'j' => {
|
||||
app.widgets.proc.scroll_down();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
'k' => {
|
||||
app.widgets.proc.scroll_up();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
'g' => {
|
||||
app.widgets.proc.scroll_top();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
'G' => {
|
||||
app.widgets.proc.scroll_bottom();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
@ -169,6 +199,12 @@ fn main() {
|
||||
draw(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
}
|
||||
KeyCode::Tab => {
|
||||
app.widgets.proc.toggle_grouping();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
} else if key_event.modifiers == KeyModifiers::CONTROL {
|
||||
@ -186,9 +222,21 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: figure out why these aren't working
|
||||
Event::Mouse(mouse_event) => match mouse_event {
|
||||
_ => {
|
||||
}
|
||||
MouseEvent::ScrollUp(_, _, _) => {
|
||||
app.widgets.proc.scroll_up();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
MouseEvent::ScrollDown(_, _, _) => {
|
||||
app.widgets.proc.scroll_down();
|
||||
if !show_help_menu {
|
||||
draw_proc(&mut terminal, &mut app).unwrap();
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Event::Resize(_width, _height) => {
|
||||
if show_help_menu {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
|
||||
use num_rational::Ratio;
|
||||
use psutil::cpu;
|
||||
@ -51,6 +52,8 @@ pub struct ProcWidget<'a> {
|
||||
selected_proc: Option<SelectedProc>,
|
||||
sorting: ProcSorting,
|
||||
sort_direction: SortDirection,
|
||||
view_offset: usize,
|
||||
scrolled: bool,
|
||||
|
||||
cpu_count: u64,
|
||||
|
||||
@ -71,6 +74,8 @@ impl ProcWidget<'_> {
|
||||
selected_proc: None,
|
||||
sorting: ProcSorting::Cpu,
|
||||
sort_direction: SortDirection::Down,
|
||||
view_offset: 0,
|
||||
scrolled: false,
|
||||
|
||||
cpu_count: cpu::cpu_count(),
|
||||
|
||||
@ -79,6 +84,58 @@ impl ProcWidget<'_> {
|
||||
processes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_count(&mut self, count: isize) {
|
||||
self.selected_row = isize::max(0, self.selected_row as isize + count) as usize;
|
||||
self.selected_proc = None;
|
||||
self.scrolled = true;
|
||||
}
|
||||
|
||||
fn scroll_to(&mut self, count: usize) {
|
||||
self.selected_row = usize::min(
|
||||
count,
|
||||
if self.grouping {
|
||||
self.grouped_procs.len()
|
||||
} else {
|
||||
self.procs.len()
|
||||
} - 1,
|
||||
);
|
||||
self.selected_proc = None;
|
||||
self.scrolled = true;
|
||||
}
|
||||
|
||||
pub fn scroll_up(&mut self) {
|
||||
self.scroll_count(-1);
|
||||
}
|
||||
|
||||
pub fn scroll_down(&mut self) {
|
||||
self.scroll_count(1);
|
||||
}
|
||||
|
||||
pub fn scroll_top(&mut self) {
|
||||
self.scroll_to(0);
|
||||
}
|
||||
|
||||
pub fn scroll_bottom(&mut self) {
|
||||
self.scroll_to(if self.grouping {
|
||||
self.grouped_procs.len()
|
||||
} else {
|
||||
self.procs.len()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn toggle_grouping(&mut self) {
|
||||
self.grouping = !self.grouping;
|
||||
self.selected_proc = None;
|
||||
}
|
||||
|
||||
fn kill_process(&self) {
|
||||
let (command, arg) = match self.selected_proc.as_ref().unwrap() {
|
||||
SelectedProc::Pid(pid) => ("kill", pid.to_string()),
|
||||
SelectedProc::Name(name) => ("pkill", name.clone()),
|
||||
};
|
||||
Command::new(command).arg(arg).spawn().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdatableWidget for ProcWidget<'_> {
|
||||
@ -191,6 +248,7 @@ impl Widget for ProcWidget<'_> {
|
||||
.unwrap_or(self.selected_row),
|
||||
None => self.selected_row,
|
||||
};
|
||||
self.scroll_to(self.selected_row);
|
||||
self.selected_proc = if self.grouping {
|
||||
Some(SelectedProc::Name(
|
||||
procs[self.selected_row].name.to_string(),
|
||||
@ -199,9 +257,18 @@ impl Widget for ProcWidget<'_> {
|
||||
Some(SelectedProc::Pid(procs[self.selected_row].num))
|
||||
};
|
||||
|
||||
if self.scrolled {
|
||||
self.scrolled = false;
|
||||
if self.selected_row > area.height as usize + self.view_offset - 4 {
|
||||
self.view_offset = self.selected_row + 4 - area.height as usize;
|
||||
} else if self.selected_row < self.view_offset {
|
||||
self.view_offset = self.selected_row;
|
||||
}
|
||||
}
|
||||
|
||||
Table::new(
|
||||
header.iter(),
|
||||
procs.into_iter().map(|proc| {
|
||||
procs.into_iter().skip(self.view_offset).map(|proc| {
|
||||
Row::StyledData(
|
||||
vec![
|
||||
proc.num.to_string(),
|
||||
@ -221,7 +288,7 @@ impl Widget for ProcWidget<'_> {
|
||||
.block(block::new(self.colorscheme, &self.title))
|
||||
.header_style(self.colorscheme.text.modifier(Modifier::BOLD))
|
||||
.widths(&[
|
||||
Constraint::Length(5),
|
||||
Constraint::Length(6),
|
||||
// Constraint::Min(5),
|
||||
Constraint::Length(u16::max((area.width as i16 - 2 - 18) as u16, 5)),
|
||||
Constraint::Length(5),
|
||||
@ -231,7 +298,7 @@ impl Widget for ProcWidget<'_> {
|
||||
.header_gap(0)
|
||||
.draw(area, buf);
|
||||
|
||||
let cursor_y = area.y + 2 + self.selected_row as u16;
|
||||
let cursor_y = area.y + 2 + self.selected_row as u16 - self.view_offset as u16;
|
||||
if cursor_y < area.y + area.height - 1 {
|
||||
for i in (area.x + 1)..(area.x + area.width - 1) {
|
||||
let cell = buf.get_mut(i, cursor_y);
|
||||
|
Loading…
Reference in New Issue
Block a user