use super::WidgetBase;
use crate::{
config::{Config, ListConfig},
ui::UIEvent,
};
use crossterm::event::KeyEvent;
use std::ops::{Deref, DerefMut};
use tui::widgets::ListState;
#[derive(Debug)]
pub struct WidgetList {
base: WidgetBase,
state: ListState,
first: usize,
size: usize,
config: ListConfig,
pub to_search: Option<String>,
}
impl WidgetList {
pub fn new(base: WidgetBase, config: &Config) -> Self {
let mut def = Self {
base,
state: ListState::default(),
first: 0,
size: 0,
config: config.list_config.clone(),
to_search: None,
};
def.state.select(Some(0));
def
}
pub fn act(&self) -> usize {
self.state.selected().unwrap_or(0)
}
pub fn index(&self) -> usize {
self.act() + self.first
}
pub fn state(&self) -> ListState {
self.state
}
pub fn set_size(&mut self, size: u16) {
self.size = size as usize;
}
pub fn down(&mut self, len: usize) {
let act = self.act();
if len <= self.size {
if len > act + 1 {
self.state.select(Some(act + 1));
}
} else if self.size <= act + 1 + self.config.list_shift {
if self.first + self.size < len {
self.first += 1;
} else if self.size > act + 1 {
self.state.select(Some(act + 1));
}
} else {
self.state.select(Some(act + 1));
}
log::trace!(
"List go down: act: {}, size: {} len: {}, first: {} shift: {}",
act,
self.size,
len,
self.first,
self.config.list_shift
);
}
pub fn up(&mut self) {
let act = self.act();
if act <= self.config.list_shift {
if self.first > 0 {
self.first -= 1;
} else if act > 0 {
self.state.select(Some(act - 1));
}
} else {
self.state.select(Some(act - 1));
}
log::trace!("List go up: act: {}", act);
}
pub fn next(&mut self, len: usize) -> Option<(usize, usize)> {
log::error!("len: {}, index: {}", len, self.index());
if len <= self.index() + 1 {
None
} else {
let old = self.index();
self.down(len);
Some((old, self.index()))
}
}
pub fn prev(&mut self) -> Option<(usize, usize)> {
if self.act() == 0 {
None
} else {
let old = self.index();
self.up();
Some((old, self.index()))
}
}
pub fn first(&mut self) {
self.state.select(Some(0));
self.first = 0;
}
pub fn last(&mut self, len: usize) {
let shown_items = len - 1;
if self.size > shown_items {
self.first = 0;
self.state.select(Some(shown_items));
} else {
self.first = len - self.size;
self.state.select(Some(self.size - 1));
}
}
pub fn set_search(&mut self, to_search: String) {
self.to_search = Some(to_search)
}
pub fn clear_search(&mut self) {
self.to_search = None
}
pub fn range(&self) -> (usize, usize) {
(self.first, self.first + self.size)
}
pub fn get_event(&self, event: &KeyEvent) -> UIEvent {
self.config.list_keybind.get_event(event)
}
pub fn handle_event(&mut self, event: UIEvent, len: usize) -> bool {
match event {
UIEvent::ListDown => self.down(len),
UIEvent::ListUp => self.up(),
UIEvent::ListFirst => self.first(),
UIEvent::ListLast => self.last(len),
UIEvent::CleanSearch => self.clear_search(),
_ => return false,
}
true
}
pub fn click(&mut self, _column: usize, row: usize, len: usize) {
let index = row - usize::from(self.base.chunk.y) - 1;
if index < len {
log::debug!("Click on item with index {index}.");
self.state.select(Some(index));
}
}
}
impl Deref for WidgetList {
type Target = WidgetBase;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for WidgetList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::todo::ToDo;
use test_log::test;
fn testing_widget(len: usize) -> WidgetList {
let mut todo = ToDo::default();
for i in 1..len {
todo.new_task(&format!("Task {}", i)).unwrap();
}
let conf = Config::default();
let mut widget = WidgetList::new(WidgetBase::new("", &conf), &conf);
widget.set_size(10);
widget
}
fn n_times(times: usize, func: impl Fn(&mut WidgetList), s: &mut WidgetList) {
for _ in 0..times {
func(s)
}
}
#[test]
fn movement_in_short_list() {
let len = 5;
let mut widget = testing_widget(len);
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
widget.down(len);
assert_eq!(widget.index(), 1);
assert_eq!(widget.act(), 1);
assert_eq!(widget.first, 0);
}
#[test]
fn movement_basic() {
let len = 50;
let mut widget = testing_widget(len);
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
widget.down(len);
assert_eq!(widget.index(), 1);
assert_eq!(widget.act(), 1);
assert_eq!(widget.first, 0);
widget.down(len);
assert_eq!(widget.index(), 2);
assert_eq!(widget.act(), 2);
assert_eq!(widget.first, 0);
widget.up();
assert_eq!(widget.index(), 1);
assert_eq!(widget.act(), 1);
assert_eq!(widget.first, 0);
widget.down(len);
assert_eq!(widget.index(), 2);
assert_eq!(widget.act(), 2);
assert_eq!(widget.first, 0);
}
#[test]
fn movement_full_list() {
let len = 50;
let mut widget = testing_widget(len);
n_times(5, |l| l.down(len), &mut widget);
assert_eq!(widget.index(), 5);
assert_eq!(widget.act(), 5);
assert_eq!(widget.first, 0);
widget.down(len);
assert_eq!(widget.index(), 6);
assert_eq!(widget.act(), 5);
assert_eq!(widget.first, 1);
widget.down(len);
assert_eq!(widget.index(), 7);
assert_eq!(widget.act(), 5);
assert_eq!(widget.first, 2);
n_times(50, |l| l.down(len), &mut widget);
assert_eq!(widget.index(), 49);
assert_eq!(widget.act(), 9);
assert_eq!(widget.first, 40);
widget.up();
assert_eq!(widget.index(), 48);
assert_eq!(widget.act(), 8);
assert_eq!(widget.first, 40);
n_times(4, WidgetList::up, &mut widget);
assert_eq!(widget.index(), 44);
assert_eq!(widget.act(), 4);
assert_eq!(widget.first, 40);
widget.up();
assert_eq!(widget.index(), 43);
assert_eq!(widget.act(), 4);
assert_eq!(widget.first, 39);
n_times(39, WidgetList::up, &mut widget);
assert_eq!(widget.index(), 4);
assert_eq!(widget.act(), 4);
assert_eq!(widget.first, 0);
widget.up();
assert_eq!(widget.index(), 3);
assert_eq!(widget.act(), 3);
assert_eq!(widget.first, 0);
n_times(3, WidgetList::up, &mut widget);
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
}
#[test]
fn move_task() {
let len = 50;
let mut widget = testing_widget(len);
assert_eq!(widget.next(len), Some((0, 1)));
assert_eq!(widget.next(len), Some((1, 2)));
assert_eq!(widget.next(len), Some((2, 3)));
assert_eq!(widget.next(len), Some((3, 4)));
assert_eq!(widget.next(len), Some((4, 5)));
assert_eq!(widget.prev(), Some((5, 4)));
assert_eq!(widget.prev(), Some((4, 3)));
assert_eq!(widget.prev(), Some((3, 2)));
assert_eq!(widget.prev(), Some((2, 1)));
assert_eq!(widget.prev(), Some((1, 0)));
widget.down(len);
assert_eq!(widget.next(len), Some((1, 2)));
widget.up();
assert_eq!(widget.next(len), Some((1, 2)));
widget.up();
assert_eq!(widget.next(len), Some((1, 2)));
}
#[test]
fn move_task_borders() {
let len = 50;
let mut widget = testing_widget(len);
assert_eq!(widget.prev(), None);
widget.down(len);
assert_eq!(widget.prev(), Some((1, 0)));
n_times(50, |l| l.down(len), &mut widget);
assert_eq!(widget.next(len), None);
widget.up();
assert_eq!(widget.next(len), Some((48, 49)));
}
#[test]
fn first_and_last_item() {
let len = 50;
let mut widget = testing_widget(len);
widget.last(len);
assert_eq!(widget.index(), 49);
assert_eq!(widget.act(), 9);
assert_eq!(widget.first, 40);
widget.first();
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
let len = 5;
let mut widget = testing_widget(len);
widget.last(len);
assert_eq!(widget.index(), 4);
assert_eq!(widget.act(), 4);
assert_eq!(widget.first, 0);
widget.first();
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
}
#[test]
fn range() {
let widget = testing_widget(50);
assert_eq!(widget.range(), (0, 10));
}
#[test]
fn handle_event() {
let len = 50;
let mut widget = testing_widget(len);
assert!(widget.handle_event(UIEvent::ListDown, len));
assert_eq!(widget.index(), 1);
assert_eq!(widget.act(), 1);
assert_eq!(widget.first, 0);
assert!(widget.handle_event(UIEvent::ListUp, len));
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
assert!(widget.handle_event(UIEvent::ListLast, len));
assert_eq!(widget.index(), 49);
assert_eq!(widget.act(), 9);
assert_eq!(widget.first, 40);
assert!(widget.handle_event(UIEvent::ListFirst, len));
assert_eq!(widget.index(), 0);
assert_eq!(widget.act(), 0);
assert_eq!(widget.first, 0);
assert!(!widget.handle_event(UIEvent::None, len));
}
}