From 3a6398e90d5bcd9ecc052653df2db048abe1b426 Mon Sep 17 00:00:00 2001 From: reugenio Date: Sun, 28 Dec 2025 20:27:54 +0100 Subject: [PATCH] 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) --- src/widgets/table_core.zig | 7 +++ .../virtual_advanced_table.zig | 49 ++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/widgets/table_core.zig b/src/widgets/table_core.zig index f2ffe3d..6c6d5a4 100644 --- a/src/widgets/table_core.zig +++ b/src/widgets/table_core.zig @@ -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); diff --git a/src/widgets/virtual_advanced_table/virtual_advanced_table.zig b/src/widgets/virtual_advanced_table/virtual_advanced_table.zig index 4ce11a1..86e2330 100644 --- a/src/widgets/virtual_advanced_table/virtual_advanced_table.zig +++ b/src/widgets/virtual_advanced_table/virtual_advanced_table.zig @@ -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 }