diff --git a/src/widgets/advanced_table/state.zig b/src/widgets/advanced_table/state.zig index 00e69dd..68cade0 100644 --- a/src/widgets/advanced_table/state.zig +++ b/src/widgets/advanced_table/state.zig @@ -825,6 +825,90 @@ pub const AdvancedTableState = struct { }; } + // ========================================================================= + // Navegación Tab Excel-style (con wrap) + // ========================================================================= + + /// Resultado de navegación Tab + pub const TabNavigateResult = enum { + /// Navegó a otra celda dentro del widget + navigated, + /// Salió del widget (Tab en última celda o Shift+Tab en primera) + tab_out, + }; + + /// Navega a siguiente celda (Tab) + /// Si está en última columna, va a primera columna de siguiente fila. + /// Si está en última fila, hace wrap a primera fila o retorna tab_out. + pub fn tabToNextCell(self: *AdvancedTableState, num_cols: usize, wrap_to_start: bool) TabNavigateResult { + if (num_cols == 0) return .tab_out; + + const row_count = self.getRowCount(); + if (row_count == 0) return .tab_out; + + const current_col: usize = if (self.selected_col >= 0) @intCast(self.selected_col) else 0; + const current_row: usize = if (self.selected_row >= 0) @intCast(self.selected_row) else 0; + + if (current_col + 1 < num_cols) { + // Siguiente columna en misma fila + self.selected_col = @intCast(current_col + 1); + return .navigated; + } + + // Última columna: ir a primera columna de siguiente fila + self.selected_col = 0; + + if (current_row + 1 < row_count) { + // Hay siguiente fila + self.selected_row = @intCast(current_row + 1); + return .navigated; + } + + // Última fila + if (wrap_to_start) { + self.selected_row = 0; + return .navigated; + } + + return .tab_out; + } + + /// Navega a celda anterior (Shift+Tab) + /// Si está en primera columna, va a última columna de fila anterior. + /// Si está en primera fila, hace wrap a última fila o retorna tab_out. + pub fn tabToPrevCell(self: *AdvancedTableState, num_cols: usize, wrap_to_end: bool) TabNavigateResult { + if (num_cols == 0) return .tab_out; + + const row_count = self.getRowCount(); + if (row_count == 0) return .tab_out; + + const current_col: usize = if (self.selected_col >= 0) @intCast(self.selected_col) else 0; + const current_row: usize = if (self.selected_row >= 0) @intCast(self.selected_row) else 0; + + if (current_col > 0) { + // Columna anterior en misma fila + self.selected_col = @intCast(current_col - 1); + return .navigated; + } + + // Primera columna: ir a última columna de fila anterior + self.selected_col = @intCast(num_cols - 1); + + if (current_row > 0) { + // Hay fila anterior + self.selected_row = @intCast(current_row - 1); + return .navigated; + } + + // Primera fila + if (wrap_to_end) { + self.selected_row = @intCast(row_count - 1); + return .navigated; + } + + return .tab_out; + } + // ========================================================================= // Internal Helpers // ========================================================================= diff --git a/src/widgets/virtual_advanced_table/state.zig b/src/widgets/virtual_advanced_table/state.zig index 755cc80..bb09ce3 100644 --- a/src/widgets/virtual_advanced_table/state.zig +++ b/src/widgets/virtual_advanced_table/state.zig @@ -492,6 +492,82 @@ pub const VirtualAdvancedTableState = struct { } } + // ========================================================================= + // Navegación Tab Excel-style (con wrap) + // ========================================================================= + + /// Resultado de navegación Tab + pub const TabNavigateResult = enum { + /// Navegó a otra celda dentro del widget + navigated, + /// Salió del widget (Tab en última celda o Shift+Tab en primera) + tab_out, + }; + + /// Navega a siguiente celda (Tab) + /// Si está en última columna, va a primera columna de siguiente fila. + /// Si está en última fila, hace wrap a primera fila o retorna tab_out. + pub fn tabToNextCell(self: *Self, num_cols: usize, visible_rows: usize, wrap_to_start: bool) TabNavigateResult { + if (num_cols == 0) return .tab_out; + + if (self.active_col + 1 < num_cols) { + // Siguiente columna en misma fila + self.active_col += 1; + return .navigated; + } + + // Última columna: ir a primera columna de siguiente fila + self.active_col = 0; + + if (self.findSelectedInWindow()) |window_idx| { + if (window_idx + 1 < self.current_window.len) { + // Hay siguiente fila + self.moveDown(visible_rows); + return .navigated; + } + } + + // Última fila + if (wrap_to_start) { + self.goToStart(); + return .navigated; + } + + return .tab_out; + } + + /// Navega a celda anterior (Shift+Tab) + /// Si está en primera columna, va a última columna de fila anterior. + /// Si está en primera fila, hace wrap a última fila o retorna tab_out. + pub fn tabToPrevCell(self: *Self, num_cols: usize, visible_rows: usize, total_rows: usize, wrap_to_end: bool) TabNavigateResult { + if (num_cols == 0) return .tab_out; + + if (self.active_col > 0) { + // Columna anterior en misma fila + self.active_col -= 1; + return .navigated; + } + + // Primera columna: ir a última columna de fila anterior + self.active_col = num_cols - 1; + + if (self.findSelectedInWindow()) |window_idx| { + if (window_idx > 0) { + // Hay fila anterior + self.moveUp(); + return .navigated; + } + } + + // Primera fila + if (wrap_to_end) { + self.goToEnd(visible_rows, total_rows); + return .navigated; + } + + return .tab_out; + } + /// Obtiene la fila global seleccionada pub fn getSelectedRow(self: *const Self) ?usize { if (self.selected_id == null) return null;