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}