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 {
113 self.selection.get_ref().contains(key)
114 }
115
116 pub fn selected_keys(&self) -> Vec<K> {
118 self.selection.get().into_iter().collect()
119 }
120
121 pub fn count(&self) -> usize {
124 self.selection.get_ref().len()
125 }
126
127 pub fn select(&self, key: K) {
129 if self.mode == SelectionMode::None {
130 return;
131 }
132 let mut set = HashSet::new();
133 set.insert(key.clone());
134 self.selection.set(set);
135 *self.anchor.borrow_mut() = Some(key);
136 self.commit_base(HashSet::new());
137 }
138
139 pub fn toggle(&self, key: K) {
141 match self.mode {
142 SelectionMode::None => {}
143 SelectionMode::Single => self.select(key),
144 SelectionMode::Multi => {
145 let mut set = self.selection.get();
146 if set.contains(&key) {
147 set.remove(&key);
148 } else {
149 set.insert(key.clone());
150 }
151 self.selection.set(set.clone());
152 *self.anchor.borrow_mut() = Some(key);
155 self.commit_base(set);
156 }
157 }
158 }
159
160 pub fn extend_to(&self, target: K, ordered_keys: &[K]) {
165 self.extend_from_base(target, ordered_keys, false);
166 }
167
168 pub fn extend_to_additive(&self, target: K, ordered_keys: &[K]) {
177 self.extend_from_base(target, ordered_keys, true);
178 }
179
180 fn extend_from_base(&self, target: K, ordered_keys: &[K], additive: bool) {
181 match self.mode {
182 SelectionMode::None => {}
183 SelectionMode::Single => self.select(target),
184 SelectionMode::Multi => {
185 let anchor = self.anchor.borrow().clone();
186 let Some(anchor) = anchor else {
187 self.select(target);
188 return;
189 };
190 let a = ordered_keys.iter().position(|k| *k == anchor);
191 let t = ordered_keys.iter().position(|k| *k == target);
192 match (a, t) {
193 (Some(a), Some(t)) => {
194 if additive && !self.extending.get() {
195 *self.base.borrow_mut() = self.selection.get();
196 }
197 let (lo, hi) = (a.min(t), a.max(t));
198 let mut set = self.base.borrow().clone();
202 for k in &ordered_keys[lo..=hi] {
203 set.insert(k.clone());
204 }
205 self.selection.set(set);
206 self.extending.set(true);
207 }
209 _ => self.select(target),
210 }
211 }
212 }
213 }
214
215 pub fn select_keys(&self, keys: impl IntoIterator<Item = K>, additive: bool) {
218 if self.mode == SelectionMode::None {
219 return;
220 }
221 let mut set = if additive {
222 self.selection.get()
223 } else {
224 HashSet::new()
225 };
226 set.extend(keys);
227 if self.mode == SelectionMode::Single && set.len() > 1 {
228 let keep = set.iter().next().cloned();
229 set = keep.into_iter().collect();
230 }
231 self.selection.set(set.clone());
232 self.commit_base(if additive { set } else { HashSet::new() });
233 }
234
235 pub fn clear(&self) {
237 self.selection.set(HashSet::new());
238 *self.anchor.borrow_mut() = None;
239 self.commit_base(HashSet::new());
240 }
241
242 pub fn prune_missing(&self, exists: impl Fn(&K) -> bool) {
247 let old = self.selection.get();
248 let new: HashSet<K> = old.iter().filter(|k| exists(k)).cloned().collect();
249 if new.len() != old.len() {
250 self.selection.set(new);
251 }
252 let drop_anchor = self.anchor.borrow().as_ref().is_some_and(|a| !exists(a));
253 if drop_anchor {
254 *self.anchor.borrow_mut() = None;
255 }
256 let mut base = self.base.borrow_mut();
259 if !base.is_empty() {
260 base.retain(|k| exists(k));
261 }
262 }
263}
264
265impl<K: ItemKey> Clone for KeyedSelectionModel<K> {
266 fn clone(&self) -> Self {
267 Self {
268 mode: self.mode,
269 selection: self.selection.clone(),
270 anchor: self.anchor.clone(),
271 base: self.base.clone(),
272 extending: self.extending.clone(),
273 #[cfg(debug_assertions)]
274 debug_adapter_holder: self.debug_adapter_holder.clone(),
275 }
276 }
277}
278
279impl<K: ItemKey> KeyedSelectionModel<K> {
280 pub fn debug_named(self, _name: impl Into<String>) -> Self {
283 #[cfg(debug_assertions)]
284 {
285 let adapter: Rc<dyn crate::debug_registry::ModelDebug> =
286 Rc::new(KeyedSelectionModelDebug {
287 selection: self.selection.clone(),
288 mode: self.mode,
289 });
290 crate::debug_registry::register(_name.into(), Rc::downgrade(&adapter));
291 *self.debug_adapter_holder.borrow_mut() = Some(adapter);
292 }
293 self
294 }
295}
296
297#[cfg(debug_assertions)]
298struct KeyedSelectionModelDebug<K: ItemKey> {
299 selection: Signal<HashSet<K>>,
300 mode: SelectionMode,
301}
302
303#[cfg(debug_assertions)]
304impl<K: ItemKey> crate::debug_registry::ModelDebug for KeyedSelectionModelDebug<K> {
305 fn kind(&self) -> &'static str {
306 "KeyedSelectionModel"
307 }
308 fn len(&self) -> usize {
309 self.selection.get().len()
310 }
311 fn debug_dump(&self, out: &mut dyn std::fmt::Write) {
312 let _ = writeln!(out, "mode = {:?}", self.mode);
313 let sel = self.selection.get();
314 if sel.is_empty() {
315 let _ = writeln!(out, "(empty)");
316 return;
317 }
318 for k in sel.iter() {
319 let _ = writeln!(out, "{:?}", k);
320 }
321 }
322}
323
324impl<K: ItemKey> std::fmt::Debug for KeyedSelectionModel<K> {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 f.debug_struct("KeyedSelectionModel")
327 .field("mode", &self.mode)
328 .field("selected_count", &self.selection.get().len())
329 .finish()
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn single_select_by_key() {
339 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Single);
340 m.select(10);
341 assert!(m.is_selected(&10));
342 m.select(20);
343 assert!(!m.is_selected(&10));
344 assert!(m.is_selected(&20));
345 }
346
347 #[test]
348 fn multi_toggle_and_count() {
349 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
350 m.toggle(1);
351 m.toggle(3);
352 assert_eq!(m.count(), 2);
353 m.toggle(1);
354 assert!(!m.is_selected(&1));
355 assert!(m.is_selected(&3));
356 }
357
358 #[test]
359 fn reversing_a_keyed_shift_gesture_shrinks_the_range() {
360 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
361 let order = vec![10_u64, 20, 30, 40, 50];
362 m.select(20);
363 m.extend_to(50, &order);
364 let mut got = m.selected_keys();
365 got.sort();
366 assert_eq!(got, vec![20, 30, 40, 50]);
367 m.extend_to(30, &order);
368 let mut got = m.selected_keys();
369 got.sort();
370 assert_eq!(got, vec![20, 30]);
371 }
372
373 #[test]
374 fn a_keyed_toggle_survives_the_next_shift_range() {
375 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
376 let order = vec![10_u64, 20, 30, 40, 50];
377 m.select(10);
378 m.toggle(30);
379 m.extend_to(50, &order);
380 let mut got = m.selected_keys();
381 got.sort();
382 assert_eq!(got, vec![10, 30, 40, 50]);
383 }
384
385 #[test]
386 fn a_keyed_additive_extend_keeps_the_earlier_range() {
387 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
388 let order = vec![10_u64, 20, 30, 40, 50, 60];
389 m.select(10);
390 m.extend_to(20, &order);
391 m.toggle(40);
392 m.extend_to_additive(60, &order);
393 let mut got = m.selected_keys();
394 got.sort();
395 assert_eq!(got, vec![10, 20, 40, 50, 60]);
396 m.extend_to_additive(50, &order);
397 let mut got = m.selected_keys();
398 got.sort();
399 assert_eq!(got, vec![10, 20, 40, 50]);
400 }
401
402 #[test]
403 fn pruning_a_deleted_key_also_drops_it_from_the_gesture_base() {
404 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
405 let order = vec![10_u64, 20, 30, 40];
406 m.select(10);
407 m.toggle(20); m.prune_missing(|k| *k != 10);
409 m.extend_to(40, &order);
410 let mut got = m.selected_keys();
411 got.sort();
412 assert_eq!(got, vec![20, 30, 40]);
414 }
415
416 #[test]
417 fn extend_to_over_visible_order() {
418 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
419 let order = vec![10_u64, 20, 30, 40, 50];
420 m.select(20); m.extend_to(40, &order);
422 let mut got = m.selected_keys();
423 got.sort();
424 assert_eq!(got, vec![20, 30, 40]);
425 }
426
427 #[test]
428 fn selection_survives_reorder_of_visible_order() {
429 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
432 m.toggle(30);
433 m.toggle(10);
434 assert!(m.is_selected(&10));
436 assert!(m.is_selected(&30));
437 assert!(!m.is_selected(&20));
438 }
439
440 #[test]
441 fn prune_missing_drops_deleted_keys_and_anchor() {
442 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
443 m.toggle(1);
444 m.toggle(2);
445 m.toggle(3); let live: HashSet<u64> = [1_u64, 4, 5].into_iter().collect();
448 m.prune_missing(|k| live.contains(k));
449 assert!(m.is_selected(&1));
450 assert!(!m.is_selected(&2));
451 assert!(!m.is_selected(&3));
452 m.extend_to(5, &[1, 4, 5]);
454 assert!(m.is_selected(&5));
455 }
456
457 #[test]
458 fn anchor_not_visible_falls_back_to_single() {
459 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::Multi);
460 m.select(99); m.extend_to(20, &[10, 20, 30]);
462 assert_eq!(m.selected_keys(), vec![20]);
464 }
465
466 #[test]
467 fn none_mode_ignores() {
468 let m: KeyedSelectionModel<u64> = KeyedSelectionModel::new(SelectionMode::None);
469 m.select(1);
470 m.toggle(2);
471 assert_eq!(m.count(), 0);
472 }
473}