1use std::collections::HashSet;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum SelectionMode {
8 None,
10 Single,
12 Multi,
14}
15
16#[derive(Clone, Debug)]
18pub struct SelectionModel {
19 mode: SelectionMode,
20 selected: HashSet<usize>,
21 anchor: Option<usize>,
23}
24
25impl SelectionModel {
26 pub fn new(mode: SelectionMode) -> Self {
28 Self {
29 mode,
30 selected: HashSet::new(),
31 anchor: None,
32 }
33 }
34
35 pub fn mode(&self) -> SelectionMode {
37 self.mode
38 }
39
40 pub fn len(&self) -> usize {
42 self.selected.len()
43 }
44
45 pub fn is_empty(&self) -> bool {
47 self.selected.is_empty()
48 }
49
50 pub fn is_selected(&self, index: usize) -> bool {
52 self.selected.contains(&index)
53 }
54
55 pub fn selected_sorted(&self) -> Vec<usize> {
57 let mut v: Vec<usize> = self.selected.iter().copied().collect();
58 v.sort_unstable();
59 v
60 }
61
62 pub fn clear(&mut self) {
64 self.selected.clear();
65 self.anchor = None;
66 }
67
68 pub fn click(&mut self, index: usize) {
71 if self.mode == SelectionMode::None {
72 return;
73 }
74 self.selected.clear();
75 self.selected.insert(index);
76 self.anchor = Some(index);
77 }
78
79 pub fn ctrl_click(&mut self, index: usize) {
83 match self.mode {
84 SelectionMode::None => {}
85 SelectionMode::Single => self.click(index),
86 SelectionMode::Multi => {
87 if !self.selected.remove(&index) {
88 self.selected.insert(index);
89 }
90 self.anchor = Some(index);
91 }
92 }
93 }
94
95 pub fn shift_click(&mut self, index: usize) {
99 match (self.mode, self.anchor) {
100 (SelectionMode::Multi, Some(anchor)) => {
101 let (lo, hi) = if anchor <= index {
102 (anchor, index)
103 } else {
104 (index, anchor)
105 };
106 self.selected.clear();
107 for i in lo..=hi {
108 self.selected.insert(i);
109 }
110 }
112 (SelectionMode::None, _) => {}
113 _ => self.click(index),
114 }
115 }
116
117 pub fn select_all(&mut self, row_count: usize) {
119 if self.mode != SelectionMode::Multi {
120 return;
121 }
122 self.selected = (0..row_count).collect();
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn single_click_replaces() {
132 let mut s = SelectionModel::new(SelectionMode::Single);
133 s.click(2);
134 assert!(s.is_selected(2));
135 s.click(5);
136 assert!(s.is_selected(5));
137 assert!(!s.is_selected(2));
138 assert_eq!(s.len(), 1);
139 }
140
141 #[test]
142 fn ctrl_click_toggles_in_multi() {
143 let mut s = SelectionModel::new(SelectionMode::Multi);
144 s.click(1);
145 s.ctrl_click(3);
146 s.ctrl_click(5);
147 assert_eq!(s.selected_sorted(), vec![1, 3, 5]);
148 s.ctrl_click(3);
150 assert_eq!(s.selected_sorted(), vec![1, 5]);
151 }
152
153 #[test]
154 fn shift_click_selects_range() {
155 let mut s = SelectionModel::new(SelectionMode::Multi);
156 s.click(2); s.shift_click(5);
158 assert_eq!(s.selected_sorted(), vec![2, 3, 4, 5]);
159 s.shift_click(0);
161 assert_eq!(s.selected_sorted(), vec![0, 1, 2]);
162 }
163
164 #[test]
165 fn select_all_and_clear() {
166 let mut s = SelectionModel::new(SelectionMode::Multi);
167 s.select_all(4);
168 assert_eq!(s.len(), 4);
169 s.clear();
170 assert!(s.is_empty());
171 }
172
173 #[test]
174 fn none_mode_is_inert() {
175 let mut s = SelectionModel::new(SelectionMode::None);
176 s.click(1);
177 s.ctrl_click(2);
178 s.shift_click(3);
179 s.select_all(10);
180 assert!(s.is_empty());
181 }
182
183 #[test]
184 fn single_mode_ctrl_click_behaves_like_click() {
185 let mut s = SelectionModel::new(SelectionMode::Single);
186 s.ctrl_click(2);
187 s.ctrl_click(4);
188 assert_eq!(s.selected_sorted(), vec![4]);
189 }
190}