refactor: refactor the data table increment position code (#1762)

* refactor: refactor the data table increment position code

* some comments

* changelog
This commit is contained in:
Clement Tsang 2025-07-29 04:24:49 -04:00 committed by GitHub
parent 4416cf6b29
commit bb0d4bdd32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 68 additions and 34 deletions

View File

@ -43,6 +43,7 @@ That said, these are more guidelines rather than hardset rules, though the proje
- [#1683](https://github.com/ClementTsang/bottom/pull/1683): Fix graph lines potentially showing up behind legends. - [#1683](https://github.com/ClementTsang/bottom/pull/1683): Fix graph lines potentially showing up behind legends.
- [#1701](https://github.com/ClementTsang/bottom/pull/1701): Fix process kill dialog occasionally causing panics. - [#1701](https://github.com/ClementTsang/bottom/pull/1701): Fix process kill dialog occasionally causing panics.
- [#1755](https://github.com/ClementTsang/bottom/pull/1755): Fix missing stats/incorrect mount name for certain entries in the disk widget. - [#1755](https://github.com/ClementTsang/bottom/pull/1755): Fix missing stats/incorrect mount name for certain entries in the disk widget.
- [#1759](https://github.com/ClementTsang/bottom/pull/1759): Fix increment for data tables if the change is greater than the number of entries left.
### Changes ### Changes

View File

@ -96,46 +96,35 @@ impl<DataType: DataToCell<H>, H: ColumnHeader, S: SortType, C: DataTableColumn<H
/// Increments the scroll position if possible by a positive/negative /// Increments the scroll position if possible by a positive/negative
/// offset. If there is a valid change, this function will also return /// offset. If there is a valid change, this function will also return
/// the new position wrapped in an [`Option`]. /// the new position wrapped in an [`Option`].
///
/// Note that despite the name, this handles both incrementing (positive
/// change) and decrementing (negative change).
pub fn increment_position(&mut self, change: i64) -> Option<usize> { pub fn increment_position(&mut self, change: i64) -> Option<usize> {
let max_index = self.data.len(); let num_entries = self.data.len();
let current_index = self.state.current_index;
if change == 0 if num_entries == 0 {
|| (change > 0 && current_index == max_index)
|| (change < 0 && current_index == 0)
{
return None; return None;
} }
let csp: Result<i64, _> = self.state.current_index.try_into(); let Ok(current_index): Result<i64, _> = self.state.current_index.try_into() else {
if let Ok(csp) = csp { return None;
let proposed = csp + change;
let proposed: Result<usize, _> = if proposed.is_negative() {
Ok(0)
} else {
proposed.try_into()
}; };
if let Ok(proposed) = proposed { // We do this to clamp the proposed index to 0 if the change is greater
let proposed = if proposed >= max_index { // than the number of entries left from the current index. This gives
max_index - 1 // a more intuitive behaviour when using things like page up/down.
} else { let proposed = current_index + change;
proposed
}; // We check num_entries > 0 above.
self.state.current_index = proposed.clamp(0, (num_entries - 1) as i64) as usize;
self.state.current_index = proposed;
self.state.scroll_direction = if change < 0 { self.state.scroll_direction = if change < 0 {
ScrollDirection::Up ScrollDirection::Up
} else { } else {
ScrollDirection::Down ScrollDirection::Down
}; };
return Some(self.state.current_index); Some(self.state.current_index)
}
}
None
} }
/// Updates the scroll position to a selected index. /// Updates the scroll position to a selected index.
@ -194,8 +183,7 @@ mod test {
} }
} }
#[test] fn create_test_table() -> DataTable<TestType, &'static str> {
fn test_data_table_operations() {
let columns = [Column::hard("a", 10), Column::hard("b", 10)]; let columns = [Column::hard("a", 10), Column::hard("b", 10)];
let props = DataTableProps { let props = DataTableProps {
title: Some("test".into()), title: Some("test".into()),
@ -207,7 +195,12 @@ mod test {
}; };
let styling = DataTableStyling::default(); let styling = DataTableStyling::default();
let mut table = DataTable::new(columns, props, styling); DataTable::new(columns, props, styling)
}
#[test]
fn test_scrolling() {
let mut table = create_test_table();
table.set_data((0..=4).map(|index| TestType { index }).collect::<Vec<_>>()); table.set_data((0..=4).map(|index| TestType { index }).collect::<Vec<_>>());
table.scroll_to_last(); table.scroll_to_last();
@ -217,6 +210,12 @@ mod test {
table.scroll_to_first(); table.scroll_to_first();
assert_eq!(table.current_index(), 0); assert_eq!(table.current_index(), 0);
assert_eq!(table.state.scroll_direction, ScrollDirection::Up); assert_eq!(table.state.scroll_direction, ScrollDirection::Up);
}
#[test]
fn test_set_position() {
let mut table = create_test_table();
table.set_data((0..=4).map(|index| TestType { index }).collect::<Vec<_>>());
table.set_position(4); table.set_position(4);
assert_eq!(table.current_index(), 4); assert_eq!(table.current_index(), 4);
@ -226,6 +225,16 @@ mod test {
assert_eq!(table.current_index(), 4); assert_eq!(table.current_index(), 4);
assert_eq!(table.state.scroll_direction, ScrollDirection::Down); assert_eq!(table.state.scroll_direction, ScrollDirection::Down);
assert_eq!(table.current_item(), Some(&TestType { index: 4 })); assert_eq!(table.current_item(), Some(&TestType { index: 4 }));
}
#[test]
fn test_increment_position() {
let mut table = create_test_table();
table.set_data((0..=4).map(|index| TestType { index }).collect::<Vec<_>>());
table.set_position(4);
assert_eq!(table.current_index(), 4);
assert_eq!(table.state.scroll_direction, ScrollDirection::Down);
table.increment_position(-1); table.increment_position(-1);
assert_eq!(table.current_index(), 3); assert_eq!(table.current_index(), 3);
@ -257,6 +266,30 @@ mod test {
assert_eq!(table.state.scroll_direction, ScrollDirection::Down); assert_eq!(table.state.scroll_direction, ScrollDirection::Down);
assert_eq!(table.current_item(), Some(&TestType { index: 4 })); assert_eq!(table.current_item(), Some(&TestType { index: 4 }));
// Make sure that overscrolling up causes clamping.
table.increment_position(-10);
assert_eq!(table.current_index(), 0);
assert_eq!(table.state.scroll_direction, ScrollDirection::Up);
assert_eq!(table.current_item(), Some(&TestType { index: 0 }));
// Make sure that overscrolling down causes clamping.
table.increment_position(100);
assert_eq!(table.current_index(), 4);
assert_eq!(table.state.scroll_direction, ScrollDirection::Down);
assert_eq!(table.current_item(), Some(&TestType { index: 4 }));
}
/// A test to ensure that scroll offsets are correctly handled when we "lose" rows.
#[test]
fn test_lose_data() {
let mut table = create_test_table();
table.set_data((0..=4).map(|index| TestType { index }).collect::<Vec<_>>());
table.set_position(4);
assert_eq!(table.current_index(), 4);
assert_eq!(table.state.scroll_direction, ScrollDirection::Down);
assert_eq!(table.current_item(), Some(&TestType { index: 4 }));
table.set_data((0..=2).map(|index| TestType { index }).collect::<Vec<_>>()); table.set_data((0..=2).map(|index| TestType { index }).collect::<Vec<_>>());
assert_eq!(table.current_index(), 2); assert_eq!(table.current_index(), 2);
assert_eq!(table.state.scroll_direction, ScrollDirection::Down); assert_eq!(table.state.scroll_direction, ScrollDirection::Down);