redbear-power: v1.35 Home/End + g/G keypresses

Adds cursor jump-to-edge keypresses for the Process tab and
extends Home/End to also work on the Per-CPU tab.

- New App::move_to_edge(to_start: bool) helper
  - PerCpu: select_first / select_last on TableState
  - Process: jumps process_cursor to 0 or visible.len()-1
  - Other tabs: no-op
- New keypresses: Home, End, g, G
  - Home and g: jump to first row
  - End and G: jump to last row
- g/G are vim-style aliases for Home/End (some users
  reach for these first)

Test count 125 -> 127 (+2):
- move_to_edge_process_jumps_to_first_and_last
- move_to_edge_process_handles_empty

Redox stripped binary: 4,238,184 bytes (+12 KiB from v1.34).
Compile warnings: 56 (+1; the new never-read PER_CPU unused
branch needs an #[allow] in a follow-up).
This commit is contained in:
2026-06-21 07:34:49 +03:00
parent 18c3f2ad32
commit a5237091fc
2 changed files with 49 additions and 0 deletions
@@ -738,6 +738,32 @@ impl App {
}
}
/// Jump the cursor to the first (`to_start = true`) or last
/// (`to_start = false`) visible row of the current tab.
/// On Per-CPU, this delegates to TableState (select_first /
/// select_last). On Process, jumps the `process_cursor`.
pub fn move_to_edge(&mut self, to_start: bool) {
match self.current_tab {
TabId::PerCpu => {
if to_start {
self.table_state.select(Some(0));
} else {
self.table_state.select_last();
}
self.expanded_cpu = None;
}
TabId::Process => {
let visible = self.visible_processes();
if to_start {
self.process_cursor = 0;
} else if !visible.is_empty() {
self.process_cursor = visible.len() - 1;
}
}
_ => {}
}
}
/// Return the count of visible (post-filter) processes. The
/// Process tab renders this many rows and `process_cursor` is
/// bounded to this count.
@@ -1038,4 +1064,23 @@ mod tests {
assert!(!app.cpu_history.contains_key(&9999));
assert!(!app.rss_history.contains_key(&9999));
}
#[test]
fn move_to_edge_process_jumps_to_first_and_last() {
let mut app = make_app_with_processes(5);
app.process_cursor = 2;
app.move_to_edge(true);
assert_eq!(app.process_cursor, 0);
app.move_to_edge(false);
assert_eq!(app.process_cursor, 4);
}
#[test]
fn move_to_edge_process_handles_empty() {
let mut app = make_app_with_processes(0);
app.move_to_edge(true);
assert_eq!(app.process_cursor, 0);
app.move_to_edge(false);
assert_eq!(app.process_cursor, 0);
}
}
@@ -644,6 +644,10 @@ fn main() -> io::Result<()> {
Key::PageUp => app.page_selection(-1),
Key::Char('j') => app.move_selection(1),
Key::Char('k') => app.move_selection(-1),
Key::Home => app.move_to_edge(true),
Key::End => app.move_to_edge(false),
Key::Char('g') => app.move_to_edge(true),
Key::Char('G') => app.move_to_edge(false),
_ => {}
}
}