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 current_row_id = buffer.row_id;
const new_row_id = row_id_getter.getRowId(pos.row); 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) { if (current_row_id != new_row_id and buffer.has_changes) {
// Cambió de fila con cambios pendientes commit // Cambió de fila con cambios pendientes commit
const info = buildCommitInfo(buffer, changes_out); const info = buildCommitInfo(buffer, changes_out);

View file

@ -197,6 +197,11 @@ pub fn virtualAdvancedTableRect(
) VirtualAdvancedTableResult { ) VirtualAdvancedTableResult {
var result = 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; if (bounds.isEmpty() or config.columns.len == 0) return result;
// Reset frame flags // Reset frame flags
@ -453,17 +458,27 @@ pub fn virtualAdvancedTableRect(
pub fn getRowId(self: @This(), row: usize) i64 { pub fn getRowId(self: @This(), row: usize) i64 {
// Fila inyectada siempre retorna NEW_ROW_ID // Fila inyectada siempre retorna NEW_ROW_ID
if (self.injected_idx) |inj_idx| { 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 // Filas después de inyección: ajustar índice hacia provider
if (row > inj_idx) { if (row > inj_idx) {
const adjusted_row = row - 1; const adjusted_row = row - 1;
if (adjusted_row >= self.total) return table_core.NEW_ROW_ID; 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 // Ghost row está al final
if (row >= self.total) return table_core.NEW_ROW_ID; if (row >= self.total) {
return self.prov.getRowId(row) orelse table_core.NEW_ROW_ID; 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 // 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| { 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_committed = true;
result.row_commit_id = info.row_id; result.row_commit_id = info.row_id;
result.row_commit_is_insert = info.is_insert; result.row_commit_is_insert = info.is_insert;
@ -526,6 +546,9 @@ pub fn virtualAdvancedTableRect(
result.injection_row_idx = info.injection_index; 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 // Indicar al panel que debe auto-editar la nueva celda
@ -985,9 +1008,14 @@ fn drawRows(
) void { ) void {
_ = result; _ = result;
// Crear PagedDataSource para acceso unificado a datos // Crear PagedDataSource en memoria estable del frame (frame arena)
var datasource = paged_datasource.PagedDataSource.init(list_state, config.columns, null); // Esto evita punteros inválidos cuando el stack se destruye
const table_ds = datasource.toDataSource(); 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) // Convertir selected_id a selected_row (índice global)
// Si hay una fila inyectada activa, esa es la seleccionada // 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) // Ctrl+N: Inyección local (insertar fila debajo de la actual)
// ========================================================================= // =========================================================================
if (events.insert_row) { 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 // Solo permitir si no hay ya una inyección activa
if (!list_state.hasInjection()) { if (!list_state.hasInjection()) {
// Calcular índice de inyección (debajo de la fila actual) // Calcular índice de inyección (debajo de la fila actual)
@ -1286,9 +1315,11 @@ fn handleKeyboard(
0; 0;
const injection_idx = current_row + 1; const injection_idx = current_row + 1;
std.debug.print("[VT-CTRL-N] current_row={} injection_idx={}\n", .{ current_row, injection_idx });
// Iniciar inyección // Iniciar inyección
list_state.startInjection(injection_idx); list_state.startInjection(injection_idx);
std.debug.print("[VT-CTRL-N] startInjection OK\n", .{});
// Mover selección a la fila inyectada // Mover selección a la fila inyectada
list_state.selected_id = null; // La fila inyectada no tiene ID aún list_state.selected_id = null; // La fila inyectada no tiene ID aún
@ -1299,8 +1330,10 @@ fn handleKeyboard(
result.selection_changed = true; result.selection_changed = true;
// Iniciar edición automática en primera columna // 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.cell_edit.startEditing(injection_idx, 0, "", null);
list_state.nav.active_col = 0; 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 // Nota: insert_row_requested ya NO se emite - la inyección es interna
} }