mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 23:54:14 +02:00
feature: add basic page up/down scrolling (#646)
Adds page up/down scrolling support to respectively scroll up/down by a full page. Note that this is mostly just to get the feature out for those interested, and is admittedly a bit rushed - I will be rewriting all logic involving event handling as part of state refactor anyways, so this will also get changed in the work done there, and therefore, I kinda just sped through this.
This commit is contained in:
parent
c92cfc644d
commit
9eabb061aa
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [0.6.7]/[0.7.0] - Unreleased
|
## [0.6.7]/[0.7.0] - Unreleased
|
||||||
|
|
||||||
|
## Feature
|
||||||
|
|
||||||
|
- [#646](https://github.com/ClementTsang/bottom/pull/646): Add `PgUp`/`PgDown` keybind support to scroll up and down a page in a table.
|
||||||
|
|
||||||
## [0.6.6] - 2021-12-22
|
## [0.6.6] - 2021-12-22
|
||||||
|
|
||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
@ -55,6 +55,7 @@ Note that key bindings are generally case-sensitive.
|
|||||||
| ++right++ <br/> ++l++ <br/> ++alt+l++ | Move right within a widget |
|
| ++right++ <br/> ++l++ <br/> ++alt+l++ | Move right within a widget |
|
||||||
| ++g+g++ , ++home++ | Jump to the first entry |
|
| ++g+g++ , ++home++ | Jump to the first entry |
|
||||||
| ++G++ , ++end++ | Jump to the last entry |
|
| ++G++ , ++end++ | Jump to the last entry |
|
||||||
|
| ++page-up++ , ++page-down++ | Scroll up/down a table by a page |
|
||||||
|
|
||||||
## Mouse bindings
|
## Mouse bindings
|
||||||
|
|
||||||
|
113
src/app.rs
113
src/app.rs
@ -1114,6 +1114,20 @@ impl App {
|
|||||||
0 => KillSignal::Cancel,
|
0 => KillSignal::Cancel,
|
||||||
sig => KillSignal::Kill(sig),
|
sig => KillSignal::Kill(sig),
|
||||||
};
|
};
|
||||||
|
} else if self.current_widget.widget_type.is_widget_table() {
|
||||||
|
if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = (
|
||||||
|
&self.current_widget.top_left_corner,
|
||||||
|
&self.current_widget.bottom_right_corner,
|
||||||
|
) {
|
||||||
|
let border_offset = if self.is_drawing_border() { 1 } else { 0 };
|
||||||
|
let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
|
||||||
|
self.app_config_fields.table_gap
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let height = brc_y - tlc_y - 2 * border_offset - header_gap_offset;
|
||||||
|
self.change_position_count(-(height as i64));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,6 +1141,20 @@ impl App {
|
|||||||
new_signal += 2;
|
new_signal += 2;
|
||||||
}
|
}
|
||||||
self.delete_dialog_state.selected_signal = KillSignal::Kill(new_signal);
|
self.delete_dialog_state.selected_signal = KillSignal::Kill(new_signal);
|
||||||
|
} else if self.current_widget.widget_type.is_widget_table() {
|
||||||
|
if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = (
|
||||||
|
&self.current_widget.top_left_corner,
|
||||||
|
&self.current_widget.bottom_right_corner,
|
||||||
|
) {
|
||||||
|
let border_offset = if self.is_drawing_border() { 1 } else { 0 };
|
||||||
|
let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
|
||||||
|
self.app_config_fields.table_gap
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let height = brc_y - tlc_y - 2 * border_offset - header_gap_offset;
|
||||||
|
self.change_position_count(height as i64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,8 +1471,6 @@ impl App {
|
|||||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
||||||
self.on_number(caught_char)
|
self.on_number(caught_char)
|
||||||
}
|
}
|
||||||
'u' => self.on_page_up(),
|
|
||||||
'd' => self.on_page_down(),
|
|
||||||
'g' => {
|
'g' => {
|
||||||
let mut is_first_g = true;
|
let mut is_first_g = true;
|
||||||
if let Some(second_char) = self.second_char {
|
if let Some(second_char) = self.second_char {
|
||||||
@ -2330,36 +2356,29 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrement_position_count(&mut self) {
|
pub fn decrement_position_count(&mut self) {
|
||||||
if !self.ignore_normal_keybinds() {
|
self.change_position_count(-1);
|
||||||
match self.current_widget.widget_type {
|
|
||||||
BottomWidgetType::Proc => {
|
|
||||||
self.increment_process_position(-1);
|
|
||||||
}
|
|
||||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
|
|
||||||
BottomWidgetType::Temp => self.increment_temp_position(-1),
|
|
||||||
BottomWidgetType::Disk => self.increment_disk_position(-1),
|
|
||||||
BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(-1),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment_position_count(&mut self) {
|
pub fn increment_position_count(&mut self) {
|
||||||
|
self.change_position_count(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_position_count(&mut self, amount: i64) {
|
||||||
if !self.ignore_normal_keybinds() {
|
if !self.ignore_normal_keybinds() {
|
||||||
match self.current_widget.widget_type {
|
match self.current_widget.widget_type {
|
||||||
BottomWidgetType::Proc => {
|
BottomWidgetType::Proc => {
|
||||||
self.increment_process_position(1);
|
self.change_process_position(amount);
|
||||||
}
|
}
|
||||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
|
BottomWidgetType::ProcSort => self.change_process_sort_position(amount),
|
||||||
BottomWidgetType::Temp => self.increment_temp_position(1),
|
BottomWidgetType::Temp => self.change_temp_position(amount),
|
||||||
BottomWidgetType::Disk => self.increment_disk_position(1),
|
BottomWidgetType::Disk => self.increment_disk_position(amount),
|
||||||
BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(1),
|
BottomWidgetType::CpuLegend => self.change_cpu_legend_position(amount),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_process_sort_position(&mut self, num_to_change_by: i64) {
|
fn change_process_sort_position(&mut self, num_to_change_by: i64) {
|
||||||
if let Some(proc_widget_state) = self
|
if let Some(proc_widget_state) = self
|
||||||
.proc_state
|
.proc_state
|
||||||
.get_mut_widget_state(self.current_widget.widget_id - 2)
|
.get_mut_widget_state(self.current_widget.widget_id - 2)
|
||||||
@ -2367,9 +2386,11 @@ impl App {
|
|||||||
let current_posn = proc_widget_state.columns.current_scroll_position;
|
let current_posn = proc_widget_state.columns.current_scroll_position;
|
||||||
let num_columns = proc_widget_state.columns.get_enabled_columns_len();
|
let num_columns = proc_widget_state.columns.get_enabled_columns_len();
|
||||||
|
|
||||||
if current_posn as i64 + num_to_change_by >= 0
|
if current_posn as i64 + num_to_change_by < 0 {
|
||||||
&& current_posn as i64 + num_to_change_by < num_columns as i64
|
proc_widget_state.columns.current_scroll_position = 0;
|
||||||
{
|
} else if current_posn as i64 + num_to_change_by >= num_columns as i64 {
|
||||||
|
proc_widget_state.columns.current_scroll_position = num_columns.saturating_sub(1);
|
||||||
|
} else {
|
||||||
proc_widget_state.columns.current_scroll_position =
|
proc_widget_state.columns.current_scroll_position =
|
||||||
(current_posn as i64 + num_to_change_by) as usize;
|
(current_posn as i64 + num_to_change_by) as usize;
|
||||||
}
|
}
|
||||||
@ -2382,7 +2403,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_cpu_legend_position(&mut self, num_to_change_by: i64) {
|
fn change_cpu_legend_position(&mut self, num_to_change_by: i64) {
|
||||||
if let Some(cpu_widget_state) = self
|
if let Some(cpu_widget_state) = self
|
||||||
.cpu_state
|
.cpu_state
|
||||||
.widget_states
|
.widget_states
|
||||||
@ -2391,9 +2412,11 @@ impl App {
|
|||||||
let current_posn = cpu_widget_state.scroll_state.current_scroll_position;
|
let current_posn = cpu_widget_state.scroll_state.current_scroll_position;
|
||||||
|
|
||||||
let cap = self.canvas_data.cpu_data.len();
|
let cap = self.canvas_data.cpu_data.len();
|
||||||
if current_posn as i64 + num_to_change_by >= 0
|
if current_posn as i64 + num_to_change_by < 0 {
|
||||||
&& current_posn as i64 + num_to_change_by < cap as i64
|
cpu_widget_state.scroll_state.current_scroll_position = 0;
|
||||||
{
|
} else if current_posn as i64 + num_to_change_by >= cap as i64 {
|
||||||
|
cpu_widget_state.scroll_state.current_scroll_position = cap.saturating_sub(1);
|
||||||
|
} else {
|
||||||
cpu_widget_state.scroll_state.current_scroll_position =
|
cpu_widget_state.scroll_state.current_scroll_position =
|
||||||
(current_posn as i64 + num_to_change_by) as usize;
|
(current_posn as i64 + num_to_change_by) as usize;
|
||||||
}
|
}
|
||||||
@ -2407,7 +2430,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the new position.
|
/// Returns the new position.
|
||||||
fn increment_process_position(&mut self, num_to_change_by: i64) -> Option<usize> {
|
fn change_process_position(&mut self, num_to_change_by: i64) -> Option<usize> {
|
||||||
if let Some(proc_widget_state) = self
|
if let Some(proc_widget_state) = self
|
||||||
.proc_state
|
.proc_state
|
||||||
.get_mut_widget_state(self.current_widget.widget_id)
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
@ -2418,13 +2441,16 @@ impl App {
|
|||||||
.finalized_process_data_map
|
.finalized_process_data_map
|
||||||
.get(&self.current_widget.widget_id)
|
.get(&self.current_widget.widget_id)
|
||||||
{
|
{
|
||||||
if current_posn as i64 + num_to_change_by >= 0
|
if current_posn as i64 + num_to_change_by < 0 {
|
||||||
&& current_posn as i64 + num_to_change_by < finalized_process_data.len() as i64
|
proc_widget_state.scroll_state.current_scroll_position = 0;
|
||||||
|
} else if current_posn as i64 + num_to_change_by
|
||||||
|
>= finalized_process_data.len() as i64
|
||||||
{
|
{
|
||||||
proc_widget_state.scroll_state.current_scroll_position =
|
proc_widget_state.scroll_state.current_scroll_position =
|
||||||
(current_posn as i64 + num_to_change_by) as usize;
|
finalized_process_data.len().saturating_sub(1);
|
||||||
} else {
|
} else {
|
||||||
return None;
|
proc_widget_state.scroll_state.current_scroll_position =
|
||||||
|
(current_posn as i64 + num_to_change_by) as usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2440,7 +2466,7 @@ impl App {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_temp_position(&mut self, num_to_change_by: i64) {
|
fn change_temp_position(&mut self, num_to_change_by: i64) {
|
||||||
if let Some(temp_widget_state) = self
|
if let Some(temp_widget_state) = self
|
||||||
.temp_state
|
.temp_state
|
||||||
.widget_states
|
.widget_states
|
||||||
@ -2448,10 +2474,14 @@ impl App {
|
|||||||
{
|
{
|
||||||
let current_posn = temp_widget_state.scroll_state.current_scroll_position;
|
let current_posn = temp_widget_state.scroll_state.current_scroll_position;
|
||||||
|
|
||||||
if current_posn as i64 + num_to_change_by >= 0
|
if current_posn as i64 + num_to_change_by < 0 {
|
||||||
&& current_posn as i64 + num_to_change_by
|
temp_widget_state.scroll_state.current_scroll_position = 0;
|
||||||
< self.canvas_data.temp_sensor_data.len() as i64
|
} else if current_posn as i64 + num_to_change_by
|
||||||
|
>= self.canvas_data.temp_sensor_data.len() as i64
|
||||||
{
|
{
|
||||||
|
temp_widget_state.scroll_state.current_scroll_position =
|
||||||
|
self.canvas_data.temp_sensor_data.len().saturating_sub(1);
|
||||||
|
} else {
|
||||||
temp_widget_state.scroll_state.current_scroll_position =
|
temp_widget_state.scroll_state.current_scroll_position =
|
||||||
(current_posn as i64 + num_to_change_by) as usize;
|
(current_posn as i64 + num_to_change_by) as usize;
|
||||||
}
|
}
|
||||||
@ -2901,9 +2931,6 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut failed_to_get = true;
|
let mut failed_to_get = true;
|
||||||
// TODO: [MOUSE] We could use a better data structure for this? Currently it's a blind
|
|
||||||
// traversal through a hashmap, using a 2d binary tree of sorts would be better.
|
|
||||||
// See: https://docs.rs/kdtree/0.6.0/kdtree/
|
|
||||||
for (new_widget_id, widget) in &self.widget_map {
|
for (new_widget_id, widget) in &self.widget_map {
|
||||||
if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) =
|
if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) =
|
||||||
(widget.top_left_corner, widget.bottom_right_corner)
|
(widget.top_left_corner, widget.bottom_right_corner)
|
||||||
@ -2985,7 +3012,7 @@ impl App {
|
|||||||
.current_scroll_position;
|
.current_scroll_position;
|
||||||
let is_tree_mode = proc_widget_state.is_tree_mode;
|
let is_tree_mode = proc_widget_state.is_tree_mode;
|
||||||
|
|
||||||
let new_position = self.increment_process_position(
|
let new_position = self.change_process_position(
|
||||||
offset_clicked_entry as i64 - visual_index as i64,
|
offset_clicked_entry as i64 - visual_index as i64,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -3008,7 +3035,7 @@ impl App {
|
|||||||
if let Some(visual_index) =
|
if let Some(visual_index) =
|
||||||
proc_widget_state.columns.column_state.selected()
|
proc_widget_state.columns.column_state.selected()
|
||||||
{
|
{
|
||||||
self.increment_process_sort_position(
|
self.change_process_sort_position(
|
||||||
offset_clicked_entry as i64 - visual_index as i64,
|
offset_clicked_entry as i64 - visual_index as i64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3022,7 +3049,7 @@ impl App {
|
|||||||
if let Some(visual_index) =
|
if let Some(visual_index) =
|
||||||
cpu_widget_state.scroll_state.table_state.selected()
|
cpu_widget_state.scroll_state.table_state.selected()
|
||||||
{
|
{
|
||||||
self.increment_cpu_legend_position(
|
self.change_cpu_legend_position(
|
||||||
offset_clicked_entry as i64 - visual_index as i64,
|
offset_clicked_entry as i64 - visual_index as i64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3036,7 +3063,7 @@ impl App {
|
|||||||
if let Some(visual_index) =
|
if let Some(visual_index) =
|
||||||
temp_widget_state.scroll_state.table_state.selected()
|
temp_widget_state.scroll_state.table_state.selected()
|
||||||
{
|
{
|
||||||
self.increment_temp_position(
|
self.change_temp_position(
|
||||||
offset_clicked_entry as i64 - visual_index as i64,
|
offset_clicked_entry as i64 - visual_index as i64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -695,6 +695,13 @@ impl Painter {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if let Some(updated_current_widget) = app_state
|
||||||
|
.widget_map
|
||||||
|
.get(&app_state.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
app_state.current_widget = updated_current_widget.clone();
|
||||||
|
}
|
||||||
|
|
||||||
app_state.is_force_redraw = false;
|
app_state.is_force_redraw = false;
|
||||||
app_state.is_determining_widget_boundary = false;
|
app_state.is_determining_widget_boundary = false;
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
|||||||
|
|
||||||
// TODO [Help]: Search in help?
|
// TODO [Help]: Search in help?
|
||||||
// TODO [Help]: Move to using tables for easier formatting?
|
// TODO [Help]: Move to using tables for easier formatting?
|
||||||
pub const GENERAL_HELP_TEXT: [&str; 30] = [
|
pub const GENERAL_HELP_TEXT: [&str; 31] = [
|
||||||
"1 - General",
|
"1 - General",
|
||||||
"q, Ctrl-c Quit",
|
"q, Ctrl-c Quit",
|
||||||
"Esc Close dialog windows, search, widgets, or exit expanded mode",
|
"Esc Close dialog windows, search, widgets, or exit expanded mode",
|
||||||
@ -260,6 +260,7 @@ pub const GENERAL_HELP_TEXT: [&str; 30] = [
|
|||||||
"+ Zoom in on chart (decrease time range)",
|
"+ Zoom in on chart (decrease time range)",
|
||||||
"- Zoom out on chart (increase time range)",
|
"- Zoom out on chart (increase time range)",
|
||||||
"= Reset zoom",
|
"= Reset zoom",
|
||||||
|
"PgUp, PgDown Scroll up/down a table by a page",
|
||||||
"Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
"Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
||||||
"Mouse click Selects the clicked widget, table entry, dialog option, or tab",
|
"Mouse click Selects the clicked widget, table entry, dialog option, or tab",
|
||||||
];
|
];
|
||||||
|
@ -125,6 +125,8 @@ pub fn handle_key_event_or_break(
|
|||||||
KeyCode::F(5) => app.toggle_tree_mode(),
|
KeyCode::F(5) => app.toggle_tree_mode(),
|
||||||
KeyCode::F(6) => app.toggle_sort(),
|
KeyCode::F(6) => app.toggle_sort(),
|
||||||
KeyCode::F(9) => app.start_killing_process(),
|
KeyCode::F(9) => app.start_killing_process(),
|
||||||
|
KeyCode::PageDown => app.on_page_down(),
|
||||||
|
KeyCode::PageUp => app.on_page_up(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user