feat(advanced_table): Unify Excel-style editing with VirtualAdvancedTable
- Add row_edit_buffer to AdvancedTableState - Connect edit_buffer to rendering via drawRowsWithDataSource - Use DRY planTabNavigation for Tab navigation + auto-commit - Add Excel-style result fields: row_committed, row_changes, etc. - Both widgets now share identical commit-on-row-change behavior
This commit is contained in:
parent
2a92c7530c
commit
454803fe03
2 changed files with 91 additions and 29 deletions
|
|
@ -193,6 +193,7 @@ pub fn advancedTableRect(
|
|||
.apply_state_colors = true,
|
||||
.draw_row_borders = true,
|
||||
.alternating_rows = config.alternating_rows,
|
||||
.edit_buffer = &table_state.row_edit_buffer,
|
||||
}, &cell_buffer);
|
||||
|
||||
// End clipping
|
||||
|
|
@ -704,40 +705,68 @@ fn handleKeyboard(
|
|||
}
|
||||
|
||||
// =========================================================================
|
||||
// Tab navigation (usando lógica del Core + config local)
|
||||
// Tab navigation con commit Excel-style (DRY: lógica en table_core)
|
||||
// =========================================================================
|
||||
if (events.tab_out and config.handle_tab) {
|
||||
if (events.tab_shift) {
|
||||
// Shift+Tab: move left, wrap to previous row
|
||||
if (table_state.selected_col > 0) {
|
||||
table_state.selectCell(
|
||||
@intCast(@max(0, table_state.selected_row)),
|
||||
@intCast(table_state.selected_col - 1),
|
||||
);
|
||||
result.selection_changed = true;
|
||||
} else if (table_state.selected_row > 0 and config.wrap_navigation) {
|
||||
table_state.selectCell(
|
||||
@intCast(table_state.selected_row - 1),
|
||||
col_count - 1,
|
||||
);
|
||||
result.selection_changed = true;
|
||||
// Wrapper para obtener row_id por índice (en AdvancedTable, usamos índice como ID)
|
||||
const RowIdGetter = struct {
|
||||
total: usize,
|
||||
|
||||
pub fn getRowId(self: @This(), row: usize) i64 {
|
||||
// Ghost row está al final
|
||||
if (row >= self.total) return table_core.NEW_ROW_ID;
|
||||
return @intCast(row);
|
||||
}
|
||||
} else {
|
||||
// Tab: move right, wrap to next row
|
||||
if (table_state.selected_col < @as(i32, @intCast(col_count)) - 1) {
|
||||
table_state.selectCell(
|
||||
@intCast(@max(0, table_state.selected_row)),
|
||||
@intCast(table_state.selected_col + 1),
|
||||
);
|
||||
result.selection_changed = true;
|
||||
} else if (table_state.selected_row < @as(i32, @intCast(row_count)) - 1 and config.wrap_navigation) {
|
||||
table_state.selectCell(
|
||||
@intCast(table_state.selected_row + 1),
|
||||
0,
|
||||
};
|
||||
|
||||
const getter = RowIdGetter{ .total = row_count };
|
||||
const current_row: usize = @intCast(@max(0, table_state.selected_row));
|
||||
const current_col: usize = @intCast(@max(0, table_state.selected_col));
|
||||
const forward = !events.tab_shift;
|
||||
// AdvancedTable: usar count de filas existentes (no tiene ghost row como VirtualAdvancedTable)
|
||||
const num_rows = row_count;
|
||||
|
||||
const plan = table_core.planTabNavigation(
|
||||
&table_state.row_edit_buffer,
|
||||
current_row,
|
||||
current_col,
|
||||
col_count,
|
||||
num_rows,
|
||||
forward,
|
||||
config.wrap_navigation,
|
||||
getter,
|
||||
&result.row_changes,
|
||||
);
|
||||
|
||||
// Ejecutar el plan
|
||||
switch (plan.action) {
|
||||
.move, .move_with_commit => {
|
||||
table_state.selectCell(plan.new_row, plan.new_col);
|
||||
result.selection_changed = true;
|
||||
|
||||
if (plan.action == .move_with_commit) {
|
||||
if (plan.commit_info) |info| {
|
||||
result.row_committed = true;
|
||||
result.row_commit_id = info.row_id;
|
||||
result.row_commit_is_insert = info.is_insert;
|
||||
result.row_changes_count = info.change_count;
|
||||
}
|
||||
}
|
||||
},
|
||||
.exit, .exit_with_commit => {
|
||||
result.tab_out = true;
|
||||
result.tab_shift = events.tab_shift;
|
||||
|
||||
if (plan.action == .exit_with_commit) {
|
||||
if (plan.commit_info) |info| {
|
||||
result.row_committed = true;
|
||||
result.row_commit_id = info.row_id;
|
||||
result.row_commit_is_insert = info.is_insert;
|
||||
result.row_changes_count = info.change_count;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ pub const AdvancedTableState = struct {
|
|||
/// Estado de edición embebido (Fase 2 refactor)
|
||||
cell_edit: table_core.CellEditState = .{},
|
||||
|
||||
/// Buffer de edición de fila Excel-style (acumula cambios antes de commit)
|
||||
row_edit_buffer: table_core.RowEditBuffer = .{},
|
||||
|
||||
/// Original value before editing (for revert on Escape)
|
||||
/// NOTA: Mantenemos esto porque CellValue es más rico que buffer crudo
|
||||
original_value: ?CellValue = null,
|
||||
|
|
@ -1016,6 +1019,36 @@ pub const AdvancedTableResult = struct {
|
|||
|
||||
// Focus
|
||||
clicked: bool = false,
|
||||
|
||||
// =========================================================================
|
||||
// Edición CRUD Excel-style (simétrico con VirtualAdvancedTableResult)
|
||||
// =========================================================================
|
||||
|
||||
/// Una fila fue completada (el usuario cambió de fila, tenía cambios pendientes)
|
||||
row_committed: bool = false,
|
||||
|
||||
/// ID de la fila que se hizo commit (índice en AdvancedTable)
|
||||
row_commit_id: i64 = table_core.NEW_ROW_ID,
|
||||
|
||||
/// Es un INSERT (ghost row) o UPDATE (fila existente)
|
||||
row_commit_is_insert: bool = false,
|
||||
|
||||
/// Cambios de la fila (válidos si row_committed = true)
|
||||
row_changes: [table_core.MAX_PENDING_COLUMNS]table_core.PendingCellChange = undefined,
|
||||
|
||||
/// Número de cambios en row_changes
|
||||
row_changes_count: usize = 0,
|
||||
|
||||
/// Tab presionado para salir del widget
|
||||
tab_out: bool = false,
|
||||
|
||||
/// Shift estaba presionado con Tab
|
||||
tab_shift: bool = false,
|
||||
|
||||
/// Obtiene los cambios como slice
|
||||
pub fn getRowChanges(self: *const AdvancedTableResult) []const table_core.PendingCellChange {
|
||||
return self.row_changes[0..self.row_changes_count];
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue