summaryrefslogtreecommitdiffstats
path: root/src/app/states.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/states.rs')
-rw-r--r--src/app/states.rs841
1 files changed, 20 insertions, 821 deletions
diff --git a/src/app/states.rs b/src/app/states.rs
index 74c8c409..a1952cda 100644
--- a/src/app/states.rs
+++ b/src/app/states.rs
@@ -1,16 +1,17 @@
-use std::{borrow::Cow, collections::HashMap, convert::TryInto, time::Instant};
+use std::{collections::HashMap, time::Instant};
use unicode_segmentation::GraphemeCursor;
-use tui::widgets::TableState;
-
use crate::{
app::{layout_manager::BottomWidgetType, query::*},
constants,
- data_conversion::CellContent,
- data_harvester::processes::{self, ProcessSorting},
+ data_harvester::processes::ProcessSorting,
};
-use ProcessSorting::*;
+
+pub mod table_state;
+pub use table_state::*;
+
+use super::widgets::ProcWidget;
#[derive(Debug)]
pub enum ScrollDirection {
@@ -39,218 +40,6 @@ pub struct CanvasTableWidthState {
pub calculated_column_widths: Vec<u16>,
}
-/// A bound on the width of a column.
-#[derive(Clone, Copy, Debug)]
-pub enum WidthBounds {
- /// A width of this type is either as long as `min`, but can otherwise shrink and grow up to a point.
- Soft {
- /// The minimum amount before giving up and hiding.
- min_width: u16,
-
- /// The desired, calculated width. Take this if possible as the base starting width.
- desired: u16,
-
- /// The max width, as a percentage of the total width available. If [`None`],
- /// then it can grow as desired.
- max_percentage: Option<f32>,
- },
-
- /// A width of this type is either as long as specified, or does not appear at all.
- Hard(u16),
-}
-
-impl WidthBounds {
- pub const fn soft_from_str(name: &'static str, max_percentage: Option<f32>) -> WidthBounds {
- let len = name.len() as u16;
- WidthBounds::Soft {
- min_width: len,
- desired: len,
- max_percentage,
- }
- }
-
- pub const fn soft_from_str_with_alt(
- name: &'static str, alt: &'static str, max_percentage: Option<f32>,
- ) -> WidthBounds {
- WidthBounds::Soft {
- min_width: alt.len() as u16,
- desired: name.len() as u16,
- max_percentage,
- }
- }
-}
-
-pub struct TableComponentColumn {
- /// The name of the column. Displayed if possible as the header.
- pub name: CellContent,
-
- /// A restriction on this column's width, if desired.
- pub width_bounds: WidthBounds,
-
- /// The calculated width of the column.
- pub calculated_width: u16,
-}
-
-impl TableComponentColumn {
- pub fn new<I>(name: I, alt: Option<I>, width_bounds: WidthBounds) -> Self
- where
- I: Into<Cow<'static, str>>,
- {
- Self {
- name: if let Some(alt) = alt {
- CellContent::HasAlt {
- alt: alt.into(),
- main: name.into(),
- }
- } else {
- CellContent::Simple(name.into())
- },
- width_bounds,
- calculated_width: 0,
- }
- }
-
- pub fn should_skip(&self) -> bool {
- self.calculated_width == 0
- }
-}
-
-/// [`TableComponentState`] deals with fields for a scrollable's current state.
-#[derive(Default)]
-pub struct TableComponentState {
- pub current_scroll_position: usize,
- pub scroll_bar: usize,
- pub scroll_direction: ScrollDirection,
- pub table_state: TableState,
- pub columns: Vec<TableComponentColumn>,
-}
-
-impl TableComponentState {
- pub fn new(columns: Vec<TableComponentColumn>) -> Self {
- Self {
- current_scroll_position: 0,
- scroll_bar: 0,
- scroll_direction: ScrollDirection::Down,
- table_state: Default::default(),
- columns,
- }
- }
-
- /// Calculates widths for the columns for this table.
- ///
- /// * `total_width` is the, well, total width available.
- /// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if
- /// false.
- ///
- /// **NOTE:** Trailing 0's may break tui-rs, remember to filter them out later!
- pub fn calculate_column_widths(&mut self, total_width: u16, left_to_right: bool) {
- use itertools::Either;
- use std::cmp::{max, min};
-
- let mut total_width_left = total_width;
-
- for column in self.columns.iter_mut() {
- column.calculated_width = 0;
- }
-
- let columns = if left_to_right {
- Either::Left(self.columns.iter_mut())
- } else {
- Either::Right(self.columns.iter_mut().rev())
- };
-
- let mut num_columns = 0;
- for column in columns {
- match &column.width_bounds {
- WidthBounds::Soft {
- min_width,
- desired,
- max_percentage,
- } => {
- let soft_limit = max(
- if let Some(max_percentage) = max_percentage {
- // Rust doesn't have an `into()` or `try_into()` for floats to integers???
- ((*max_percentage * f32::from(total_width)).ceil()) as u16
- } else {
- *desired
- },
- *min_width,
- );
- let space_taken = min(min(soft_limit, *desired), total_width_left);
-
- if *min_width > space_taken {
- break;
- } else if space_taken > 0 {
- total_width_left = total_width_left.saturating_sub(space_taken + 1);
- column.calculated_width = space_taken;
- num_columns += 1;
- }
- }
- WidthBounds::Hard(width) => {
- let space_taken = min(*width, total_width_left);
-
- if *width > space_taken {
- break;
- } else if space_taken > 0 {
- total_width_left = total_width_left.saturating_sub(space_taken + 1);
- column.calculated_width = space_taken;
- num_columns += 1;
- }
- }
- }
- }
-
- if num_columns > 0 {
- // Redistribute remaining.
- let mut num_dist = num_columns;
- let amount_per_slot = total_width_left / num_dist;
- total_width_left %= num_dist;
- for column in self.columns.iter_mut() {
- if num_dist == 0 {
- break;
- }
-
- if column.calculated_width > 0 {
- if total_width_left > 0 {
- column.calculated_width += amount_per_slot + 1;
- total_width_left -= 1;
- } else {
- column.calculated_width += amount_per_slot;
- }
-
- num_dist -= 1;
- }
- }
- }
- }
-
- /// Updates the position if possible, and if there is a valid change, returns the new position.
- pub fn update_position(&mut self, change: i64, num_entries: usize) -> Option<usize> {
- if change == 0 {
- return None;
- }
-
- let csp: Result<i64, _> = self.current_scroll_position.try_into();
- if let Ok(csp) = csp {
- let proposed: Result<usize, _> = (csp + change).try_into();
- if let Ok(proposed) = proposed {
- if proposed < num_entries {
- self.current_scroll_position = proposed;
- if change < 0 {
- self.scroll_direction = ScrollDirection::Up;
- } else {
- self.scroll_direction = ScrollDirection::Down;
- }
-
- return Some(self.current_scroll_position);
- }
- }
- }
-
- None
- }
-}
-
#[derive(PartialEq)]
pub enum KillSignal {
Cancel,
@@ -342,555 +131,20 @@ impl AppSearchState {
}
}
-/// ProcessSearchState only deals with process' search's current settings and state.
-pub struct ProcessSearchState {
- pub search_state: AppSearchState,
- pub is_ignoring_case: bool,
- pub is_searching_whole_word: bool,
- pub is_searching_with_regex: bool,
-}
-
-impl Default for ProcessSearchState {
- fn default() -> Self {
- ProcessSearchState {
- search_state: AppSearchState::default(),
- is_ignoring_case: true,
- is_searching_whole_word: false,
- is_searching_with_regex: false,
- }
- }
-}
-
-impl ProcessSearchState {
- pub fn search_toggle_ignore_case(&mut self) {
- self.is_ignoring_case = !self.is_ignoring_case;
- }
-
- pub fn search_toggle_whole_word(&mut self) {
- self.is_searching_whole_word = !self.is_searching_whole_word;
- }
-
- pub fn search_toggle_regex(&mut self) {
- self.is_searching_with_regex = !self.is_searching_with_regex;
- }
-}
-
-pub struct ColumnInfo {
- pub enabled: bool,
- pub shortcut: Option<&'static str>,
- // FIXME: Move column width logic here!
- // pub hard_width: Option<u16>,
- // pub max_soft_width: Option<f64>,
-}
-
-pub struct ProcColumn {
- pub ordered_columns: Vec<ProcessSorting>,
- /// The y location of headers. Since they're all aligned, it's just one value.
- pub column_header_y_loc: Option<u16>,
- /// The x start and end bounds for each header.
- pub column_header_x_locs: Option<Vec<(u16, u16)>>,
- pub column_mapping: HashMap<ProcessSorting, ColumnInfo>,
- pub longest_header_len: u16,
- pub column_state: TableState,
- pub scroll_direction: ScrollDirection,
- pub current_scroll_position: usize,
- pub previous_scroll_position: usize,
- pub backup_prev_scroll_position: usize,
-}
-
-impl Default for ProcColumn {
- fn default() -> Self {
- let ordered_columns = vec![
- Count,
- Pid,
- ProcessName,
- Command,
- CpuPercent,
- Mem,
- MemPercent,
- ReadPerSecond,
- WritePerSecond,
- TotalRead,
- TotalWrite,
- User,
- State,
- ];
-
- let mut column_mapping = HashMap::new();
- let mut longest_header_len = 0;
- for column in ordered_columns.clone() {
- longest_header_len = std::cmp::max(longest_header_len, column.to_string().len());
- match column {
- CpuPercent => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: true,
- shortcut: Some("c"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- MemPercent => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: true,
- shortcut: Some("m"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- Mem => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: false,
- shortcut: Some("m"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- ProcessName => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: true,
- shortcut: Some("n"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- Command => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: false,
- shortcut: Some("n"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- Pid => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: true,
- shortcut: Some("p"),
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- Count => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: false,
- shortcut: None,
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- User => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: cfg!(target_family = "unix"),
- shortcut: None,
- },
- );
- }
- _ => {
- column_mapping.insert(
- column,
- ColumnInfo {
- enabled: true,
- shortcut: None,
- // hard_width: None,
- // max_soft_width: None,
- },
- );
- }
- }
- }
- let longest_header_len = longest_header_len as u16;
-
- ProcColumn {
- ordered_columns,
- column_mapping,
- longest_header_len,
- column_state: TableState::default(),
- scroll_direction: ScrollDirection::default(),
- current_scroll_position: 0,
- previous_scroll_position: 0,
- backup_prev_scroll_position: 0,
- column_header_y_loc: None,
- column_header_x_locs: None,
- }
- }
-}
-
-impl ProcColumn {
- /// Returns its new status.
- pub fn toggle(&mut self, column: &ProcessSorting) -> Option<bool> {
- if let Some(mapping) = self.column_mapping.get_mut(column) {
- mapping.enabled = !(mapping.enabled);
- Some(mapping.enabled)
- } else {
- None
- }
- }
-
- pub fn try_set(&mut self, column: &ProcessSorting, setting: bool) -> Option<bool> {
- if let Some(mapping) = self.column_mapping.get_mut(column) {
- mapping.enabled = setting;
- Some(mapping.enabled)
- } else {
- None
- }
- }
-
- pub fn try_enable(&mut self, column: &ProcessSorting) -> Option<bool> {
- if let Some(mapping) = self.column_mapping.get_mut(column) {
- mapping.enabled = true;
- Some(mapping.enabled)
- } else {
- None
- }
- }
-
- pub fn try_disable(&mut self, column: &ProcessSorting) -> Option<bool> {
- if let Some(mapping) = self.column_mapping.get_mut(column) {
- mapping.enabled = false;
- Some(mapping.enabled)
- } else {
- None
- }
- }
-
- pub fn is_enabled(&self, column: &ProcessSorting) -> bool {
- if let Some(mapping) = self.column_mapping.get(column) {
- mapping.enabled
- } else {
- false
- }
- }
-
- pub fn get_enabled_columns_len(&self) -> usize {
- self.ordered_columns
- .iter()
- .filter_map(|column_type| {
- if let Some(col_map) = self.column_mapping.get(column_type) {
- if col_map.enabled {
- Some(1)
- } else {
- None
- }
- } else {
- None
- }
- })
- .sum()
- }
-
- /// NOTE: ALWAYS call this when opening the sorted window.
- pub fn set_to_sorted_index_from_type(&mut self, proc_sorting_type: &ProcessSorting) {
- // TODO [Custom Columns]: If we add custom columns, this may be needed!
- // Since column indices will change, this runs the risk of OOB. So, if you change columns, CALL THIS AND ADAPT!
- let mut true_index = 0;
- for column in &self.ordered_columns {
- if *column == *proc_sorting_type {
- break;
- }
- if self.column_mapping.get(column).unwrap().enabled {
- true_index += 1;
- }
- }
-
- self.current_scroll_position = true_index;
- self.backup_prev_scroll_position = self.previous_scroll_position;
- }
-
- /// This function sets the scroll position based on the index.
- pub fn set_to_sorted_index_from_visual_index(&mut self, visual_index: usize) {
- self.current_scroll_position = visual_index;
- self.backup_prev_scroll_position = self.previous_scroll_position;
- }
-
- pub fn get_column_headers(
- &self, proc_sorting_type: &ProcessSorting, sort_reverse: bool,
- ) -> Vec<String> {
- const DOWN_ARROW: char = '▼';
- const UP_ARROW: char = '▲';
-
- // TODO: Gonna have to figure out how to do left/right GUI notation if we add it.
- self.ordered_columns
- .iter()
- .filter_map(|column_type| {
- let mapping = self.column_mapping.get(column_type).unwrap();
- let mut command_str = String::default();
- if let Some(command) = mapping.shortcut {
- command_str = format!("({})", command);
- }
-
- if mapping.enabled {
- Some(format!(
- "{}{}{}",
- column_type,
- command_str,
- if proc_sorting_type == column_type {
- if sort_reverse {
- DOWN_ARROW
- } else {
- UP_ARROW
- }
- } else {
- ' '
- }
- ))
- } else {
- None
- }
- })
- .collect()
- }
-}
-
-pub struct ProcWidgetState {
- pub process_search_state: ProcessSearchState,
- pub is_grouped: bool,
- pub scroll_state: TableComponentState,
- pub process_sorting_type: processes::ProcessSorting,
- pub is_process_sort_descending: bool,
- pub is_using_command: bool,
- pub current_column_index: usize,
- pub is_sort_open: bool,
- pub columns: ProcColumn,
- pub is_tree_mode: bool,
- pub table_width_state: CanvasTableWidthState,
- pub requires_redraw: bool,
-}
-
-impl ProcWidgetState {
- pub fn init(
- is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool,
- show_memory_as_values: bool, is_tree_mode: bool, is_using_command: bool,
- ) -> Self {
- let mut process_search_state = ProcessSearchState::default();
-
- if is_case_sensitive {
- // By default it's off
- process_search_state.search_toggle_ignore_case();
- }
- if is_match_whole_word {
- process_search_state.search_toggle_whole_word();
- }
- if is_use_regex {
- process_search_state.search_toggle_regex();
- }
-
- let (process_sorting_type, is_process_sort_descending) = if is_tree_mode {
- (processes::ProcessSorting::Pid, false)
- } else {
- (processes::ProcessSorting::CpuPercent, true)
- };
-
- // TODO: If we add customizable columns, this should pull from config
- let mut columns = ProcColumn::default();
- columns.set_to_sorted_index_from_type(&process_sorting_type);
- if is_grouped {
- // Normally defaults to showing by PID, toggle count on instead.
- columns.toggle(&ProcessSorting::Count);
- columns.toggle(&ProcessSorting::Pid);
- }
- if show_memory_as_values {
- // Normally defaults to showing by percent, toggle value on instead.
- columns.toggle(&ProcessSorting::Mem);
- columns.toggle(&ProcessSorting::MemPercent);
- }
- if is_using_command {
- columns.toggle(&ProcessSorting::ProcessName);
- columns.toggle(&ProcessSorting::Command);
- }
-
- ProcWidgetState {
- process_search_state,
- is_grouped,
- scroll_state: TableComponentState::default(),
- process_sorting_type,
- is_process_sort_descending,
- is_using_command,
- current_column_index: 0,
- is_sort_open: false,
- columns,
- is_tree_mode,
- table_width_state: CanvasTableWidthState::default(),
- requires_redraw: false,
- }
- }
-
- /// Updates sorting when using the column list.
- /// ...this really should be part of the ProcColumn struct (along with the sorting fields),
- /// but I'm too lazy.
- ///
- /// Sorry, future me, you're gonna have to refactor this later. Too busy getting
- /// the feature to work in the first place! :)
- pub fn update_sorting_with_columns(&mut self) {
- let mut true_index = 0;
- let mut enabled_index = 0;
- let target_itx = self.columns.current_scroll_position;
- for column in &self.columns.ordered_columns {
- let enabled = self.columns.column_mapping.get(column).unwrap().enabled;
- if enabled_index == target_itx && enabled {
- break;
- }
- if enabled {
- enabled_index += 1;
- }
- true_index += 1;
- }
-
- if let Some(new_sort_type) = self.columns.ordered_columns.get(true_index) {
- if *new_sort_type == self.process_sorting_type {
- // Just reverse the search if we're reselecting!
- self.is_process_sort_descending = !(self.is_process_sort_descending);
- } else {
- self.process_sorting_type = new_sort_type.clone();
- match self.process_sorting_type {
- ProcessSorting::State
- | ProcessSorting::Pid
- | ProcessSorting::ProcessName
- | ProcessSorting::Command => {
- // Also invert anything that uses alphabetical sorting by default.
- self.is_process_sort_descending = false;
- }
- _ => {
- self.is_process_sort_descending = true;
- }
- }
- }
- }
- }
-
- pub fn toggle_command_and_name(&mut self, is_using_command: bool) {
- if let Some(pn) = self
- .columns
- .column_mapping
- .get_mut(&ProcessSorting::ProcessName)
- {
- pn.enabled = !is_using_command;
- }
- if let Some(c) = self
- .columns
- .column_mapping
- .get_mut(&ProcessSorting::Command)
- {
- c.enabled = is_using_command;
- }
- }
-
- pub fn get_search_cursor_position(&self) -> usize {
- self.process_search_state
- .search_state
- .grapheme_cursor
- .cur_cursor()
- }
-
- pub fn get_char_cursor_position(&self) -> usize {
- self.process_search_state.search_state.char_cursor_position
- }
-
- pub fn is_search_enabled(&self) -> bool {
- self.process_search_state.search_state.is_enabled
- }
-
- pub fn get_current_search_query(&self) -> &String {
- &self.process_search_state.search_state.current_search_query
- }
-
- pub fn update_query(&mut self) {
- if self
- .process_search_state
- .search_state
- .current_search_query
- .is_empty()
- {
- self.process_search_state.search_state.is_blank_search = true;
- self.process_search_state.search_state.is_invalid_search = false;
- self.process_search_state.search_state.error_message = None;
- } else {
- let parsed_query = self.parse_query();
- // debug!("Parsed query: {:#?}", parsed_query);
-
- if let Ok(parsed_query) = parsed_query {
- self.process_search_state.search_state.query = Some(parsed_query);
- self.process_search_state.search_state.is_blank_search = false;
- self.process_search_state.search_state.is_invalid_search = false;
- self.process_search_state.search_state.error_message = None;
- } else if let Err(err) = parsed_query {
- self.process_search_state.search_state.is_blank_search = false;
- self.process_search_state.search_state.is_invalid_search = true;
- self.process_search_state.search_state.error_message = Some(err.to_string());
- }
- }
- self.scroll_state.scroll_bar = 0;
- self.scroll_state.current_scroll_position = 0;
- }
-
- pub fn clear_search(&mut self) {
- self.process_search_state.search_state.reset();
- }
-
- pub fn search_walk_forward(&mut self, start_position: usize) {
- self.process_search_state
- .search_state
- .grapheme_cursor
- .next_boundary(
- &self.process_search_state.search_state.current_search_query[start_position..],
- start_position,
- )
- .unwrap();
- }
-
- pub fn search_walk_back(&mut self, start_position: usize) {
- self.process_search_state
- .search_state
- .grapheme_cursor
- .prev_boundary(
- &self.process_search_state.search_state.current_search_query[..start_position],
- 0,
- )
- .unwrap();
- }
-}
-
pub struct ProcState {
- pub widget_states: HashMap<u64, ProcWidgetState>,
- pub force_update: Option<u64>,
- pub force_update_all: bool,
+ pub widget_states: HashMap<u64, ProcWidget>,
}
impl ProcState {
- pub fn init(widget_states: HashMap<u64, ProcWidgetState>) -> Self {
- ProcState {
- widget_states,
- force_update: None,
- force_update_all: false,
- }
+ pub fn init(widget_states: HashMap<u64, ProcWidget>) -> Self {
+ ProcState { widget_states }
}
- pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut ProcWidgetState> {
+ pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut ProcWidget> {
self.widget_states.get_mut(&widget_id)
}
- pub fn get_widget_state(&self, widget_id: u64) -> Option<&ProcWidgetState> {
+ pub fn get_widget_state(&self, widget_id: u64) -> Option<&ProcWidget> {
self.widget_states.get(&widget_id)
}
}
@@ -941,8 +195,7 @@ pub struct CpuWidgetState {
impl CpuWidgetState {
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
- const CPU_LEGEND_HEADER: [(Cow<'static, str>, Option<Cow<'static, str>>); 2] =
- [(Cow::Borrowed("CPU"), None), (Cow::Borrowed("Use%"), None)];
+ const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
const WIDTHS: [WidthBounds; CPU_LEGEND_HEADER.len()] = [
WidthBounds::soft_from_str("CPU", Some(0.5)),
WidthBounds::soft_from_str("Use%", Some(0.5)),
@@ -952,7 +205,7 @@ impl CpuWidgetState {
CPU_LEGEND_HEADER
.iter()
.zip(WIDTHS)
- .map(|(c, width)| TableComponentColumn::new(c.0.clone(), c.1.clone(), width))
+ .map(|(c, width)| TableComponentColumn::new(CellContent::new(*c, None), width))
.collect(),
);
@@ -1040,7 +293,9 @@ impl Default for TempWidgetState {
TEMP_HEADERS
.iter()
.zip(WIDTHS)
- .map(|(header, width)| TableComponentColumn::new(*header, None, width))
+ .map(|(header, width)| {
+ TableComponentColumn::new(CellContent::new(*header, None), width)
+ })
.collect(),
),
}
@@ -1087,7 +342,9 @@ impl Default for DiskWidgetState {
DISK_HEADERS
.iter()
.zip(WIDTHS)
- .map(|(header, width)| TableComponentColumn::new(*header, None, width))
+ .map(|(header, width)| {
+ TableComponentColumn::new(CellContent::new(*header, None), width)
+ })
.collect(),
),
}
@@ -1169,61 +426,3 @@ pub struct ConfigCategory {
pub struct ConfigOption {
pub set_function: Box<dyn Fn() -> anyhow::Result<()>>,
}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_scroll_update_position() {
- fn check_scroll_update(
- scroll: &mut TableComponentState, change: i64, max: usize, ret: Option<usize>,
- new_position: usize,
- ) {
- assert_eq!(scroll.update_position(change, max), ret);
- assert_eq!(scroll.current_scroll_position, new_position);
- }
-
- let mut scroll = TableComponentState {
- current_scroll_position: 5,
- scroll_bar: 0,
- scroll_direction: ScrollDirection::Down,
- table_state: Default::default(),
- columns: vec![],
- };
- let s = &mut scroll;
-
- // Update by 0. Should not change.
- check_scroll_update(s, 0, 15, None, 5);
-
- // Update by 5. Should increment to index 10.
- check_scroll_update(s, 5, 15, Some(10), 10);
-
- // Update by 5. Should not change.
- check_scroll_update(s, 5, 15, None, 10);
-
- // Update by 4. Should increment to index 14 (supposed max).
- check_scroll_update(s, 4, 15, Some(14), 14);
-
- // Update by 1. Should do nothing.
- check_scroll_update(s, 1, 15, None, 14);
-
- // Update by -15. Should do nothing.
- check_scroll_update(s, -15, 15, None, 14);
-
- // Update by -14. Should land on position 0.
- check_scroll_update(s, -14, 15, Some(0), 0);
-
- // Update by -1. Should do nothing.
- check_scroll_update(s, -15, 15, None, 0);
-
- // Update by 0. Should do nothing.
- check_scroll_update(s, 0, 15, None, 0);
-
- // Update by 15. Should do nothing.
- check_scroll_update(s, 15, 15, None, 0);
-
- // Update by 15 but with a larger bound. Should increment to 15.
- check_scroll_update(s, 15, 16, Some(15), 15);
- }
-}