1use std::cell::RefCell;
42use std::collections::HashSet;
43use std::rc::Rc;
44
45use teksilo_core::signal::Signal;
46
47use crate::dnd_types::ItemKey;
48use crate::selection_model::SelectionMode;
49
50pub struct KeyedSelectionModel<K: ItemKey> {
58 mode: SelectionMode,
59 selection: Signal<HashSet<K>>,
60 anchor: Rc<RefCell<Option<K>>>,
61 base: Rc<RefCell<HashSet<K>>>,
67 extending: Rc<std::cell::Cell<bool>>,
71 #[cfg(debug_assertions)]
74 debug_adapter_holder: Rc<RefCell<Option<Rc<dyn crate::debug_registry::ModelDebug>>>>,
75}
76
77impl<K: ItemKey> KeyedSelectionModel<K> {
78 pub fn new(mode: SelectionMode) -> Self {
80 Self {
81 mode,
82 selection: Signal::new(HashSet::new()),
83 anchor: Rc::new(RefCell::new(None)),
84 base: Rc::new(RefCell::new(HashSet::new())),
85 extending: Rc::new(std::cell::Cell::new(false)),
86 #[cfg(debug_assertions)]
87 debug_adapter_holder: Rc::new(RefCell::new(None)),
88 }
89 }
90
91 fn commit_base(&self, base: HashSet<K>) {
93 *self.base.borrow_mut() = base;
94 self.extending.set(false);
95 }
96
97 pub fn mode(&self) -> SelectionMode {
99 self.mode
100 }
101
102 pub fn selection_signal(&self) -> Signal<HashSet<K>> {
104 self.selection.clone()
105 }
106
107 pub fn is_selected(&self, key: &K) -> bool {
109 self.selection.get().contains(key)
110 }
111
112 pub fn selected_keys(&self) -> Vec<K> {
114 self.selection.get().into_iter().collect()
115 }
116
117 pub fn count(&self) -> usize {
119 self.selection.get().len()
120 }
121
122 pub fn select(&self, key: K) {
124 if self.mode == SelectionMode::None {
125 return;
126 }
127 let mut set = HashSet::new();
128 set.insert(key.clone());
129 self.selection.set(set);
130 *self.anchor.borrow_mut() = Some(key);
131 self.commit_base(HashSet::new());
132 }
133
134 pub fn toggle(&self, key: K) {
136 match self.mode {
137 SelectionMode::None => {}
138 SelectionMode::Single => self.select(key),
139 SelectionMode::Multi => {
140 let mut set = self.selection.get();
141 if set.contains(&key) {
142 set.remove(&key);
143 } else {
144 set.insert(key.clone());
145 }
146 self.selection.set(set.clone());
147 *self.anchor.borrow_mut() = Some(key);
150 self.commit_base(set);
151 }
152 }
153 }
154
155 pub fn extend_to(&self, target: K, ordered_keys: &[K]) {
160 self.extend_from_base(target, ordered_keys, false);
161 }
162
163 pub fn extend_to_additive(&self, target: K, ordered_keys: &[K]) {
172 self.extend_from_base(target, ordered_keys, true);
173 }
174
175 fn extend_from_base(&self, target: K, ordered_keys: &[K], additive: bool) {
176 match self.mode {
177 SelectionMode::None => {}
178 SelectionMode::Single => self.select(target),
179 SelectionMode::Multi => {
180 let anchor = self.anchor.borrow().clone();
181 let Some(anchor) = anchor else {
182 self.select(target);
183 return;
184 };
185 let a = ordered_keys.iter().position(|k| *k == anchor);
186 let t = ordered_keys.iter().position(|k| *k == target);
187 match (a, t) {
188 (Some(a), Some(t)) => {
189 if additive && !self.extending.get() {
190 *self.base.borrow_mut() = self.selection.get();
191 }
192 let (lo, hi) = (a.min(t), a.max(t));
193 let mut set = self.base.borrow().clone();
197 for k in &ordered_keys[lo..=hi] {
198 set.insert(k.clone());
199 }
200 self.selection.set(set);
201 self.extending.set(true);
202 }
204 _ => self.select(target),
205 }
206 }
207 }
208 }
209
210 pub fn select_keys(&self, keys: impl IntoIterator<Item = K>, additive: bool) {
213 if self.mode == SelectionMode::None {
214 return;
215 }
216 let mut set = if additive {
217 self.selection.get()
218 } else {
219 HashSet::new()
220 };
221 set.extend(keys);
222 if self.mode == SelectionMode::Single && set.len() > 1 {
223 let keep = set.iter().next().cloned();
224 set = keep.into_iter().collect();
225 }
226 self.selection.set(set.clone());
227 self.commit_base(if additive { set } else { HashSet::new() });
228 }
229
230 pub fn clear(&self) {
232 self.selection.set(HashSet::new());
233 *self.anchor.borrow_mut() = None;
234 self.commit_base(HashSet::new());
235 }
236
237 pub fn prune_missing(&self, exists: impl Fn(&K) -> bool) {
242 let old = self.selection.get();
243 let new: HashSet<K> = old.iter().filter(|k| exists(k)).cloned().collect();
244 if new.len() != old.len() {
245 self.selection.set(new);
246 }
247 let drop_anchor = self.anchor.borrow().as_ref().is_some_and(|a| !exists(a));
248 if drop_anchor {
249 *self.anchor.borrow_mut() = None;
250 }
251 let mut base = self.base.borrow_mut();
254 if !base.is_empty() {
255 base.retain(|k| exists(k));
256 }
257 }
258}
259
260impl<K: ItemKey> Clone for KeyedSelectionModel<K> {
261 fn clone(&self) -> Self {
262 Self {
263 mode: self.mode,
264 selection: self.selection.clone(),
265 anchor: self.anchor.clone(),
266 base: self.base.clone(),
267 extending: self.extending.clone(),
268 #[cfg(debug_assertions)]
269 debug_adapter_holder: self.debug_adapter_holder.clone(),
270 }
271 }
272}
273
274impl<K: ItemKey> KeyedSelectionModel<K> {
275 pub fn debug_named(self, _name: impl Into<String>) -> Self {
278 #[cfg(debug_assertions)]
279 {
280 let adapter: Rc<dyn crate::debug_registry::ModelDebug> =
281 Rc::new(KeyedSelectionModelDebug {
282 selection: self.selection.clone(),
283 mode: self.mode,
284 });
285 crate::debug_registry::register(_name.into(), Rc::downgrade(&adapter));
286 *self.debug_adapter_holder.borrow_mut() = Some(adapter);
287 }
288 self
289 }
290}
291
292#[cfg(debug_assertions)]
293struct KeyedSelectionModelDebug<K: ItemKey> {
294 selection: Signal<HashSet<K>>,
295 mode: SelectionMode,
296}
297
298#[cfg(debug_assertions)]
299impl<K: ItemKey> crate::debug_registry::ModelDebug for KeyedSelectionModelDebug<K> {
300 fn kind(&self) -> &'static str {
301 "KeyedSelectionModel"
302 }
303 fn len(&self) -> usize {
304 self.selection.get().len()
305 }
306 fn debug_dump(&self, out: &mut dyn std::fmt::Write) {
307 let _ = writeln!(out, "mode = {:?}", self.mode);
308 let sel = self.selection.get();
309 if sel.is_empty() {
310 let _ = writeln!(out, "(empty)");
311 return;
312 }
313 for k in sel.iter() {
314 let _ = writeln!(out, "{:?}", k);
315 }
316 }
317}
318
319impl<K: ItemKey> std::fmt::Debug for KeyedSelectionModel<K> {
320 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321 f.debug_struct("KeyedSelectionModel")
322 .field("mode", &self.mode)
323 .field("selected_count", &self.selection.get().len())
324 .finish()
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn single_select_by_key() {
334 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Single);
335 m.select(10);
336 assert!(m.is_selected(&10));
337 m.select(20);
338 assert!(!m.is_selected(&10));
339 assert!(m.is_selected(&20));
340 }
341
342 #[test]
343 fn multi_toggle_and_count() {
344 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
345 m.toggle(1);
346 m.toggle(3);
347 assert_eq!(m.count(), 2);
348 m.toggle(1);
349 assert!(!m.is_selected(&1));
350 assert!(m.is_selected(&3));
351 }
352
353 #[test]
354 fn reversing_a_keyed_shift_gesture_shrinks_the_range() {
355 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
356 let order = vec![10_u64, 20, 30, 40, 50];
357 m.select(20);
358 m.extend_to(50, &order);
359 let mut got = m.selected_keys();
360 got.sort();
361 assert_eq!(got, vec![20, 30, 40, 50]);
362 m.extend_to(30, &order);
363 let mut got = m.selected_keys();
364 got.sort();
365 assert_eq!(got, vec![20, 30]);
366 }
367
368 #[test]
369 fn a_keyed_toggle_survives_the_next_shift_range() {
370 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
371 let order = vec![10_u64, 20, 30, 40, 50];
372 m.select(10);
373 m.toggle(30);
374 m.extend_to(50, &order);
375 let mut got = m.selected_keys();
376 got.sort();
377 assert_eq!(got, vec![10, 30, 40, 50]);
378 }
379
380 #[test]
381 fn a_keyed_additive_extend_keeps_the_earlier_range() {
382 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
383 let order = vec![10_u64, 20, 30, 40, 50, 60];
384 m.select(10);
385 m.extend_to(20, &order);
386 m.toggle(40);
387 m.extend_to_additive(60, &order);
388 let mut got = m.selected_keys();
389 got.sort();
390 assert_eq!(got, vec![10, 20, 40, 50, 60]);
391 m.extend_to_additive(50, &order);
392 let mut got = m.selected_keys();
393 got.sort();
394 assert_eq!(got, vec![10, 20, 40, 50]);
395 }
396
397 #[test]
398 fn pruning_a_deleted_key_also_drops_it_from_the_gesture_base() {
399 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
400 let order = vec![10_u64, 20, 30, 40];
401 m.select(10);
402 m.toggle(20); m.prune_missing(|k| *k != 10);
404 m.extend_to(40, &order);
405 let mut got = m.selected_keys();
406 got.sort();
407 assert_eq!(got, vec![20, 30, 40]);
409 }
410
411 #[test]
412 fn extend_to_over_visible_order() {
413 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
414 let order = vec![10_u64, 20, 30, 40, 50];
415 m.select(20); m.extend_to(40, &order);
417 let mut got = m.selected_keys();
418 got.sort();
419 assert_eq!(got, vec![20, 30, 40]);
420 }
421
422 #[test]
423 fn selection_survives_reorder_of_visible_order() {
424 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
427 m.toggle(30);
428 m.toggle(10);
429 assert!(m.is_selected(&10));
431 assert!(m.is_selected(&30));
432 assert!(!m.is_selected(&20));
433 }
434
435 #[test]
436 fn prune_missing_drops_deleted_keys_and_anchor() {
437 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
438 m.toggle(1);
439 m.toggle(2);
440 m.toggle(3); let live: HashSet<u64> = [1_u64, 4, 5].into_iter().collect();
443 m.prune_missing(|k| live.contains(k));
444 assert!(m.is_selected(&1));
445 assert!(!m.is_selected(&2));
446 assert!(!m.is_selected(&3));
447 m.extend_to(5, &[1, 4, 5]);
449 assert!(m.is_selected(&5));
450 }
451
452 #[test]
453 fn anchor_not_visible_falls_back_to_single() {
454 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
455 m.select(99); m.extend_to(20, &[10, 20, 30]);
457 assert_eq!(m.selected_keys(), vec![20]);
459 }
460
461 #[test]
462 fn none_mode_ignores() {
463 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::None);
464 m.select(1);
465 m.toggle(2);
466 assert_eq!(m.count(), 0);
467 }
468}