git_igitt/widgets/
list.rs

1use tui::style::Style;
2use tui::text::Text;
3
4#[derive(Debug, Clone, Default)]
5pub struct ListState {
6    pub offset: usize,
7    pub selected: Option<usize>,
8    pub scroll_x: u16,
9}
10
11impl ListState {
12    pub fn selected(&self) -> Option<usize> {
13        self.selected
14    }
15
16    pub fn select(&mut self, index: Option<usize>) {
17        self.selected = index;
18        if index.is_none() {
19            self.offset = 0;
20        }
21    }
22}
23
24pub trait ListItem {
25    fn is_selectable(&self) -> bool;
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct DefaultListItem<'a> {
30    pub content: Text<'a>,
31    pub style: Style,
32}
33
34impl<'a> DefaultListItem<'a> {
35    pub fn new<T>(content: T) -> DefaultListItem<'a>
36    where
37        T: Into<Text<'a>>,
38    {
39        DefaultListItem {
40            content: content.into(),
41            style: Style::default(),
42        }
43    }
44
45    pub fn style(mut self, style: Style) -> DefaultListItem<'a> {
46        self.style = style;
47        self
48    }
49
50    pub fn height(&self) -> usize {
51        self.content.height()
52    }
53}
54
55pub struct StatefulList<T> {
56    pub state: ListState,
57    pub items: Vec<T>,
58}
59
60impl<T: ListItem> Default for StatefulList<T> {
61    fn default() -> StatefulList<T> {
62        StatefulList {
63            state: ListState::default(),
64            items: Vec::new(),
65        }
66    }
67}
68
69impl<T: ListItem> StatefulList<T> {
70    pub fn with_items(items: Vec<T>) -> StatefulList<T> {
71        StatefulList {
72            state: ListState::default(),
73            items,
74        }
75    }
76
77    pub fn fwd(&mut self, steps: usize) -> bool {
78        match self.state.selected() {
79            Some(sel) => {
80                for _ in 0..steps {
81                    if !self.next() {
82                        break;
83                    }
84                }
85                if let Some(new_sel) = self.state.selected() {
86                    sel != new_sel
87                } else {
88                    true
89                }
90            }
91            None => self.next(),
92        }
93    }
94
95    pub fn bwd(&mut self, steps: usize) -> bool {
96        match self.state.selected() {
97            Some(sel) => {
98                for _ in 0..steps {
99                    if !self.previous() {
100                        break;
101                    }
102                }
103                if let Some(new_sel) = self.state.selected() {
104                    sel != new_sel
105                } else {
106                    true
107                }
108            }
109            None => self.next(),
110        }
111    }
112
113    fn next(&mut self) -> bool {
114        match self.state.selected() {
115            Some(i) => {
116                if i < self.items.len() - 1 {
117                    for (idx, item) in self.items.iter().enumerate().skip(i + 1) {
118                        if item.is_selectable() {
119                            self.state.select(Some(idx));
120                            return true;
121                        }
122                    }
123                }
124            }
125            None => {
126                for (idx, item) in self.items.iter().enumerate() {
127                    if item.is_selectable() {
128                        self.state.select(Some(idx));
129                        return true;
130                    }
131                }
132            }
133        };
134        false
135    }
136
137    fn previous(&mut self) -> bool {
138        match self.state.selected() {
139            Some(i) => {
140                if i > 0 {
141                    for (idx, item) in self
142                        .items
143                        .iter()
144                        .enumerate()
145                        .rev()
146                        .skip(self.items.len() - i)
147                    {
148                        if item.is_selectable() {
149                            self.state.select(Some(idx));
150                            return true;
151                        }
152                    }
153                }
154            }
155            None => {
156                for (idx, item) in self.items.iter().enumerate() {
157                    if item.is_selectable() {
158                        self.state.select(Some(idx));
159                        return true;
160                    }
161                }
162            }
163        };
164        false
165    }
166
167    pub fn unselect(&mut self) {
168        self.state.select(None);
169    }
170}