Skip to main content

wiki_tui/ui/
stateful_list.rs

1use ratatui::widgets::ListState;
2
3#[derive(Default)]
4pub enum ScrollBehaviour {
5    StickToTop,
6    StickToBottom,
7    StickToEnds,
8    #[default]
9    Circle,
10}
11
12pub struct StatefulList<T> {
13    state: ListState,
14    items: Vec<T>,
15    behvaiour: ScrollBehaviour,
16}
17
18impl<T> StatefulList<T> {
19    pub fn with_items(items: Vec<T>) -> Self {
20        StatefulList {
21            state: ListState::default().with_selected(Some(0)),
22            items,
23            behvaiour: ScrollBehaviour::default(),
24        }
25    }
26
27    pub fn scroll_behavior(mut self, behavior: ScrollBehaviour) -> Self {
28        self.behvaiour = behavior;
29        self
30    }
31
32    pub fn get_items(&self) -> &Vec<T> {
33        &self.items
34    }
35
36    pub fn get_items_mut(&mut self) -> &mut Vec<T> {
37        &mut self.items
38    }
39
40    pub fn get_state_mut(&mut self) -> &mut ListState {
41        &mut self.state
42    }
43
44    pub fn next(&mut self) {
45        let i = match self.state.selected() {
46            Some(i) => {
47                if i >= self.items.len().saturating_sub(1) {
48                    match self.behvaiour {
49                        ScrollBehaviour::StickToBottom | ScrollBehaviour::StickToEnds => i,
50                        ScrollBehaviour::StickToTop | ScrollBehaviour::Circle => 0,
51                    }
52                } else {
53                    i.saturating_add(1)
54                }
55            }
56            None => 0,
57        };
58        self.state.select(Some(i));
59    }
60
61    pub fn previous(&mut self) {
62        let i = match self.state.selected() {
63            Some(i) => {
64                if i == 0 {
65                    match self.behvaiour {
66                        ScrollBehaviour::StickToBottom | ScrollBehaviour::Circle => {
67                            self.items.len().saturating_sub(1)
68                        }
69                        ScrollBehaviour::StickToTop | ScrollBehaviour::StickToEnds => i,
70                    }
71                } else {
72                    i.saturating_sub(1)
73                }
74            }
75            None => 0,
76        };
77        self.state.select(Some(i))
78    }
79
80    pub fn unselect(&mut self) {
81        self.state.select(None)
82    }
83
84    pub fn is_selected(&self) -> bool {
85        self.state.selected().is_some()
86    }
87
88    pub fn selected(&self) -> Option<&T> {
89        if self.state.selected().unwrap_or_default() >= self.items.len() {
90            return None;
91        }
92        self.state.selected().map(|i| &self.items[i])
93    }
94}