sql_cli/ui/traits/
navigation.rs

1use crate::app_state_container::AppStateContainer;
2use crate::buffer::{AppMode, BufferAPI};
3use crate::ui::viewport_manager::{RowNavigationResult, ViewportManager};
4use std::cell::RefCell;
5// Arc import removed - no longer needed
6
7/// Trait that provides navigation behavior for TUI components
8/// This extracts navigation methods from `EnhancedTui` to reduce coupling
9pub trait NavigationBehavior {
10    // Required methods - these provide access to TUI internals
11    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>>;
12    fn buffer_mut(&mut self) -> &mut dyn BufferAPI;
13    fn buffer(&self) -> &dyn BufferAPI;
14    fn state_container(&self) -> &AppStateContainer;
15    fn state_container_mut(&mut self) -> &mut AppStateContainer; // Added for mutable access
16    fn get_row_count(&self) -> usize;
17
18    // Mode switching method that needs to be implemented by EnhancedTui to handle shadow_state
19    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str);
20
21    // Helper method that stays in the trait
22    fn apply_row_navigation_result(&mut self, result: RowNavigationResult) {
23        // Use centralized sync method
24        self.sync_row_state(result.row_position);
25
26        // Update scroll offset if viewport changed
27        if result.viewport_changed {
28            let mut offset = self.buffer().get_scroll_offset();
29            offset.0 = result.row_scroll_offset;
30            self.buffer_mut().set_scroll_offset(offset);
31            self.state_container().navigation_mut().scroll_offset.0 = result.row_scroll_offset;
32        }
33    }
34
35    /// Centralized method to sync row state across all components
36    /// This ensures Buffer, `AppStateContainer`, and any other row tracking stays in sync
37    fn sync_row_state(&mut self, row: usize) {
38        // 1. Update Buffer's selected row
39        self.buffer_mut().set_selected_row(Some(row));
40
41        // 2. Update AppStateContainer navigation state
42        self.state_container().navigation_mut().selected_row = row;
43
44        // 3. Also update via set_table_selected_row for consistency
45        // This ensures any internal bookkeeping in AppStateContainer is maintained
46        self.state_container().set_table_selected_row(Some(row));
47    }
48
49    // ========== Row Navigation Methods ==========
50
51    fn next_row(&mut self) {
52        let nav_result = {
53            let mut viewport_borrow = self.viewport_manager().borrow_mut();
54            viewport_borrow
55                .as_mut()
56                .map(crate::ui::viewport_manager::ViewportManager::navigate_row_down)
57        };
58
59        if let Some(nav_result) = nav_result {
60            self.apply_row_navigation_result(nav_result);
61        }
62    }
63
64    fn previous_row(&mut self) {
65        let nav_result = {
66            let mut viewport_borrow = self.viewport_manager().borrow_mut();
67            viewport_borrow
68                .as_mut()
69                .map(crate::ui::viewport_manager::ViewportManager::navigate_row_up)
70        };
71
72        if let Some(nav_result) = nav_result {
73            self.apply_row_navigation_result(nav_result);
74        }
75    }
76
77    fn goto_first_row(&mut self) {
78        let total_rows = self.get_row_count();
79        if total_rows > 0 {
80            let nav_result = {
81                let mut viewport_borrow = self.viewport_manager().borrow_mut();
82                viewport_borrow
83                    .as_mut()
84                    .map(|vm| vm.navigate_to_first_row(total_rows))
85            };
86
87            if let Some(nav_result) = nav_result {
88                self.apply_row_navigation_result(nav_result);
89            }
90        }
91    }
92
93    fn goto_last_row(&mut self) {
94        let total_rows = self.get_row_count();
95        if total_rows > 0 {
96            let nav_result = {
97                let mut viewport_borrow = self.viewport_manager().borrow_mut();
98                viewport_borrow
99                    .as_mut()
100                    .map(|vm| vm.navigate_to_last_row(total_rows))
101            };
102
103            if let Some(nav_result) = nav_result {
104                self.apply_row_navigation_result(nav_result);
105            }
106        }
107    }
108
109    fn page_down(&mut self) {
110        let nav_result = {
111            let mut viewport_borrow = self.viewport_manager().borrow_mut();
112            viewport_borrow
113                .as_mut()
114                .map(crate::ui::viewport_manager::ViewportManager::page_down)
115        };
116
117        if let Some(nav_result) = nav_result {
118            self.apply_row_navigation_result(nav_result);
119        }
120    }
121
122    fn page_up(&mut self) {
123        let nav_result = {
124            let mut viewport_borrow = self.viewport_manager().borrow_mut();
125            viewport_borrow
126                .as_mut()
127                .map(crate::ui::viewport_manager::ViewportManager::page_up)
128        };
129
130        if let Some(nav_result) = nav_result {
131            self.apply_row_navigation_result(nav_result);
132        }
133    }
134
135    fn half_page_down(&mut self) {
136        let nav_result = {
137            let mut viewport_borrow = self.viewport_manager().borrow_mut();
138            viewport_borrow
139                .as_mut()
140                .map(crate::ui::viewport_manager::ViewportManager::half_page_down)
141        };
142
143        if let Some(nav_result) = nav_result {
144            self.apply_row_navigation_result(nav_result);
145        }
146    }
147
148    fn half_page_up(&mut self) {
149        let nav_result = {
150            let mut viewport_borrow = self.viewport_manager().borrow_mut();
151            viewport_borrow
152                .as_mut()
153                .map(crate::ui::viewport_manager::ViewportManager::half_page_up)
154        };
155
156        if let Some(nav_result) = nav_result {
157            self.apply_row_navigation_result(nav_result);
158        }
159    }
160
161    fn goto_line(&mut self, line_number: usize) {
162        let total_rows = self.get_row_count();
163        if line_number > 0 && line_number <= total_rows {
164            let target_row = line_number - 1; // Convert to 0-indexed
165            let nav_result = {
166                let mut viewport_borrow = self.viewport_manager().borrow_mut();
167                viewport_borrow.as_mut().map(|vm| vm.goto_line(target_row))
168            };
169
170            if let Some(nav_result) = nav_result {
171                self.apply_row_navigation_result(nav_result);
172                self.state_container_mut()
173                    .set_status_message(format!("Jumped to row {line_number} (centered)"));
174            }
175        } else {
176            self.state_container_mut().set_status_message(format!(
177                "Row {line_number} out of range (max: {total_rows})"
178            ));
179        }
180    }
181
182    /// Navigate to the top of the current viewport
183    fn goto_viewport_top(&mut self) {
184        let nav_result = {
185            let mut viewport_borrow = self.viewport_manager().borrow_mut();
186            viewport_borrow
187                .as_mut()
188                .map(crate::ui::viewport_manager::ViewportManager::navigate_to_viewport_top)
189        };
190
191        if let Some(nav_result) = nav_result {
192            self.apply_row_navigation_result(nav_result);
193        }
194    }
195
196    /// Navigate to the middle of the current viewport
197    fn goto_viewport_middle(&mut self) {
198        let nav_result = {
199            let mut viewport_borrow = self.viewport_manager().borrow_mut();
200            viewport_borrow
201                .as_mut()
202                .map(crate::ui::viewport_manager::ViewportManager::navigate_to_viewport_middle)
203        };
204
205        if let Some(nav_result) = nav_result {
206            self.apply_row_navigation_result(nav_result);
207        }
208    }
209
210    /// Navigate to the bottom of the current viewport
211    fn goto_viewport_bottom(&mut self) {
212        let nav_result = {
213            let mut viewport_borrow = self.viewport_manager().borrow_mut();
214            viewport_borrow
215                .as_mut()
216                .map(crate::ui::viewport_manager::ViewportManager::navigate_to_viewport_bottom)
217        };
218
219        if let Some(nav_result) = nav_result {
220            self.apply_row_navigation_result(nav_result);
221        }
222    }
223
224    /// Complete jump-to-row operation (called on Enter key)
225    fn complete_jump_to_row(&mut self, input: &str) {
226        if let Ok(row_num) = input.parse::<usize>() {
227            self.goto_line(row_num);
228        } else {
229            self.state_container_mut()
230                .set_status_message("Invalid row number".to_string());
231        }
232
233        // Use proper mode synchronization that updates both buffer and shadow_state
234        self.set_mode_with_sync(AppMode::Results, "jump_to_row_completed");
235
236        // Clear jump-to-row state
237        let jump_state = self.state_container_mut().jump_to_row_mut();
238        jump_state.input.clear();
239        jump_state.is_active = false;
240    }
241}