#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableState {
pub(crate) offset: usize,
pub(crate) selected: Option<usize>,
pub(crate) selected_column: Option<usize>,
}
impl TableState {
pub const fn new() -> Self {
Self {
offset: 0,
selected: None,
selected_column: None,
}
}
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn with_offset(mut self, offset: usize) -> Self {
self.offset = offset;
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn with_selected<T>(mut self, selected: T) -> Self
where
T: Into<Option<usize>>,
{
self.selected = selected.into();
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn with_selected_column<T>(mut self, selected: T) -> Self
where
T: Into<Option<usize>>,
{
self.selected_column = selected.into();
self
}
#[must_use = "method moves the value of self and returns the modified value"]
pub fn with_selected_cell<T>(mut self, selected: T) -> Self
where
T: Into<Option<(usize, usize)>>,
{
if let Some((r, c)) = selected.into() {
self.selected = Some(r);
self.selected_column = Some(c);
} else {
self.selected = None;
self.selected_column = None;
}
self
}
pub const fn offset(&self) -> usize {
self.offset
}
pub const fn offset_mut(&mut self) -> &mut usize {
&mut self.offset
}
pub const fn selected(&self) -> Option<usize> {
self.selected
}
pub const fn selected_column(&self) -> Option<usize> {
self.selected_column
}
pub const fn selected_cell(&self) -> Option<(usize, usize)> {
if let (Some(r), Some(c)) = (self.selected, self.selected_column) {
return Some((r, c));
}
None
}
pub const fn selected_mut(&mut self) -> &mut Option<usize> {
&mut self.selected
}
pub const fn selected_column_mut(&mut self) -> &mut Option<usize> {
&mut self.selected_column
}
pub const fn select(&mut self, index: Option<usize>) {
self.selected = index;
if index.is_none() {
self.offset = 0;
}
}
pub const fn select_column(&mut self, index: Option<usize>) {
self.selected_column = index;
}
pub const fn select_cell(&mut self, indexes: Option<(usize, usize)>) {
if let Some((r, c)) = indexes {
self.selected = Some(r);
self.selected_column = Some(c);
} else {
self.offset = 0;
self.selected = None;
self.selected_column = None;
}
}
pub fn select_next(&mut self) {
let next = self.selected.map_or(0, |i| i.saturating_add(1));
self.select(Some(next));
}
pub fn select_next_column(&mut self) {
let next = self.selected_column.map_or(0, |i| i.saturating_add(1));
self.select_column(Some(next));
}
pub fn select_previous(&mut self) {
let previous = self.selected.map_or(usize::MAX, |i| i.saturating_sub(1));
self.select(Some(previous));
}
pub fn select_previous_column(&mut self) {
let previous = self
.selected_column
.map_or(usize::MAX, |i| i.saturating_sub(1));
self.select_column(Some(previous));
}
pub const fn select_first(&mut self) {
self.select(Some(0));
}
pub const fn select_first_column(&mut self) {
self.select_column(Some(0));
}
pub const fn select_last(&mut self) {
self.select(Some(usize::MAX));
}
pub const fn select_last_column(&mut self) {
self.select_column(Some(usize::MAX));
}
pub fn scroll_down_by(&mut self, amount: u16) {
let selected = self.selected.unwrap_or_default();
self.select(Some(selected.saturating_add(amount as usize)));
}
pub fn scroll_up_by(&mut self, amount: u16) {
let selected = self.selected.unwrap_or_default();
self.select(Some(selected.saturating_sub(amount as usize)));
}
pub fn scroll_right_by(&mut self, amount: u16) {
let selected = self.selected_column.unwrap_or_default();
self.select_column(Some(selected.saturating_add(amount as usize)));
}
pub fn scroll_left_by(&mut self, amount: u16) {
let selected = self.selected_column.unwrap_or_default();
self.select_column(Some(selected.saturating_sub(amount as usize)));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let state = TableState::new();
assert_eq!(state.offset, 0);
assert_eq!(state.selected, None);
assert_eq!(state.selected_column, None);
}
#[test]
fn with_offset() {
let state = TableState::new().with_offset(1);
assert_eq!(state.offset, 1);
}
#[test]
fn with_selected() {
let state = TableState::new().with_selected(Some(1));
assert_eq!(state.selected, Some(1));
}
#[test]
fn with_selected_column() {
let state = TableState::new().with_selected_column(Some(1));
assert_eq!(state.selected_column, Some(1));
}
#[test]
fn with_selected_cell_none() {
let state = TableState::new().with_selected_cell(None);
assert_eq!(state.selected, None);
assert_eq!(state.selected_column, None);
}
#[test]
fn offset() {
let state = TableState::new();
assert_eq!(state.offset(), 0);
}
#[test]
fn offset_mut() {
let mut state = TableState::new();
*state.offset_mut() = 1;
assert_eq!(state.offset, 1);
}
#[test]
fn selected() {
let state = TableState::new();
assert_eq!(state.selected(), None);
}
#[test]
fn selected_column() {
let state = TableState::new();
assert_eq!(state.selected_column(), None);
}
#[test]
fn selected_cell() {
let state = TableState::new();
assert_eq!(state.selected_cell(), None);
}
#[test]
fn selected_mut() {
let mut state = TableState::new();
*state.selected_mut() = Some(1);
assert_eq!(state.selected, Some(1));
}
#[test]
fn selected_column_mut() {
let mut state = TableState::new();
*state.selected_column_mut() = Some(1);
assert_eq!(state.selected_column, Some(1));
}
#[test]
fn select() {
let mut state = TableState::new();
state.select(Some(1));
assert_eq!(state.selected, Some(1));
}
#[test]
fn select_none() {
let mut state = TableState::new().with_selected(Some(1));
state.select(None);
assert_eq!(state.selected, None);
}
#[test]
fn select_column() {
let mut state = TableState::new();
state.select_column(Some(1));
assert_eq!(state.selected_column, Some(1));
}
#[test]
fn select_column_none() {
let mut state = TableState::new().with_selected_column(Some(1));
state.select_column(None);
assert_eq!(state.selected_column, None);
}
#[test]
fn select_cell() {
let mut state = TableState::new();
state.select_cell(Some((1, 5)));
assert_eq!(state.selected_cell(), Some((1, 5)));
}
#[test]
fn select_cell_none() {
let mut state = TableState::new().with_selected_cell(Some((1, 5)));
state.select_cell(None);
assert_eq!(state.selected, None);
assert_eq!(state.selected_column, None);
assert_eq!(state.selected_cell(), None);
}
#[test]
fn test_table_state_navigation() {
let mut state = TableState::default();
state.select_first();
assert_eq!(state.selected, Some(0));
state.select_previous(); assert_eq!(state.selected, Some(0));
state.select_next();
assert_eq!(state.selected, Some(1));
state.select_previous();
assert_eq!(state.selected, Some(0));
state.select_last();
assert_eq!(state.selected, Some(usize::MAX));
state.select_next(); assert_eq!(state.selected, Some(usize::MAX));
state.select_previous();
assert_eq!(state.selected, Some(usize::MAX - 1));
state.select_next();
assert_eq!(state.selected, Some(usize::MAX));
let mut state = TableState::default();
state.select_next();
assert_eq!(state.selected, Some(0));
let mut state = TableState::default();
state.select_previous();
assert_eq!(state.selected, Some(usize::MAX));
let mut state = TableState::default();
state.select(Some(2));
state.scroll_down_by(4);
assert_eq!(state.selected, Some(6));
let mut state = TableState::default();
state.scroll_up_by(3);
assert_eq!(state.selected, Some(0));
state.select(Some(6));
state.scroll_up_by(4);
assert_eq!(state.selected, Some(2));
state.scroll_up_by(4);
assert_eq!(state.selected, Some(0));
let mut state = TableState::default();
state.select_first_column();
assert_eq!(state.selected_column, Some(0));
state.select_previous_column();
assert_eq!(state.selected_column, Some(0));
state.select_next_column();
assert_eq!(state.selected_column, Some(1));
state.select_previous_column();
assert_eq!(state.selected_column, Some(0));
state.select_last_column();
assert_eq!(state.selected_column, Some(usize::MAX));
state.select_previous_column();
assert_eq!(state.selected_column, Some(usize::MAX - 1));
let mut state = TableState::default().with_selected_column(Some(12));
state.scroll_right_by(4);
assert_eq!(state.selected_column, Some(16));
state.scroll_left_by(20);
assert_eq!(state.selected_column, Some(0));
state.scroll_right_by(100);
assert_eq!(state.selected_column, Some(100));
state.scroll_left_by(20);
assert_eq!(state.selected_column, Some(80));
}
}