use crate::data::data_view::DataView;
use crate::ui::rendering::render_state::RenderState;
use crate::ui::viewport_manager::ViewportManager;
use std::sync::Arc;
use tracing::{debug, info, trace};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TablePosition {
pub row: usize,
pub column: usize,
}
pub struct TableWidgetManager {
position: TablePosition,
previous_position: Option<TablePosition>,
viewport_manager: Option<ViewportManager>,
render_state: RenderState,
dataview: Option<Arc<DataView>>,
scroll_offset: (usize, usize),
}
impl Default for TableWidgetManager {
fn default() -> Self {
Self::new()
}
}
impl TableWidgetManager {
#[must_use]
pub fn new() -> Self {
Self {
position: TablePosition { row: 0, column: 0 },
previous_position: None,
viewport_manager: None,
render_state: RenderState::new(),
dataview: None,
scroll_offset: (0, 0),
}
}
pub fn set_dataview(&mut self, dataview: Arc<DataView>) {
debug!("TableWidgetManager: Setting new dataview");
self.dataview = Some(dataview.clone());
if let Some(ref mut vm) = self.viewport_manager {
vm.set_dataview(dataview);
} else {
self.viewport_manager = Some(ViewportManager::new(dataview));
}
self.render_state.on_data_change();
}
pub fn navigate_to(&mut self, row: usize, column: usize) {
let old_pos = self.position;
info!(
"TableWidgetManager: Navigate from ({}, {}) to ({}, {})",
old_pos.row, old_pos.column, row, column
);
if self.position.row != row || self.position.column != column {
self.previous_position = Some(self.position);
self.position = TablePosition { row, column };
info!("TableWidgetManager: Position changed, marking dirty for re-render");
if let Some(ref mut vm) = self.viewport_manager {
vm.set_crosshair(row, column);
info!(
"TableWidgetManager: Updated ViewportManager crosshair to ({}, {})",
row, column
);
let viewport_height = 79; let viewport_width = 100;
let new_row_offset = if row < self.scroll_offset.0 {
info!(
"TableWidgetManager: Row {} is above viewport, scrolling up",
row
);
row } else if row >= self.scroll_offset.0 + viewport_height {
let centered = row.saturating_sub(viewport_height / 2);
info!(
"TableWidgetManager: Row {} is below viewport, centering at {}",
row, centered
);
centered } else {
trace!(
"TableWidgetManager: Row {} is visible in current viewport",
row
);
self.scroll_offset.0 };
if new_row_offset != self.scroll_offset.0 {
info!(
"TableWidgetManager: Changing scroll offset from {} to {}",
self.scroll_offset.0, new_row_offset
);
self.scroll_offset.0 = new_row_offset;
vm.set_viewport(
new_row_offset,
self.scroll_offset.1,
viewport_width as u16,
viewport_height as u16,
);
}
}
self.render_state.on_navigation_change();
info!("TableWidgetManager: State marked dirty, will trigger re-render");
} else {
trace!("TableWidgetManager: Position unchanged, no re-render needed");
}
}
pub fn move_cursor(&mut self, row_delta: isize, col_delta: isize) {
let new_row = (self.position.row as isize + row_delta).max(0) as usize;
let new_col = (self.position.column as isize + col_delta).max(0) as usize;
if let Some(ref dv) = self.dataview {
let max_row = dv.row_count().saturating_sub(1);
let max_col = dv.column_count().saturating_sub(1);
let clamped_row = new_row.min(max_row);
let clamped_col = new_col.min(max_col);
self.navigate_to(clamped_row, clamped_col);
}
}
pub fn navigate_to_search_match(&mut self, row: usize, column: usize) {
info!(
"TableWidgetManager: Navigate to search match at ({}, {})",
row, column
);
self.navigate_to(row, column);
self.render_state.on_search_update();
}
#[must_use]
pub fn needs_render(&self) -> bool {
self.render_state.needs_render()
}
pub fn rendered(&mut self) {
self.render_state.rendered();
self.previous_position = None;
}
#[must_use]
pub fn position(&self) -> TablePosition {
self.position
}
#[must_use]
pub fn previous_position(&self) -> Option<TablePosition> {
self.previous_position
}
pub fn force_render(&mut self) {
debug!("TableWidgetManager: Forcing render");
self.render_state.force_render();
}
pub fn set_high_frequency_mode(&mut self, enabled: bool) {
self.render_state.set_high_frequency_mode(enabled);
}
#[must_use]
pub fn viewport_manager(&self) -> Option<&ViewportManager> {
self.viewport_manager.as_ref()
}
pub fn viewport_manager_mut(&mut self) -> Option<&mut ViewportManager> {
self.viewport_manager.as_mut()
}
pub fn on_debounced_search(&mut self, row: usize, column: usize) {
info!(
"TableWidgetManager: Debounced search navigating to ({}, {})",
row, column
);
self.navigate_to(row, column);
self.render_state.on_debounced_action();
}
#[must_use]
pub fn render_state(&self) -> &RenderState {
&self.render_state
}
pub fn set_scroll_offset(&mut self, row_offset: usize, col_offset: usize) {
if self.scroll_offset != (row_offset, col_offset) {
debug!(
"TableWidgetManager: Scroll offset changed to ({}, {})",
row_offset, col_offset
);
self.scroll_offset = (row_offset, col_offset);
self.render_state.on_navigation_change();
}
}
#[must_use]
pub fn scroll_offset(&self) -> (usize, usize) {
self.scroll_offset
}
pub fn check_and_render<F>(&mut self, mut render_fn: F) -> bool
where
F: FnMut(&TablePosition, &RenderState),
{
if self.needs_render() {
info!("═══════════════════════════════════════════════════════");
info!("TableWidgetManager: RENDERING TABLE");
info!(
" Crosshair position: ({}, {})",
self.position.row, self.position.column
);
info!(
" Scroll offset: ({}, {})",
self.scroll_offset.0, self.scroll_offset.1
);
info!(" Render reason: {:?}", self.render_state.dirty_reason());
if let Some(prev) = self.previous_position {
info!(" Previous position: ({}, {})", prev.row, prev.column);
}
info!("═══════════════════════════════════════════════════════");
render_fn(&self.position, &self.render_state);
self.rendered();
info!("TableWidgetManager: Render complete");
true
} else {
trace!("TableWidgetManager: No render needed (not dirty or throttled)");
false
}
}
}