sqli/tui/
navigation.rs

1use std::collections::HashMap;
2use anyhow::Result;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum PaneId {
6    Header,
7    Collections,
8    Workspace,
9    Results,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FocusType {
14    Inactive,
15    Active,
16    Editing,
17}
18
19pub struct PaneInfo {
20    pub id: PaneId,
21    pub element_count: usize,
22    pub current_element: usize,
23    pub focus_type: FocusType,
24}
25
26impl PaneInfo {
27    pub fn new(id: PaneId, element_count: usize) -> Self {
28        Self {
29            id,
30            element_count,
31            current_element: 0,
32            focus_type: FocusType::Inactive,
33        }
34    }
35
36    pub fn is_active(&self) -> bool {
37        matches!(self.focus_type, FocusType::Active | FocusType::Editing)
38    }
39    
40    pub fn is_editing(&self) -> bool {
41        matches!(self.focus_type, FocusType::Editing)
42    }
43    
44    pub fn activate(&mut self) {
45        self.focus_type = FocusType::Active;
46    }
47    
48    pub fn start_editing(&mut self) {
49        self.focus_type = FocusType::Editing;
50    }
51    
52    pub fn deactivate(&mut self) {
53        self.focus_type = FocusType::Inactive;
54    }
55    
56    pub fn stop_editing(&mut self) {
57        self.focus_type = if self.focus_type == FocusType::Editing {
58            FocusType::Active
59        } else {
60            self.focus_type
61        };
62    }
63    
64    pub fn next_element(&mut self) -> bool {
65        if self.element_count <= 1 {
66            return false;
67        }
68        
69        self.current_element = (self.current_element + 1) % self.element_count;
70        true
71    }
72    
73    pub fn prev_element(&mut self) -> bool {
74        if self.element_count <= 1 {
75            return false;
76        }
77        
78        self.current_element = if self.current_element == 0 {
79            self.element_count - 1
80        } else {
81            self.current_element - 1
82        };
83        true
84    }
85}
86
87pub struct NavigationManager {
88    panes: HashMap<PaneId, PaneInfo>,
89    tab_order: Vec<PaneId>,
90    active_pane: Option<PaneId>,
91}
92
93impl Default for NavigationManager {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl NavigationManager {
100    pub fn new() -> Self {
101        Self {
102            panes: HashMap::new(),
103            tab_order: Vec::new(),
104            active_pane: None,
105        }
106    }
107    
108    pub fn register_pane(&mut self, id: PaneId, element_count: usize) {
109        let info = PaneInfo::new(id, element_count);
110        self.panes.insert(id, info);
111        
112        if !self.tab_order.contains(&id) {
113            self.tab_order.push(id);
114        }
115        
116        if self.active_pane.is_none() {
117            self.active_pane = Some(id);
118            if let Some(pane) = self.panes.get_mut(&id) {
119                pane.activate();
120            }
121        }
122    }
123    
124    pub fn get_pane_info(&self, id: PaneId) -> Option<&PaneInfo> {
125        self.panes.get(&id)
126    }
127    
128    pub fn get_pane_info_mut(&mut self, id: PaneId) -> Option<&mut PaneInfo> {
129        self.panes.get_mut(&id)
130    }
131    
132    pub fn get_active_pane(&self) -> Option<&PaneInfo> {
133        self.get_pane_info(self.active_pane?)
134    }
135
136    pub fn active_pane(&self) -> Option<PaneId> {
137        self.active_pane
138    }
139    
140    pub fn is_active(&self, id: PaneId) -> bool {
141        self.active_pane == Some(id)
142    }
143    
144    pub fn activate_pane(&mut self, id: PaneId) -> Result<()> {
145        if !self.panes.contains_key(&id) {
146            return Err(anyhow::anyhow!("Pane not registered: {:?}", id));
147        }
148        
149        if let Some(active_id) = self.active_pane {
150            if let Some(pane) = self.panes.get_mut(&active_id) {
151                pane.deactivate();
152            }
153        }
154        
155        if let Some(pane) = self.panes.get_mut(&id) {
156            pane.activate();
157        }
158        
159        self.active_pane = Some(id);
160        Ok(())
161    }
162    
163    pub fn cycle_pane(&mut self, reverse: bool) -> Result<PaneId> {
164        if self.tab_order.is_empty() {
165            return Err(anyhow::anyhow!("No panes registered"));
166        }
167        
168        let current_idx = if let Some(id) = self.active_pane {
169            self.tab_order.iter().position(|&p| p == id).unwrap_or(0)
170        } else {
171            0
172        };
173        
174        let next_idx = if reverse {
175            if current_idx == 0 {
176                self.tab_order.len() - 1
177            } else {
178                current_idx - 1
179            }
180        } else {
181            (current_idx + 1) % self.tab_order.len()
182        };
183        
184        let next_id = self.tab_order[next_idx];
185        self.activate_pane(next_id)?;
186        
187        Ok(next_id)
188    }
189    
190    pub fn start_editing(&mut self, id: PaneId) -> Result<()> {
191        if !self.panes.contains_key(&id) {
192            return Err(anyhow::anyhow!("Pane not registered: {:?}", id));
193        }
194        
195        if self.active_pane != Some(id) {
196            self.activate_pane(id)?;
197        }
198        
199        if let Some(pane) = self.panes.get_mut(&id) {
200            pane.start_editing();
201        }
202        
203        Ok(())
204    }
205    
206    pub fn stop_editing(&mut self, id: PaneId) -> Result<()> {
207        if !self.panes.contains_key(&id) {
208            return Err(anyhow::anyhow!("Pane not registered: {:?}", id));
209        }
210        
211        if let Some(pane) = self.panes.get_mut(&id) {
212            pane.stop_editing();
213        }
214        
215        Ok(())
216    }
217    
218    pub fn handle_tab(&mut self, reverse: bool) -> Result<(PaneId, bool)> {
219        if let Some(id) = self.active_pane {
220            if let Some(pane) = self.panes.get_mut(&id) {
221                if pane.is_editing() && pane.element_count > 1 {
222                    let handled = if reverse {
223                        pane.prev_element()
224                    } else {
225                        pane.next_element()
226                    };
227                    
228                    if handled {
229                        return Ok((id, true));
230                    }
231                }
232            }
233        }
234        
235        let next_id = self.cycle_pane(reverse)?;
236        Ok((next_id, false))
237    }
238    
239    pub fn cycle_tab_order(&mut self, id: PaneId, new_position: usize) -> Result<()> {
240        if !self.panes.contains_key(&id) {
241            return Err(anyhow::anyhow!("Pane not registered: {:?}", id));
242        }
243        
244        if let Some(pos) = self.tab_order.iter().position(|&p| p == id) {
245            self.tab_order.remove(pos);
246        }
247        
248        let pos = new_position.min(self.tab_order.len());
249        self.tab_order.insert(pos, id);
250        
251        Ok(())
252    }
253}