leptos_arrow_grid/
selection.rs1use std::collections::HashSet;
7
8#[derive(Clone, Debug, Default)]
10pub struct SelectionState {
11 pub selected: HashSet<u64>,
13 pub anchor: Option<u64>,
15 pub cursor: Option<u64>,
17 pub dragging: bool,
19}
20
21impl SelectionState {
22 pub fn clear(&mut self) {
24 self.selected.clear();
25 self.anchor = None;
26 self.cursor = None;
27 self.dragging = false;
28 }
29
30 pub fn select_all(&mut self, total_rows: u64) {
32 self.selected = (0..total_rows).collect();
33 self.anchor = Some(0);
34 self.cursor = total_rows.checked_sub(1);
35 }
36
37 pub fn is_selected(&self, row: u64) -> bool {
39 self.selected.contains(&row)
40 }
41
42 pub fn count(&self) -> usize {
44 self.selected.len()
45 }
46
47 pub fn on_pointer_down(&mut self, row: u64, ctrl: bool, shift: bool, total_rows: u64) {
49 if ctrl {
50 if !self.selected.remove(&row) {
52 self.selected.insert(row);
53 }
54 self.cursor = Some(row);
55 } else if shift {
56 let from = self.anchor.or(self.cursor).unwrap_or(row);
58 let lo = from.min(row);
59 let hi = from.max(row).min(total_rows.saturating_sub(1));
60 self.selected.clear();
61 for i in lo..=hi {
62 self.selected.insert(i);
63 }
64 self.cursor = Some(row);
65 } else {
66 self.selected.clear();
68 self.selected.insert(row);
69 self.anchor = Some(row);
70 self.cursor = Some(row);
71 self.dragging = true;
72 }
73 }
74
75 pub fn on_pointer_enter_drag(&mut self, row: u64, total_rows: u64) {
77 if !self.dragging {
78 return;
79 }
80 let anchor = self.anchor.or(self.cursor).unwrap_or(row);
81 let lo = anchor.min(row);
82 let hi = anchor.max(row).min(total_rows.saturating_sub(1));
83 self.selected.clear();
84 for i in lo..=hi {
85 self.selected.insert(i);
86 }
87 self.cursor = Some(row);
88 }
89
90 pub fn on_pointer_up(&mut self) {
92 self.dragging = false;
93 }
94
95 pub fn on_context_menu(&mut self, row: u64) {
98 if !self.selected.contains(&row) {
99 self.selected.clear();
100 self.selected.insert(row);
101 self.anchor = Some(row);
102 self.cursor = Some(row);
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn single_click() {
113 let mut s = SelectionState::default();
114 s.on_pointer_down(5, false, false, 100);
115 assert!(s.is_selected(5));
116 assert_eq!(s.count(), 1);
117 assert_eq!(s.anchor, Some(5));
118 }
119
120 #[test]
121 fn ctrl_click_toggle() {
122 let mut s = SelectionState::default();
123 s.on_pointer_down(5, false, false, 100);
124 s.on_pointer_up();
125 s.on_pointer_down(10, true, false, 100);
126 assert!(s.is_selected(5));
127 assert!(s.is_selected(10));
128 assert_eq!(s.count(), 2);
129 s.on_pointer_down(5, true, false, 100);
131 assert!(!s.is_selected(5));
132 assert_eq!(s.count(), 1);
133 }
134
135 #[test]
136 fn shift_click_range() {
137 let mut s = SelectionState::default();
138 s.on_pointer_down(5, false, false, 100);
139 s.on_pointer_up();
140 s.on_pointer_down(10, false, true, 100);
141 assert_eq!(s.count(), 6); for i in 5..=10 {
143 assert!(s.is_selected(i));
144 }
145 }
146
147 #[test]
148 fn drag_select() {
149 let mut s = SelectionState::default();
150 s.on_pointer_down(5, false, false, 100);
151 s.on_pointer_enter_drag(8, 100);
152 assert_eq!(s.count(), 4); s.on_pointer_up();
154 assert!(!s.dragging);
155 }
156
157 #[test]
158 fn select_all() {
159 let mut s = SelectionState::default();
160 s.select_all(5);
161 assert_eq!(s.count(), 5);
162 assert_eq!(s.anchor, Some(0));
163 assert_eq!(s.cursor, Some(4));
164 }
165
166 #[test]
167 fn context_menu_unselected_drops() {
168 let mut s = SelectionState::default();
169 s.on_pointer_down(5, false, false, 100);
170 s.on_pointer_up();
171 s.on_pointer_down(10, true, false, 100);
172 assert_eq!(s.count(), 2);
173 s.on_context_menu(20);
175 assert_eq!(s.count(), 1);
176 assert!(s.is_selected(20));
177 }
178
179 #[test]
180 fn context_menu_selected_keeps() {
181 let mut s = SelectionState::default();
182 s.on_pointer_down(5, false, false, 100);
183 s.on_pointer_up();
184 s.on_pointer_down(10, true, false, 100);
185 assert_eq!(s.count(), 2);
186 s.on_context_menu(5);
188 assert_eq!(s.count(), 2);
189 }
190
191 #[test]
192 fn clear() {
193 let mut s = SelectionState::default();
194 s.select_all(100);
195 s.clear();
196 assert_eq!(s.count(), 0);
197 assert!(s.anchor.is_none());
198 assert!(s.cursor.is_none());
199 }
200}