1use crate::data::data_view::DataView;
7use crate::ui::rendering::render_state::RenderState;
8use crate::ui::viewport_manager::ViewportManager;
9use std::sync::Arc;
10use tracing::{debug, info, trace};
11
12#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct TablePosition {
15 pub row: usize,
16 pub column: usize,
17}
18
19pub struct TableWidgetManager {
21 position: TablePosition,
23 previous_position: Option<TablePosition>,
25 viewport_manager: Option<ViewportManager>,
27 render_state: RenderState,
29 dataview: Option<Arc<DataView>>,
31 scroll_offset: (usize, usize),
33}
34
35impl TableWidgetManager {
36 pub fn new() -> Self {
38 Self {
39 position: TablePosition { row: 0, column: 0 },
40 previous_position: None,
41 viewport_manager: None,
42 render_state: RenderState::new(),
43 dataview: None,
44 scroll_offset: (0, 0),
45 }
46 }
47
48 pub fn set_dataview(&mut self, dataview: Arc<DataView>) {
50 debug!("TableWidgetManager: Setting new dataview");
51 self.dataview = Some(dataview.clone());
52
53 if let Some(ref mut vm) = self.viewport_manager {
55 vm.set_dataview(dataview);
56 } else {
57 self.viewport_manager = Some(ViewportManager::new(dataview));
58 }
59
60 self.render_state.on_data_change();
61 }
62
63 pub fn navigate_to(&mut self, row: usize, column: usize) {
65 let old_pos = self.position;
66 info!(
67 "TableWidgetManager: Navigate from ({}, {}) to ({}, {})",
68 old_pos.row, old_pos.column, row, column
69 );
70
71 if self.position.row != row || self.position.column != column {
73 self.previous_position = Some(self.position);
74 self.position = TablePosition { row, column };
75
76 info!("TableWidgetManager: Position changed, marking dirty for re-render");
77
78 if let Some(ref mut vm) = self.viewport_manager {
80 vm.set_crosshair(row, column);
81 info!(
82 "TableWidgetManager: Updated ViewportManager crosshair to ({}, {})",
83 row, column
84 );
85
86 let viewport_height = 79; let viewport_width = 100; let new_row_offset = if row < self.scroll_offset.0 {
92 info!(
93 "TableWidgetManager: Row {} is above viewport, scrolling up",
94 row
95 );
96 row } else if row >= self.scroll_offset.0 + viewport_height {
98 let centered = row.saturating_sub(viewport_height / 2);
99 info!(
100 "TableWidgetManager: Row {} is below viewport, centering at {}",
101 row, centered
102 );
103 centered } else {
105 trace!(
106 "TableWidgetManager: Row {} is visible in current viewport",
107 row
108 );
109 self.scroll_offset.0 };
111
112 if new_row_offset != self.scroll_offset.0 {
113 info!(
114 "TableWidgetManager: Changing scroll offset from {} to {}",
115 self.scroll_offset.0, new_row_offset
116 );
117 self.scroll_offset.0 = new_row_offset;
118 vm.set_viewport(
119 new_row_offset,
120 self.scroll_offset.1,
121 viewport_width as u16,
122 viewport_height as u16,
123 );
124 }
125 }
126
127 self.render_state.on_navigation_change();
129 info!("TableWidgetManager: State marked dirty, will trigger re-render");
130 } else {
131 trace!("TableWidgetManager: Position unchanged, no re-render needed");
132 }
133 }
134
135 pub fn move_cursor(&mut self, row_delta: isize, col_delta: isize) {
137 let new_row = (self.position.row as isize + row_delta).max(0) as usize;
138 let new_col = (self.position.column as isize + col_delta).max(0) as usize;
139
140 if let Some(ref dv) = self.dataview {
142 let max_row = dv.row_count().saturating_sub(1);
143 let max_col = dv.column_count().saturating_sub(1);
144 let clamped_row = new_row.min(max_row);
145 let clamped_col = new_col.min(max_col);
146
147 self.navigate_to(clamped_row, clamped_col);
148 }
149 }
150
151 pub fn navigate_to_search_match(&mut self, row: usize, column: usize) {
153 info!(
154 "TableWidgetManager: Navigate to search match at ({}, {})",
155 row, column
156 );
157
158 self.navigate_to(row, column);
160 self.render_state.on_search_update();
161 }
162
163 pub fn needs_render(&self) -> bool {
165 self.render_state.needs_render()
166 }
167
168 pub fn rendered(&mut self) {
170 self.render_state.rendered();
171 self.previous_position = None;
173 }
174
175 pub fn position(&self) -> TablePosition {
177 self.position
178 }
179
180 pub fn previous_position(&self) -> Option<TablePosition> {
182 self.previous_position
183 }
184
185 pub fn force_render(&mut self) {
187 debug!("TableWidgetManager: Forcing render");
188 self.render_state.force_render();
189 }
190
191 pub fn set_high_frequency_mode(&mut self, enabled: bool) {
193 self.render_state.set_high_frequency_mode(enabled);
194 }
195
196 pub fn viewport_manager(&self) -> Option<&ViewportManager> {
198 self.viewport_manager.as_ref()
199 }
200
201 pub fn viewport_manager_mut(&mut self) -> Option<&mut ViewportManager> {
203 self.viewport_manager.as_mut()
204 }
205
206 pub fn on_debounced_search(&mut self, row: usize, column: usize) {
208 info!(
209 "TableWidgetManager: Debounced search navigating to ({}, {})",
210 row, column
211 );
212 self.navigate_to(row, column);
213 self.render_state.on_debounced_action();
214 }
215
216 pub fn render_state(&self) -> &RenderState {
218 &self.render_state
219 }
220
221 pub fn set_scroll_offset(&mut self, row_offset: usize, col_offset: usize) {
223 if self.scroll_offset != (row_offset, col_offset) {
224 debug!(
225 "TableWidgetManager: Scroll offset changed to ({}, {})",
226 row_offset, col_offset
227 );
228 self.scroll_offset = (row_offset, col_offset);
229 self.render_state.on_navigation_change();
230 }
231 }
232
233 pub fn scroll_offset(&self) -> (usize, usize) {
235 self.scroll_offset
236 }
237
238 pub fn check_and_render<F>(&mut self, mut render_fn: F) -> bool
241 where
242 F: FnMut(&TablePosition, &RenderState),
243 {
244 if self.needs_render() {
245 info!("═══════════════════════════════════════════════════════");
246 info!("TableWidgetManager: RENDERING TABLE");
247 info!(
248 " Crosshair position: ({}, {})",
249 self.position.row, self.position.column
250 );
251 info!(
252 " Scroll offset: ({}, {})",
253 self.scroll_offset.0, self.scroll_offset.1
254 );
255 info!(" Render reason: {:?}", self.render_state.dirty_reason());
256 if let Some(prev) = self.previous_position {
257 info!(" Previous position: ({}, {})", prev.row, prev.column);
258 }
259 info!("═══════════════════════════════════════════════════════");
260
261 render_fn(&self.position, &self.render_state);
263
264 self.rendered();
266
267 info!("TableWidgetManager: Render complete");
268 true
269 } else {
270 trace!("TableWidgetManager: No render needed (not dirty or throttled)");
271 false
272 }
273 }
274}