refactor(tables): Add tabToNextCell/tabToPrevCell to both AdvancedTable and VirtualAdvancedTable
Norma #6: Abstract to library level - Tab navigation with wrap now in library, not application - AdvancedTableState.tabToNextCell/tabToPrevCell for in-memory data - VirtualAdvancedTableState.tabToNextCell/tabToPrevCell for paginated data - DRY: same logic available in both table widgets
This commit is contained in:
parent
702c33c13a
commit
c2f0fbb19d
2 changed files with 160 additions and 0 deletions
|
|
@ -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
|
// Internal Helpers
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
|
||||||
|
|
@ -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
|
/// Obtiene la fila global seleccionada
|
||||||
pub fn getSelectedRow(self: *const Self) ?usize {
|
pub fn getSelectedRow(self: *const Self) ?usize {
|
||||||
if (self.selected_id == null) return null;
|
if (self.selected_id == null) return null;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue