1use crate::{ColumnFilter, RowSource, SortDirection, SortState};
8
9#[derive(Clone, Debug, Default)]
21pub struct HeaderSortState {
22 pub column: Option<usize>,
24 pub ascending: bool,
26}
27
28impl HeaderSortState {
29 pub fn new() -> Self {
31 Self {
32 column: None,
33 ascending: true,
34 }
35 }
36
37 pub fn toggle(&mut self, col: usize) {
42 match self.column {
43 Some(c) if c == col => {
44 if self.ascending {
45 self.ascending = false;
46 } else {
47 self.column = None;
49 self.ascending = true;
50 }
51 }
52 _ => {
53 self.column = Some(col);
54 self.ascending = true;
55 }
56 }
57 }
58
59 pub fn indicator(&self, col: usize) -> &'static str {
64 match self.column {
65 Some(c) if c == col => {
66 if self.ascending {
67 "▲"
68 } else {
69 "▼"
70 }
71 }
72 _ => "",
73 }
74 }
75
76 pub fn as_sort_state(&self) -> Option<SortState> {
79 self.column.map(|col| {
80 let dir = if self.ascending {
81 SortDirection::Ascending
82 } else {
83 SortDirection::Descending
84 };
85 SortState::new(col, dir)
86 })
87 }
88}
89
90pub fn move_column(order: &mut Vec<usize>, from: usize, to: usize) {
97 if from >= order.len() || to >= order.len() {
98 return;
99 }
100 let col = order.remove(from);
101 order.insert(to, col);
102}
103
104#[derive(Clone, Debug)]
114pub struct TableIndex {
115 sort_index: Vec<usize>,
116 filter_index: Vec<usize>,
117 sort_dirty: bool,
118 filter_dirty: bool,
119}
120
121impl TableIndex {
122 pub fn new() -> Self {
124 Self {
125 sort_index: Vec::new(),
126 filter_index: Vec::new(),
127 sort_dirty: true,
128 filter_dirty: true,
129 }
130 }
131
132 pub fn invalidate_sort(&mut self) {
134 self.sort_dirty = true;
135 self.filter_dirty = true;
136 }
137
138 pub fn invalidate_filter(&mut self) {
140 self.filter_dirty = true;
141 }
142
143 pub fn is_sort_dirty(&self) -> bool {
145 self.sort_dirty
146 }
147
148 pub fn is_filter_dirty(&self) -> bool {
150 self.filter_dirty
151 }
152
153 pub fn sort_index(&mut self, rows: &dyn RowSource, sort: &HeaderSortState) -> &[usize] {
157 if self.sort_dirty {
158 self.sort_index = match sort.as_sort_state() {
159 Some(st) => crate::sort_indices(rows, st.column, st.direction),
160 None => (0..rows.row_count()).collect(),
161 };
162 self.sort_dirty = false;
163 }
164 &self.sort_index
165 }
166
167 pub fn filter_index(
172 &mut self,
173 rows: &dyn RowSource,
174 sort: &HeaderSortState,
175 filters: &[ColumnFilter],
176 ) -> &[usize] {
177 self.sort_index(rows, sort);
179 if self.filter_dirty {
180 let active: Vec<&ColumnFilter> = filters.iter().filter(|f| !f.is_inactive()).collect();
181 if active.is_empty() {
182 self.filter_index = self.sort_index.clone();
183 } else {
184 self.filter_index = self
185 .sort_index
186 .iter()
187 .copied()
188 .filter(|&i| {
189 let row = rows.row(i);
190 active.iter().all(|f| f.matches(&row))
191 })
192 .collect();
193 }
194 self.filter_dirty = false;
195 }
196 &self.filter_index
197 }
198}
199
200impl Default for TableIndex {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206use crate::SelectionModel;
209
210pub fn handle_row_click(
217 selection: &mut SelectionModel,
218 row: usize,
219 ctrl: bool,
220 shift: bool,
221 last_clicked: &mut Option<usize>,
222) {
223 if shift {
224 selection.shift_click(row);
225 } else if ctrl {
226 selection.ctrl_click(row);
227 } else {
228 selection.click(row);
229 }
230 *last_clicked = Some(row);
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use crate::{Cell, ColumnDef, SelectionMode};
237
238 #[test]
241 fn sort_toggle_new_column() {
242 let mut s = HeaderSortState::new();
243 s.toggle(0);
244 assert_eq!(s.column, Some(0));
245 assert!(s.ascending);
246 }
247
248 #[test]
249 fn sort_toggle_same_column() {
250 let mut s = HeaderSortState::new();
251 s.toggle(0); s.toggle(0); assert_eq!(s.column, Some(0));
254 assert!(!s.ascending);
255 s.toggle(0); assert_eq!(s.column, None);
257 }
258
259 #[test]
260 fn sort_indicator_active() {
261 let mut s = HeaderSortState::new();
262 s.toggle(0);
263 assert_eq!(s.indicator(0), "▲");
264 s.toggle(0);
265 assert_eq!(s.indicator(0), "▼");
266 }
267
268 #[test]
269 fn sort_indicator_inactive() {
270 let mut s = HeaderSortState::new();
271 s.toggle(0);
272 assert_eq!(s.indicator(1), "");
273 }
274
275 #[test]
278 fn move_column_forward() {
279 let mut order = vec![0usize, 1, 2];
280 move_column(&mut order, 0, 2);
281 assert_eq!(order, vec![1, 2, 0]);
282 }
283
284 #[test]
285 fn move_column_backward() {
286 let mut order = vec![0usize, 1, 2];
287 move_column(&mut order, 2, 0);
288 assert_eq!(order, vec![2, 0, 1]);
289 }
290
291 #[test]
292 fn move_column_no_op() {
293 let mut order = vec![0usize, 1, 2];
294 move_column(&mut order, 1, 1);
295 assert_eq!(order, vec![0, 1, 2]);
296 }
297
298 struct SimpleData {
301 rows: Vec<Vec<Cell>>,
302 cols: Vec<ColumnDef>,
303 }
304 impl RowSource for SimpleData {
305 fn row_count(&self) -> usize {
306 self.rows.len()
307 }
308 fn row(&self, i: usize) -> Vec<Cell> {
309 self.rows[i].clone()
310 }
311 fn column_defs(&self) -> &[ColumnDef] {
312 &self.cols
313 }
314 }
315
316 fn make_data() -> SimpleData {
317 SimpleData {
318 rows: vec![vec![Cell::Int(3)], vec![Cell::Int(1)], vec![Cell::Int(2)]],
319 cols: vec![],
320 }
321 }
322
323 #[test]
324 fn sort_index_invalidate() {
325 let mut idx = TableIndex::new();
326 let data = make_data();
328 let sort = HeaderSortState::new();
329 let _ = idx.sort_index(&data, &sort);
330 assert!(!idx.is_sort_dirty());
331 idx.invalidate_sort();
332 assert!(idx.is_sort_dirty());
333 }
334
335 #[test]
336 fn filter_index_invalidate() {
337 let mut idx = TableIndex::new();
338 let data = make_data();
339 let sort = HeaderSortState::new();
340 let _ = idx.filter_index(&data, &sort, &[]);
341 assert!(!idx.is_filter_dirty());
342 idx.invalidate_filter();
343 assert!(idx.is_filter_dirty());
344 }
345
346 #[test]
349 fn handle_click_selects_row() {
350 let mut sel = SelectionModel::new(SelectionMode::Multi);
351 let mut last = None;
352 handle_row_click(&mut sel, 3, false, false, &mut last);
353 assert!(sel.is_selected(3));
354 assert_eq!(last, Some(3));
355 }
356
357 #[test]
358 fn handle_ctrl_click_toggles() {
359 let mut sel = SelectionModel::new(SelectionMode::Multi);
360 let mut last = None;
361 handle_row_click(&mut sel, 3, true, false, &mut last);
362 assert!(sel.is_selected(3));
363 handle_row_click(&mut sel, 3, true, false, &mut last);
365 assert!(!sel.is_selected(3));
366 }
367
368 #[test]
369 fn handle_shift_click_range() {
370 let mut sel = SelectionModel::new(SelectionMode::Multi);
371 let mut last = None;
372 handle_row_click(&mut sel, 2, false, false, &mut last);
373 handle_row_click(&mut sel, 5, false, true, &mut last);
374 assert_eq!(sel.selected_sorted(), vec![2, 3, 4, 5]);
375 }
376}