git_igitt/widgets/
list.rs1use 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}