fix(virtual_table): PagedDataSource en frameAllocator

- PagedDataSource ahora se crea en ctx.frameAllocator()
- Memoria estable durante todo el frame de rendering
- Eliminado cached_paged_datasource del state (punteros auto-ref peligrosos)
- Fix secundario para estabilidad (no era causa del crash Ctrl+N)
This commit is contained in:
reugenio 2025-12-28 20:27:54 +01:00
parent b3a33ec4f3
commit 3a6398e90d
2 changed files with 48 additions and 8 deletions

View file

@ -1703,6 +1703,13 @@ pub fn planTabNavigation(
const current_row_id = buffer.row_id;
const new_row_id = row_id_getter.getRowId(pos.row);
std.debug.print("[PLAN-TAB] current_row={} current_col={} -> new_row={} new_col={}\n", .{
current_row, current_col, pos.row, pos.col,
});
std.debug.print("[PLAN-TAB] buffer.row_id={} getter.getRowId({})={} has_changes={}\n", .{
current_row_id, pos.row, new_row_id, buffer.has_changes,
});
if (current_row_id != new_row_id and buffer.has_changes) {
// Cambió de fila con cambios pendientes commit
const info = buildCommitInfo(buffer, changes_out);

View file

@ -197,6 +197,11 @@ pub fn virtualAdvancedTableRect(
) VirtualAdvancedTableResult {
var result = VirtualAdvancedTableResult{};
// Debug: verificar si hay inyección al inicio del frame
if (list_state.injected_row_idx != null) {
std.debug.print("[VT-FRAME] Frame con inyección activa: idx={}\n", .{list_state.injected_row_idx.?});
}
if (bounds.isEmpty() or config.columns.len == 0) return result;
// Reset frame flags
@ -453,17 +458,27 @@ pub fn virtualAdvancedTableRect(
pub fn getRowId(self: @This(), row: usize) i64 {
// Fila inyectada siempre retorna NEW_ROW_ID
if (self.injected_idx) |inj_idx| {
if (row == inj_idx) return table_core.NEW_ROW_ID;
if (row == inj_idx) {
std.debug.print("[ROW-ID-GETTER] row={} == inj_idx={} -> NEW_ROW_ID\n", .{ row, inj_idx });
return table_core.NEW_ROW_ID;
}
// Filas después de inyección: ajustar índice hacia provider
if (row > inj_idx) {
const adjusted_row = row - 1;
if (adjusted_row >= self.total) return table_core.NEW_ROW_ID;
return self.prov.getRowId(adjusted_row) orelse table_core.NEW_ROW_ID;
const id = self.prov.getRowId(adjusted_row) orelse table_core.NEW_ROW_ID;
std.debug.print("[ROW-ID-GETTER] row={} > inj_idx={}, adjusted={} -> id={}\n", .{ row, inj_idx, adjusted_row, id });
return id;
}
}
// Ghost row está al final
if (row >= self.total) return table_core.NEW_ROW_ID;
return self.prov.getRowId(row) orelse table_core.NEW_ROW_ID;
if (row >= self.total) {
std.debug.print("[ROW-ID-GETTER] row={} >= total={} -> NEW_ROW_ID (ghost)\n", .{ row, self.total });
return table_core.NEW_ROW_ID;
}
const id = self.prov.getRowId(row) orelse table_core.NEW_ROW_ID;
std.debug.print("[ROW-ID-GETTER] row={} normal -> id={}\n", .{ row, id });
return id;
}
};
@ -512,8 +527,13 @@ pub fn virtualAdvancedTableRect(
}
// Si hay commit, establecer flags
if (plan.action == .move_with_commit) {
// IMPORTANTE: Solo hacer commit si REALMENTE cambió de fila
// Tab dentro de la misma fila NO debe triggerar commit (Excel-style)
if (plan.action == .move_with_commit and plan.new_row != current_row) {
if (plan.commit_info) |info| {
std.debug.print("[VT-TAB] Commit real: row {} -> {}, is_insert={}\n", .{
current_row, plan.new_row, info.is_insert,
});
result.row_committed = true;
result.row_commit_id = info.row_id;
result.row_commit_is_insert = info.is_insert;
@ -526,6 +546,9 @@ pub fn virtualAdvancedTableRect(
result.injection_row_idx = info.injection_index;
}
}
} else if (plan.action == .move_with_commit and plan.new_row == current_row) {
std.debug.print("[VT-TAB] Commit SUPRIMIDO: misma fila {}, manteniendo buffer\n", .{current_row});
// NO limpiar el buffer - mantener cambios pendientes
}
// Indicar al panel que debe auto-editar la nueva celda
@ -985,9 +1008,14 @@ fn drawRows(
) void {
_ = result;
// Crear PagedDataSource para acceso unificado a datos
var datasource = paged_datasource.PagedDataSource.init(list_state, config.columns, null);
const table_ds = datasource.toDataSource();
// Crear PagedDataSource en memoria estable del frame (frame arena)
// Esto evita punteros inválidos cuando el stack se destruye
const pds_ptr = ctx.frameAllocator().create(paged_datasource.PagedDataSource) catch {
std.debug.print("[VT-ERROR] No se pudo crear PagedDataSource en frame arena\n", .{});
return;
};
pds_ptr.* = paged_datasource.PagedDataSource.init(list_state, config.columns, null);
const table_ds = pds_ptr.toDataSource();
// Convertir selected_id a selected_row (índice global)
// Si hay una fila inyectada activa, esa es la seleccionada
@ -1277,6 +1305,7 @@ fn handleKeyboard(
// Ctrl+N: Inyección local (insertar fila debajo de la actual)
// =========================================================================
if (events.insert_row) {
std.debug.print("[VT-CTRL-N] insert_row event, hasInjection={}\n", .{list_state.hasInjection()});
// Solo permitir si no hay ya una inyección activa
if (!list_state.hasInjection()) {
// Calcular índice de inyección (debajo de la fila actual)
@ -1286,9 +1315,11 @@ fn handleKeyboard(
0;
const injection_idx = current_row + 1;
std.debug.print("[VT-CTRL-N] current_row={} injection_idx={}\n", .{ current_row, injection_idx });
// Iniciar inyección
list_state.startInjection(injection_idx);
std.debug.print("[VT-CTRL-N] startInjection OK\n", .{});
// Mover selección a la fila inyectada
list_state.selected_id = null; // La fila inyectada no tiene ID aún
@ -1299,8 +1330,10 @@ fn handleKeyboard(
result.selection_changed = true;
// Iniciar edición automática en primera columna
std.debug.print("[VT-CTRL-N] about to startEditing\n", .{});
list_state.cell_edit.startEditing(injection_idx, 0, "", null);
list_state.nav.active_col = 0;
std.debug.print("[VT-CTRL-N] DONE\n", .{});
}
// Nota: insert_row_requested ya NO se emite - la inyección es interna
}