1use std::collections::{HashMap, HashSet};
4
5use crate::graph::Graph;
6use crate::types::{NodeId, Position};
8
9use super::modes::{KeyboardShortcut, NavigationDirection, SelectionMode};
10use super::visual_feedback::VisualFeedback;
11
12#[derive(Debug, Clone, Default)]
14pub struct SelectionManager {
15 selected_nodes: HashSet<NodeId>,
17 mode: SelectionMode,
19 rectangle_bounds: Option<(Position, Position)>,
21 visual_feedback: HashMap<NodeId, VisualFeedback>,
23}
24
25impl SelectionManager {
26 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn selected_nodes(&self) -> &HashSet<NodeId> {
33 &self.selected_nodes
34 }
35
36 pub fn is_selected(&self, node_id: &NodeId) -> bool {
38 self.selected_nodes.contains(node_id)
39 }
40
41 pub fn selection_count(&self) -> usize {
43 self.selected_nodes.len()
44 }
45
46 pub fn select_node(&mut self, node_id: NodeId) {
48 match self.mode {
49 SelectionMode::Single => {
50 let previously_selected: Vec<_> = self.selected_nodes.iter().cloned().collect();
53 for prev_node_id in previously_selected {
54 self.set_selection_visual_feedback(&prev_node_id, false);
55 }
56 self.selected_nodes.clear();
57 self.selected_nodes.insert(node_id.clone());
58 self.set_selection_visual_feedback(&node_id, true);
60 }
61 SelectionMode::Multi => {
62 self.selected_nodes.insert(node_id.clone());
64 self.set_selection_visual_feedback(&node_id, true);
65 }
66 SelectionMode::Rectangle => {
67 }
69 }
70 }
71
72 pub fn toggle_node(&mut self, node_id: NodeId) {
74 if self.selected_nodes.contains(&node_id) {
75 self.deselect_node(&node_id);
76 } else {
77 self.select_node(node_id);
78 }
79 }
80
81 pub fn deselect_node(&mut self, node_id: &NodeId) {
83 self.selected_nodes.remove(node_id);
84 self.set_selection_visual_feedback(node_id, false);
85 }
86
87 pub fn clear_selection(&mut self) {
89 let selected_nodes: Vec<_> = self.selected_nodes.iter().cloned().collect();
91 for node_id in selected_nodes {
92 self.set_selection_visual_feedback(&node_id, false);
93 }
94 self.selected_nodes.clear();
95 self.rectangle_bounds = None;
96 }
97
98 pub fn set_mode(&mut self, mode: SelectionMode) {
100 self.mode = mode;
101 if mode != SelectionMode::Rectangle {
102 self.rectangle_bounds = None;
103 }
104 }
105
106 pub fn mode(&self) -> &SelectionMode {
108 &self.mode
109 }
110
111 pub fn start_rectangle_selection(&mut self, start: Position) {
113 self.mode = SelectionMode::Rectangle;
114 self.rectangle_bounds = Some((start, start));
115 }
116
117 pub fn update_rectangle_selection(&mut self, end: Position) {
119 if let Some((start, _)) = self.rectangle_bounds {
120 self.rectangle_bounds = Some((start, end));
121 }
122 }
123
124 pub fn complete_rectangle_selection<N, E>(&mut self, graph: &Graph<N, E>) -> Vec<NodeId>
126 where
127 N: Clone,
128 E: Clone,
129 {
130 let selected = if let Some((start, end)) = self.rectangle_bounds {
131 let min_x = start.x.min(end.x);
132 let max_x = start.x.max(end.x);
133 let min_y = start.y.min(end.y);
134 let max_y = start.y.max(end.y);
135
136 let mut nodes_in_rectangle = Vec::new();
137
138 for node in graph.nodes() {
139 let pos = &node.position;
140 if pos.x >= min_x && pos.x <= max_x && pos.y >= min_y && pos.y <= max_y {
141 nodes_in_rectangle.push(node.id.clone());
142 }
143 }
144
145 self.selected_nodes.clear();
147 for node_id in &nodes_in_rectangle {
148 self.selected_nodes.insert(node_id.clone());
149 self.set_selection_visual_feedback(node_id, true);
150 }
151
152 nodes_in_rectangle
153 } else {
154 Vec::new()
155 };
156
157 self.rectangle_bounds = None;
158 self.mode = SelectionMode::Single;
159 selected
160 }
161
162 pub fn rectangle_bounds(&self) -> Option<(Position, Position)> {
164 self.rectangle_bounds
165 }
166
167 pub fn navigate_selection<N, E>(
169 &mut self,
170 graph: &Graph<N, E>,
171 direction: NavigationDirection,
172 ) -> Option<NodeId>
173 where
174 N: Clone,
175 E: Clone,
176 {
177 if graph.node_count() == 0 {
178 return None;
179 }
180
181 let current_selection = self.selected_nodes.iter().next().cloned();
182
183 match direction {
184 NavigationDirection::Next => {
185 let nodes: Vec<_> = graph.nodes().collect();
186 if let Some(current) = current_selection {
187 if let Some(current_index) = nodes.iter().position(|n| n.id == current) {
188 let next_index = (current_index + 1) % nodes.len();
189 let next_node_id = nodes[next_index].id.clone();
190 self.selected_nodes.clear();
191 self.selected_nodes.insert(next_node_id.clone());
192 self.set_selection_visual_feedback(&next_node_id, true);
193 Some(next_node_id)
194 } else {
195 None
196 }
197 } else {
198 let first_node_id = nodes[0].id.clone();
200 self.selected_nodes.clear();
201 self.selected_nodes.insert(first_node_id.clone());
202 self.set_selection_visual_feedback(&first_node_id, true);
203 Some(first_node_id)
204 }
205 }
206 NavigationDirection::Previous => {
207 let nodes: Vec<_> = graph.nodes().collect();
208 if let Some(current) = current_selection {
209 if let Some(current_index) = nodes.iter().position(|n| n.id == current) {
210 let prev_index = if current_index == 0 {
211 nodes.len() - 1
212 } else {
213 current_index - 1
214 };
215 let prev_node_id = nodes[prev_index].id.clone();
216 self.selected_nodes.clear();
217 self.selected_nodes.insert(prev_node_id.clone());
218 self.set_selection_visual_feedback(&prev_node_id, true);
219 Some(prev_node_id)
220 } else {
221 None
222 }
223 } else {
224 let last_node_id = nodes[nodes.len() - 1].id.clone();
226 self.selected_nodes.clear();
227 self.selected_nodes.insert(last_node_id.clone());
228 self.set_selection_visual_feedback(&last_node_id, true);
229 Some(last_node_id)
230 }
231 }
232 }
233 }
234
235 pub fn handle_keyboard_shortcut<N, E>(
273 &mut self,
274 graph: &Graph<N, E>,
275 shortcut: KeyboardShortcut,
276 ) where
277 N: Clone,
278 E: Clone,
279 {
280 match shortcut {
281 KeyboardShortcut::SelectAll => {
282 self.selected_nodes.clear();
283 for node in graph.nodes() {
284 self.selected_nodes.insert(node.id.clone());
285 self.set_selection_visual_feedback(&node.id, true);
286 }
287 }
288 KeyboardShortcut::Escape => {
289 self.clear_selection();
290 }
291 KeyboardShortcut::ArrowRight => {
292 self.navigate_selection(graph, NavigationDirection::Next);
293 }
294 KeyboardShortcut::ArrowLeft => {
295 self.navigate_selection(graph, NavigationDirection::Previous);
296 }
297 KeyboardShortcut::ArrowUp => {
298 self.navigate_selection(graph, NavigationDirection::Previous);
301 }
302 KeyboardShortcut::ArrowDown => {
303 self.navigate_selection(graph, NavigationDirection::Next);
304 }
305 KeyboardShortcut::Delete => {
306 self.clear_selection();
309 }
310 }
311 }
312
313 pub fn handle_destructive_keyboard_shortcut<N, E>(
315 &mut self,
316 graph: &mut Graph<N, E>,
317 shortcut: KeyboardShortcut,
318 ) where
319 N: Clone,
320 E: Clone,
321 {
322 match shortcut {
323 KeyboardShortcut::Delete => {
324 let nodes_to_remove: Vec<_> = self.selected_nodes.iter().cloned().collect();
326 for node_id in nodes_to_remove {
327 let _ = graph.remove_node(&node_id);
328 }
329 self.clear_selection();
331 }
332 _ => {
333 self.handle_keyboard_shortcut(graph, shortcut);
335 }
336 }
337 }
338
339 pub fn set_visual_feedback(&mut self, node_id: &NodeId, feedback: VisualFeedback) {
343 if feedback.has_any_state() {
344 self.visual_feedback.insert(node_id.clone(), feedback);
345 } else {
346 self.visual_feedback.remove(node_id);
347 }
348 }
349
350 pub fn get_visual_feedback(&self, node_id: &NodeId) -> Option<&VisualFeedback> {
352 self.visual_feedback.get(node_id)
353 }
354
355 pub fn has_visual_feedback(&self, node_id: &NodeId) -> bool {
357 self.visual_feedback.contains_key(node_id)
358 }
359
360 pub fn set_hover_state(&mut self, node_id: &NodeId, hovered: bool) {
362 let mut feedback = self.visual_feedback.get(node_id).cloned().unwrap_or_default();
363 feedback.set_hovered(hovered);
364 self.set_visual_feedback(node_id, feedback);
365 }
366
367 pub fn set_highlight_state(&mut self, node_id: &NodeId, highlighted: bool) {
369 let mut feedback = self.visual_feedback.get(node_id).cloned().unwrap_or_default();
370 feedback.set_highlighted(highlighted);
371 self.set_visual_feedback(node_id, feedback);
372 }
373
374 pub fn set_animation_progress(&mut self, node_id: &NodeId, progress: f64) {
376 let mut feedback = self.visual_feedback.get(node_id).cloned().unwrap_or_default();
377 feedback.set_animation_progress(progress);
378 self.set_visual_feedback(node_id, feedback);
379 }
380
381 pub fn nodes_with_visual_feedback(&self) -> Vec<NodeId> {
383 self.visual_feedback.keys().cloned().collect()
384 }
385
386 pub fn clear_all_visual_feedbacks(&mut self) {
388 self.visual_feedback.clear();
389 }
390
391 pub fn clear_visual_feedback(&mut self, node_id: &NodeId) {
393 self.visual_feedback.remove(node_id);
394 }
395
396 fn set_selection_visual_feedback(&mut self, node_id: &NodeId, selected: bool) {
398 let mut feedback = self.visual_feedback.get(node_id).cloned().unwrap_or_default();
399 feedback.set_selected(selected);
400 self.set_visual_feedback(node_id, feedback);
401 }
402
403 pub fn select_all<N, E>(&mut self, graph: &Graph<N, E>) {
405 self.selected_nodes.clear();
406 for node in graph.nodes() {
407 self.selected_nodes.insert(node.id.clone());
408 }
409 }
410
411 pub fn select_group(&mut self, group_manager: &crate::groups::GroupManager, group_id: &crate::types::GroupId) {
413 self.selected_nodes.clear();
414 if let Some(group) = group_manager.get_group(group_id) {
415 for node_id in &group.members {
416 self.selected_nodes.insert(node_id.clone());
417 }
418 }
419 }
420
421 pub fn select_node_with_group(
423 &mut self,
424 group_manager: &crate::groups::GroupManager,
425 node_id: crate::types::NodeId,
426 select_whole_group: bool,
427 ) {
428 if select_whole_group {
429 if let Some(group_id) = group_manager.get_node_group(&node_id) {
431 self.select_group(group_manager, group_id);
432 return;
433 }
434 }
435
436 self.select_node(node_id);
438 }
439
440 pub fn get_selected_groups(&self, group_manager: &crate::groups::GroupManager) -> std::collections::HashSet<crate::types::GroupId> {
442 let mut selected_groups = std::collections::HashSet::new();
443
444 for node_id in &self.selected_nodes {
445 if let Some(group_id) = group_manager.get_node_group(node_id) {
446 selected_groups.insert(group_id.clone());
447 }
448 }
449
450 selected_groups
451 }
452}