egui_charts/drawings/services/
selection.rs1use std::collections::HashSet;
13
14#[derive(Debug, Clone, Default)]
16pub struct SelectionService {
17 selected: HashSet<usize>,
19 primary: Option<usize>,
21 multi_select_mode: bool,
23}
24
25impl SelectionService {
26 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn select(&mut self, id: usize) -> bool {
35 if self.multi_select_mode {
36 let changed = self.selected.insert(id);
38 self.primary = Some(id);
39 changed
40 } else {
41 let was_selected = self.selected.contains(&id) && self.selected.len() == 1;
43 self.selected.clear();
44 self.selected.insert(id);
45 self.primary = Some(id);
46 !was_selected
47 }
48 }
49
50 pub fn toggle(&mut self, id: usize) -> bool {
54 if self.selected.contains(&id) {
55 self.selected.remove(&id);
56 if self.primary == Some(id) {
57 self.primary = self.selected.iter().copied().next();
58 }
59 true
60 } else {
61 self.selected.insert(id);
62 self.primary = Some(id);
63 true
64 }
65 }
66
67 pub fn deselect(&mut self, id: usize) -> bool {
71 let removed = self.selected.remove(&id);
72 if self.primary == Some(id) {
73 self.primary = self.selected.iter().copied().next();
74 }
75 removed
76 }
77
78 pub fn deselect_all(&mut self) -> bool {
82 let had_selection = !self.selected.is_empty();
83 self.selected.clear();
84 self.primary = None;
85 had_selection
86 }
87
88 #[inline]
90 pub fn is_selected(&self, id: usize) -> bool {
91 self.selected.contains(&id)
92 }
93
94 #[inline]
96 pub fn has_selection(&self) -> bool {
97 !self.selected.is_empty()
98 }
99
100 #[inline]
102 pub fn primary(&self) -> Option<usize> {
103 self.primary
104 }
105
106 #[inline]
108 pub fn selected_ids(&self) -> impl Iterator<Item = usize> + '_ {
109 self.selected.iter().copied()
110 }
111
112 #[inline]
114 pub fn selection_count(&self) -> usize {
115 self.selected.len()
116 }
117
118 pub fn set_multi_select_mode(&mut self, enabled: bool) {
120 self.multi_select_mode = enabled;
121 }
122
123 #[inline]
125 pub fn is_multi_select_mode(&self) -> bool {
126 self.multi_select_mode
127 }
128
129 pub fn select_all(&mut self, ids: impl IntoIterator<Item = usize>) {
131 for id in ids {
132 self.selected.insert(id);
133 if self.primary.is_none() {
134 self.primary = Some(id);
135 }
136 }
137 }
138
139 pub fn prune(&mut self, existing_ids: &HashSet<usize>) {
143 self.selected.retain(|id| existing_ids.contains(id));
144 if let Some(p) = self.primary
145 && !existing_ids.contains(&p)
146 {
147 self.primary = self.selected.iter().copied().next();
148 }
149 }
150
151 pub fn single_selection(&self) -> Option<usize> {
155 if self.selected.len() == 1 {
156 self.selected.iter().copied().next()
157 } else {
158 None
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
171 fn select_replaces_or_extends_by_mode() {
172 let cases = [
173 (false, [true, true, true, true, false], vec![2]),
174 (true, [true, true, true, false, false], vec![1, 2, 3]),
175 ];
176 for (multi, changed, selected) in cases {
177 let mut sel = SelectionService::new();
178 sel.set_multi_select_mode(multi);
179
180 let returned = [1, 2, 3, 2, 2].map(|id| sel.select(id));
181
182 assert_eq!(returned, changed, "multi={multi}");
183 let mut ids: Vec<_> = sel.selected_ids().collect();
184 ids.sort_unstable();
185 assert_eq!(ids, selected, "multi={multi}");
186 assert_eq!(sel.primary(), Some(2), "multi={multi}");
187 }
188 }
189
190 #[test]
191 fn test_toggle() {
192 let mut sel = SelectionService::new();
193
194 sel.toggle(1);
195 assert!(sel.is_selected(1));
196
197 sel.toggle(1);
198 assert!(!sel.is_selected(1));
199 }
200
201 #[test]
202 fn test_deselect_all() {
203 let mut sel = SelectionService::new();
204 sel.set_multi_select_mode(true);
205 sel.select(1);
206 sel.select(2);
207
208 assert!(sel.deselect_all());
209 assert!(!sel.has_selection());
210 assert_eq!(sel.primary(), None);
211 }
212
213 #[test]
214 fn test_prune() {
215 let mut sel = SelectionService::new();
216 sel.set_multi_select_mode(true);
217 sel.select(1);
218 sel.select(2);
219 sel.select(3);
220
221 let existing: HashSet<usize> = [1, 3].into_iter().collect();
223 sel.prune(&existing);
224
225 assert!(sel.is_selected(1));
226 assert!(!sel.is_selected(2));
227 assert!(sel.is_selected(3));
228 assert_eq!(sel.selection_count(), 2);
229 }
230
231 #[test]
232 fn test_select_all() {
233 let mut sel = SelectionService::new();
234 sel.select_all([1, 2, 3]);
235
236 let mut ids: Vec<_> = sel.selected_ids().collect();
237 ids.sort_unstable();
238 assert_eq!(ids, [1, 2, 3]);
239 assert_eq!(sel.primary(), Some(1));
240 }
241
242 #[test]
243 fn test_single_selection_legacy() {
244 let mut sel = SelectionService::new();
245
246 assert_eq!(sel.single_selection(), None);
248
249 sel.select(5);
251 assert_eq!(sel.single_selection(), Some(5));
252
253 sel.set_multi_select_mode(true);
255 sel.select(6);
256 assert_eq!(sel.single_selection(), None);
257 }
258}