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 Default for TableWidgetManager {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl TableWidgetManager {
42 #[must_use]
44 pub fn new() -> Self {
45 Self {
46 position: TablePosition { row: 0, column: 0 },
47 previous_position: None,
48 viewport_manager: None,
49 render_state: RenderState::new(),
50 dataview: None,
51 scroll_offset: (0, 0),
52 }
53 }
54
55 pub fn set_dataview(&mut self, dataview: Arc<DataView>) {
57 debug!("TableWidgetManager: Setting new dataview");
58 self.dataview = Some(dataview.clone());
59
60 if let Some(ref mut vm) = self.viewport_manager {
62 vm.set_dataview(dataview);
63 } else {
64 self.viewport_manager = Some(ViewportManager::new(dataview));
65 }
66
67 self.render_state.on_data_change();
68 }
69
70 pub fn navigate_to(&mut self, row: usize, column: usize) {
72 let old_pos = self.position;
73 info!(
74 "TableWidgetManager: Navigate from ({}, {}) to ({}, {})",
75 old_pos.row, old_pos.column, row, column
76 );
77
78 if self.position.row != row || self.position.column != column {
80 self.previous_position = Some(self.position);
81 self.position = TablePosition { row, column };
82
83 info!("TableWidgetManager: Position changed, marking dirty for re-render");
84
85 if let Some(ref mut vm) = self.viewport_manager {
87 vm.set_crosshair(row, column);
88 info!(
89 "TableWidgetManager: Updated ViewportManager crosshair to ({}, {})",
90 row, column
91 );
92
93 let viewport_height = 79; let viewport_width = 100; let new_row_offset = if row < self.scroll_offset.0 {
99 info!(
100 "TableWidgetManager: Row {} is above viewport, scrolling up",
101 row
102 );
103 row } else if row >= self.scroll_offset.0 + viewport_height {
105 let centered = row.saturating_sub(viewport_height / 2);
106 info!(
107 "TableWidgetManager: Row {} is below viewport, centering at {}",
108 row, centered
109 );
110 centered } else {
112 trace!(
113 "TableWidgetManager: Row {} is visible in current viewport",
114 row
115 );
116 self.scroll_offset.0 };
118
119 if new_row_offset != self.scroll_offset.0 {
120 info!(
121 "TableWidgetManager: Changing scroll offset from {} to {}",
122 self.scroll_offset.0, new_row_offset
123 );
124 self.scroll_offset.0 = new_row_offset;
125 vm.set_viewport(
126 new_row_offset,
127 self.scroll_offset.1,
128 viewport_width as u16,
129 viewport_height as u16,
130 );
131 }
132 }
133
134 self.render_state.on_navigation_change();
136 info!("TableWidgetManager: State marked dirty, will trigger re-render");
137 } else {
138 trace!("TableWidgetManager: Position unchanged, no re-render needed");
139 }
140 }
141
142 pub fn move_cursor(&mut self, row_delta: isize, col_delta: isize) {
144 let new_row = (self.position.row as isize + row_delta).max(0) as usize;
145 let new_col = (self.position.column as isize + col_delta).max(0) as usize;
146
147 if let Some(ref dv) = self.dataview {
149 let max_row = dv.row_count().saturating_sub(1);
150 let max_col = dv.column_count().saturating_sub(1);
151 let clamped_row = new_row.min(max_row);
152 let clamped_col = new_col.min(max_col);
153
154 self.navigate_to(clamped_row, clamped_col);
155 }
156 }
157
158 pub fn navigate_to_search_match(&mut self, row: usize, column: usize) {
160 info!(
161 "TableWidgetManager: Navigate to search match at ({}, {})",
162 row, column
163 );
164
165 self.navigate_to(row, column);
167 self.render_state.on_search_update();
168 }
169
170 #[must_use]
172 pub fn needs_render(&self) -> bool {
173 self.render_state.needs_render()
174 }
175
176 pub fn rendered(&mut self) {
178 self.render_state.rendered();
179 self.previous_position = None;
181 }
182
183 #[must_use]
185 pub fn position(&self) -> TablePosition {
186 self.position
187 }
188
189 #[must_use]
191 pub fn previous_position(&self) -> Option<TablePosition> {
192 self.previous_position
193 }
194
195 pub fn force_render(&mut self) {
197 debug!("TableWidgetManager: Forcing render");
198 self.render_state.force_render();
199 }
200
201 pub fn set_high_frequency_mode(&mut self, enabled: bool) {
203 self.render_state.set_high_frequency_mode(enabled);
204 }
205
206 #[must_use]
208 pub fn viewport_manager(&self) -> Option<&ViewportManager> {
209 self.viewport_manager.as_ref()
210 }
211
212 pub fn viewport_manager_mut(&mut self) -> Option<&mut ViewportManager> {
214 self.viewport_manager.as_mut()
215 }
216
217 pub fn on_debounced_search(&mut self, row: usize, column: usize) {
219 info!(
220 "TableWidgetManager: Debounced search navigating to ({}, {})",
221 row, column
222 );
223 self.navigate_to(row, column);
224 self.render_state.on_debounced_action();
225 }
226
227 #[must_use]
229 pub fn render_state(&self) -> &RenderState {
230 &self.render_state
231 }
232
233 pub fn set_scroll_offset(&mut self, row_offset: usize, col_offset: usize) {
235 if self.scroll_offset != (row_offset, col_offset) {
236 debug!(
237 "TableWidgetManager: Scroll offset changed to ({}, {})",
238 row_offset, col_offset
239 );
240 self.scroll_offset = (row_offset, col_offset);
241 self.render_state.on_navigation_change();
242 }
243 }
244
245 #[must_use]
247 pub fn scroll_offset(&self) -> (usize, usize) {
248 self.scroll_offset
249 }
250
251 pub fn check_and_render<F>(&mut self, mut render_fn: F) -> bool
254 where
255 F: FnMut(&TablePosition, &RenderState),
256 {
257 if self.needs_render() {
258 info!("═══════════════════════════════════════════════════════");
259 info!("TableWidgetManager: RENDERING TABLE");
260 info!(
261 " Crosshair position: ({}, {})",
262 self.position.row, self.position.column
263 );
264 info!(
265 " Scroll offset: ({}, {})",
266 self.scroll_offset.0, self.scroll_offset.1
267 );
268 info!(" Render reason: {:?}", self.render_state.dirty_reason());
269 if let Some(prev) = self.previous_position {
270 info!(" Previous position: ({}, {})", prev.row, prev.column);
271 }
272 info!("═══════════════════════════════════════════════════════");
273
274 render_fn(&self.position, &self.render_state);
276
277 self.rendered();
279
280 info!("TableWidgetManager: Render complete");
281 true
282 } else {
283 trace!("TableWidgetManager: No render needed (not dirty or throttled)");
284 false
285 }
286 }
287}