flow_rs_core/selection/
manager.rs

1//! Selection state manager
2
3use std::collections::{HashMap, HashSet};
4
5use crate::graph::Graph;
6// use crate::groups::GroupManager; // TODO: Implement GroupManager
7use crate::types::{NodeId, Position};
8
9use super::modes::{KeyboardShortcut, NavigationDirection, SelectionMode};
10use super::visual_feedback::VisualFeedback;
11
12/// Selection state manager
13#[derive(Debug, Clone, Default)]
14pub struct SelectionManager {
15    /// Currently selected nodes
16    selected_nodes: HashSet<NodeId>,
17    /// Current selection mode
18    mode: SelectionMode,
19    /// Rectangle selection bounds (start, end)
20    rectangle_bounds: Option<(Position, Position)>,
21    /// Visual feedback states for nodes
22    visual_feedback: HashMap<NodeId, VisualFeedback>,
23}
24
25impl SelectionManager {
26    /// Create a new selection manager
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    /// Get currently selected nodes
32    pub fn selected_nodes(&self) -> &HashSet<NodeId> {
33        &self.selected_nodes
34    }
35
36    /// Check if a node is selected
37    pub fn is_selected(&self, node_id: &NodeId) -> bool {
38        self.selected_nodes.contains(node_id)
39    }
40
41    /// Get the number of selected nodes
42    pub fn selection_count(&self) -> usize {
43        self.selected_nodes.len()
44    }
45
46    /// Select a node (behavior depends on current mode)
47    pub fn select_node(&mut self, node_id: NodeId) {
48        match self.mode {
49            SelectionMode::Single => {
50                // Single mode: replace selection
51                // Clear visual feedback for all previously selected nodes
52                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                // Update visual feedback
59                self.set_selection_visual_feedback(&node_id, true);
60            }
61            SelectionMode::Multi => {
62                // Multi mode: add to selection
63                self.selected_nodes.insert(node_id.clone());
64                self.set_selection_visual_feedback(&node_id, true);
65            }
66            SelectionMode::Rectangle => {
67                // Rectangle mode: handled by complete_rectangle_selection
68            }
69        }
70    }
71
72    /// Toggle node selection (add if not selected, remove if selected)
73    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    /// Deselect a node
82    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    /// Clear all selections
88    pub fn clear_selection(&mut self) {
89        // Clear visual feedback for all selected nodes
90        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    /// Set selection mode
99    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    /// Get current selection mode
107    pub fn mode(&self) -> &SelectionMode {
108        &self.mode
109    }
110
111    /// Start rectangle selection
112    pub fn start_rectangle_selection(&mut self, start: Position) {
113        self.mode = SelectionMode::Rectangle;
114        self.rectangle_bounds = Some((start, start));
115    }
116
117    /// Update rectangle selection
118    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    /// Complete rectangle selection and select nodes within bounds
125    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            // Clear existing selection and select rectangle nodes
146            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    /// Get rectangle selection bounds
163    pub fn rectangle_bounds(&self) -> Option<(Position, Position)> {
164        self.rectangle_bounds
165    }
166
167    /// Select nodes by keyboard navigation (move selection to next/previous node)
168    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                    // No selection, select first node
199                    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                    // No selection, select last node
225                    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    // TODO: Implement GroupManager functionality
236    // /// Get all selected groups
237    // pub fn selected_groups(&self, group_manager: &GroupManager) -> Vec<GroupId> {
238    //     let mut selected_groups = Vec::new();
239    //     for group_id in group_manager.group_ids() {
240    //         if self.is_group_fully_selected(group_manager, &group_id) {
241    //             selected_groups.push(group_id);
242    //         }
243    //     }
244    //     selected_groups
245    // }
246
247    // /// Check if all nodes in a group are selected
248    // pub fn is_group_fully_selected(&self, group_manager: &GroupManager, group_id: &GroupId) -> bool {
249    //     if let Some(group) = group_manager.get_group(group_id) {
250    //         for node_id in &group.members {
251    //             if !self.selected_nodes.contains(node_id) {
252    //                 return false;
253    //             }
254    //         }
255    //         !group.members.is_empty()
256    //     } else {
257    //         false
258    //     }
259    // }
260
261    // /// Deselect all nodes in a group
262    // pub fn deselect_group(&mut self, group_manager: &GroupManager, group_id: &GroupId) {
263    //     if let Some(group) = group_manager.get_group(group_id) {
264    //         for node_id in &group.members {
265    //             self.selected_nodes.remove(node_id);
266    //             self.set_selection_visual_feedback(node_id, false);
267    //         }
268    //     }
269    // }
270
271    /// Handle keyboard shortcuts for selection management (read-only operations)
272    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                // For now, treat up/down same as left/right
299                // In future, we could implement spatial navigation
300                self.navigate_selection(graph, NavigationDirection::Previous);
301            }
302            KeyboardShortcut::ArrowDown => {
303                self.navigate_selection(graph, NavigationDirection::Next);
304            }
305            KeyboardShortcut::Delete => {
306                // Delete operation requires mutable graph - this will be handled separately
307                // For now, we only clear the selection
308                self.clear_selection();
309            }
310        }
311    }
312
313    /// Handle destructive keyboard shortcuts that modify the graph
314    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                // Remove all selected nodes from the graph
325                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                // Clear selection after deletion
330                self.clear_selection();
331            }
332            _ => {
333                // For non-destructive operations, use the regular method
334                self.handle_keyboard_shortcut(graph, shortcut);
335            }
336        }
337    }
338
339    // Visual Feedback System Methods
340
341    /// Set visual feedback for a node
342    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    /// Get visual feedback for a node
351    pub fn get_visual_feedback(&self, node_id: &NodeId) -> Option<&VisualFeedback> {
352        self.visual_feedback.get(node_id)
353    }
354
355    /// Check if a node has visual feedback
356    pub fn has_visual_feedback(&self, node_id: &NodeId) -> bool {
357        self.visual_feedback.contains_key(node_id)
358    }
359
360    /// Set hover state for a node
361    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    /// Set highlight state for a node
368    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    /// Set animation progress for a node
375    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    /// Get all nodes with visual feedback
382    pub fn nodes_with_visual_feedback(&self) -> Vec<NodeId> {
383        self.visual_feedback.keys().cloned().collect()
384    }
385
386    /// Clear all visual feedback
387    pub fn clear_all_visual_feedbacks(&mut self) {
388        self.visual_feedback.clear();
389    }
390
391    /// Clear visual feedback for a specific node
392    pub fn clear_visual_feedback(&mut self, node_id: &NodeId) {
393        self.visual_feedback.remove(node_id);
394    }
395
396    /// Helper method to set selection visual feedback
397    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    /// Select all nodes in the graph
404    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    /// Select a group (replacing current selection)
412    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    /// Select a node, optionally selecting its entire group
422    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            // Find the group containing this node
430            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        // Select just the node
437        self.select_node(node_id);
438    }
439
440    /// Get all groups that have at least one selected node
441    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}